Skip to content

Fix mutation settlement when the covering transition arrives first - #213

Open
leventov wants to merge 1 commit into
convex:mainfrom
convex-in-prod:fix/mutation-settlement-after-applied-transition
Open

leventov wants to merge 1 commit into
convex:mainfrom
convex-in-prod:fix/mutation-settlement-after-applied-transition

Conversation

@leventov

@leventov leventov commented Nov 2, 2026

Copy link
Copy Markdown

A successful mutation can remain pending after the client has already applied its write. If a transition to timestamp 10 arrives before the mutation response reporting commit timestamp 10, onResponse marks the request completed but waits for another transition to resolve its promise. The optimistic update also remains applied during that wait.

Pass the current remote query-set timestamp into RequestManager.onResponse and complete the mutation immediately when commitTimestamp <= appliedTimestamp. This uses the same read-your-writes condition as removeCompleted and the existing completion/notification path, which removes the optimistic update before promise continuations run. A snapshot behind the commit still waits for a covering transition.

The comparison deliberately uses the current connection's applied query state. maxObservedTimestamp is insufficient: mutation responses advance it before query state catches up, and it survives reconnects while the remote query set resets. There are no public API or protocol changes.

The WebSocket regression test delivers the covering transition first, then the mutation response, with no subsequent transition. It verifies the authoritative query value and reflected-mutation notification are visible before the promise continuation, and the inflight count returns to zero.

Validation: this settlement logic and regression test previously passed in the downstream SDK, alongside its focused sync suite, typecheck, lint, and package build. Checks were not rerun for this upstream-only extraction.

A covering query transition can arrive before its mutation response. The
request manager then waits for another transition even though the applied
snapshot already includes the write, retaining the pending promise and
optimistic update unnecessarily.

Complete successful responses when their commit timestamp is at or before
the current connection's applied query timestamp. Reuse the existing
completion bookkeeping and query notification path so optimistic updates
are removed before promise continuations run. Keep waiting when query state
is behind the commit.

Use the remote query set timestamp, not the maximum observed timestamp:
mutation responses advance the latter, and it survives reconnects while
query state resets. This changes no public API, wire protocol, or backend
behavior.

Carry the WebSocket regression test that verifies settlement without another
transition, optimistic cleanup and notifications before promise continuation,
and the final inflight mutation count.
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