Migrate to DB format 0.47.0-alpha.2 and register column families (#829) - #861
Conversation
The core-component registry and the operation-attempt ledger were implemented with their column families deliberately unregistered, because `StateDb::open` creates every family in `MAP_NAMES` while `migrate_data_dir` returns early for an already-compatible marker: registering either name without a format transition would have altered a compatible data directory with no version change to record it. The same transition is what the install-state fields on `Agent` and `ExternalService` need, since a value written before them cannot decode through the current table layouts. The format is still in prerelease, so this extends `migrate_0_46_to_0_47` rather than adding an alpha.1-to-alpha.2 migration beside it. The extended migration opens a pinned 39-name list with `create_missing_column_families`, which creates whichever of the three new families is absent, and then rewrites every agent and external service that predates the install-state fields, leaving a row already carrying them byte-identical and failing on one that matches neither layout. Intermediate migrations now open the families the database physically holds rather than a static list. `VERSION` is written only after the whole chain succeeds, so a retry can meet 36 legacy families, all 37 of alpha.1, or those plus either or both of the new ones, and only the physical set opens all of them. Closes #829 Part of #831
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #861 +/- ##
==========================================
+ Coverage 84.26% 84.50% +0.24%
==========================================
Files 92 92
Lines 36331 36927 +596
==========================================
+ Hits 30613 31207 +594
- Misses 5718 5720 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
[Reviewer Round 1] No findings. The alpha compatibility range and single extended migration correctly cover both 0.46.x and alpha.1 sources, while the pinned 39-family list with missing-family creation makes retries safe without coupling this historical migration to live table constants (src/migration.rs:219-279, src/migration.rs:551-598). Agent and external-service values are decoded with the same strict varint options as table storage, probe the current layout before converting the old one, preserve current bytes, and include the key plus both decoder errors when neither schema matches (src/migration.rs:282-340). The migration-only layouts retain the exact field order and defaults required by the issue (src/migration/migration_structures.rs:113-208). The tests meaningfully exercise alpha.1/default conversion, partial-column-family retry states, current bytes with an unknown lifecycle index, corrupt values, layout drift, and the states.db write boundary. PR hygiene is also complete: it closes #829 and includes a test plan. |
|
[Review Verdict Round 1: APPROVED] |
Summary
Moves the database format from
0.47.0-alpha.1to0.47.0-alpha.2, registers the two column families the core-component registry and the operation-attempt ledger were already written against, and converts stored agent and external-service values to the layout carrying install state.Following the documented alpha-to-alpha convention, this extends the existing
migrate_0_46_to_0_47entry rather than adding an alpha.1-to-alpha.2 one: its requirement becomes>=0.46.0,<0.47.0-alpha.2and its target0.47.0-alpha.2, so one migration accepts every supported 0.46.x and alpha.1 source. Its old list-and-create_cfbody is replaced by a single open of the pinned 39-nameMAP_NAMES_V0_47_ALPHA_2literal withcreate_missing_column_families(true), which idempotently creates whichever of customer deletion jobs, core components, and operation attempts is absent. That list is a literal-string snapshot rather than liveMAP_NAMES, so a later rename or format bump cannot change what this historical migration creates.CORE_COMPONENTSandOPERATION_ATTEMPTSjoinMAP_NAMES(37 → 39), withStateDbopeners and publicStore::core_component_map()/Store::operation_attempt_map()accessors. The now-false registration-deferment comments and#[allow(dead_code)]annotations are gone, and both table test helpers openMAP_NAMESdirectly instead of pushing a duplicate name. The new tables start empty; nothing is backfilled.Values in the agents and external services families are migrated in place, keys and existing fields preserved, with
installed_version = None,installed_commit = None,lifecycle = 0, andbound_addrs = []for rows that lack them. Four migration-only layouts are pinned inmigration_structures.rs— old and current for each record type — reusing the live component types, withlifecyclestored asu8so an unrecognized index still reads back. Values are decoded and encoded withbincode::DefaultOptions, the varint encodingsrc/tables.rsuses, not the fixint event helpers. The current layout is probed first, so a current-shaped row keeps its exact stored bytes and only an old-shaped row is rewritten; a row matching neither layout aborts the migration reporting the raw key and both decoding errors, asmigrate_event_country_codesdoes. Rewrites are batched atEVENT_MIGRATION_BATCH_SIZEthrough awrite_migration_batchhelper shared with the country-code migration.For retry safety,
map_names_for_existing_formatnow returns an owned list derived fromexisting_map_names, so every intermediate migration opens exactly the non-default families physically present rather than choosing among static lists or inferring a layout from a count. An interrupted run may hold 36 legacy families, all 37 of alpha.1, or those plus either or both new ones, and the retry runs through to the extended migration that repairs the rest. The three historical source layouts (MAP_NAMES_V0_42,MAP_NAMES_V0_43_TO_V0_46, and the new 37-nameMAP_NAMES_V0_47_ALPHA_1) are pinned and#[cfg(test)]-gated so the non-test library carries no unused private constants; the alpha.2 target list stays in production because the migration opens it.The migration body writes only inside
data_dir/states.db, and the rollback-snapshot boundary is documented next to the migration chain. The unreleased changelog entries are amended in place rather than duplicated.Closes #829
Test plan
cargo fmt -- --check --config group_imports=StdExternalCratepassescargo clippy --bins --tests --all-features -- -D warningspassescargo test --all-featurespassesStoreaccessors usableUnknownAgentandExternalServicevalues serialized through the live tableValueencoding deserialize into the pinned current-layout structs field-for-field and re-encode to identical bytes, guarding against live-type and private-layout driftstates.dbunchanged