M1: doc-level free list, sessions, and a spec runner that no longer overstates #1
56
PLAN.md
56
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,
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user