R-1251 (P1): a pending put-back blocks every start — a cut restore's copy on an unbound drive waits (a restore hold), is poured back when the drive binds before any start; a failed put-back holds the app (H2); Stop during a restore still puts back (H7). Commit only, unreleased.
gates / gates (push) Successful in 1m18s
gates / gates (push) Successful in 1m18s
This commit is contained in:
@@ -222,7 +222,7 @@ func (s *Server) restartStacks(names []string) {
|
||||
// Slice 4 / R-379: the drive-return gate starts apps UNATTENDED, so it must honour a hold
|
||||
// exactly as the customer's button does. Until v0.237.0 it did not — a held app whose drive
|
||||
// blinked would have been started again. "A hold that only one path honours is not a hold."
|
||||
if s.appHeld(name) {
|
||||
if !s.resumePutBack(name) || s.appHeld(name) {
|
||||
continue
|
||||
}
|
||||
if err := s.stackMgr.StartStack(name); err != nil {
|
||||
@@ -346,6 +346,13 @@ func (s *Server) ReconcileDriveGates() {
|
||||
}
|
||||
}
|
||||
s.restartStacks(stopped)
|
||||
// R-1251: a put-back that waited for a drive runs when one returns (the app was held, not gate-stopped,
|
||||
// so it is not in `stopped`); the guard starts it afterwards through the gated starter.
|
||||
if s.backupMgr != nil {
|
||||
if started := s.backupMgr.AppStopGuard().ResumeWaitingPutBacks(); len(started) > 0 {
|
||||
s.logger.Printf("[WARN] [gate] drive RETURNED %s — %d interrupted restore(s) put back, then started: %v", a.Path, len(started), started)
|
||||
}
|
||||
}
|
||||
if err := s.settings.ClearDisconnected(a.Path); err != nil {
|
||||
s.logger.Printf("[WARN] [gate] clear disconnected %s: %v", a.Path, err)
|
||||
}
|
||||
@@ -469,7 +476,9 @@ func (s *Server) processGuestBootChange() {
|
||||
}
|
||||
recreate := func(bs bootStack) {
|
||||
s.logger.Printf("[INFO] [gate] boot %s: live bind confirmed — recreating drive-backed app %s (state=%s) onto %s", resp.GuestBootID, bs.name, bs.state, bs.hdd)
|
||||
if s.appHeld(bs.name) {
|
||||
// R-1251: a cut restore whose copy waited for THIS drive is poured back first — the recreate used to start
|
||||
// the app on the cut restore's data, and a later start poured the old copy over what the household wrote.
|
||||
if !s.resumePutBack(bs.name) || s.appHeld(bs.name) {
|
||||
return
|
||||
}
|
||||
_ = s.stackMgr.StopStack(bs.name)
|
||||
@@ -713,6 +722,15 @@ func (s *Server) notifyDriveReturned(path string, isTarget map[string]bool) {
|
||||
s.notifier.NotifyStorageReconnected(label)
|
||||
}
|
||||
|
||||
// resumePutBack (R-1251) pours back a cut restore's copy that waited for this app's drive, BEFORE any start. false =
|
||||
// the app stays held (the copy is still unreachable, or the put-back failed and the app is held for support). Nil-safe.
|
||||
func (s *Server) resumePutBack(name string) bool {
|
||||
if s.backupMgr == nil {
|
||||
return true
|
||||
}
|
||||
return s.backupMgr.AppStopGuard().ResumePutBack(name)
|
||||
}
|
||||
|
||||
// appHeld reports whether an app carries a hold (failed update or failed restore) and logs the skip.
|
||||
// Nil-safe: no backup manager means no hold store, so nothing is held.
|
||||
func (s *Server) appHeld(name string) bool {
|
||||
|
||||
Reference in New Issue
Block a user