From 9ed574050f58bf75226095df67579cdea5b8ffeb Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 29 Jul 2026 23:03:11 +0800 Subject: [PATCH] fix: save last_good.json on successful reload, no rollback on failure --- agent/internal/httpapi/server.go | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/agent/internal/httpapi/server.go b/agent/internal/httpapi/server.go index e068f70..28a8031 100644 --- a/agent/internal/httpapi/server.go +++ b/agent/internal/httpapi/server.go @@ -416,13 +416,33 @@ func (s *Server) writeConfigAndReload(ctx context.Context, body []byte, restoreB return fmt.Errorf("write config failed: %w", err) } - // Signal media-server to reload. Don't wait for health check — - // graph rebuild can take 10-30s and the 1s health check timeout - // causes false failures. Edge-server will load the new config on - // next restart if hot-reload fails. + // Signal media-server to reload. reloadCtx, reloadCancel := context.WithTimeout(ctx, 5*time.Second) defer reloadCancel() - return s.ms.Reload(reloadCtx) + if err := s.ms.Reload(reloadCtx); err != nil { + return fmt.Errorf("reload failed: %w", err) + } + + // Wait for media-server to recover, then save last_good as known-safe backup. + deadline := time.After(60 * time.Second) + ticker := time.NewTicker(3 * time.Second) + defer ticker.Stop() + for { + select { + case <-deadline: + return fmt.Errorf("media-server did not recover within 60s; config saved, will apply on restart") + case <-ticker.C: + if s.mediaServerHealthy() { + // Config works — persist as known-good backup. + if data, err := os.ReadFile(s.agentCfg.ConfigPath); err == nil { + _ = files.WriteFileAtomic(s.agentCfg.ConfigPath+".last_good.json", data, 0o644) + } + return nil + } + case <-ctx.Done(): + return ctx.Err() + } + } } // mediaServerHealthy checks whether the media-server process is running