Skip to content

fix: clear restored leases and release run reservations before completion - #828

Merged
crypt0rr merged 1 commit into
mainfrom
fix/restore-lease-and-run-reservation
Sep 25, 2026
Merged

crypt0rr merged 1 commit into
mainfrom
fix/restore-lease-and-run-reservation

Conversation

@crypt0rr

Copy link
Copy Markdown
Owner

What changes

  • Restore clears copied leases. The shared restore staging path (stageRestore) deletes the daemon_lease row and all job_leases rows copied from the backup. This runs in the same transaction as the pending-delivery policy, so restore --dry-run does the same in its private copy.
    • The destination's own live-daemon check (refuseActiveDaemon) and the sidecar refusals are unchanged.
    • Backup output is unchanged and never touches the live database.
  • Manual runs release their reservation before the completion callback. App.StartManagedRun releases the job's run reservation with CompareAndDelete after the run returns and before done runs.
    • A deferred CompareAndDelete still covers a panic.
    • Comparing the stored value keeps an earlier run from removing a newer run's reservation.
    • The deferred release runs before wg.Done, so StopRun waits for it.
  • The real-stack browser test retries Scan now on 409. A clickScanNow helper waits for the POST /api/v1/jobs/<id>/run response and retries only on 409, because the scan row is saved before the run releases its job lease.
  • README and SECURITY.md describe the restore lease behavior.

Why

Validation

  • Store regression tests:
    • TestRestoreClearsLeasesCopiedFromLiveDaemonBackup failed before with restore 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.
    • TestRestoreLeaseCleanupFailureLeavesDestinationUnchanged
    • TestClearRestoredLeasesSkipsMissingTablesAndReportsErrors
  • App regression tests:
    • TestStartManagedRunAcceptsNextRunFromCompletionCallback failed before with job is already running.
    • TestManagedRunReleaseKeepsNewerReservation failed before because the newer reservation was removed.
    • TestManagedRunReleasesReservationAfterPanic
  • Checks:
    • go vet ./...
    • go test -race for internal/store, internal/app and internal/web
    • pinned golangci-lint with --new-from-rev=origin/main: 0 issues
    • Go changed-line coverage: 100%
  • Frontend and browser checks:
    • npm run lint, npm run build and npm run test:coverage
    • npm run test:e2e: both real-stack tests pass on all four browser projects.
  • After rebasing onto schema 48, the build, vet and the targeted restore and run-reservation tests pass again.

Closes #821
Closes #826

@crypt0rr crypt0rr added the bug Something isn't working label Sep 25, 2026
…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
crypt0rr force-pushed the fix/restore-lease-and-run-reservation branch from 1a22d2d to 7a343cf Compare September 25, 2026 15:20
@crypt0rr
crypt0rr merged commit 7af95e1 into main Sep 25, 2026
7 checks passed
@crypt0rr
crypt0rr deleted the fix/restore-lease-and-run-reservation branch September 25, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

1 participant