The old gate timed one read against four at once and failed when four cost more than three times one. On a ten-core machine, the same build, with reads that do share:
| Threads | Unmodified (reads share) | Exclusive-lock copy (reads queue) | Verdict at the 3× bar |
|---|---|---|---|
| 10 | 1.68–2.10× | 3.61–4.35× | correct both ways |
| 2 | 2.34–2.76× | 3.89–3.91× | passes by 8% |
| 1 | 4.02–4.06× | 3.90–4.08× | fails both — indistinguishable |
That bottom row is the whole issue. Given one usable thread, a build that shares and a build that serializes produce the same number. The gate could not tell a lock from a core count, and CI's 3.41–4.59× for the sharing build sits inside the serialized band. Its message — "they are queueing, not sharing" — was false every time it appeared.
The test's own comment blamed a loaded CI box. Load was not the cause; cores were. A confident wrong explanation had been sitting in the source, and nothing could check it until there was CI.
A read now counts itself. Two atomic adds on a call that walks a whole collection maintain how many
reads are inside a database at this instant and the most that ever were at once;
TestReadsShareTheDatabase asserts the high-water mark reached two.
Two is not a tuned threshold — it is where sharing begins. A database behind an exclusive lock cannot reach two on any number of cores, because the second read was never let in. So the measurement no longer depends on the machine being fast, only on it having more than one thread.
On the very runner that had been failing, it found four reads inside the database together, in three milliseconds. The code had been sharing the whole time.
4 connections counted for 5s; reads reached the database and ran there, and
never two of them at once: a read waited for the one before it to finish,
which is queueing, not sharing
That is the new gate against a copy of the same commit with the read lock made exclusive — reproduced independently, at two thread counts. Note what the message asserts before it accuses: that reads reached the database and ran there. A run that measured nothing cannot masquerade as a run that measured queueing.
At one usable thread, two reads cannot be inside a database at once whatever the locking allows — the overlap is absent in fact, not prevented by a lock, and nothing can tell those apart. The test says so and fails loudly rather than skipping, because this repository's CI treats a skip as red on purpose:
GOMAXPROCS is 1, and two reads cannot be inside a database at once on one
thread whatever the locking allows, so nothing here could be measured: this
test needs at least two
Worth stressing what that precondition is not: it does not need cores to be free. Two fully-loaded threads still run these reads inside each other, only slower. That is exactly the difference from the gate it replaces.
TestReadsRunTogetherAtN32 was deleted. It asserted nothing, cost every run 20,000
documents and 32 connections, and logged a ratio that is readers ÷ cores — 19.45× on CI, which reads
alarming and means nothing. A log line that needs a paragraph to stop being alarming is worse than no
log line.
This is the argument for CI, made by CI, within an hour of it existing — and a better argument than ISS-28 could make for itself. The defect sat in a gate guarding a Done ticket, on every machine with few cores, and nothing on a developer's laptop was ever going to say so.