mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 23:57:15 +00:00
fix(xray): retry a failed pending-restart instead of dropping the config change
The 30s cron consumed the need-restart flag with IsNeedRestartAndSetFalse before calling RestartXray and only logged a failure. If RestartXray failed early (a transient GetXrayConfig DB error) the old process kept running the old config, the crash detector saw a running process and never retried, and the flag stayed cleared — so an admin's saved change silently never reached the core. Move the consume/restart/retry into ApplyPendingRestart, which re-arms the flag on failure so the next tick retries.
This commit is contained in:
@@ -1188,6 +1188,20 @@ func (s *XrayService) IsNeedRestartAndSetFalse() bool {
|
|||||||
return isNeedXrayRestart.CompareAndSwap(true, false)
|
return isNeedXrayRestart.CompareAndSwap(true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyPendingRestart consumes the need-restart flag and restarts Xray. If the
|
||||||
|
// restart fails (for example GetXrayConfig hits a transient DB error and leaves
|
||||||
|
// the old process running), it re-arms the flag so the next tick retries instead
|
||||||
|
// of silently dropping the pending config change.
|
||||||
|
func (s *XrayService) ApplyPendingRestart() {
|
||||||
|
if !s.IsNeedRestartAndSetFalse() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := s.RestartXray(false); err != nil {
|
||||||
|
logger.Error("restart xray failed:", err)
|
||||||
|
s.SetToNeedRestart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DidXrayCrash checks if Xray crashed by verifying it's not running and wasn't manually stopped.
|
// DidXrayCrash checks if Xray crashed by verifying it's not running and wasn't manually stopped.
|
||||||
func (s *XrayService) DidXrayCrash() bool {
|
func (s *XrayService) DidXrayCrash() bool {
|
||||||
return !s.IsXrayRunning() && !isManuallyStopped.Load()
|
return !s.IsXrayRunning() && !isManuallyStopped.Load()
|
||||||
|
|||||||
@@ -21,3 +21,26 @@ func TestRestartXrayRespectsManualStop(t *testing.T) {
|
|||||||
t.Fatal("a non-forced restart cleared a deliberate manual stop and would revive xray")
|
t.Fatal("a non-forced restart cleared a deliberate manual stop and would revive xray")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the pending-restart reconcile consumes the need-restart flag but the
|
||||||
|
// restart itself fails, the flag must be re-armed so the config change is
|
||||||
|
// retried rather than silently dropped.
|
||||||
|
func TestApplyPendingRestartReArmsFlagOnFailure(t *testing.T) {
|
||||||
|
setupSettingTestDB(t)
|
||||||
|
if err := (&SettingService{}).saveSetting("xrayTemplateConfig", "{ not valid json"); err != nil {
|
||||||
|
t.Fatalf("seed template: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
isManuallyStopped.Store(false)
|
||||||
|
isNeedXrayRestart.Store(false)
|
||||||
|
})
|
||||||
|
isManuallyStopped.Store(false)
|
||||||
|
|
||||||
|
svc := &XrayService{}
|
||||||
|
svc.SetToNeedRestart()
|
||||||
|
svc.ApplyPendingRestart()
|
||||||
|
|
||||||
|
if !isNeedXrayRestart.Load() {
|
||||||
|
t.Fatal("a failed restart must re-arm the need-restart flag so the pending config change is retried")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-6
@@ -314,12 +314,7 @@ func (s *Server) startTask(restartXray bool) {
|
|||||||
|
|
||||||
// Check if xray needs to be restarted every 30 seconds
|
// Check if xray needs to be restarted every 30 seconds
|
||||||
_, _ = s.cron.AddFunc(cadenceXrayRestart, func() {
|
_, _ = s.cron.AddFunc(cadenceXrayRestart, func() {
|
||||||
if s.xrayService.IsNeedRestartAndSetFalse() {
|
s.xrayService.ApplyPendingRestart()
|
||||||
err := s.xrayService.RestartXray(false)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("restart xray failed:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user