From e416ad179aaebafe1e36d67154887bd4f075acc1 Mon Sep 17 00:00:00 2001 From: "A.Shakhmatov" Date: Sun, 9 Aug 2026 15:14:07 +0300 Subject: [PATCH] plan: sessions, and the three measurements that corrected it Records what `lsid` support is and what it deliberately is not, so M4 inherits the decisions rather than the questions. The part worth keeping is not the design but its corrections: three of the assumptions this stage was planned on turned out to be wrong when measured against a real mongod, and one of them -- that unknown fields inside `lsid` are tolerated -- would have shipped a divergence nothing in the test corpus could have caught. Also states that the scorecard did not move, 194/97/196 either side, which was the prediction rather than a surprise: the corpus has no session entities at all. What is new is that the prediction is now checkable -- after the event assertions landed, a command wrongly refused here would show up as a changed event stream instead of silently. --- PLAN.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/PLAN.md b/PLAN.md index 92d54ab..7186101 100644 --- a/PLAN.md +++ b/PLAN.md @@ -823,16 +823,62 @@ has to be its own commit with its own re-recorded scorecard. the anchor rewritten — resuming at it returned updated documents twice, caught by draining a collection being updated underneath. - Still open in M1: the doc-level free list and sessions plumbing (`lsid` - accepted). The eight reclamation bugs above were cleared first, as - preconditions for the free list rather than as work of their own; - command-monitoring (`expectEvents`) landed next, so that the free list and - sessions are measured by an instrument that is no longer known to overstate. + Still open in M1: the doc-level free list. The eight reclamation bugs above + were cleared first, as preconditions for the free list rather than as work of + their own; command-monitoring (`expectEvents`) landed next, so that what + followed is measured by an instrument no longer known to overstate. **A prerequisite the free list must honour**, recorded here while it is still being designed: *an offset that was ever a record start must remain a record start.* `doc_bytes` reads a `u32` length prefix in place, so an offset landing mid-record after a re-split is a garbage-length read rather than a wrong answer — and an offsets cursor holds exactly such offsets. +- **M1 sessions** — *settled and implemented.* `lsid` is parsed, validated and + deliberately acted on in no way; `txnNumber`, `startTransaction` and + `autocommit` are refused; `endSessions` validates the array it discards. + Every code and message was **measured against mongod 8.3.7** with a raw + OP_MSG probe, because the driver overwrites `lsid` with its own session and a + malformed one cannot be sent through it. Three measurements contradicted the + design they were checking: + - **Unknown fields inside `lsid` are rejected** (IDLUnknownField 40415). The + design said to tolerate them, reasoning that the server tolerates unknown + fields everywhere. It does not, here. + - **`uid` is accepted** — the hash of the credentials owning the session, + which a driver sends as soon as authentication is on. Rejecting it would + have broken every command in M7. + - **An unknown command with a malformed `lsid` answers CommandNotFound**, so + command lookup precedes session validation, which is where the check sits. + + The refusals, so M4 does not reopen them: + - **No session registry.** A session here would own nothing: no transactions + to scope, no retryable writes (a driver disables them for a standalone), + and cursors that outlive their connection for reasons of their own. It + would be a mutex on the dispatch path guarding state nothing reads. M4's + transaction state machine gets to say what shape it needs. + - **`lsid` is not echoed.** Measured: mongod answers a well-formed one with + exactly `{ok: 1}`. A driver reads only `$clusterTime` and `operationTime` + back, and a standalone sends neither — correctly, since without + `operationTime` there is nothing for `afterClusterTime` to attach to and + causal consistency stays off. + - **`startSession` and `refreshSessions` are not implemented.** Both are real + mongod commands, but a driver calls neither — it generates session ids + locally — so CommandNotFound is the honest answer. Candidates for M4. + + Five divergences from mongod remain, all deliberate. Three share one cause: + mongod keeps a per-command table of which commands accept `txnNumber` at all + and answers Location50889 or OperationNotSupportedInTransaction 263 for those + that do not, *before* reaching the standalone refusal. We have no such table + and give the standalone answer uniformly, so replies are identical for every + CRUD command — everything a driver would send these fields on — and differ + only on things like `ping`, where mongod is more specific rather than + differently right. The other two are `startSession`/`refreshSessions` above, + with `commitTransaction` alongside them, reachable only by a client whose + write this server has already refused. + + **Effect on the scorecard: exactly zero, and that was the prediction.** + 194/97/196 before and after. The corpus has no `session` entity, no operation + taking a `session` argument, and no `lsid` assertion. The value here is + protocol hygiene, not a number — and after Stage 1 a wrongly-refused command + would have shown up as a changed event stream rather than silently. - **M2 aggregation**: stage/expression tiers, which spec-test files are the gate, whether $lookup/$unwind/facet make the first cut. - **M4 transactions**: snapshot isolation over mmap (COW vs undo), read