Skip to content

A durable MQTT session can save a position above a local transaction that has not committed yet, and skip it on resume #3112

Description

@dawsontoth

A durable MQTT session can still save a position above a local transaction that has taken its log key but not yet committed. Once that transaction commits, its message is delivered below the saved position, and a reconnect never replays it. #3103 (#3101) bounds a saved position only by deliveries the session has already seen, so it does not reach this window. Kris raised this in review of #3103, with the idea below.

Sequence (reproduced on the merged #3103 code, 15fdc80d5, with the heldTransaction helper in unitTests/server/durableSessionResume.test.js):

  1. Transaction L takes its key (call it 50) with a write and stays open.
  2. Another transaction commits with key 100, is delivered, and is acknowledged. Nothing is unacknowledged or queued, so the checkpoint saves progress(), which is 100.
  3. L commits and is delivered. Its bound, just below 50, is below the saved position, and advancePositions() never lowers a generation-bound position.
  4. The client disconnects before acknowledging L. The resumed session replays only keys after 100, so L is silently skipped.

In the run, the saved position stayed at the higher key across the acknowledgement and the disconnect, and the resumed session received only a later write. The same window covers a late transaction already queued behind the message being sent, which the session cannot see either (the open finding noted on #3103).

Idea: bound the saved position by the certified origin-closed floor from #3085. getOriginClosedFloor(rootStore) (resources/originClosedFloor.ts:368) returns a floor below which no local transaction can still append, so a position at or below it cannot be passed by a later local commit. This is #2928's second candidate fix ("cap cursors at the oldest open local write transaction"), and #3085 now provides the write-path coverage that fix needed.

Open design questions:

Test: the sequence above, as a unit test beside the #3101 tests, should fail before the fix and pass after it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    P2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions