Skip to content

fix(offline-transactions): enforce strict FIFO when retries are pending#1300

Open
KyleAMathews wants to merge 4 commits intomainfrom
fix/offline-keyscheduler-strict-fifo
Open

fix(offline-transactions): enforce strict FIFO when retries are pending#1300
KyleAMathews wants to merge 4 commits intomainfrom
fix/offline-keyscheduler-strict-fifo

Conversation

@KyleAMathews
Copy link
Collaborator

@KyleAMathews KyleAMathews commented Feb 25, 2026

Summary

Enforce strict FIFO ordering in KeyScheduler.getNextBatch so that a transaction waiting for retry delay blocks all later transactions from executing. Previously, later transactions could jump the queue, causing dependent UPDATE-before-INSERT failures.

Root Cause

getNextBatch used .find() to locate any pending transaction that was ready to run. If the oldest transaction had failed and was in a retry backoff window (nextAttemptAt in the future), .find() would skip past it and return a later transaction that was ready. This violated the FIFO contract — a newer transaction could execute before an older one that it depended on.

Approach

Replace the .find() scan with a check on only this.pendingTransactions[0] (the oldest transaction). If it's not ready, return an empty batch. If it is ready, return it.

// Before: find ANY ready transaction (could skip the first)
const readyTransaction = this.pendingTransactions.find((tx) =>
  this.isReadyToRun(tx),
)

// After: only check the FIRST transaction (strict FIFO)
const firstTransaction = this.pendingTransactions[0]!
if (!this.isReadyToRun(firstTransaction)) {
  return []
}
return [firstTransaction]

The scheduleNextRetry mechanism in TransactionExecutor ensures that when the first transaction's retry delay elapses, execution resumes automatically.

Key Invariants

  • The queue always processes in createdAt order — no transaction can execute before an older one
  • When the head transaction is in retry backoff, the entire queue waits
  • scheduleNextRetry provides the eventual recovery path after a break

Non-goals

  • No changes to the retry/backoff timing logic itself
  • No changes to TransactionExecutor or the execution loop
  • Pre-existing concerns (e.g., markStarted/markFailed not validating transaction identity) are out of scope

Verification

pnpm --filter @tanstack/offline-transactions test -- KeyScheduler.test.ts

8 tests cover: FIFO blocking during retry, delay elapse, queue advancement after completion, isRunning guard, full retry-then-success lifecycle, out-of-order scheduling, updateTransaction sort preservation, and empty queue boundary.

Files Changed

  • packages/offline-transactions/src/executor/KeyScheduler.ts — Replace .find() scan with strict first-element check; use non-null assertion after length guard
  • packages/offline-transactions/tests/KeyScheduler.test.ts — New test file with 8 tests covering the FIFO ordering contract

🤖 Generated with Claude Code

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: 9dce679

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@tanstack/offline-transactions Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 25, 2026

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@1300

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@1300

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@1300

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@1300

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@1300

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@1300

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@1300

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@1300

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@1300

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@1300

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@1300

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@1300

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@1300

commit: 9dce679

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Size Change: -2 B (0%)

Total Size: 92.6 kB

Filename Size Change
./packages/db/dist/esm/query/builder/index.js 4.09 kB -2 B (-0.05%)
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.22 kB
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.32 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.75 kB
./packages/db/dist/esm/collection/mutations.js 2.34 kB
./packages/db/dist/esm/collection/state.js 3.49 kB
./packages/db/dist/esm/collection/subscription.js 3.71 kB
./packages/db/dist/esm/collection/sync.js 2.41 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.7 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.69 kB
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 2.17 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 538 B
./packages/db/dist/esm/local-only.js 808 B
./packages/db/dist/esm/local-storage.js 2.1 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.75 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.43 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 2.23 kB
./packages/db/dist/esm/query/compiler/index.js 2.04 kB
./packages/db/dist/esm/query/compiler/joins.js 2.11 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.45 kB
./packages/db/dist/esm/query/compiler/select.js 1.09 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.55 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 2.42 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.62 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.3 kB
./packages/db/dist/esm/SortedMap.js 1.3 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils.js 924 B
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 952 B
./packages/db/dist/esm/utils/cursor.js 457 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Size Change: 0 B

Total Size: 3.7 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.34 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 559 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

KyleAMathews and others added 3 commits February 25, 2026 11:18
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant