Переглянути джерело

refactor(web): centralize background job cadences (#5269)

n0ctal 16 годин тому
батько
коміт
d14f341b21
2 змінених файлів з 63 додано та 10 видалено
  1. 34 0
      internal/web/cadence_test.go
  2. 29 10
      internal/web/web.go

+ 34 - 0
internal/web/cadence_test.go

@@ -0,0 +1,34 @@
+package web
+
+import (
+	"testing"
+
+	"github.com/robfig/cron/v3"
+)
+
+// All centralized background-job cadences must remain valid cron specs. This is
+// the guard for the "single tuning surface" refactor: editing a cadence to an
+// invalid spec fails here instead of silently dropping a job at startup.
+//
+// NOTE: package web embeds the built frontend (//go:embed all:dist), so this
+// test compiles only after `npm run build` has populated web/dist — the normal
+// repo build flow.
+func TestJobCadencesAreValidCronSpecs(t *testing.T) {
+	cadences := map[string]string{
+		"cadenceXrayRunning":   cadenceXrayRunning,
+		"cadenceXrayRestart":   cadenceXrayRestart,
+		"cadenceXrayTraffic":   cadenceXrayTraffic,
+		"cadenceMtproto":       cadenceMtproto,
+		"cadenceClientIPScan":  cadenceClientIPScan,
+		"cadenceNodeHeartbeat": cadenceNodeHeartbeat,
+		"cadenceNodeTraffic":   cadenceNodeTraffic,
+		"cadenceOutboundSub":   cadenceOutboundSub,
+		"cadenceCheckHash":     cadenceCheckHash,
+		"cadenceCPUAlarm":      cadenceCPUAlarm,
+	}
+	for name, spec := range cadences {
+		if _, err := cron.ParseStandard(spec); err != nil {
+			t.Errorf("%s = %q is not a valid cron spec: %v", name, spec, err)
+		}
+	}
+}

+ 29 - 10
internal/web/web.go

@@ -252,6 +252,25 @@ func (s *Server) initRouter() (*gin.Engine, error) {
 	return engine, nil
 }
 
+// Background-job cadences. Centralized here as the single tuning surface; the
+// values are unchanged from the historical hardcoded cron specs. Follow-up:
+// make these configurable via settings, add per-tick jitter to de-synchronize
+// fleet load, skip expensive jobs when no WebSocket clients are connected or
+// node/xray state is unchanged, and export per-job duration/skipped/error
+// counters.
+const (
+	cadenceXrayRunning   = "@every 1s"
+	cadenceXrayRestart   = "@every 30s"
+	cadenceXrayTraffic   = "@every 5s"
+	cadenceMtproto       = "@every 10s"
+	cadenceClientIPScan  = "@every 10s"
+	cadenceNodeHeartbeat = "@every 5s"
+	cadenceNodeTraffic   = "@every 5s"
+	cadenceOutboundSub   = "@every 5m"
+	cadenceCheckHash     = "@every 2m"
+	cadenceCPUAlarm      = "@every 10s"
+)
+
 // startTask schedules background jobs (Xray checks, traffic jobs, cron
 // jobs) which the panel relies on for periodic maintenance and monitoring.
 func (s *Server) startTask(restartXray bool) {
@@ -262,10 +281,10 @@ func (s *Server) startTask(restartXray bool) {
 		}
 	}
 	// Check whether xray is running every second
-	s.cron.AddJob("@every 1s", job.NewCheckXrayRunningJob())
+	s.cron.AddJob(cadenceXrayRunning, job.NewCheckXrayRunningJob())
 
 	// Check if xray needs to be restarted every 30 seconds
-	s.cron.AddFunc("@every 30s", func() {
+	s.cron.AddFunc(cadenceXrayRestart, func() {
 		if s.xrayService.IsNeedRestartAndSetFalse() {
 			err := s.xrayService.RestartXray(false)
 			if err != nil {
@@ -276,23 +295,23 @@ func (s *Server) startTask(restartXray bool) {
 
 	go func() {
 		time.Sleep(time.Second * 5)
-		s.cron.AddJob("@every 5s", job.NewXrayTrafficJob())
+		s.cron.AddJob(cadenceXrayTraffic, job.NewXrayTrafficJob())
 	}()
 
 	// Reconcile mtproto (mtg) sidecars and scrape their traffic
 	mtJob := job.NewMtprotoJob()
-	s.cron.AddJob("@every 10s", mtJob)
+	s.cron.AddJob(cadenceMtproto, mtJob)
 	go mtJob.Run()
 
 	// check client ips from log file every 10 sec
-	s.cron.AddJob("@every 10s", job.NewCheckClientIpJob())
+	s.cron.AddJob(cadenceClientIPScan, job.NewCheckClientIpJob())
 
-	s.cron.AddJob("@every 5s", job.NewNodeHeartbeatJob())
+	s.cron.AddJob(cadenceNodeHeartbeat, job.NewNodeHeartbeatJob())
 
-	s.cron.AddJob("@every 5s", job.NewNodeTrafficSyncJob())
+	s.cron.AddJob(cadenceNodeTraffic, job.NewNodeTrafficSyncJob())
 
 	// Outbound subscription auto-refresh (respects per-sub updateInterval)
-	s.cron.AddJob("@every 5m", job.NewOutboundSubscriptionJob())
+	s.cron.AddJob(cadenceOutboundSub, job.NewOutboundSubscriptionJob())
 
 	// check client ips from log file every day
 	s.cron.AddJob("@daily", job.NewClearLogsJob())
@@ -339,12 +358,12 @@ func (s *Server) startTask(restartXray bool) {
 		}
 
 		// check for Telegram bot callback query hash storage reset
-		s.cron.AddJob("@every 2m", job.NewCheckHashStorageJob())
+		s.cron.AddJob(cadenceCheckHash, job.NewCheckHashStorageJob())
 
 		// Check CPU load and alarm to TgBot if threshold passes
 		cpuThreshold, err := s.settingService.GetTgCpu()
 		if (err == nil) && (cpuThreshold > 0) {
-			s.cron.AddJob("@every 10s", job.NewCheckCpuJob())
+			s.cron.AddJob(cadenceCPUAlarm, job.NewCheckCpuJob())
 		}
 	} else {
 		s.cron.Remove(entry)