fix: clear restored leases and release run reservations before completion - #828
Merged
Merged
Conversation
…tion A backup taken from a running daemon carries that daemon's fresh daemon_lease row and its scan leases, and restore installed them unchanged. For two minutes the restored daemon refused to start with "another EdgeWatch daemon holds the database lease", and a repeat restore onto the stopped destination was refused as if a daemon were active. The shared staging path now deletes the copied daemon_lease and job_leases rows in the same transaction as the pending-delivery policy and session invalidation, so restore --dry-run applies the same step. No process can be attached to the private staged copy, so every copied lease is stale, including CLI scan leases that would otherwise keep a job busy until they expire. The destination's own live-daemon and sidecar refusals are unchanged. Backup output is unchanged. StartManagedRun released a manual run's reservation only after the done callback returned. The web callback broadcasts scan.completed, and the console then re-enables Scan now, so an immediate click got 409 job_active although the scan had finished. The reservation is now released with CompareAndDelete after the run returns and before done runs. A deferred CompareAndDelete still covers a panic, and comparing the value keeps an earlier goroutine from removing a newer run's reservation. The deferred release now runs before wg.Done, so StopRun returns only after it. The real-stack browser test clicked Scan now as soon as the previous scan row appeared. The row is saved before the run releases its job lease, so the click could still get 409. A clickScanNow helper waits for the run response and retries only on 409. Closes #821 Closes #826 # Conflicts: # README.md
crypt0rr
force-pushed
the
fix/restore-lease-and-run-reservation
branch
from
September 25, 2026 15:20
1a22d2d to
7a343cf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
stageRestore) deletes thedaemon_leaserow and alljob_leasesrows copied from the backup. This runs in the same transaction as the pending-delivery policy, sorestore --dry-rundoes the same in its private copy.refuseActiveDaemon) and the sidecar refusals are unchanged.Backupoutput is unchanged and never touches the live database.App.StartManagedRunreleases the job's run reservation withCompareAndDeleteafter the run returns and beforedoneruns.CompareAndDeletestill covers a panic.wg.Done, soStopRunwaits for it.clickScanNowhelper waits for thePOST /api/v1/jobs/<id>/runresponse and retries only on 409, because the scan row is saved before the run releases its job lease.Why
scan.completed, and the console then re-enables Scan now.TestConsoleBaselineChangeIncidentFlowon fix(store): keep live SQLite sidecars and refuse unsafe writable opens #818 and the real-stack browser test on iphone-13 on fix(scanner): keep scan coverage and probe budgets truthful #819.Validation
TestRestoreClearsLeasesCopiedFromLiveDaemonBackupfailed before withrestore refused because an EdgeWatch daemon is active (owner old-daemon …). It also checks that a restore over a destination with a live daemon is still refused.TestRestoreLeaseCleanupFailureLeavesDestinationUnchangedTestClearRestoredLeasesSkipsMissingTablesAndReportsErrorsTestStartManagedRunAcceptsNextRunFromCompletionCallbackfailed before withjob is already running.TestManagedRunReleaseKeepsNewerReservationfailed before because the newer reservation was removed.TestManagedRunReleasesReservationAfterPanicgo vet ./...go test -raceforinternal/store,internal/appandinternal/web--new-from-rev=origin/main: 0 issuesnpm run lint,npm run buildandnpm run test:coveragenpm run test:e2e: both real-stack tests pass on all four browser projects.Closes #821
Closes #826