From df3d890dcdc3dfdd789c6f3f148d02a2b32f8889 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Tue, 29 Sep 2026 15:48:52 -0400 Subject: [PATCH 1/3] Document that a restore ends subscriptions opened before it Co-Authored-By: Claude Opus 5.5 --- reference/backups/operations.md | 2 ++ release-notes/v5-lincoln/5.3.md | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/reference/backups/operations.md b/reference/backups/operations.md index 2e04b33d..d295f3bc 100644 --- a/reference/backups/operations.md +++ b/reference/backups/operations.md @@ -114,6 +114,8 @@ Restores a database in place from a managed backup. `backup_id` defaults to the Through a running server this runs as a background [job](../operations-api/operations.md#jobs): Harper closes the database across all worker threads, restores it, and reloads it. This works only when no loaded component is holding the database open — if one does, the job ends in `ERROR` (surfaced by [`get_job`](../operations-api/operations.md#get_job)) telling you to restore offline. Restoring the `system` database is rejected up front, before a job is created, as is `target_database` while the server is running. These cases require running the command from the CLI with the server stopped. See [when can a database be restored?](./overview.md#when-can-a-database-be-restored) + Subscriptions opened before a restore do not carry over to the restored data. When Harper reloads the database, each one ends, and its last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. + From the CLI with the server stopped, `target_database=` restores into a separate database instead of overwriting the source. The target must not already exist, or must be an empty directory; Harper picks the new database up on the next start. If a restore is interrupted before it completes (crash, power loss), Harper marks the database as incompletely restored and refuses to load it on the next start, logging an incomplete-restore error. Recover by rerunning `restore_backup` for the same database and `backup_id` — do not try to load or hand-repair the directory. diff --git a/release-notes/v5-lincoln/5.3.md b/release-notes/v5-lincoln/5.3.md index ea35b2c4..08fce5aa 100644 --- a/release-notes/v5-lincoln/5.3.md +++ b/release-notes/v5-lincoln/5.3.md @@ -46,6 +46,12 @@ Keys already stored are not re-checked, and a value already sealed as `enc:v1:` Each SSH key's block in `/ssh/config` now runs from its `#` line through a `# END harper ssh key ` line, with a `# BEGIN harper ssh key ` line directly under the first. `get_ssh_key`, `list_ssh_keys` and `delete_ssh_key` read those lines instead of guessing where a block ends, so a line you add outside a key's lines is never removed with the key or read as its setting. Each node adds the lines to its existing config when it starts, without changing what ssh resolves, and a node rolled back to an earlier version still reads and deletes the blocks as it did before. See [The key's block in the ssh config](/reference/v5/operations-api/operations#the-keys-block-in-the-ssh-config). +## Backups + +### Subscriptions End When Their Database Is Restored + +An online `restore_backup` now ends every subscription to the database that was opened before the restore, once Harper reloads the database. Each one's last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. Previously such a subscription could stall silently, or start receiving the restored database's writes once another subscriber attached. See [`restore_backup`](/reference/v5/backups/operations#restore_backup). + ## Security ### Route-Owned Authentication From 34e011d0f723d18df84c2002d95e4378ab445a6c Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Tue, 29 Sep 2026 17:25:58 -0400 Subject: [PATCH 2/3] Say that a restore which fails before changing anything ends subscriptions with DATABASE_CLOSING Follows HarperFast/harper#2914's review: a reopen under an unchanged generation now ends the subscriptions from before it with the retryable DatabaseClosingError. Co-Authored-By: Claude Opus 5.5 --- reference/backups/operations.md | 2 +- release-notes/v5-lincoln/5.3.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/backups/operations.md b/reference/backups/operations.md index d295f3bc..d59d2dcb 100644 --- a/reference/backups/operations.md +++ b/reference/backups/operations.md @@ -114,7 +114,7 @@ Restores a database in place from a managed backup. `backup_id` defaults to the Through a running server this runs as a background [job](../operations-api/operations.md#jobs): Harper closes the database across all worker threads, restores it, and reloads it. This works only when no loaded component is holding the database open — if one does, the job ends in `ERROR` (surfaced by [`get_job`](../operations-api/operations.md#get_job)) telling you to restore offline. Restoring the `system` database is rejected up front, before a job is created, as is `target_database` while the server is running. These cases require running the command from the CLI with the server stopped. See [when can a database be restored?](./overview.md#when-can-a-database-be-restored) - Subscriptions opened before a restore do not carry over to the restored data. When Harper reloads the database, each one ends, and its last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. + Subscriptions opened before a restore do not carry over to the restored data. When Harper reloads the database, each one ends, and its last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. A restore that fails before it changes anything reloads the database as it was; those subscriptions end instead with a retryable `DatabaseClosingError` (status code 503, code `DATABASE_CLOSING`); resubscribe to continue. From the CLI with the server stopped, `target_database=` restores into a separate database instead of overwriting the source. The target must not already exist, or must be an empty directory; Harper picks the new database up on the next start. diff --git a/release-notes/v5-lincoln/5.3.md b/release-notes/v5-lincoln/5.3.md index 08fce5aa..9a9c97d6 100644 --- a/release-notes/v5-lincoln/5.3.md +++ b/release-notes/v5-lincoln/5.3.md @@ -50,7 +50,7 @@ Each SSH key's block in `/ssh/config` now runs from its `#` line ### Subscriptions End When Their Database Is Restored -An online `restore_backup` now ends every subscription to the database that was opened before the restore, once Harper reloads the database. Each one's last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. Previously such a subscription could stall silently, or start receiving the restored database's writes once another subscriber attached. See [`restore_backup`](/reference/v5/backups/operations#restore_backup). +An online `restore_backup` now ends every subscription to the database that was opened before the restore, once Harper reloads the database. Each one's last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. A restore that fails before it changes anything reloads the database as it was, and its subscriptions end with a retryable `DatabaseClosingError` (status code 503, code `DATABASE_CLOSING`) instead. Previously such a subscription could stall silently, or start receiving the restored database's writes once another subscriber attached. See [`restore_backup`](/reference/v5/backups/operations#restore_backup). ## Security From fd54ac282711bc79452d8a2ee7ef770da8f93ebf Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Tue, 29 Sep 2026 17:42:37 -0400 Subject: [PATCH 3/3] Say the restore behavior change in plain text instead of a badge at the start of a paragraph CONTRIBUTING.md reserves VersionBadge for standalone placement after headings; the reference pages phrase a mid-section change as "As of vX". Co-Authored-By: Claude Opus 5.5 --- reference/backups/operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/backups/operations.md b/reference/backups/operations.md index d59d2dcb..e3c146dd 100644 --- a/reference/backups/operations.md +++ b/reference/backups/operations.md @@ -114,7 +114,7 @@ Restores a database in place from a managed backup. `backup_id` defaults to the Through a running server this runs as a background [job](../operations-api/operations.md#jobs): Harper closes the database across all worker threads, restores it, and reloads it. This works only when no loaded component is holding the database open — if one does, the job ends in `ERROR` (surfaced by [`get_job`](../operations-api/operations.md#get_job)) telling you to restore offline. Restoring the `system` database is rejected up front, before a job is created, as is `target_database` while the server is running. These cases require running the command from the CLI with the server stopped. See [when can a database be restored?](./overview.md#when-can-a-database-be-restored) - Subscriptions opened before a restore do not carry over to the restored data. When Harper reloads the database, each one ends, and its last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. A restore that fails before it changes anything reloads the database as it was; those subscriptions end instead with a retryable `DatabaseClosingError` (status code 503, code `DATABASE_CLOSING`); resubscribe to continue. +As of v5.3.0, subscriptions opened before a restore do not carry over to the restored data. When Harper reloads the database, each one ends, and its last message is a `DatabaseGenerationChangedError` (status code 409, code `DATABASE_GENERATION_CHANGED`). Resubscribe to resynchronize against the restored state. A restore that fails before it changes anything reloads the database as it was; those subscriptions end instead with a retryable `DatabaseClosingError` (status code 503, code `DATABASE_CLOSING`); resubscribe to continue. From the CLI with the server stopped, `target_database=` restores into a separate database instead of overwriting the source. The target must not already exist, or must be an empty directory; Harper picks the new database up on the next start.