R-1256: four restore edges (review round 2 HUNT-1 H4/H5/H6/H8) — each test seen failing first
gates / gates (push) Successful in 31s

H4: a failed definition write in the off-site restore writes the live definition back before the start;
    a failed write-back holds the app (restore_mixed).
H5: a put-back whose stop fails pours nothing, keeps the copy and holds the app (off-site, local, power-cut);
    a failed definition write-back keeps the copy (D20); a failed 'volume rm -f' fails that volume (no merge).
H6: TryBeginRestoreOp/RestoreBusy: check every operation and record the restore in one step under m.mu;
    every web handler and the kept loads begin through it; install-time kept load asks about the mirror too.
H8: SweepUnusedRestoreCopies every 6 h: removes a copy only with no hold, no marker naming it, no restore
    running, and older than 14 days.

Commit only; rides the next controller release.
This commit is contained in:
admin committed 2026-10-28 22:24:16 +01:00
1 parent ed3927f03a
commit 342a394bff
20 files changed
+860 -50

No files matched your search

+20 -5
View File
@@ -450,7 +450,10 @@ func (s *Server) offboxRestoreHandler(w http.ResponseWriter, r *http.Request) {
return
}
full := mode == "full"
s.backupMgr.BeginRestoreOp("offbox-restore", app)
if msg, ok := s.beginRestoreOp("offbox-restore", app); !ok {
offboxRedirectTo(w, r, restoreWizardPath(app), msg, true)
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()
@@ -538,7 +541,10 @@ func (s *Server) offboxReconstituteHandler(w http.ResponseWriter, r *http.Reques
// data"; this one answers "yes, into a different place than the backup recorded". One checkbox
// carrying both would be the two-decisions-one-button shape R-48 removed from this surface.
ackPlacement := r.FormValue("ack_placement") == "1"
s.backupMgr.BeginRestoreOp("offbox-reconstitute", app)
if msg, ok := s.beginRestoreOp("offbox-reconstitute", app); !ok {
offboxRedirectTo(w, r, restoreWizardPath(app), msg, true)
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)
defer cancel()
@@ -678,7 +684,10 @@ func (s *Server) offboxPlaceHandler(w http.ResponseWriter, r *http.Request) {
offboxRedirectTo(w, r, restoreWizardPath(app), offsiteScratchIncompleteKey, true)
return
}
s.backupMgr.BeginRestoreOp("offbox-place", app)
if msg, ok := s.beginRestoreOp("offbox-place", app); !ok {
offboxRedirectTo(w, r, restoreWizardPath(app), msg, true)
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()
@@ -711,7 +720,10 @@ func (s *Server) sharesRestoreHandler(w http.ResponseWriter, r *http.Request) {
offboxRedirectTo(w, r, "/backups/restore", msg, true)
return
}
s.backupMgr.BeginRestoreOp("shares-restore", backup.SharesDisplayName)
if msg, ok := s.beginRestoreOp("shares-restore", backup.SharesDisplayName); !ok {
offboxRedirectTo(w, r, "/backups/restore", msg, true)
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()
@@ -737,7 +749,10 @@ func (s *Server) sharesPlaceHandler(w http.ResponseWriter, r *http.Request) {
offboxRedirectTo(w, r, "/backups/restore", msg, true)
return
}
s.backupMgr.BeginRestoreOp("shares-place", backup.SharesDisplayName)
if msg, ok := s.beginRestoreOp("shares-place", backup.SharesDisplayName); !ok {
offboxRedirectTo(w, r, "/backups/restore", msg, true)
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()