71 lines
2.5 KiB
Go
71 lines
2.5 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/metazla/meta-core/internal/kv"
|
|
)
|
|
|
|
// SchemaVersion is the key-layout version this build of meta-core expects.
|
|
// Distinct from the snapshot manifest version (which is on-disk format);
|
|
// this number describes the record layout — primarily, "are roots UUIDs or
|
|
// content-hash tokens, and do reverse-index keys cid:* exist?"
|
|
//
|
|
// The Redis → SQLite move did not change the record layout (file:<rid>/<field>,
|
|
// cid:<cid>), so it did not bump this: the one-time migration copied records
|
|
// verbatim.
|
|
//
|
|
// Bump only when a layout change can't be detected from the data itself or
|
|
// healed by the write path. Each bump is a flag day in alpha: operators wipe
|
|
// and restart.
|
|
const SchemaVersion = 2
|
|
|
|
// SchemaVersionKey is the sentinel under which we record SchemaVersion.
|
|
// Checked on every meta-core boot.
|
|
const SchemaVersionKey = "meta-core:schema-version"
|
|
|
|
// EnsureSchemaVersion verifies the store matches the expected schema version.
|
|
// Called once at startup, before any other reads/writes.
|
|
//
|
|
// Behavior:
|
|
// - sentinel present and matches expected → OK
|
|
// - sentinel missing AND no roots → fresh store, stamp sentinel
|
|
// - sentinel missing AND roots exist → legacy data, refuse
|
|
// - sentinel present but mismatched → refuse, point operator at clean:all
|
|
//
|
|
// Refusal is hard-fail; the caller is expected to log.Fatalf and exit.
|
|
// The error message names the wipe remediation because in alpha there
|
|
// is no automated migration — operators bring up a clean store.
|
|
func (c *Client) EnsureSchemaVersion(expected int) error {
|
|
return c.update(func(tx *kv.Tx) error {
|
|
ctx := tx.Context()
|
|
have, ok, err := tx.Get(ctx, SchemaVersionKey)
|
|
if err != nil {
|
|
return fmt.Errorf("get schema version: %w", err)
|
|
}
|
|
if !ok {
|
|
n, err := tx.Count(ctx, rootsNS)
|
|
if err != nil {
|
|
return fmt.Errorf("count roots: %w", err)
|
|
}
|
|
if n > 0 {
|
|
return fmt.Errorf(
|
|
"the store has %d indexed roots but no %q sentinel — this is alpha "+
|
|
"data from before the UUID-rooted schema landed. Delete the "+
|
|
"database file to wipe and restart, or downgrade meta-core to a compatible build",
|
|
n, SchemaVersionKey)
|
|
}
|
|
return tx.Put(SchemaVersionKey, strconv.Itoa(expected))
|
|
}
|
|
if have == strconv.Itoa(expected) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf(
|
|
"store schema version is %q, this build of meta-core expects %d. "+
|
|
"Delete the database file to wipe and restart, or use a meta-core "+
|
|
"build that matches the existing data",
|
|
have, expected)
|
|
})
|
|
}
|