Migrations

Redis has no DDL, so Familia migrations operate on live data — renaming keys, reshaping hash fields, backfilling values, adjusting TTLs. The migration system provides idempotent execution, dry-run support, dependency ordering, and a Redis-backed registry that tracks what has run.

Architecture

Base ← Model    (SCAN + per-record callback)
     ← Pipeline (SCAN + batched Redis pipelining)

Base gives you raw redis access for key-level operations. Model iterates Horreum objects through process_record(obj, key). Pipeline adds should_process? / build_update_fields for bulk HSET-style updates through Redis pipelines.

All three share the same lifecycle: preparemigration_needed?migrate (or process_record / pipeline dispatch) → optional down for rollback.

Registry

Applied state lives in Redis, not in files or a relational table:

Key Type Content
{prefix}:applied Sorted Set migration_id scored by timestamp
{prefix}:metadata Hash migration_id → JSON (duration, keys scanned/modified, reversibility)
{prefix}:schema Hash model_name → SHA256 digest of fields+types
{prefix}:backup:{id} Hash (TTL) field-level rollback data

Default prefix: familia:migrations. The registry answers applied?, pending, status, schema_changed?, and schema_drift queries.

Execution model

Runner resolves dependencies via topological sort (Kahn's algorithm), then runs pending migrations in order. Each migration is instantiated, prepared, checked with migration_needed?, and executed. The registry is updated only on non-dry-run success.

Rollback validates three preconditions: the migration is applied, no dependents are applied, and the migration implements down.

Dry-run control

for_realsies_this_time? { ... } gates destructive operations. In dry-run mode the block is skipped and the registry is not updated. dry_run_only? { ... } provides the inverse gate for preview-only logging.

Lua scripts

Familia::Migration::Script provides atomic Redis operations for common migration patterns: rename_field, copy_field, delete_field, rename_key_preserve_ttl, backup_and_modify_field. Custom scripts can be registered and executed through the same interface.

Source files

lib/familia/migration.rb and lib/familia/migration/. Each file's first line states its purpose.