mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-05 07:22:17 +02:00
The queue was a single JSON document rewritten in full on every mutation. That cannot survive a torn write, and a change to the record shape had no upgrade path other than refusing to load — load_from_disk fails closed on an unknown schema_version, which would have stranded every operator on a version bump. Replace it with RecordingRepository: one record per task in a B+Tree, with every write routed through BPlusTreeRecoveryJournal. A deleted or corrupt database is now rebuilt from the recovery history instead of being lost, and a future record-shape change becomes a migration step rather than a refusal to start. - backend/repository/src/recording_repository.rs owns the persisted shape and RecordingRecoverySchema V1. It knows nothing about execution: the DVR decides what a task means and hands over a set to commit atomically. - commit() diffs the candidate against what is stored, so the recovery journal stays proportional to what actually changed rather than to the size of the queue. - The partition (queued/scheduled/active/finished) is stored explicitly. Deriving it from the state would be wrong: `active` is a distinct slot and two tasks in the same state can sit in different partitions. - Repository calls run on spawn_blocking; it fsyncs both the journal and the B+Tree, so it must not run on a runtime worker thread. RecordingTaskState moves to shared, along with the label() helper and the TransferStatusDto conversion that were inherent impls in the DVR. The repository crate cannot depend on the DVR, and the state is part of the persisted contract either way. Recovery generations are written under backup_dir, so they survive the loss of the storage volume. There is no migration from recordings_state.json: an existing queue is discarded on upgrade.