HarperFast / HarperFast/harper
Backup repository operations require a loaded database; add a directory-only mode, a per-repository management lock, and restore pins
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
Part of #2632 (item 4). Design note §5.5, §5.6 and §7.3 in `docs/proposals/archive-restore.md` on branch `design/archive-restore`. Both restore routes (items 5 and 6) and the importer (item 7) depend on this.
## Problems
1. **Repository operations require a loaded database.** `list_backups`, `delete_backup`, `purge_backups` and `verify_backup` all call `requireRocksRootStore` (`dataLayer/rocksdbBackup.ts:308-337`, `:473`), which needs a loaded database with at least one table. A repository for a database that does not exist yet (an import into a new name) or that is blocked by a restore marker has no loaded root, so none of these work — which means recovery from a failed restore on Fabric would end in a filesystem intervention. The offline functions (`listBackupsOffline`, `verifyBackupOffline`, …) already operate on the directory alone.
2. **A restore's source can be deleted out from under it.** Between a restore request and its execution (especially across the restart route), `delete_backup` or `purge_backups keep_count` can remove the very backup being restored, or the pre-restore backup a rollback depends on. A pin check that is not atomic with deletion is check-then-act: delete reads "unpinned", pauses, the pin lands, delete resumes.
3. **The binding's lock covers only engine files.** `backups.restore` holds `.backup.lock` shared and writers hold it exclusive, but Harper's blob snapshot and manifest phases run outside it (`:318-358`, `:374-392`) — this is the minimum slice of #2031 the restore routes cannot do without.
## What to build
- The operation forms of `list`/`verify`/`delete`/`purge` take the directory-only path whenever the database is absent or blocked, behind the same super-user check.
- A **per-repository management lock** (`tryFileLock` on a file in the repository directory, exclusive) held across: installing a pin; a delete/purge from admission through its last unlink; and the blob/manifest finalization of `create_backup` and (later) `import_backup`.
- **Pins**: `delete_backup` and `purge_backups` — operation and offline CLI — refuse an id any pending, applying or terminal restore intent names, or that an in-flight online restore holds. Pins are installed inside the management lock.
- `getStorageSpaceStats()` gains the sample's `updatedAt` in its return value (needed by the importer's disk ledger; `server/storageReclamation.ts:160-185`).
## Acceptance
- `list_backups`/`verify_backup`/`delete_backup`/`purge_backups` work against a repository whose database is absent, and against one blocked by a restore marker.
- Unit: a delete paused across a pin install is refused when it resumes; `purge_backups keep_count` never removes a pinned id; concurrent `create_backup` finalization and `purge_backups` serialize.
- #2031's broader end-to-end serialization can still ship separately; this issue takes only the part above.
Contributor guide
Assessment
This issue has not been assessed yet.