Add the core-component registry table (#826) - #856
Conversation
REView, aice-web-next, roxyd and bootroot are host-fixed
infrastructure: they are neither agents nor external services, so
neither of the existing per-node tables fits them. Register them in
their own table, keyed by (component, host), recording the build
installed there and the install or run state it is in.
Both halves of the key are variable-length strings, so the key is a
length-prefixed serialized tuple rather than a byte concatenation,
which would map ("ab", "c") and ("a", "bc") onto the same bytes. One
encoder and its exact inverse serve every read and write path.
The persisted value holds the lifecycle as a raw variant index, as the
agent and external-service tables do, so a number written by a newer
build reads back as Unknown instead of failing the whole row.
The column family is deliberately left out of MAP_NAMES: registering
it would silently add a column family to a data dir already at a
compatible version, which migrate_data_dir returns early for.
Registration and the Store accessor land with the format bump.
Closes #826
Insert is not the only path that could put a second row on a pair that already has one: update also writes, and renaming a row moves it onto a pair the caller may not have read. Pin that it is refused and that both rows survive, so the single-row invariant cannot be lost to a later change in the update path. Part of #826
Iteration was the one read path never exercised over more than one row: multi-row cases only counted the iterator, and only a single-row case decoded it. That left the path that hands `from_key_value` raw keys — rather than keys `get` had just encoded — untested for the case where the decoder has to tell several rows apart. Part of #826
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #856 +/- ##
==========================================
+ Coverage 83.67% 83.83% +0.16%
==========================================
Files 91 92 +1
Lines 35064 35419 +355
==========================================
+ Hits 29340 29695 +355
Misses 5724 5724 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
[Reviewer Round 1] No findings. The implementation matches the issue’s deliberately deferred-registration design: The PR body correctly closes #826 and includes a complete test plan; the review thread contains no implementation-created issue. |
|
[Review Verdict Round 1: APPROVED] |
Suggested squash commitTitle Body |
Adds
src/tables/core_component.rs: the registry of the platform's own host-fixed infrastructure —review,aice-web-next,roxydandbootroot— which is neither an agent nor an external service. One row per(component, host)records the installed build and its install/run state.CoreComponentwith the six fields the issue names,componentas aString(the reason is recorded in a doc comment on the type, so it is not re-argued in review). Re-exported fromsrc/tables.rsandsrc/lib.rs, and sealed insrc/types.rs.u8variant index in the privateValuestruct, calling the dependency'sLifecycle::to_stored_index/Lifecycle::from_stored_indexrather than adding a second mapping. An unrecognized stored number reads back asLifecycle::Unknowninstead of failing the whole row.Key::to_bytes/Key::from_bytespair encodes the pair as a length-prefixed serialized tuple through the crate's existingserialize/deserializehelpers, so("ab", "c")and("a", "bc")land on distinct keys. Every read and write path —get,delete,unique_key— goes through it, andKeystays private to the module.open,get,delete,update, and iteration via the blanketIterableimpl. Inserts go throughTable::insert, so a second row for an occupied pair is refused byMap::insertrather than overwriting;updateonto an occupied pair is likewise refused.CORE_COMPONENTSis defined next to the other name constants but left out ofMAP_NAMES, with a comment stating that registration and theStoreaccessor land with the format bump.opentherefore carries an#[allow(dead_code)]explaining the same.#[cfg(test)] mod testsinside the module and open their own database over atempfile::tempdir()with a rawOptimisticTransactionDB::open_cf, holding acrate::test::acquire_db_permit()guard for as long as the database is open.Closes #826
Test plan
installed_version/installed_commitbothNoneandSome, and bothinstaller_managedvalues("ab", "c")and("a", "bc")produce distinct keys, are stored simultaneously, and eachgetreturns its own row("roxyd", "host-01.example.com")and for the adversarial pairsroxydrows on three hosts coexist, each is independently gettable, and deleting one leaves the other two intactbootrootrow stores and reads backinstaller_managed = true; areviewrow reads backfalse(component, host)returns an error and leaves the stored row unchangedlifecycleround-trips through every variant9still reads back: the row yieldsLifecycle::Unknownwithcomponent,host,installed_version,installed_commitandinstaller_managedintactCHANGELOG.mdgains an entry under## [Unreleased]cargo fmt -- --check --config group_imports=StdExternalCratepassescargo clippy --bins --tests --all-features -- -D warningspassescargo test --all-featuresis greenMAP_NAMES,COMPATIBLE_VERSION_REQ, migrations,AgentandExternalServiceare untouched