prevent duplicate reload request in same time

This commit is contained in:
Cytown 2026-03-18 16:30:52 +08:00
parent cee79ea4dd
commit 9b7bb78676
3 changed files with 34 additions and 9 deletions

View file

@ -14,7 +14,7 @@ func reloadCommand() Definition {
if err := rt.ReloadConfig(); err != nil {
return req.Reply("Failed to reload configuration: " + err.Error())
}
return req.Reply("Configuration reloaded successfully!")
return req.Reply("Config reload triggered!")
},
}
}

View file

@ -7,6 +7,7 @@ import (
"os/signal"
"path/filepath"
"sync"
"sync/atomic"
"syscall"
"time"
@ -55,6 +56,7 @@ type services struct {
DeviceService *devices.Service
HealthServer *health.Server
manualReloadChan chan struct{}
reloading atomic.Bool
}
type startupBlockedProvider struct {
@ -122,11 +124,16 @@ func Run(debug bool, configPath string, allowEmptyStartup bool) error {
manualReloadChan := make(chan struct{}, 1)
runningServices.manualReloadChan = manualReloadChan
reloadTrigger := func() error {
if !runningServices.reloading.CompareAndSwap(false, true) {
return fmt.Errorf("reload already in progress")
}
select {
case manualReloadChan <- struct{}{}:
return nil
default:
return fmt.Errorf("reload already in progress")
// Should not happen, but reset flag if channel is full
runningServices.reloading.Store(false)
return fmt.Errorf("reload already queued")
}
}
runningServices.HealthServer.SetReloadFunc(reloadTrigger)
@ -158,7 +165,11 @@ func Run(debug bool, configPath string, allowEmptyStartup bool) error {
shutdownGateway(runningServices, agentLoop, provider, true)
return nil
case newCfg := <-configReloadChan:
err := handleConfigReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup)
if !runningServices.reloading.CompareAndSwap(false, true) {
logger.Warn("Config reload skipped: another reload is in progress")
continue
}
err := executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup)
if err != nil {
logger.Errorf("Config reload failed: %v", err)
}
@ -167,13 +178,15 @@ func Run(debug bool, configPath string, allowEmptyStartup bool) error {
newCfg, err := config.LoadConfig(configPath)
if err != nil {
logger.Errorf("Error loading config for manual reload: %v", err)
runningServices.reloading.Store(false)
continue
}
if err = newCfg.ValidateModelList(); err != nil {
logger.Errorf("Config validation failed: %v", err)
runningServices.reloading.Store(false)
continue
}
err = handleConfigReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup)
err = executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup)
if err != nil {
logger.Errorf("Manual reload failed: %v", err)
} else {
@ -183,6 +196,19 @@ func Run(debug bool, configPath string, allowEmptyStartup bool) error {
}
}
func executeReload(
ctx context.Context,
agentLoop *agent.AgentLoop,
newCfg *config.Config,
provider *providers.LLMProvider,
runningServices *services,
msgBus *bus.MessageBus,
allowEmptyStartup bool,
) error {
defer runningServices.reloading.Store(false)
return handleConfigReload(ctx, agentLoop, newCfg, provider, runningServices, msgBus, allowEmptyStartup)
}
func createStartupProvider(
cfg *config.Config,
allowEmptyStartup bool,

View file

@ -18,7 +18,6 @@ type Server struct {
checks map[string]Check
startTime time.Time
reloadFunc func() error
reloadMu sync.Mutex
}
type Check struct {
@ -111,8 +110,8 @@ func (s *Server) RegisterCheck(name string, checkFn func() (bool, string)) {
// SetReloadFunc sets the callback function for config reload.
func (s *Server) SetReloadFunc(fn func() error) {
s.reloadMu.Lock()
defer s.reloadMu.Unlock()
s.mu.Lock()
defer s.mu.Unlock()
s.reloadFunc = fn
}
@ -124,9 +123,9 @@ func (s *Server) reloadHandler(w http.ResponseWriter, r *http.Request) {
return
}
s.reloadMu.Lock()
s.mu.Lock()
reloadFunc := s.reloadFunc
s.reloadMu.Unlock()
s.mu.Unlock()
if reloadFunc == nil {
w.Header().Set("Content-Type", "application/json")