@@ -41,47 +41,121 @@ type schedulerCallbackPayload struct {
4141// - 任何错误仅记录日志,不阻断模板创建/更新主流程
4242func (e * ExptTemplateManagerImpl ) syncSchedulerForTemplate (ctx context.Context , template * entity.ExptTemplate ) {
4343 if e == nil || e .scheduleAdapter == nil || template == nil {
44+ logs .CtxWarn (ctx , "[expt_template_sched] skip sync: nil receiver/adapter/template (e_nil=%v, adapter_nil=%v, template_nil=%v)" ,
45+ e == nil , e != nil && e .scheduleAdapter == nil , template == nil )
4446 return
4547 }
4648 templateID := template .GetID ()
4749 spaceID := template .GetSpaceID ()
4850 if templateID <= 0 || spaceID <= 0 {
51+ logs .CtxWarn (ctx , "[expt_template_sched] skip sync: invalid id, space_id=%d, template_id=%d" , spaceID , templateID )
4952 return
5053 }
5154
5255 bizKey := buildScheduleBizKey (spaceID , templateID )
5356 source := template .ExptSource
57+ cronActivate := template .ExptInfo != nil && template .ExptInfo .CronActivate
58+
59+ // 入口日志:先把所有判定输入打齐,便于一行定位"为什么没下发"
60+ logs .CtxInfo (ctx , "[expt_template_sched] start sync, biz_key=%s, source_type=%v, cron_activate=%v, has_scheduler=%v, scheduler=%s" ,
61+ bizKey ,
62+ schedSourceTypeName (source ),
63+ cronActivate ,
64+ source != nil && source .Scheduler != nil ,
65+ schedDescribeScheduler (source ))
5466
5567 // 非 Evaluation 来源不接管定时调度;同时清理可能遗留的任务以避免误触发
5668 if source == nil || source .SourceType != entity .SourceType_Evaluation {
69+ logs .CtxInfo (ctx , "[expt_template_sched] non-evaluation source, will close any existing job, biz_key=%s, source_type=%v" ,
70+ bizKey , schedSourceTypeName (source ))
5771 if err := e .scheduleAdapter .CloseJob (ctx , bizKey ); err != nil {
58- logs .CtxWarn (ctx , "[expt_template ] close schedule job failed (non-evaluation source), biz_key=%s, err=%v" , bizKey , err )
72+ logs .CtxWarn (ctx , "[expt_template_sched ] close schedule job failed (non-evaluation source), biz_key=%s, err=%v" , bizKey , err )
5973 }
6074 return
6175 }
6276
6377 // 模板未启用 cron 或 Scheduler 配置缺失/未启用 → 关闭已存在任务
64- cronActivate := template .ExptInfo != nil && template .ExptInfo .CronActivate
6578 if ! cronActivate || source .Scheduler == nil || ! isSchedulerEnabled (source .Scheduler ) {
79+ reason := schedDisabledReason (cronActivate , source .Scheduler )
80+ logs .CtxInfo (ctx , "[expt_template_sched] schedule disabled, will close any existing job, biz_key=%s, reason=%s" ,
81+ bizKey , reason )
6682 if err := e .scheduleAdapter .CloseJob (ctx , bizKey ); err != nil {
67- logs .CtxWarn (ctx , "[expt_template ] close schedule job failed, biz_key=%s, err=%v" , bizKey , err )
83+ logs .CtxWarn (ctx , "[expt_template_sched ] close schedule job failed, biz_key=%s, reason=%s, err=%v" , bizKey , reason , err )
6884 }
6985 return
7086 }
7187
7288 param , err := buildCreatePeriodicJobParam (bizKey , spaceID , templateID , source .Scheduler )
7389 if err != nil {
74- logs .CtxError (ctx , "[expt_template ] build create periodic job param failed, biz_key=%s, err=%v" , bizKey , err )
90+ logs .CtxError (ctx , "[expt_template_sched ] build create periodic job param failed, biz_key=%s, err=%v" , bizKey , err )
7591 return
7692 }
93+ logs .CtxInfo (ctx , "[expt_template_sched] dispatch CreatePeriodicJob, biz_key=%s, crontab=%s, started_at=%v, ended_at=%v, callback=%s, payload=%s" ,
94+ param .BizKey , param .Crontab , param .StartedAt , param .EndedAt , param .CallbackMethod , param .CallbackPayload )
7795 if err := e .scheduleAdapter .CreatePeriodicJob (ctx , param ); err != nil {
78- logs .CtxError (ctx , "[expt_template ] create periodic schedule job failed, biz_key=%s, err=%v" , bizKey , err )
96+ logs .CtxError (ctx , "[expt_template_sched ] create periodic schedule job failed, biz_key=%s, crontab=%s, err=%v" , bizKey , param . Crontab , err )
7997 return
8098 }
81- logs .CtxInfo (ctx , "[expt_template ] schedule job synced, biz_key=%s, frequency=%s, crontab=%s" ,
99+ logs .CtxInfo (ctx , "[expt_template_sched ] schedule job synced, biz_key=%s, frequency=%s, crontab=%s" ,
82100 bizKey , * source .Scheduler .Frequency , param .Crontab )
83101}
84102
103+ // schedSourceTypeName 把 SourceType 打成可读字符串,便于日志快速定位
104+ func schedSourceTypeName (src * entity.ExptSource ) string {
105+ if src == nil {
106+ return "<nil_source>"
107+ }
108+ return fmt .Sprintf ("%d" , src .SourceType )
109+ }
110+
111+ // schedDescribeScheduler 把 Scheduler 字段一行展开,所有可空字段都安全打印
112+ func schedDescribeScheduler (src * entity.ExptSource ) string {
113+ if src == nil || src .Scheduler == nil {
114+ return "<nil_scheduler>"
115+ }
116+ s := src .Scheduler
117+ enabled := false
118+ if s .Enabled != nil {
119+ enabled = * s .Enabled
120+ }
121+ freq := ""
122+ if s .Frequency != nil {
123+ freq = * s .Frequency
124+ }
125+ var trigger , start , end int64
126+ if s .TriggerAt != nil {
127+ trigger = * s .TriggerAt
128+ }
129+ if s .StartTime != nil {
130+ start = * s .StartTime
131+ }
132+ if s .EndTime != nil {
133+ end = * s .EndTime
134+ }
135+ return fmt .Sprintf ("{enabled=%v,frequency=%q,trigger_at=%d,start_time=%d,end_time=%d}" ,
136+ enabled , freq , trigger , start , end )
137+ }
138+
139+ // schedDisabledReason 推导本次"未下发周期任务"的具体原因,便于排查
140+ func schedDisabledReason (cronActivate bool , s * entity.ExptSchedulerDO ) string {
141+ if ! cronActivate {
142+ return "expt_info.cron_activate=false"
143+ }
144+ if s == nil {
145+ return "scheduler is nil (DTO 转换/落库链路没有保留 Scheduler?)"
146+ }
147+ if s .Enabled == nil || ! * s .Enabled {
148+ return "scheduler.enabled=false"
149+ }
150+ if s .Frequency == nil || * s .Frequency == "" {
151+ return "scheduler.frequency is empty"
152+ }
153+ if s .TriggerAt == nil || * s .TriggerAt <= 0 {
154+ return "scheduler.trigger_at is empty"
155+ }
156+ return "unknown"
157+ }
158+
85159// isSchedulerEnabled 判断 ExptSchedulerDO 是否启用且配置完整
86160func isSchedulerEnabled (s * entity.ExptSchedulerDO ) bool {
87161 if s == nil {
0 commit comments