-
Notifications
You must be signed in to change notification settings - Fork 1
11 Migration System.md
Quiver separates "migration" into two complementary but independent layers, with a separate stable entity identity mechanism for namespace or class-name refactors.
Format migration: old file format → current file format
Schema migration: old field structure → current field structure
Entity identity: legacy/stable TypeName → current CLR entity type
| Layer | Goal | Entry API | Typical scenario | Does not handle |
|---|---|---|---|---|
| Format Migration | Upgrade the on-disk container format | Vorcyc.Quiver.Migration.QuiverMigrator.MigrateAsync(...) |
v1/v2/v3 .vdb → v4 .vdb
|
Inferring renamed fields, guessing legacy type names |
| Schema Migration | Evolve entity field structure |
ConfigureMigration<T>() / MigrationBuilder<T> / SchemaMigrationRule
|
Added/removed fields, property renames, value transforms | File-format upgrades, unmatched TypeName values |
| Stable entity identity | Keep the file TypeName stable |
[QuiverEntity("...")] + typeMap + UnknownTypeHandling
|
Namespace/class-name refactors, sharing data across projects | Field-structure changes, binary-format upgrades |
Format migration upgrades an old .vdb container to the current v4 segmented format. The entry point lives in a separate namespace:
using Vorcyc.Quiver.Migration;
await QuiverMigrator.MigrateAsync(
sourceFile: "old-v3.vdb",
destinationFile: "data-v4.vdb",
typeMap: new Dictionary<string, Type>
{
["Old.Namespace.Document"] = typeof(Document)
},
options: new MigrateOptions { Overwrite = true });The v4 runtime QuiverDbContext.LoadAsync() no longer opens v1/v2/v3 containers directly. It throws QuiverFormatVersionException and asks the caller to run the offline migrator first.
Schema migration handles entity field evolution. It runs during or after deserialization and addresses property renames and value transforms:
public class MyDb : QuiverDbContext
{
public QuiverSet<Document> Documents { get; set; } = null!;
public MyDb() : base(new QuiverDbOptions { DatabasePath = "data.vdb" })
{
ConfigureMigration<Document>(m => m
.RenameProperty("OldTitle", "Title")
.TransformValue("Score", v => v is int i ? (double)i : v));
}
}Adding or removing fields requires no configuration: new fields use CLR defaults; removed fields are skipped while loading.
If the schema fingerprint stored in a v4 file does not match the current CLR entity declaration and no migration rule was registered for that type, the runtime throws Vorcyc.Quiver.Migration.QuiverSchemaMismatchException. Register ConfigureMigration<T>() to handle the difference, or set QuiverDbOptions.IgnoreSchemaFingerprintMismatch = true if you explicitly accept the default compatibility behavior.
For detailed rules, see Schema Migration (schema evolution details).
If an old file is both an old format and has renamed fields or value conversions, pass both:
-
typeMap: legacy fileTypeName→ current CLR type -
migrationRules: old field structure → current field structure
var rule = MigrationBuilder<Document>.Build(m => m
.RenameProperty("OldTitle", "Title"));
await QuiverMigrator.MigrateAsync(
sourceFile: "old-v3.vdb",
destinationFile: "data-v4.vdb",
typeMap: new Dictionary<string, Type>
{
["Old.Namespace.Document"] = typeof(Document)
},
migrationRules: new Dictionary<string, SchemaMigrationRule>
{
["Old.Namespace.Document"] = rule
});Key point:
migrationRuleskeys should match the legacy type-name keys used bytypeMap, because the rules are applied while decoding the old file.
[QuiverEntity("...")] solves stable entity identity. It is not format migration and not schema migration.
[QuiverEntity("Document/v1")]
public class Document
{
[QuiverKey] public string Id { get; set; } = "";
[QuiverVector(384)]
public float[] Embedding { get; set; } = [];
}By default, Quiver uses Type.FullName as the file TypeName. This means namespace or class-name changes alter the on-disk identity. With [QuiverEntity("Document/v1")], new files are written with the stable name; the runtime also accepts the old FullName as a compatibility alias.
During development or migration validation, enable strict unknown-type checks:
new QuiverDbOptions
{
DatabasePath = "data.vdb",
UnknownTypeHandling = UnknownTypeHandling.Throw
};- Back up the old file.
- If the old application used WAL, run
LoadAsync()+SaveAsync()once on a 3.2.x app to merge.walinto.vdb. - Identify the legacy
TypeNamevalues stored in the file and configuretypeMapfor each one. - If field structure changed, build the corresponding
SchemaMigrationRule. - Run
QuiverMigrator.MigrateAsync(...)to generate a v4 file. - Validate the v4 file with
QuiverDbFile.InspectAsync(path, verifyCrc: true). - Load the result with the current v4 runtime using
LoadAsync().
| # | 章节 |
|---|---|
| 01 | 版本说明 |
| 02 | 产品概述 |
| 03 | 架构概述 |
| 04 | 快速开始 |
| 05 | 核心概念 |
| 06 | 距离度量 |
| 07 | 索引类型 |
| 08 | CRUD 操作 |
| 09 | 向量搜索 |
| 10 | 持久化存储 |
| 11 | 迁移系统 |
| 11a | 模式迁移 |
| 12 | 多向量字段支持 |
| 13 | 线程安全与并发 |
| 14 | 生命周期管理 |
| 15 | 配置选项 |
| 16 | 内部实现细节 |
| 17 | 完整示例 |
| 18 | API 参考速查表 |
| 19 | 使用建议 |