Repository navigation
The collection holding a record stops holding a second copy of its key - #220
Merged
matrixarkai merged 1 commit intoOct 24, 2026
Merged
Conversation
`records` was a map from a key to a record that already held that key: two
sixteen-byte handles on one allocation, once per object. A record knows its own
key, so the collection is now ordered BY that key rather than beside it.
meta.records.struct_inline 72 -> 56
the metadata service's share 128 -> 112
the metadata line 346 -> 330, and the FIXED term 164,256 -> 164,240
-- the bucket's own record is one more entry
THE COMPILER PRICED THE OTHER ROUTE AND IT WAS THE WRONG ONE
The obvious change is to drop `MetadataRecord::key`. Done as a probe, that is 66
errors over 29 files -- but the error count is not the cost. The cost is that
about thirty-two signatures carry a record WITHOUT its key
(`validate_meta_key(&record.key)`, `shard_contains_key(&record.key)`, the proof
encoder, the footprint audit), and each would have to carry a key beside it.
Handoff envelopes would become pairs. This keeps every one of those sites
compiling and touches the collection instead.
WHAT IT COSTS IS AN ORD/EQ CONTRACT, AND THAT IS WHY THERE IS A WRAPPER
`BTreeSet` requires `Ord` and `Eq` to AGREE: if `a == b` then `a.cmp(&b)` must be
`Equal`, or the set's ordering invariant is unspecified rather than merely
wrong. The ordering has to be the key, because the key order is what
`records_range` serves prefix scans from. So equality is the key too.
`KeyedRecord` therefore says NOTHING about whether two records hold the same
value, and its record is PRIVATE so that nothing outside the collection can
compare two of them by accident. A cross-type `PartialEq<MetadataRecord>` was
deliberately NOT added: it would have made
`self.records.get(k) != Some(record)` read as "equal" for two records holding
different values, which is a drift detector that detects nothing.
`MatrixMetaService` does not derive `PartialEq`, and `MetaSnapshot` keeps its
map -- it is a transient encode and decode structure, not resident state -- so
no container's derived equality becomes a key-only comparison.
AND ONE HAZARD THAT IS NOT IN THE ORDERING
`BTreeSet::insert` KEEPS the element already there and returns `false`. A map's
`insert` overwrites. So filing a new version of a record with `insert` would
leave the OLD version in the collection and return a value that reads like
success -- with every byte figure, every count and every ordering assertion
still correct. A write would simply not take effect.
`file_record` uses `replace`, and
`filing_a_record_over_an_existing_key_replaces_it` is the guard: a second write
at one key must not add a record, the filed version must be the NEW one, and it
must agree with the object as it now stands. It also asserts what the set makes
impossible -- that a record is always found under the key it holds, through the
same allocation -- and that a rewrite keeps the allocation already there rather
than copying the characters again.
THREE HELPERS, SO THE CALL SITES KEEP THEIR SHAPE
A set yields wrappers, so every `for (key, record)` and every `get` would have
to unwrap. `record(key)`, `records_iter()` and `records_range(..)` put the map's
own shapes back, which is what kept this to about fifty call sites instead of a
hundred. `records.len()` and `records.is_empty()` did not change at all.
`get_mut` has no equivalent and should not: changing a record in place could
change where it belongs. Its one caller is a test, which now takes the record
out, changes it, and files it back.
THE LEG THAT WEIGHS IT, AND THE LEG THAT BREAKS IT DOWN, ARE DIFFERENT FILES
I moved the breakdown in `footprint_audit.rs` and remeasured: the slope was
still 128 and every detail figure was right. The total that feeds the published
line is a different function in `services/meta/footprint.rs`, and it was still
charging `size_of::<SharedText>() + size_of::<MetadataRecord>()` per entry.
Moving that took the slope to 112.
A round-trip of the breakdown against its own total cannot catch this: the
breakdown shrank and the total did not, and the sum assertion is not what runs
first. Grep the type's name in every `size_of` before believing a figure did not
move.
WHAT BECAME STRUCTURAL RATHER THAN MEASURED
`meta.records.record_key_capacity`, `key_text_duplicated` and `key_text_shared`
measured that the map key and the record's own key were one allocation. There is
no second holder to share with any more, so those three are true BY
CONSTRUCTION. They are kept -- they are what the document publishes, and
reintroducing a second holder would move them -- but a type is doing the work a
tripwire used to, and the document says so now instead of claiming a sharing
discipline that no longer exists.
`measure_what_a_one_word_text_handle_would_save` lost its fourth leg for the
same reason, the THIRD time that measurement has gone stale: it was still
adding a map-key handle to `meta.records.struct_inline`, which would have
published a saving of sixteen where eight is available. Six holders an object
now, not seven: 48 inline, net 32 or 16 without unsafe code.
THE FIGURES, EACH RE-MEASURED
1 KiB row 3.38x -> 3.36x 4 KiB and 64 KiB rows unchanged
striped 2.03x -> 2.01x replicated 3.51x -> 3.50x
metadata an object at 100 / 1,000 / 4,000 / 8,000 538 / 494 / 371 / 350
delete trade holding 1,791,633, emptied 2,260,646, 26.0% -> 26.1% larger
the share table objects 210 (63.6%), records 112 (33.9%), keys 8 (2.4%)
WHAT IS LEFT
112 today
104 the deleted flag into the version's top bit -- one bit in its own word
96 and the key's characters owned rather than shared, which is the one rung
that carries a trade: one copy per retained change, about 40,960 bytes of
fixed term, paying above 2,560 objects
MEASURED
Suite: 412 passing on the default feature set and 433 with every
feature, with the same 21 failures in both, name for name the baseline in
tools/known_failures.txt. Green on cargo build --all-targets for both feature sets with
warnings denied, and on cargo doc for both.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
recordswas a map from a key to a record that already held that key — two sixteen-bytehandles on one allocation, once per object. A record knows its own key, so the collection is now
ordered by that key rather than beside it.
meta.records.struct_inlineThe compiler priced the other route and it was the wrong one
The obvious change is to drop
MetadataRecord::key. Done as a probe: 66 errors over 29files — but the error count is not the cost. The cost is that about thirty-two signatures
carry a record without its key (
validate_meta_key(&record.key),shard_contains_key(&record.key), the proof encoder, the footprint audit), and each would haveto carry a key beside it; handoff envelopes would become pairs. This keeps every one of those
sites compiling and changes the collection instead.
What it costs is an
Ord/Eqcontract, and that is why there is a wrapperBTreeSetrequiresOrdandEqto agree: ifa == bthena.cmp(&b)must beEqual,or the set's ordering invariant is unspecified rather than merely wrong. The ordering has to be
the key, because the key order is what
records_rangeserves prefix scans from. So equality isthe key too.
KeyedRecordtherefore says nothing about whether two records hold the same value, and itsrecord is private so nothing outside the collection can compare two of them by accident. A
cross-type
PartialEq<MetadataRecord>was deliberately not added: it would makeself.records.get(k) != Some(record)read as "equal" for two records holding different values —a drift detector that detects nothing.
MatrixMetaServicedoes not derivePartialEq, andMetaSnapshotkeeps its map (it is atransient encode/decode structure, not resident state), so no container's derived equality
becomes a key-only comparison.
And one hazard that is not in the ordering
BTreeSet::insertkeeps the element already there and returnsfalse. A map'sinsertoverwrites. So filing a new version of a record with
insertwould leave the old version inthe collection and return a value that reads like success — with every byte figure, every count
and every ordering assertion still correct. A write would simply not take effect.
file_recordusesreplace, andfiling_a_record_over_an_existing_key_replaces_itis theguard: a second write at one key must not add a record, the filed version must be the new one,
and it must agree with the object as it now stands. It also asserts what the set makes
impossible — a record is always found under the key it holds, through the same allocation — and
that a rewrite keeps the allocation already there rather than copying the characters again.
Three helpers, so the call sites keep their shape
A set yields wrappers, so every
for (key, record)and everygetwould have to unwrap.record(key),records_iter()andrecords_range(..)put the map's own shapes back, which iswhat kept this to about fifty call sites instead of a hundred.
records.len()andrecords.is_empty()did not change at all.get_muthas no equivalent and should not: changing a record in place could change where itbelongs. Its one caller is a test, which now takes the record out, changes it, and files it back.
The leg that weighs it and the leg that breaks it down are different files
I moved the breakdown in
footprint_audit.rsand remeasured: the slope was still 128 andevery detail figure was right. The total that feeds the published line is a different function in
services/meta/footprint.rs, still chargingsize_of::<SharedText>() + size_of::<MetadataRecord>()per entry. Moving that took the slope to 112.
A round-trip of the breakdown against its own total cannot catch this — the breakdown shrank and
the total did not, and the sum assertion is not what runs first. Grep the type's name in every
size_ofbefore believing a figure did not move.What became structural rather than measured
meta.records.record_key_capacity,key_text_duplicatedandkey_text_sharedmeasured that themap key and the record's own key were one allocation. There is no second holder to share with any
more, so those three are true by construction. They are kept — they are what the document
publishes, and reintroducing a second holder would move them — but a type is doing the work a
tripwire used to, and the document now says that instead of claiming a sharing discipline that no
longer exists.
measure_what_a_one_word_text_handle_would_savelost its fourth leg for the same reason — thethird time that measurement has gone stale. It was still adding a map-key handle to
meta.records.struct_inline, which would have published a saving of sixteen where eight isavailable. Six holders an object now, not seven: 48 inline, net 32, or 16 without unsafe
code.
The figures, each re-measured
What is left
Measured
cargo build --all-targets --lockedand--all-features,RUSTFLAGS=-D warnings— greencargo doc --no-deps --lockedand--all-features,RUSTDOCFLAGS=-D warnings— green🤖 Generated with Claude Code