miroir-r3j.4: Verify P3.4 schema versioning implementation
The schema versioning system is already fully implemented. Verified all acceptance criteria: - First run creates schema at initial version (SQLite: schema_versions table) - Second run is no-op (pending_migrations returns empty) - Store version > binary version fails with SchemaVersionAhead error - Both SQLite and Redis share migration metadata via build_registry() Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d8d81a12a8
commit
30fe7895e4
1 changed files with 101 additions and 0 deletions
101
notes/miroir-r3j.4.md
Normal file
101
notes/miroir-r3j.4.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# P3.4 Schema Versioning Verification
|
||||
|
||||
## Summary
|
||||
|
||||
The schema versioning system described in the bead acceptance criteria is **already fully implemented** in the codebase. This document verifies that all acceptance criteria are met.
|
||||
|
||||
## Implementation Components
|
||||
|
||||
### 1. Schema Migrations Module (`crates/miroir-core/src/schema_migrations.rs`)
|
||||
|
||||
- **`Migration` struct**: Holds version number and SQL content
|
||||
- **`MigrationRegistry`**: Manages migration lifecycle
|
||||
- `max_version()`: Returns highest migration version (binary version)
|
||||
- `pending_migrations(current_version)`: Returns migrations to apply
|
||||
- `validate_version(store_version)`: Checks store ≤ binary version
|
||||
- **`build_registry()`**: Embeds migration files via `include_str!` macro
|
||||
|
||||
### 2. SQLite Backend (`crates/miroir-core/src/task_store/sqlite.rs`)
|
||||
|
||||
**Migration table**: `schema_versions (version INTEGER PRIMARY KEY, applied_at INTEGER)`
|
||||
|
||||
**`run_migration()` function**:
|
||||
1. Creates `schema_versions` table if not exists
|
||||
2. Reads `MAX(version)` as current store version (0 if empty)
|
||||
3. Validates `store_version ≤ binary_version` via `registry().validate_version()`
|
||||
4. Applies pending migrations in order
|
||||
5. Records each migration in `schema_versions`
|
||||
|
||||
### 3. Redis Backend (`crates/miroir-core/src/task_store/redis.rs`)
|
||||
|
||||
**Migration key**: `miroir:schema_version` (holds single integer)
|
||||
|
||||
**`migrate()` method**:
|
||||
1. Gets current value from `miroir:schema_version` key (None if new)
|
||||
2. Validates `store_version ≤ binary_version`
|
||||
3. Sets `miroir:schema_version = binary_version`
|
||||
4. Note: Redis doesn't need SQL migrations (no tables), but tracks version for compatibility
|
||||
|
||||
### 4. Startup Integration (`crates/miroir-core/src/task_registry.rs`)
|
||||
|
||||
Both `TaskRegistry::sqlite()` and `TaskRegistry::redis()` call `store.migrate()` immediately after opening the store.
|
||||
|
||||
## Acceptance Criteria Verification
|
||||
|
||||
| Criterion | Status | Evidence |
|
||||
|-----------|--------|----------|
|
||||
| First run creates schema at initial version | ✅ | SQLite: `run_migration()` creates `schema_versions` table, applies migrations 001-003 |
|
||||
| Second run is no-op; single SELECT | ✅ | Test `migration_is_idempotent` passes; `pending_migrations()` returns empty when current == max |
|
||||
| Store version > binary version fails | ✅ | Test `schema_version_ahead_fails` validates `SchemaVersionAhead` error |
|
||||
| Both backends share migration metadata | ✅ | Both use `schema_migrations::build_registry()` and `MigrationRegistry` |
|
||||
|
||||
## Migration Files
|
||||
|
||||
- `001_initial.sql`: Core tables (tasks, node_settings_version, aliases, sessions, idempotency_cache, jobs, leader_lease)
|
||||
- `002_feature_tables.sql`: Feature tables (canaries, canary_runs, cdc_cursors, tenant_map, rollover_policies, search_ui_config, admin_sessions)
|
||||
- `003_task_registry_fields.sql`: No-op (fields already in 001)
|
||||
|
||||
## Test Coverage
|
||||
|
||||
**schema_migrations module tests** (all pass):
|
||||
- `test_registry_max_version`
|
||||
- `test_pending_migrations`
|
||||
- `test_validate_version_success`
|
||||
- `test_validate_version_store_ahead`
|
||||
- `test_duplicate_version_panics`
|
||||
|
||||
**SQLite backend tests** (all pass):
|
||||
- `migration_is_idempotent`: Verifies second migrate() call is no-op
|
||||
- `schema_version_recorded`: Verifies MAX(version) matches binary version
|
||||
- `schema_version_ahead_fails`: Verifies error when store_version > binary_version
|
||||
- `task_survives_store_reopen`: Verifies data survives close/reopen cycle
|
||||
- `all_tables_survive_store_reopen`: Verifies all 14 tables persist
|
||||
|
||||
**Redis backend test** (code correct, requires Docker):
|
||||
- `test_redis_migrate`: Verifies migrate() succeeds (requires testcontainers Redis)
|
||||
|
||||
## Error Handling
|
||||
|
||||
**`MiroirError::SchemaVersionAhead`**:
|
||||
```rust
|
||||
SchemaVersionAhead {
|
||||
store_version: i64, // The store's migration version
|
||||
binary_version: i64, // This binary's max migration version
|
||||
}
|
||||
```
|
||||
|
||||
This error is returned when:
|
||||
- SQLite: `schema_versions.MAX(version) > binary_version`
|
||||
- Redis: `miroir:schema_version > binary_version`
|
||||
|
||||
## Version Numbering
|
||||
|
||||
- Monotonic integers: 1, 2, 3, ... (not 001, 002 as in filenames)
|
||||
- Version 1 = `001_initial.sql`
|
||||
- Version 2 = `002_feature_tables.sql`
|
||||
- Version 3 = `003_task_registry_fields.sql`
|
||||
- Binary version = `registry().max_version()`
|
||||
|
||||
## Conclusion
|
||||
|
||||
The P3.4 schema versioning system is **complete and operational**. All acceptance criteria are met, tests pass, and both SQLite and Redis backends share the same migration metadata structure via `schema_migrations::build_registry()`.
|
||||
Loading…
Add table
Reference in a new issue