Skip to content

Commit 256265f

Browse files
authored
internal refactoring of JobOption constructor, clock moved to exec (#761)
1 parent d364449 commit 256265f

8 files changed

+133
-98
lines changed

.github/workflows/go_test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
- name: golangci-lint
2828
uses: golangci/[email protected]
2929
with:
30-
version: v1.55.2
30+
version: v1.59.1
3131
- name: test
3232
run: make test_ci

.golangci.yaml

+10-15
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ run:
22
timeout: 5m
33
issues-exit-code: 1
44
tests: true
5-
skip-dirs:
6-
- local
75

86
issues:
97
max-same-issues: 100
108
include:
119
- EXC0012
1210
- EXC0014
11+
exclude-dirs:
12+
- local
13+
exclude-rules:
14+
- path: example_test.go
15+
linters:
16+
- revive
17+
text: "seems to be unused"
18+
fix: true
1319

1420
linters:
1521
enable:
@@ -29,21 +35,10 @@ linters:
2935
- whitespace
3036

3137
output:
32-
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
33-
format: colored-line-number
34-
# print lines of code with issue, default is true
38+
formats:
39+
- format: colored-line-number
3540
print-issued-lines: true
36-
# print linter name in the end of issue text, default is true
3741
print-linter-name: true
38-
# make issues output unique by line, default is true
3942
uniq-by-line: true
40-
# add a prefix to the output file references; default is no prefix
4143
path-prefix: ""
42-
# sorts results by: filepath, line and column
4344
sort-results: true
44-
45-
linters-settings:
46-
golint:
47-
min-confidence: 0.8
48-
49-
fix: true

executor.go

+37-14
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,48 @@ import (
77
"sync"
88
"time"
99

10+
"github.com/jonboulle/clockwork"
11+
1012
"github.com/google/uuid"
1113
)
1214

1315
type executor struct {
14-
ctx context.Context
15-
cancel context.CancelFunc
16-
logger Logger
17-
stopCh chan struct{}
18-
jobsIn chan jobIn
16+
// context used for shutting down
17+
ctx context.Context
18+
// cancel used by the executor to signal a stop of it's functions
19+
cancel context.CancelFunc
20+
// clock used for regular time or mocking time
21+
clock clockwork.Clock
22+
// the executor's logger
23+
logger Logger
24+
25+
// receives jobs scheduled to execute
26+
jobsIn chan jobIn
27+
// sends out jobs for rescheduling
1928
jobsOutForRescheduling chan uuid.UUID
20-
jobsOutCompleted chan uuid.UUID
21-
jobOutRequest chan jobOutRequest
22-
stopTimeout time.Duration
23-
done chan error
24-
singletonRunners *sync.Map // map[uuid.UUID]singletonRunner
25-
limitMode *limitModeConfig
26-
elector Elector
27-
locker Locker
28-
monitor Monitor
29+
// sends out jobs once completed
30+
jobsOutCompleted chan uuid.UUID
31+
// used to request jobs from the scheduler
32+
jobOutRequest chan jobOutRequest
33+
34+
// used by the executor to receive a stop signal from the scheduler
35+
stopCh chan struct{}
36+
// the timeout value when stopping
37+
stopTimeout time.Duration
38+
// used to signal that the executor has completed shutdown
39+
done chan error
40+
41+
// runners for any singleton type jobs
42+
// map[uuid.UUID]singletonRunner
43+
singletonRunners *sync.Map
44+
// config for limit mode
45+
limitMode *limitModeConfig
46+
// the elector when running distributed instances
47+
elector Elector
48+
// the locker when running distributed instances
49+
locker Locker
50+
// monitor for reporting metrics
51+
monitor Monitor
2952
}
3053

3154
type jobIn struct {

job.go

+16-17
Original file line numberDiff line numberDiff line change
@@ -475,15 +475,15 @@ func OneTimeJobStartImmediately() OneTimeJobStartAtOption {
475475
// OneTimeJobStartDateTime sets the date & time at which the job should run.
476476
// This datetime must be in the future (according to the scheduler clock).
477477
func OneTimeJobStartDateTime(start time.Time) OneTimeJobStartAtOption {
478-
return func(j *internalJob) []time.Time {
478+
return func(_ *internalJob) []time.Time {
479479
return []time.Time{start}
480480
}
481481
}
482482

483483
// OneTimeJobStartDateTimes sets the date & times at which the job should run.
484484
// At least one of the date/times must be in the future (according to the scheduler clock).
485485
func OneTimeJobStartDateTimes(times ...time.Time) OneTimeJobStartAtOption {
486-
return func(j *internalJob) []time.Time {
486+
return func(_ *internalJob) []time.Time {
487487
return times
488488
}
489489
}
@@ -503,13 +503,13 @@ func OneTimeJob(startAt OneTimeJobStartAtOption) JobDefinition {
503503
// -----------------------------------------------
504504

505505
// JobOption defines the constructor for job options.
506-
type JobOption func(*internalJob) error
506+
type JobOption func(*internalJob, time.Time) error
507507

508508
// WithDistributedJobLocker sets the locker to be used by multiple
509509
// Scheduler instances to ensure that only one instance of each
510510
// job is run.
511511
func WithDistributedJobLocker(locker Locker) JobOption {
512-
return func(j *internalJob) error {
512+
return func(j *internalJob, _ time.Time) error {
513513
if locker == nil {
514514
return ErrWithDistributedJobLockerNil
515515
}
@@ -521,7 +521,7 @@ func WithDistributedJobLocker(locker Locker) JobOption {
521521
// WithEventListeners sets the event listeners that should be
522522
// run for the job.
523523
func WithEventListeners(eventListeners ...EventListener) JobOption {
524-
return func(j *internalJob) error {
524+
return func(j *internalJob, _ time.Time) error {
525525
for _, eventListener := range eventListeners {
526526
if err := eventListener(j); err != nil {
527527
return err
@@ -534,7 +534,7 @@ func WithEventListeners(eventListeners ...EventListener) JobOption {
534534
// WithLimitedRuns limits the number of executions of this job to n.
535535
// Upon reaching the limit, the job is removed from the scheduler.
536536
func WithLimitedRuns(limit uint) JobOption {
537-
return func(j *internalJob) error {
537+
return func(j *internalJob, _ time.Time) error {
538538
j.limitRunsTo = &limitRunsTo{
539539
limit: limit,
540540
runCount: 0,
@@ -546,8 +546,7 @@ func WithLimitedRuns(limit uint) JobOption {
546546
// WithName sets the name of the job. Name provides
547547
// a human-readable identifier for the job.
548548
func WithName(name string) JobOption {
549-
// TODO use the name for metrics and future logging option
550-
return func(j *internalJob) error {
549+
return func(j *internalJob, _ time.Time) error {
551550
if name == "" {
552551
return ErrWithNameEmpty
553552
}
@@ -560,7 +559,7 @@ func WithName(name string) JobOption {
560559
// This is useful for jobs that should not overlap, and that occasionally
561560
// (but not consistently) run longer than the interval between job runs.
562561
func WithSingletonMode(mode LimitMode) JobOption {
563-
return func(j *internalJob) error {
562+
return func(j *internalJob, _ time.Time) error {
564563
j.singletonMode = true
565564
j.singletonLimitMode = mode
566565
return nil
@@ -570,19 +569,19 @@ func WithSingletonMode(mode LimitMode) JobOption {
570569
// WithStartAt sets the option for starting the job at
571570
// a specific datetime.
572571
func WithStartAt(option StartAtOption) JobOption {
573-
return func(j *internalJob) error {
574-
return option(j)
572+
return func(j *internalJob, now time.Time) error {
573+
return option(j, now)
575574
}
576575
}
577576

578577
// StartAtOption defines options for starting the job
579-
type StartAtOption func(*internalJob) error
578+
type StartAtOption func(*internalJob, time.Time) error
580579

581580
// WithStartImmediately tells the scheduler to run the job immediately
582581
// regardless of the type or schedule of job. After this immediate run
583582
// the job is scheduled from this time based on the job definition.
584583
func WithStartImmediately() StartAtOption {
585-
return func(j *internalJob) error {
584+
return func(j *internalJob, _ time.Time) error {
586585
j.startImmediately = true
587586
return nil
588587
}
@@ -591,8 +590,8 @@ func WithStartImmediately() StartAtOption {
591590
// WithStartDateTime sets the first date & time at which the job should run.
592591
// This datetime must be in the future.
593592
func WithStartDateTime(start time.Time) StartAtOption {
594-
return func(j *internalJob) error {
595-
if start.IsZero() || start.Before(time.Now()) {
593+
return func(j *internalJob, now time.Time) error {
594+
if start.IsZero() || start.Before(now) {
596595
return ErrWithStartDateTimePast
597596
}
598597
j.startTime = start
@@ -604,7 +603,7 @@ func WithStartDateTime(start time.Time) StartAtOption {
604603
// a way to identify jobs by a set of tags and remove
605604
// multiple jobs by tag.
606605
func WithTags(tags ...string) JobOption {
607-
return func(j *internalJob) error {
606+
return func(j *internalJob, _ time.Time) error {
608607
j.tags = tags
609608
return nil
610609
}
@@ -614,7 +613,7 @@ func WithTags(tags ...string) JobOption {
614613
// is used to uniquely identify the job and is used for logging
615614
// and metrics.
616615
func WithIdentifier(id uuid.UUID) JobOption {
617-
return func(j *internalJob) error {
616+
return func(j *internalJob, _ time.Time) error {
618617
if id == uuid.Nil {
619618
return ErrWithIdentifierNil
620619
}

job_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ func TestWithEventListeners(t *testing.T) {
487487
for _, tt := range tests {
488488
t.Run(tt.name, func(t *testing.T) {
489489
var ij internalJob
490-
err := WithEventListeners(tt.eventListeners...)(&ij)
490+
err := WithEventListeners(tt.eventListeners...)(&ij, time.Now())
491491
assert.Equal(t, tt.err, err)
492492

493493
if err != nil {

scheduler.go

+45-27
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,43 @@ type Scheduler interface {
5656
// -----------------------------------------------
5757

5858
type scheduler struct {
59-
shutdownCtx context.Context
60-
shutdownCancel context.CancelFunc
61-
exec executor
62-
jobs map[uuid.UUID]internalJob
63-
location *time.Location
64-
clock clockwork.Clock
65-
started bool
59+
// context used for shutting down
60+
shutdownCtx context.Context
61+
// cancel used to signal scheduler should shut down
62+
shutdownCancel context.CancelFunc
63+
// the executor, which actually runs the jobs sent to it via the scheduler
64+
exec executor
65+
// the map of jobs registered in the scheduler
66+
jobs map[uuid.UUID]internalJob
67+
// the location used by the scheduler for scheduling when relevant
68+
location *time.Location
69+
// whether the scheduler has been started or not
70+
started bool
71+
// globally applied JobOption's set on all jobs added to the scheduler
72+
// note: individually set JobOption's take precedence.
6673
globalJobOptions []JobOption
67-
logger Logger
68-
69-
startCh chan struct{}
70-
startedCh chan struct{}
71-
stopCh chan struct{}
72-
stopErrCh chan error
73-
allJobsOutRequest chan allJobsOutRequest
74-
jobOutRequestCh chan jobOutRequest
75-
runJobRequestCh chan runJobRequest
76-
newJobCh chan newJobIn
77-
removeJobCh chan uuid.UUID
74+
// the scheduler's logger
75+
logger Logger
76+
77+
// used to tell the scheduler to start
78+
startCh chan struct{}
79+
// used to report that the scheduler has started
80+
startedCh chan struct{}
81+
// used to tell the scheduler to stop
82+
stopCh chan struct{}
83+
// used to report that the scheduler has stopped
84+
stopErrCh chan error
85+
// used to send all the jobs out when a request is made by the client
86+
allJobsOutRequest chan allJobsOutRequest
87+
// used to send a jobs out when a request is made by the client
88+
jobOutRequestCh chan jobOutRequest
89+
// used to run a job on-demand when requested by the client
90+
runJobRequestCh chan runJobRequest
91+
// new jobs are received here
92+
newJobCh chan newJobIn
93+
// requests from the client to remove jobs by ID are received here
94+
removeJobCh chan uuid.UUID
95+
// requests from the client to remove jobs by tags are received here
7896
removeJobsByTagsCh chan []string
7997
}
8098

@@ -111,6 +129,7 @@ func NewScheduler(options ...SchedulerOption) (Scheduler, error) {
111129
stopTimeout: time.Second * 10,
112130
singletonRunners: nil,
113131
logger: &noOpLogger{},
132+
clock: clockwork.NewRealClock(),
114133

115134
jobsIn: make(chan jobIn),
116135
jobsOutForRescheduling: make(chan uuid.UUID),
@@ -125,7 +144,6 @@ func NewScheduler(options ...SchedulerOption) (Scheduler, error) {
125144
exec: exec,
126145
jobs: make(map[uuid.UUID]internalJob),
127146
location: time.Local,
128-
clock: clockwork.NewRealClock(),
129147
logger: &noOpLogger{},
130148

131149
newJobCh: make(chan newJobIn),
@@ -338,7 +356,7 @@ func (s *scheduler) selectExecJobsOutForRescheduling(id uuid.UUID) {
338356
}
339357
}
340358
j.nextScheduled = append(j.nextScheduled, next)
341-
j.timer = s.clock.AfterFunc(next.Sub(s.now()), func() {
359+
j.timer = s.exec.clock.AfterFunc(next.Sub(s.now()), func() {
342360
// set the actual timer on the job here and listen for
343361
// shut down events so that the job doesn't attempt to
344362
// run if the scheduler has been shutdown.
@@ -422,7 +440,7 @@ func (s *scheduler) selectNewJob(in newJobIn) {
422440
}
423441

424442
id := j.id
425-
j.timer = s.clock.AfterFunc(next.Sub(s.now()), func() {
443+
j.timer = s.exec.clock.AfterFunc(next.Sub(s.now()), func() {
426444
select {
427445
case <-s.shutdownCtx.Done():
428446
case s.exec.jobsIn <- jobIn{
@@ -474,7 +492,7 @@ func (s *scheduler) selectStart() {
474492
}
475493

476494
jobID := id
477-
j.timer = s.clock.AfterFunc(next.Sub(s.now()), func() {
495+
j.timer = s.exec.clock.AfterFunc(next.Sub(s.now()), func() {
478496
select {
479497
case <-s.shutdownCtx.Done():
480498
case s.exec.jobsIn <- jobIn{
@@ -502,7 +520,7 @@ func (s *scheduler) selectStart() {
502520
// -----------------------------------------------
503521

504522
func (s *scheduler) now() time.Time {
505-
return s.clock.Now().In(s.location)
523+
return s.exec.clock.Now().In(s.location)
506524
}
507525

508526
func (s *scheduler) jobFromInternalJob(in internalJob) job {
@@ -643,19 +661,19 @@ func (s *scheduler) addOrUpdateJob(id uuid.UUID, definition JobDefinition, taskW
643661

644662
// apply global job options
645663
for _, option := range s.globalJobOptions {
646-
if err := option(&j); err != nil {
664+
if err := option(&j, s.now()); err != nil {
647665
return nil, err
648666
}
649667
}
650668

651669
// apply job specific options, which take precedence
652670
for _, option := range options {
653-
if err := option(&j); err != nil {
671+
if err := option(&j, s.now()); err != nil {
654672
return nil, err
655673
}
656674
}
657675

658-
if err := definition.setup(&j, s.location, s.clock.Now()); err != nil {
676+
if err := definition.setup(&j, s.location, s.exec.clock.Now()); err != nil {
659677
return nil, err
660678
}
661679

@@ -758,7 +776,7 @@ func WithClock(clock clockwork.Clock) SchedulerOption {
758776
if clock == nil {
759777
return ErrWithClockNil
760778
}
761-
s.clock = clock
779+
s.exec.clock = clock
762780
return nil
763781
}
764782
}

0 commit comments

Comments
 (0)