forked from gochore/dcron
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cron.go
154 lines (136 loc) · 2.98 KB
/
cron.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package dcron
import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/robfig/cron/v3"
)
// CronMeta is a read only wrapper for Cron.
type CronMeta interface {
// Hostname returns current hostname.
Hostname() string
// Statistics returns statistics info of the cron's all jobs.
Statistics() Statistics
// Jobs returns the cron's all jobs as JobMeta.
Jobs() []JobMeta
}
// Cron keeps track of any number of jobs, invoking the associated func as specified.
type Cron struct {
hostname string
cron *cron.Cron
lock Lock
jobs []*innerJob
location *time.Location
context context.Context
contextCancel context.CancelFunc
}
// NewCron returns a cron with specified options.
func NewCron(options ...CronOption) *Cron {
ret := &Cron{
location: time.Local,
}
ret.hostname, _ = os.Hostname()
for _, option := range options {
option(ret)
}
ret.cron = cron.New(
cron.WithSeconds(),
cron.WithLogger(cron.DiscardLogger),
cron.WithLocation(ret.location),
)
return ret
}
// AddJobs helps to add multiple jobs.
func (c *Cron) AddJobs(jobs ...Job) error {
var errs []string
for _, job := range jobs {
if err := c.addJob(job); err != nil {
errs = append(errs, fmt.Sprintf("add job %s: %v", job.Key(), err))
}
}
if len(errs) != 0 {
return errors.New(strings.Join(errs, "; "))
}
return nil
}
func (c *Cron) addJob(job Job) error {
if job.Key() == "" {
return errors.New("empty key")
}
for _, j := range c.jobs {
if j.key == job.Key() {
return errors.New("added already")
}
}
j := &innerJob{
cron: c,
entryGetter: c.cron,
key: job.Key(),
spec: job.Spec(),
run: job.Run,
}
for _, option := range job.Options() {
option(j)
}
if j.retryTimes < 1 {
j.retryTimes = 1
}
entryID, err := c.cron.AddJob(j.Spec(), j)
if err != nil {
return err
}
j.entryID = entryID
c.jobs = append(c.jobs, j)
return nil
}
// Start the cron scheduler in its own goroutine, or no-op if already started.
func (c *Cron) Start() {
if c.context != nil {
go func() {
<-c.context.Done()
c.Stop()
}()
}
c.cron.Start()
}
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
// A context is returned so the caller can wait for running jobs to complete.
func (c *Cron) Stop() context.Context {
if c.contextCancel != nil {
c.contextCancel()
}
return c.cron.Stop()
}
// Run the cron scheduler, or no-op if already running.
func (c *Cron) Run() {
if c.context != nil {
go func() {
<-c.context.Done()
c.Stop()
}()
}
c.cron.Run()
}
// Hostname implements CronMeta.Hostname
func (c *Cron) Hostname() string {
return c.hostname
}
// Statistics implements CronMeta.Statistics
func (c *Cron) Statistics() Statistics {
ret := Statistics{}
for _, j := range c.jobs {
ret = ret.Add(j.statistics)
}
return ret
}
// Jobs implements CronMeta.Jobs
func (c *Cron) Jobs() []JobMeta {
var ret []JobMeta
for _, j := range c.jobs {
ret = append(ret, j)
}
return ret
}