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,
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