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.
This commit is contained in:
A.Shakhmatov
2026-08-09 15:14:07 +03:00
parent 3e2d0ab134
commit e416ad179a

56
PLAN.md
View File

@@ -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, the anchor rewritten — resuming at it returned updated documents twice,
caught by draining a collection being updated underneath. caught by draining a collection being updated underneath.
Still open in M1: the doc-level free list and sessions plumbing (`lsid` Still open in M1: the doc-level free list. The eight reclamation bugs above
accepted). The eight reclamation bugs above were cleared first, as were cleared first, as preconditions for the free list rather than as work of
preconditions for the free list rather than as work of their own; their own; command-monitoring (`expectEvents`) landed next, so that what
command-monitoring (`expectEvents`) landed next, so that the free list and followed is measured by an instrument no longer known to overstate.
sessions are measured by an instrument that is no longer known to overstate.
**A prerequisite the free list must honour**, recorded here while it is **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 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 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 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. 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 - **M2 aggregation**: stage/expression tiers, which spec-test files are
the gate, whether $lookup/$unwind/facet make the first cut. the gate, whether $lookup/$unwind/facet make the first cut.
- **M4 transactions**: snapshot isolation over mmap (COW vs undo), read - **M4 transactions**: snapshot isolation over mmap (COW vs undo), read