Codec migrations
Migration is a storage workflow built from the catalogue and normal current writers. It is not a second serialisation implementation.
Migration behaviours
Each descriptor declares one behaviour:
| Behaviour | Meaning |
|---|---|
None |
The external or versionless representation needs no codec migration |
Reframe |
The body is unchanged and can be streamed into the current frame |
Rewrite |
Decode the old body and call the normal current writer |
CoordinatedRewrite |
Rewrite related family files together, such as .fdt plus .fdx |
Unsupported |
Inspection is available but rebuilding from source data is required |
Changing only legacy framing to canonical Frame v1 is normally a reframe. A body-layout change requires a rewrite.
Operator workflow
Always inspect and back up before executing a migration.
using Rowles.LeanCorpus.Index.Migration;
using Rowles.LeanCorpus.Store;
using var directory = new MMapDirectory("./index");
var plan = IndexCodecMigrator.Plan(directory);
foreach (var action in plan.Actions)
Console.WriteLine($"{action.Kind}: {action.Description}");
if (!plan.CanExecute)
throw new InvalidOperationException("The index has an unsupported migration path.");
var result = IndexCodecMigrator.Migrate(directory, new IndexCodecMigrationOptions
{
DryRun = false,
StagingDirectory = "./index.migration"
});
The migrator stages a complete candidate, invokes normal current writers or reframers, validates it deeply, publishes it atomically, and records recovery state. An interrupted migration is detected on the next run.
Writer monotonicity
Every current write path must be monotonic:
legacy read -> direct write -> current
legacy read -> merge -> current
legacy read -> migration -> current
migration -> merge -> current
Tests must inspect emitted bytes and catalogue versions. A successful search result alone does not prove that a merge or migration avoided a format downgrade.
Coordinated families
Stored fields and term vectors use paired data/index files. Rewrite the pair through the normal writer, validate offset monotonicity and body bounds, then publish both. Do not independently reframe one member if its offsets refer to the other's physical layout.
Third-party coordinated families register an ICodecFamilyMigrationCoordinator on the family descriptor. The coordinator receives bounded source bodies and staging outputs keyed by stable format ID. LeanCorpus invokes it once per family and segment, closes the staging outputs, wraps every result in its descriptor's current canonical frame, and publishes the files with the rest of the migrated segment.
Compound segments require logical member inspection, staged member rewrites and repacking. Validation after repacking must see the same format IDs and versions as loose files.
Adding a version
When a body changes:
- increment the catalogue's current body-format version;
- keep the old version readable if support is promised;
- mark only the newest version writable;
- declare the old version's legacy framing and migration behaviour;
- update the reader branch and normal writer;
- add a historical fixture that was not produced by the current writer;
- test direct, flush, merge and migration output;
- add corruption and boundary cases;
- update the on-disk format documentation.
Do not increment a body-format version merely because the outer frame changed.
Backwards and downgrade policy
LeanCorpus 3.0 reads supported formats from 2.0 onwards and can migrate supported paths. Current writers emit canonical Frame v1. Older LeanCorpus releases do not understand that frame, so opening a 3.0-written index with a 2.x binary is unsupported. Keep a verified backup if rollback to an older application version may be required.