~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/hatk

AT Protocol app framework

1
0
main
48 lines · 2.6 KB Raw History
# Auto-Schema Migration Design

**Goal:** When a developer changes a lexicon, hatk automatically migrates the database schema on startup and re-syncs affected data. No migration files, no manual commands.

## How It Works

On every startup, after `initDatabase` creates any missing tables but before setup hooks or indexing:

1. For each collection table, query the DB for its current columns
   - SQLite: `PRAGMA table_info(tableName)`
   - DuckDB: `SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ?`

2. Compare against `generateTableSchema()` output from the current lexicons

3. Emit changes:
   - **New column in lexicon** → `ALTER TABLE ADD COLUMN name TYPE [DEFAULT NULL]`
   - **Column removed from lexicon** → `ALTER TABLE DROP COLUMN name`
   - **Column type changed** → drop column + re-add with new type
   - **New child/union table** → already handled by `CREATE TABLE IF NOT EXISTS`
   - **Child/union table removed** → `DROP TABLE IF EXISTS`

4. Apply same logic to child tables and union branch tables

5. Log every change:
   ```
   [migration] added column "rating" INTEGER to "fm.teal.alpha.feed.play"
   [migration] dropped column "old_field" from "fm.teal.alpha.feed.play"
   ```

6. No automatic re-sync for column changes — existing data is already indexed, and new fields won't exist in old network records. But if any collection table is **empty** (newly added collection), mark all repos as `pending` so the backfill cycle picks up records for the new collection.

## What This Means for Developers

- Change a lexicon field → restart dev server → schema updates automatically → data re-syncs
- Add a new collection → restart → table created → backfill picks it up
- Remove a collection → restart → warning logged, run `hatk destroy collection` to clean up
- No migration files, no migration history, no `hatk migrate` command

## Edge Cases

- **SQLite ALTER TABLE limitations**: SQLite doesn't support `DROP COLUMN` before 3.35.0. `better-sqlite3` bundles SQLite 3.45+, so this is fine.
- **Indexes**: If a ref column is added, the corresponding index is created. If removed, the index is dropped with the column.
- **FTS**: FTS indexes are rebuilt on every startup anyway, so schema changes are automatically reflected.
- **Data loss on column drop/type change**: Acceptable because data comes from the AT Protocol network and is recoverable via backfill. Log clearly so developers understand what happened.

## Implementation Location

Add a `migrateSchema()` function in `database/db.ts` called from `initDatabase` after DDL execution. It receives the `TableSchema[]` and compares against the live database state.