@@ -66,6 +66,17 @@ type Scheduler interface {
66
66
Stop ()
67
67
}
68
68
69
+ type dispatchedJob struct {
70
+ ctx context.Context
71
+ jobDetail * JobDetail
72
+ }
73
+
74
+ type contextKey string
75
+
76
+ // JobMetadataContextKey is a context key used to store and retrieve [JobMetadata]
77
+ // from the [context.Context] passed to a job's Execute method.
78
+ const JobMetadataContextKey = contextKey ("JobMetadata" )
79
+
69
80
// StdScheduler implements the [Scheduler] interface.
70
81
type StdScheduler struct {
71
82
mtx sync.RWMutex
@@ -74,7 +85,7 @@ type StdScheduler struct {
74
85
interrupt chan struct {}
75
86
cancel context.CancelFunc
76
87
feeder chan ScheduledJob
77
- dispatch chan ScheduledJob
88
+ dispatch chan dispatchedJob
78
89
started bool
79
90
80
91
queue JobQueue
@@ -126,6 +137,20 @@ type SchedulerConfig struct {
126
137
// Adjust OutdatedThreshold to establish an acceptable delay time and
127
138
// ensure regular job execution.
128
139
MisfiredChan chan ScheduledJob
140
+
141
+ // JobMetadata, when set to true, enables the scheduler to enrich the context
142
+ // passed to a job's Execute method with JobMetadata. This provides custom
143
+ // Job implementations with access to additional contextual information
144
+ // about the job.
145
+ JobMetadata bool
146
+ }
147
+
148
+ // JobMetadata provides read-only details about a specific job execution's scheduling
149
+ // context. It is added to the context by the scheduler.
150
+ type JobMetadata struct {
151
+ // RunTime is the Unix timestamp at which the job was originally scheduled to
152
+ // begin execution.
153
+ RunTime int64
129
154
}
130
155
131
156
// SchedulerOpt is a functional option type used to configure an [StdScheduler].
@@ -141,6 +166,23 @@ func WithBlockingExecution() SchedulerOpt {
141
166
}
142
167
}
143
168
169
+ // WithJobMetadata configures the scheduler to attach a [JobMetadata] struct to the
170
+ // [context.Context] passed to each job's Execute method.
171
+ //
172
+ // To retrieve this metadata within a custom [Job] implementation:
173
+ //
174
+ // func (j *customJob) Execute(ctx context.Context) error {
175
+ // md, ok := ctx.Value(quartz.JobMetadataContextKey).(quartz.JobMetadata)
176
+ // // use md as needed
177
+ // return nil
178
+ // }
179
+ func WithJobMetadata () SchedulerOpt {
180
+ return func (c * StdScheduler ) error {
181
+ c .opts .JobMetadata = true
182
+ return nil
183
+ }
184
+ }
185
+
144
186
// WithWorkerLimit configures the number of worker goroutines for concurrent job execution.
145
187
// This option is only used when blocking execution is disabled. If blocking execution
146
188
// is enabled, this setting will be ignored. The workerLimit must be non-negative.
@@ -246,7 +288,7 @@ func NewStdScheduler(opts ...SchedulerOpt) (Scheduler, error) {
246
288
scheduler := & StdScheduler {
247
289
interrupt : make (chan struct {}, 1 ),
248
290
feeder : make (chan ScheduledJob ),
249
- dispatch : make (chan ScheduledJob ),
291
+ dispatch : make (chan dispatchedJob ),
250
292
queue : NewJobQueue (),
251
293
queueLocker : & sync.Mutex {},
252
294
opts : config ,
@@ -320,13 +362,13 @@ func (sched *StdScheduler) Start(ctx context.Context) {
320
362
ctx , sched .cancel = context .WithCancel (ctx )
321
363
go func () { <- ctx .Done (); sched .Stop () }()
322
364
365
+ // start worker pool if configured
366
+ sched .startWorkers (ctx )
367
+
323
368
// start scheduler execution loop
324
369
sched .wg .Add (1 )
325
370
go sched .startExecutionLoop (ctx )
326
371
327
- // starts worker pool if configured
328
- sched .startWorkers (ctx )
329
-
330
372
sched .started = true
331
373
}
332
374
@@ -549,8 +591,8 @@ func (sched *StdScheduler) startWorkers(ctx context.Context) {
549
591
select {
550
592
case <- ctx .Done ():
551
593
return
552
- case scheduled := <- sched .dispatch :
553
- sched .executeWithRetries (ctx , scheduled . JobDetail () )
594
+ case dispatched := <- sched .dispatch :
595
+ sched .executeWithRetries (dispatched . ctx , dispatched . jobDetail )
554
596
}
555
597
}
556
598
}()
@@ -584,6 +626,14 @@ func (sched *StdScheduler) calculateNextTick() time.Duration {
584
626
func (sched * StdScheduler ) executeAndReschedule (ctx context.Context ) {
585
627
// fetch a job for processing
586
628
scheduled , valid := sched .fetchAndReschedule ()
629
+ // attach job metadata to the context
630
+ if sched .opts .JobMetadata {
631
+ ctx = context .WithValue (
632
+ ctx ,
633
+ JobMetadataContextKey ,
634
+ JobMetadata {RunTime : scheduled .NextRunTime ()},
635
+ )
636
+ }
587
637
588
638
// execute the job
589
639
if valid {
@@ -594,7 +644,10 @@ func (sched *StdScheduler) executeAndReschedule(ctx context.Context) {
594
644
sched .executeWithRetries (ctx , scheduled .JobDetail ())
595
645
case sched .opts .WorkerLimit > 0 :
596
646
select {
597
- case sched .dispatch <- scheduled :
647
+ case sched .dispatch <- dispatchedJob {
648
+ ctx : ctx ,
649
+ jobDetail : scheduled .JobDetail (),
650
+ }:
598
651
case <- ctx .Done ():
599
652
return
600
653
}
0 commit comments