perf(scheduler): harden jobs for scalable execution
This commit is contained in:
@@ -130,8 +130,9 @@ func (w *ScheduleDispatchWorker) run(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) runOnce(parent context.Context) {
|
||||
ctx, cancel := w.stageContext(parent)
|
||||
count, err := w.hydrateMissingNextRuns(ctx)
|
||||
runtimeCfg := w.runtimeConfig()
|
||||
ctx, cancel := w.stageContext(parent, runtimeCfg)
|
||||
count, err := w.hydrateMissingNextRuns(ctx, runtimeCfg)
|
||||
cancel()
|
||||
if err != nil {
|
||||
if w.logger != nil {
|
||||
@@ -141,8 +142,8 @@ func (w *ScheduleDispatchWorker) runOnce(parent context.Context) {
|
||||
w.logger.Info("schedule dispatch hydrated next_run_at", zap.Int("task_count", count))
|
||||
}
|
||||
|
||||
ctx, cancel = w.stageContext(parent)
|
||||
paused, stats, err := w.shouldPauseDispatch(ctx)
|
||||
ctx, cancel = w.stageContext(parent, runtimeCfg)
|
||||
paused, stats, err := w.shouldPauseDispatch(ctx, runtimeCfg)
|
||||
cancel()
|
||||
if err != nil {
|
||||
if w.logger != nil {
|
||||
@@ -160,8 +161,8 @@ func (w *ScheduleDispatchWorker) runOnce(parent context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel = w.stageContext(parent)
|
||||
tasks, err := w.claimDueSchedules(ctx)
|
||||
ctx, cancel = w.stageContext(parent, runtimeCfg)
|
||||
tasks, err := w.claimDueSchedules(ctx, runtimeCfg)
|
||||
cancel()
|
||||
if err != nil {
|
||||
if w.logger != nil {
|
||||
@@ -173,14 +174,16 @@ func (w *ScheduleDispatchWorker) runOnce(parent context.Context) {
|
||||
w.dispatchBatch(parent, tasks)
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) RunOnce(ctx context.Context) (map[string]any, error) {
|
||||
func (w *ScheduleDispatchWorker) RunOnce(ctx context.Context, run JobRunContext) (map[string]any, error) {
|
||||
if w == nil || w.pool == nil || w.promptRuleService == nil {
|
||||
return map[string]any{"skipped": true, "reason": "worker_not_ready"}, nil
|
||||
}
|
||||
startedAt := time.Now()
|
||||
runtimeCfg := w.runtimeConfig()
|
||||
runtimeCfg.ApplyJobRun(run)
|
||||
|
||||
stageCtx, cancel := w.stageContext(ctx)
|
||||
hydratedCount, err := w.hydrateMissingNextRuns(stageCtx)
|
||||
stageCtx, cancel := w.stageContext(ctx, runtimeCfg)
|
||||
hydratedCount, err := w.hydrateMissingNextRuns(stageCtx, runtimeCfg)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
@@ -188,8 +191,8 @@ func (w *ScheduleDispatchWorker) RunOnce(ctx context.Context) (map[string]any, e
|
||||
}, err
|
||||
}
|
||||
|
||||
stageCtx, cancel = w.stageContext(ctx)
|
||||
paused, stats, err := w.shouldPauseDispatch(stageCtx)
|
||||
stageCtx, cancel = w.stageContext(ctx, runtimeCfg)
|
||||
paused, stats, err := w.shouldPauseDispatch(stageCtx, runtimeCfg)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
@@ -204,11 +207,12 @@ func (w *ScheduleDispatchWorker) RunOnce(ctx context.Context) (map[string]any, e
|
||||
"queue_depth": stats.Messages,
|
||||
"queue_consumers": stats.Consumers,
|
||||
"duration_ms": time.Since(startedAt).Milliseconds(),
|
||||
"batch_size": runtimeCfg.BatchSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
stageCtx, cancel = w.stageContext(ctx)
|
||||
tasks, err := w.claimDueSchedules(stageCtx)
|
||||
stageCtx, cancel = w.stageContext(ctx, runtimeCfg)
|
||||
tasks, err := w.claimDueSchedules(stageCtx, runtimeCfg)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
@@ -221,11 +225,11 @@ func (w *ScheduleDispatchWorker) RunOnce(ctx context.Context) (map[string]any, e
|
||||
"hydrated_count": hydratedCount,
|
||||
"claimed_count": len(tasks),
|
||||
"duration_ms": time.Since(startedAt).Milliseconds(),
|
||||
"batch_size": runtimeCfg.BatchSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) hydrateMissingNextRuns(ctx context.Context) (int, error) {
|
||||
runtimeCfg := w.runtimeConfig()
|
||||
func (w *ScheduleDispatchWorker) hydrateMissingNextRuns(ctx context.Context, runtimeCfg scheduleDispatchRuntimeConfig) (int, error) {
|
||||
tx, err := w.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -318,8 +322,7 @@ func (w *ScheduleDispatchWorker) hydrateMissingNextRuns(ctx context.Context) (in
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) claimDueSchedules(ctx context.Context) ([]dueScheduleTask, error) {
|
||||
runtimeCfg := w.runtimeConfig()
|
||||
func (w *ScheduleDispatchWorker) claimDueSchedules(ctx context.Context, runtimeCfg scheduleDispatchRuntimeConfig) ([]dueScheduleTask, error) {
|
||||
tx, err := w.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -539,11 +542,10 @@ func (w *ScheduleDispatchWorker) dispatchBatch(parent context.Context, tasks []d
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) shouldPauseDispatch(ctx context.Context) (bool, rabbitmq.QueueStats, error) {
|
||||
func (w *ScheduleDispatchWorker) shouldPauseDispatch(ctx context.Context, runtimeCfg scheduleDispatchRuntimeConfig) (bool, rabbitmq.QueueStats, error) {
|
||||
if w == nil || w.rabbitMQ == nil {
|
||||
return false, rabbitmq.QueueStats{}, nil
|
||||
}
|
||||
runtimeCfg := w.runtimeConfig()
|
||||
if runtimeCfg.QueueBackpressure <= 0 {
|
||||
return false, rabbitmq.QueueStats{}, nil
|
||||
}
|
||||
@@ -565,6 +567,32 @@ func scheduleDispatchRuntimeFromConfig(cfg config.SchedulerConfig) scheduleDispa
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *scheduleDispatchRuntimeConfig) ApplyJobRun(run JobRunContext) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
if run.Job != nil {
|
||||
if run.Job.BatchSize != nil && *run.Job.BatchSize > 0 {
|
||||
cfg.BatchSize = *run.Job.BatchSize
|
||||
}
|
||||
if run.Job.TimeoutSeconds > 0 {
|
||||
cfg.Timeout = time.Duration(run.Job.TimeoutSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
if value := AsInt(run.Config, "dispatch_workers", 0); value > 0 {
|
||||
cfg.DispatchWorkers = value
|
||||
}
|
||||
if value := AsInt(run.Config, "queue_backpressure", 0); value > 0 {
|
||||
cfg.QueueBackpressure = value
|
||||
}
|
||||
if value := AsInt(run.Config, "batch_size", 0); value > 0 && (run.Job == nil || run.Job.BatchSize == nil) {
|
||||
cfg.BatchSize = value
|
||||
}
|
||||
if timeout := AsDuration(run.Config, "timeout", 0); timeout > 0 && (run.Job == nil || run.Job.TimeoutSeconds <= 0) {
|
||||
cfg.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func schedulerDispatchInterval(cfg config.SchedulerConfig) time.Duration {
|
||||
if cfg.DispatchInterval > 0 {
|
||||
return cfg.DispatchInterval
|
||||
@@ -593,11 +621,15 @@ func schedulerDispatchConcurrency(cfg config.SchedulerConfig) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) stageContext(parent context.Context) (context.Context, context.CancelFunc) {
|
||||
func (w *ScheduleDispatchWorker) stageContext(parent context.Context, runtimeCfg ...scheduleDispatchRuntimeConfig) (context.Context, context.CancelFunc) {
|
||||
if parent == nil {
|
||||
parent = context.Background()
|
||||
}
|
||||
return context.WithTimeout(parent, w.runtimeConfig().Timeout)
|
||||
cfg := w.runtimeConfig()
|
||||
if len(runtimeCfg) > 0 {
|
||||
cfg = runtimeCfg[0]
|
||||
}
|
||||
return context.WithTimeout(parent, cfg.Timeout)
|
||||
}
|
||||
|
||||
func (w *ScheduleDispatchWorker) invalidateScheduleTaskCaches(updates []scheduleTaskCacheUpdate) {
|
||||
|
||||
Reference in New Issue
Block a user