Update deps
This commit is contained in:
4
vendor/github.com/go-co-op/gocron/v2/.golangci.yaml
generated
vendored
4
vendor/github.com/go-co-op/gocron/v2/.golangci.yaml
generated
vendored
@@ -20,10 +20,9 @@ issues:
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- exportloopref
|
||||
- copyloopvar
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
@@ -39,6 +38,5 @@ output:
|
||||
- format: colored-line-number
|
||||
print-issued-lines: true
|
||||
print-linter-name: true
|
||||
uniq-by-line: true
|
||||
path-prefix: ""
|
||||
sort-results: true
|
||||
|
||||
2
vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml
generated
vendored
2
vendor/github.com/go-co-op/gocron/v2/.pre-commit-config.yaml
generated
vendored
@@ -12,7 +12,7 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v1.55.2
|
||||
rev: v1.64.5
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
- repo: https://github.com/TekWizely/pre-commit-golang
|
||||
|
||||
4
vendor/github.com/go-co-op/gocron/v2/Makefile
generated
vendored
4
vendor/github.com/go-co-op/gocron/v2/Makefile
generated
vendored
@@ -6,7 +6,7 @@ fmt:
|
||||
@go list -f {{.Dir}} ./... | xargs -I{} gofmt -w -s {}
|
||||
|
||||
lint:
|
||||
@grep "^func " example_test.go | sort -c
|
||||
@grep "^func [a-zA-Z]" example_test.go | sort -c
|
||||
@golangci-lint run
|
||||
|
||||
test:
|
||||
@@ -16,7 +16,7 @@ test_coverage:
|
||||
@go test -race -v $(GO_FLAGS) -count=1 -coverprofile=coverage.out -covermode=atomic $(GO_PKGS)
|
||||
|
||||
test_ci:
|
||||
@TEST_ENV=ci go test -race -v $(GO_FLAGS) -count=1 $(GO_PKGS)
|
||||
@go test -race -v $(GO_FLAGS) -count=1 $(GO_PKGS)
|
||||
|
||||
mocks:
|
||||
@go generate ./...
|
||||
|
||||
17
vendor/github.com/go-co-op/gocron/v2/README.md
generated
vendored
17
vendor/github.com/go-co-op/gocron/v2/README.md
generated
vendored
@@ -151,6 +151,7 @@ The provided NewLogger uses the standard library's log package.
|
||||
### Metrics
|
||||
Metrics may be collected from the execution of each job.
|
||||
- [**Monitor**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#Monitor):
|
||||
- [**MonitorStatus**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#MonitorStatus) (includes status and error (if any) of the Job)
|
||||
A monitor can be used to collect metrics for each job from a scheduler.
|
||||
- Implementations: [go-co-op monitors](https://github.com/go-co-op?q=-monitor&type=all&language=&sort=)
|
||||
(don't see what you need? request on slack to get a repo created to contribute it!)
|
||||
@@ -168,8 +169,20 @@ We appreciate the support for free and open source software!
|
||||
|
||||
This project is supported by:
|
||||
|
||||
- [Jetbrains](https://www.jetbrains.com/?from=gocron)
|
||||
- [Sentry](https://sentry.io/welcome/)
|
||||
[Jetbrains](https://www.jetbrains.com/?from=gocron)
|
||||
|
||||

|
||||
|
||||
|
||||
[Sentry](https://sentry.io/welcome/)
|
||||
|
||||
<p align="left">
|
||||
<p align="left">
|
||||
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
|
||||
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84" />
|
||||
</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
4
vendor/github.com/go-co-op/gocron/v2/errors.go
generated
vendored
4
vendor/github.com/go-co-op/gocron/v2/errors.go
generated
vendored
@@ -9,6 +9,7 @@ var (
|
||||
ErrDailyJobAtTimeNil = fmt.Errorf("gocron: DailyJob: atTime within atTimes must not be nil")
|
||||
ErrDailyJobAtTimesNil = fmt.Errorf("gocron: DailyJob: atTimes must not be nil")
|
||||
ErrDailyJobHours = fmt.Errorf("gocron: DailyJob: atTimes hours must be between 0 and 23 inclusive")
|
||||
ErrDailyJobZeroInterval = fmt.Errorf("gocron: DailyJob: interval must be greater than 0")
|
||||
ErrDailyJobMinutesSeconds = fmt.Errorf("gocron: DailyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
|
||||
ErrDurationJobIntervalZero = fmt.Errorf("gocron: DurationJob: time interval is 0")
|
||||
ErrDurationRandomJobMinMax = fmt.Errorf("gocron: DurationRandomJob: minimum duration must be less than maximum duration")
|
||||
@@ -20,6 +21,7 @@ var (
|
||||
ErrMonthlyJobAtTimesNil = fmt.Errorf("gocron: MonthlyJob: atTimes must not be nil")
|
||||
ErrMonthlyJobDaysNil = fmt.Errorf("gocron: MonthlyJob: daysOfTheMonth must not be nil")
|
||||
ErrMonthlyJobHours = fmt.Errorf("gocron: MonthlyJob: atTimes hours must be between 0 and 23 inclusive")
|
||||
ErrMonthlyJobZeroInterval = fmt.Errorf("gocron: MonthlyJob: interval must be greater than 0")
|
||||
ErrMonthlyJobMinutesSeconds = fmt.Errorf("gocron: MonthlyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
|
||||
ErrNewJobTaskNil = fmt.Errorf("gocron: NewJob: Task must not be nil")
|
||||
ErrNewJobTaskNotFunc = fmt.Errorf("gocron: NewJob: Task.Function must be of kind reflect.Func")
|
||||
@@ -33,9 +35,11 @@ var (
|
||||
ErrWeeklyJobAtTimesNil = fmt.Errorf("gocron: WeeklyJob: atTimes must not be nil")
|
||||
ErrWeeklyJobDaysOfTheWeekNil = fmt.Errorf("gocron: WeeklyJob: daysOfTheWeek must not be nil")
|
||||
ErrWeeklyJobHours = fmt.Errorf("gocron: WeeklyJob: atTimes hours must be between 0 and 23 inclusive")
|
||||
ErrWeeklyJobZeroInterval = fmt.Errorf("gocron: WeeklyJob: interval must be greater than 0")
|
||||
ErrWeeklyJobMinutesSeconds = fmt.Errorf("gocron: WeeklyJob: atTimes minutes and seconds must be between 0 and 59 inclusive")
|
||||
ErrPanicRecovered = fmt.Errorf("gocron: panic recovered")
|
||||
ErrWithClockNil = fmt.Errorf("gocron: WithClock: clock must not be nil")
|
||||
ErrWithContextNil = fmt.Errorf("gocron: WithContext: context must not be nil")
|
||||
ErrWithDistributedElectorNil = fmt.Errorf("gocron: WithDistributedElector: elector must not be nil")
|
||||
ErrWithDistributedLockerNil = fmt.Errorf("gocron: WithDistributedLocker: locker must not be nil")
|
||||
ErrWithDistributedJobLockerNil = fmt.Errorf("gocron: WithDistributedJobLocker: locker must not be nil")
|
||||
|
||||
194
vendor/github.com/go-co-op/gocron/v2/executor.go
generated
vendored
194
vendor/github.com/go-co-op/gocron/v2/executor.go
generated
vendored
@@ -31,8 +31,13 @@ type executor struct {
|
||||
// used to request jobs from the scheduler
|
||||
jobOutRequest chan jobOutRequest
|
||||
|
||||
// sends out job needs to update the next runs
|
||||
jobUpdateNextRuns chan uuid.UUID
|
||||
|
||||
// used by the executor to receive a stop signal from the scheduler
|
||||
stopCh chan struct{}
|
||||
// ensure that stop runs before the next call to start and only runs once
|
||||
stopOnce *sync.Once
|
||||
// the timeout value when stopping
|
||||
stopTimeout time.Duration
|
||||
// used to signal that the executor has completed shutdown
|
||||
@@ -49,6 +54,8 @@ type executor struct {
|
||||
locker Locker
|
||||
// monitor for reporting metrics
|
||||
monitor Monitor
|
||||
// monitorStatus for reporting metrics
|
||||
monitorStatus MonitorStatus
|
||||
}
|
||||
|
||||
type jobIn struct {
|
||||
@@ -83,6 +90,7 @@ func (e *executor) start() {
|
||||
// any other uses within the executor should create a context
|
||||
// using the executor context as parent.
|
||||
e.ctx, e.cancel = context.WithCancel(context.Background())
|
||||
e.stopOnce = &sync.Once{}
|
||||
|
||||
// the standardJobsWg tracks
|
||||
standardJobsWg := &waitGroupWithMutex{}
|
||||
@@ -126,7 +134,7 @@ func (e *executor) start() {
|
||||
|
||||
// spin off into a goroutine to unblock the executor and
|
||||
// allow for processing for more work
|
||||
go func() {
|
||||
go func(executorCtx context.Context) {
|
||||
// make sure to cancel the above context per the docs
|
||||
// // Canceling this context releases resources associated with it, so code should
|
||||
// // call cancel as soon as the operations running in this Context complete.
|
||||
@@ -206,8 +214,7 @@ func (e *executor) start() {
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-e.stopCh:
|
||||
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
|
||||
case <-executorCtx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
@@ -223,7 +230,7 @@ func (e *executor) start() {
|
||||
}(*j)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}(e.ctx)
|
||||
case <-e.stopCh:
|
||||
e.stop(standardJobsWg, singletonJobsWg, limitModeJobsWg)
|
||||
return
|
||||
@@ -245,6 +252,14 @@ func (e *executor) sendOutForRescheduling(jIn *jobIn) {
|
||||
jIn.shouldSendOut = false
|
||||
}
|
||||
|
||||
func (e *executor) sendOutForNextRunUpdate(jIn *jobIn) {
|
||||
select {
|
||||
case e.jobUpdateNextRuns <- jIn.id:
|
||||
case <-e.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (e *executor) limitModeRunner(name string, in chan jobIn, wg *waitGroupWithMutex, limitMode LimitMode, rescheduleLimiter chan struct{}) {
|
||||
e.logger.Debug("gocron: limitModeRunner starting", "name", name)
|
||||
for {
|
||||
@@ -368,27 +383,42 @@ func (e *executor) runJob(j internalJob, jIn jobIn) {
|
||||
e.incrementJobCounter(j, Skip)
|
||||
return
|
||||
}
|
||||
} else if j.locker != nil {
|
||||
} else if !j.disabledLocker && j.locker != nil {
|
||||
lock, err := j.locker.Lock(j.ctx, j.name)
|
||||
if err != nil {
|
||||
_ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err)
|
||||
e.sendOutForRescheduling(&jIn)
|
||||
e.incrementJobCounter(j, Skip)
|
||||
e.sendOutForNextRunUpdate(&jIn)
|
||||
return
|
||||
}
|
||||
defer func() { _ = lock.Unlock(j.ctx) }()
|
||||
} else if e.locker != nil {
|
||||
} else if !j.disabledLocker && e.locker != nil {
|
||||
lock, err := e.locker.Lock(j.ctx, j.name)
|
||||
if err != nil {
|
||||
_ = callJobFuncWithParams(j.afterLockError, j.id, j.name, err)
|
||||
e.sendOutForRescheduling(&jIn)
|
||||
e.incrementJobCounter(j, Skip)
|
||||
e.sendOutForNextRunUpdate(&jIn)
|
||||
return
|
||||
}
|
||||
defer func() { _ = lock.Unlock(j.ctx) }()
|
||||
}
|
||||
|
||||
_ = callJobFuncWithParams(j.beforeJobRuns, j.id, j.name)
|
||||
|
||||
err := callJobFuncWithParams(j.beforeJobRunsSkipIfBeforeFuncErrors, j.id, j.name)
|
||||
if err != nil {
|
||||
e.sendOutForRescheduling(&jIn)
|
||||
|
||||
select {
|
||||
case e.jobsOutCompleted <- j.id:
|
||||
case <-e.ctx.Done():
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
e.sendOutForRescheduling(&jIn)
|
||||
select {
|
||||
case e.jobsOutCompleted <- j.id:
|
||||
@@ -396,14 +426,20 @@ func (e *executor) runJob(j internalJob, jIn jobIn) {
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
err := e.callJobWithRecover(j)
|
||||
if j.afterJobRunsWithPanic != nil {
|
||||
err = e.callJobWithRecover(j)
|
||||
} else {
|
||||
err = callJobFuncWithParams(j.function, j.parameters...)
|
||||
}
|
||||
e.recordJobTiming(startTime, time.Now(), j)
|
||||
if err != nil {
|
||||
_ = callJobFuncWithParams(j.afterJobRunsWithError, j.id, j.name, err)
|
||||
e.incrementJobCounter(j, Fail)
|
||||
e.recordJobTimingWithStatus(startTime, time.Now(), j, Fail, err)
|
||||
} else {
|
||||
_ = callJobFuncWithParams(j.afterJobRuns, j.id, j.name)
|
||||
e.incrementJobCounter(j, Success)
|
||||
e.recordJobTimingWithStatus(startTime, time.Now(), j, Success, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +462,12 @@ func (e *executor) recordJobTiming(start time.Time, end time.Time, j internalJob
|
||||
}
|
||||
}
|
||||
|
||||
func (e *executor) recordJobTimingWithStatus(start time.Time, end time.Time, j internalJob, status JobStatus, err error) {
|
||||
if e.monitorStatus != nil {
|
||||
e.monitorStatus.RecordJobTimingWithStatus(start, end, j.id, j.name, j.tags, status, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *executor) incrementJobCounter(j internalJob, status JobStatus) {
|
||||
if e.monitor != nil {
|
||||
e.monitor.IncrementJob(j.id, j.name, j.tags, status)
|
||||
@@ -433,86 +475,88 @@ func (e *executor) incrementJobCounter(j internalJob, status JobStatus) {
|
||||
}
|
||||
|
||||
func (e *executor) stop(standardJobsWg, singletonJobsWg, limitModeJobsWg *waitGroupWithMutex) {
|
||||
e.logger.Debug("gocron: stopping executor")
|
||||
// we've been asked to stop. This is either because the scheduler has been told
|
||||
// to stop all jobs or the scheduler has been asked to completely shutdown.
|
||||
//
|
||||
// cancel tells all the functions to stop their work and send in a done response
|
||||
e.cancel()
|
||||
e.stopOnce.Do(func() {
|
||||
e.logger.Debug("gocron: stopping executor")
|
||||
// we've been asked to stop. This is either because the scheduler has been told
|
||||
// to stop all jobs or the scheduler has been asked to completely shutdown.
|
||||
//
|
||||
// cancel tells all the functions to stop their work and send in a done response
|
||||
e.cancel()
|
||||
|
||||
// the wait for job channels are used to report back whether we successfully waited
|
||||
// for all jobs to complete or if we hit the configured timeout.
|
||||
waitForJobs := make(chan struct{}, 1)
|
||||
waitForSingletons := make(chan struct{}, 1)
|
||||
waitForLimitMode := make(chan struct{}, 1)
|
||||
// the wait for job channels are used to report back whether we successfully waited
|
||||
// for all jobs to complete or if we hit the configured timeout.
|
||||
waitForJobs := make(chan struct{}, 1)
|
||||
waitForSingletons := make(chan struct{}, 1)
|
||||
waitForLimitMode := make(chan struct{}, 1)
|
||||
|
||||
// the waiter context is used to cancel the functions waiting on jobs.
|
||||
// this is done to avoid goroutine leaks.
|
||||
waiterCtx, waiterCancel := context.WithCancel(context.Background())
|
||||
// the waiter context is used to cancel the functions waiting on jobs.
|
||||
// this is done to avoid goroutine leaks.
|
||||
waiterCtx, waiterCancel := context.WithCancel(context.Background())
|
||||
|
||||
// wait for standard jobs to complete
|
||||
go func() {
|
||||
e.logger.Debug("gocron: waiting for standard jobs to complete")
|
||||
// wait for standard jobs to complete
|
||||
go func() {
|
||||
// this is done in a separate goroutine, so we aren't
|
||||
// blocked by the WaitGroup's Wait call in the event
|
||||
// that the waiter context is cancelled.
|
||||
// This particular goroutine could leak in the event that
|
||||
// some long-running standard job doesn't complete.
|
||||
standardJobsWg.Wait()
|
||||
e.logger.Debug("gocron: standard jobs completed")
|
||||
waitForJobs <- struct{}{}
|
||||
e.logger.Debug("gocron: waiting for standard jobs to complete")
|
||||
go func() {
|
||||
// this is done in a separate goroutine, so we aren't
|
||||
// blocked by the WaitGroup's Wait call in the event
|
||||
// that the waiter context is cancelled.
|
||||
// This particular goroutine could leak in the event that
|
||||
// some long-running standard job doesn't complete.
|
||||
standardJobsWg.Wait()
|
||||
e.logger.Debug("gocron: standard jobs completed")
|
||||
waitForJobs <- struct{}{}
|
||||
}()
|
||||
<-waiterCtx.Done()
|
||||
}()
|
||||
<-waiterCtx.Done()
|
||||
}()
|
||||
|
||||
// wait for per job singleton limit mode runner jobs to complete
|
||||
go func() {
|
||||
e.logger.Debug("gocron: waiting for singleton jobs to complete")
|
||||
// wait for per job singleton limit mode runner jobs to complete
|
||||
go func() {
|
||||
singletonJobsWg.Wait()
|
||||
e.logger.Debug("gocron: singleton jobs completed")
|
||||
waitForSingletons <- struct{}{}
|
||||
e.logger.Debug("gocron: waiting for singleton jobs to complete")
|
||||
go func() {
|
||||
singletonJobsWg.Wait()
|
||||
e.logger.Debug("gocron: singleton jobs completed")
|
||||
waitForSingletons <- struct{}{}
|
||||
}()
|
||||
<-waiterCtx.Done()
|
||||
}()
|
||||
<-waiterCtx.Done()
|
||||
}()
|
||||
|
||||
// wait for limit mode runners to complete
|
||||
go func() {
|
||||
e.logger.Debug("gocron: waiting for limit mode jobs to complete")
|
||||
// wait for limit mode runners to complete
|
||||
go func() {
|
||||
limitModeJobsWg.Wait()
|
||||
e.logger.Debug("gocron: limitMode jobs completed")
|
||||
waitForLimitMode <- struct{}{}
|
||||
e.logger.Debug("gocron: waiting for limit mode jobs to complete")
|
||||
go func() {
|
||||
limitModeJobsWg.Wait()
|
||||
e.logger.Debug("gocron: limitMode jobs completed")
|
||||
waitForLimitMode <- struct{}{}
|
||||
}()
|
||||
<-waiterCtx.Done()
|
||||
}()
|
||||
<-waiterCtx.Done()
|
||||
}()
|
||||
|
||||
// now either wait for all the jobs to complete,
|
||||
// or hit the timeout.
|
||||
var count int
|
||||
timeout := time.Now().Add(e.stopTimeout)
|
||||
for time.Now().Before(timeout) && count < 3 {
|
||||
select {
|
||||
case <-waitForJobs:
|
||||
count++
|
||||
case <-waitForSingletons:
|
||||
count++
|
||||
case <-waitForLimitMode:
|
||||
count++
|
||||
default:
|
||||
// now either wait for all the jobs to complete,
|
||||
// or hit the timeout.
|
||||
var count int
|
||||
timeout := time.Now().Add(e.stopTimeout)
|
||||
for time.Now().Before(timeout) && count < 3 {
|
||||
select {
|
||||
case <-waitForJobs:
|
||||
count++
|
||||
case <-waitForSingletons:
|
||||
count++
|
||||
case <-waitForLimitMode:
|
||||
count++
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
if count < 3 {
|
||||
e.done <- ErrStopJobsTimedOut
|
||||
e.logger.Debug("gocron: executor stopped - timed out")
|
||||
} else {
|
||||
e.done <- nil
|
||||
e.logger.Debug("gocron: executor stopped")
|
||||
}
|
||||
waiterCancel()
|
||||
if count < 3 {
|
||||
e.done <- ErrStopJobsTimedOut
|
||||
e.logger.Debug("gocron: executor stopped - timed out")
|
||||
} else {
|
||||
e.done <- nil
|
||||
e.logger.Debug("gocron: executor stopped")
|
||||
}
|
||||
waiterCancel()
|
||||
|
||||
if e.limitMode != nil {
|
||||
e.limitMode.started = false
|
||||
}
|
||||
if e.limitMode != nil {
|
||||
e.limitMode.started = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
190
vendor/github.com/go-co-op/gocron/v2/job.go
generated
vendored
190
vendor/github.com/go-co-op/gocron/v2/job.go
generated
vendored
@@ -6,23 +6,25 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/robfig/cron/v3"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// internalJob stores the information needed by the scheduler
|
||||
// to manage scheduling, starting and stopping the job
|
||||
type internalJob struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
id uuid.UUID
|
||||
name string
|
||||
tags []string
|
||||
ctx context.Context
|
||||
parentCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
id uuid.UUID
|
||||
name string
|
||||
tags []string
|
||||
cron Cron
|
||||
jobSchedule
|
||||
|
||||
// as some jobs may queue up, it's possible to
|
||||
@@ -40,11 +42,13 @@ type internalJob struct {
|
||||
startImmediately bool
|
||||
stopTime time.Time
|
||||
// event listeners
|
||||
afterJobRuns func(jobID uuid.UUID, jobName string)
|
||||
beforeJobRuns func(jobID uuid.UUID, jobName string)
|
||||
afterJobRunsWithError func(jobID uuid.UUID, jobName string, err error)
|
||||
afterJobRunsWithPanic func(jobID uuid.UUID, jobName string, recoverData any)
|
||||
afterLockError func(jobID uuid.UUID, jobName string, err error)
|
||||
afterJobRuns func(jobID uuid.UUID, jobName string)
|
||||
beforeJobRuns func(jobID uuid.UUID, jobName string)
|
||||
beforeJobRunsSkipIfBeforeFuncErrors func(jobID uuid.UUID, jobName string) error
|
||||
afterJobRunsWithError func(jobID uuid.UUID, jobName string, err error)
|
||||
afterJobRunsWithPanic func(jobID uuid.UUID, jobName string, recoverData any)
|
||||
afterLockError func(jobID uuid.UUID, jobName string, err error)
|
||||
disabledLocker bool
|
||||
|
||||
locker Locker
|
||||
}
|
||||
@@ -80,6 +84,10 @@ type task struct {
|
||||
type Task func() task
|
||||
|
||||
// NewTask provides the job's task function and parameters.
|
||||
// If you set the first argument of your Task func to be a context.Context,
|
||||
// gocron will pass in a context (either the default Job context, or one
|
||||
// provided via WithContext) to the job and will cancel the context on shutdown.
|
||||
// This allows you to listen for and handle cancellation within your job.
|
||||
func NewTask(function any, parameters ...any) Task {
|
||||
return func() task {
|
||||
return task{
|
||||
@@ -97,6 +105,20 @@ type limitRunsTo struct {
|
||||
runCount uint
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// -----------------------------------------------
|
||||
// --------------- Custom Cron -------------------
|
||||
// -----------------------------------------------
|
||||
// -----------------------------------------------
|
||||
|
||||
// Cron defines the interface that must be
|
||||
// implemented to provide a custom cron implementation for
|
||||
// the job. Pass in the implementation using the JobOption WithCronImplementation.
|
||||
type Cron interface {
|
||||
IsValid(crontab string, location *time.Location, now time.Time) error
|
||||
Next(lastRun time.Time) time.Time
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// -----------------------------------------------
|
||||
// --------------- Job Variants ------------------
|
||||
@@ -109,21 +131,29 @@ type JobDefinition interface {
|
||||
setup(j *internalJob, l *time.Location, now time.Time) error
|
||||
}
|
||||
|
||||
var _ JobDefinition = (*cronJobDefinition)(nil)
|
||||
// Default cron implementation
|
||||
|
||||
type cronJobDefinition struct {
|
||||
crontab string
|
||||
withSeconds bool
|
||||
func newDefaultCronImplementation(withSeconds bool) Cron {
|
||||
return &defaultCron{
|
||||
withSeconds: withSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now time.Time) error {
|
||||
var _ Cron = (*defaultCron)(nil)
|
||||
|
||||
type defaultCron struct {
|
||||
cronSchedule cron.Schedule
|
||||
withSeconds bool
|
||||
}
|
||||
|
||||
func (c *defaultCron) IsValid(crontab string, location *time.Location, now time.Time) error {
|
||||
var withLocation string
|
||||
if strings.HasPrefix(c.crontab, "TZ=") || strings.HasPrefix(c.crontab, "CRON_TZ=") {
|
||||
withLocation = c.crontab
|
||||
if strings.HasPrefix(crontab, "TZ=") || strings.HasPrefix(crontab, "CRON_TZ=") {
|
||||
withLocation = crontab
|
||||
} else {
|
||||
// since the user didn't provide a timezone default to the location
|
||||
// passed in by the scheduler. Default: time.Local
|
||||
withLocation = fmt.Sprintf("CRON_TZ=%s %s", location.String(), c.crontab)
|
||||
withLocation = fmt.Sprintf("CRON_TZ=%s %s", location.String(), crontab)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -143,8 +173,32 @@ func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now ti
|
||||
if cronSchedule.Next(now).IsZero() {
|
||||
return ErrCronJobInvalid
|
||||
}
|
||||
c.cronSchedule = cronSchedule
|
||||
return nil
|
||||
}
|
||||
|
||||
j.jobSchedule = &cronJob{cronSchedule: cronSchedule}
|
||||
func (c *defaultCron) Next(lastRun time.Time) time.Time {
|
||||
return c.cronSchedule.Next(lastRun)
|
||||
}
|
||||
|
||||
// default cron job implementation
|
||||
var _ JobDefinition = (*cronJobDefinition)(nil)
|
||||
|
||||
type cronJobDefinition struct {
|
||||
crontab string
|
||||
cron Cron
|
||||
}
|
||||
|
||||
func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now time.Time) error {
|
||||
if j.cron != nil {
|
||||
c.cron = j.cron
|
||||
}
|
||||
|
||||
if err := c.cron.IsValid(c.crontab, location, now); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
j.jobSchedule = &cronJob{crontab: c.crontab, cronSchedule: c.cron}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -156,8 +210,8 @@ func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now ti
|
||||
// `CRON_TZ=America/Chicago * * * * *`
|
||||
func CronJob(crontab string, withSeconds bool) JobDefinition {
|
||||
return cronJobDefinition{
|
||||
crontab: crontab,
|
||||
withSeconds: withSeconds,
|
||||
crontab: crontab,
|
||||
cron: newDefaultCronImplementation(withSeconds),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +306,10 @@ func (d dailyJobDefinition) setup(j *internalJob, location *time.Location, _ tim
|
||||
return ErrDailyJobMinutesSeconds
|
||||
}
|
||||
|
||||
if d.interval == 0 {
|
||||
return ErrDailyJobZeroInterval
|
||||
}
|
||||
|
||||
ds := dailyJob{
|
||||
interval: d.interval,
|
||||
atTimes: atTimesDate,
|
||||
@@ -270,6 +328,9 @@ type weeklyJobDefinition struct {
|
||||
|
||||
func (w weeklyJobDefinition) setup(j *internalJob, location *time.Location, _ time.Time) error {
|
||||
var ws weeklyJob
|
||||
if w.interval == 0 {
|
||||
return ErrWeeklyJobZeroInterval
|
||||
}
|
||||
ws.interval = w.interval
|
||||
|
||||
if w.daysOfTheWeek == nil {
|
||||
@@ -304,8 +365,7 @@ type Weekdays func() []time.Weekday
|
||||
// NewWeekdays provide the days of the week the job should run.
|
||||
func NewWeekdays(weekday time.Weekday, weekdays ...time.Weekday) Weekdays {
|
||||
return func() []time.Weekday {
|
||||
weekdays = append(weekdays, weekday)
|
||||
return weekdays
|
||||
return append([]time.Weekday{weekday}, weekdays...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,6 +395,9 @@ type monthlyJobDefinition struct {
|
||||
|
||||
func (m monthlyJobDefinition) setup(j *internalJob, location *time.Location, _ time.Time) error {
|
||||
var ms monthlyJob
|
||||
if m.interval == 0 {
|
||||
return ErrMonthlyJobZeroInterval
|
||||
}
|
||||
ms.interval = m.interval
|
||||
|
||||
if m.daysOfTheMonth == nil {
|
||||
@@ -353,11 +416,9 @@ func (m monthlyJobDefinition) setup(j *internalJob, location *time.Location, _ t
|
||||
}
|
||||
}
|
||||
daysStart = removeSliceDuplicatesInt(daysStart)
|
||||
slices.Sort(daysStart)
|
||||
ms.days = daysStart
|
||||
|
||||
daysEnd = removeSliceDuplicatesInt(daysEnd)
|
||||
slices.Sort(daysEnd)
|
||||
ms.daysFromEnd = daysEnd
|
||||
|
||||
atTimesDate, err := convertAtTimesToDateTime(m.atTimes, location)
|
||||
@@ -390,8 +451,7 @@ type DaysOfTheMonth func() days
|
||||
// -5 == 5 days before the end of the month.
|
||||
func NewDaysOfTheMonth(day int, moreDays ...int) DaysOfTheMonth {
|
||||
return func() days {
|
||||
moreDays = append(moreDays, day)
|
||||
return moreDays
|
||||
return append([]int{day}, moreDays...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +463,14 @@ func (a atTime) time(location *time.Location) time.Time {
|
||||
return time.Date(0, 0, 0, int(a.hours), int(a.minutes), int(a.seconds), 0, location)
|
||||
}
|
||||
|
||||
// TimeFromAtTime is a helper function to allow converting AtTime into a time.Time value
|
||||
// Note: the time.Time value will have zero values for all Time fields except Hours, Minutes, Seconds.
|
||||
//
|
||||
// For example: time.Date(0, 0, 0, 1, 1, 1, 0, time.UTC)
|
||||
func TimeFromAtTime(at AtTime, loc *time.Location) time.Time {
|
||||
return at().time(loc)
|
||||
}
|
||||
|
||||
// AtTime defines a function that returns the internal atTime
|
||||
type AtTime func() atTime
|
||||
|
||||
@@ -421,8 +489,7 @@ type AtTimes func() []AtTime
|
||||
// the job should be run
|
||||
func NewAtTimes(atTime AtTime, atTimes ...AtTime) AtTimes {
|
||||
return func() []AtTime {
|
||||
atTimes = append(atTimes, atTime)
|
||||
return atTimes
|
||||
return append([]AtTime{atTime}, atTimes...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,6 +608,16 @@ func WithDistributedJobLocker(locker Locker) JobOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisabledDistributedJobLocker disables the distributed job locker.
|
||||
// This is useful when a global distributed locker has been set on the scheduler
|
||||
// level using WithDistributedLocker and need to be disabled for specific jobs.
|
||||
func WithDisabledDistributedJobLocker(disabled bool) JobOption {
|
||||
return func(j *internalJob, _ time.Time) error {
|
||||
j.disabledLocker = disabled
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithEventListeners sets the event listeners that should be
|
||||
// run for the job.
|
||||
func WithEventListeners(eventListeners ...EventListener) JobOption {
|
||||
@@ -578,6 +655,15 @@ func WithName(name string) JobOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithCronImplementation sets the custom Cron implementation for the job.
|
||||
// This is only utilized for the CronJob type.
|
||||
func WithCronImplementation(c Cron) JobOption {
|
||||
return func(j *internalJob, _ time.Time) error {
|
||||
j.cron = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSingletonMode keeps the job from running again if it is already running.
|
||||
// This is useful for jobs that should not overlap, and that occasionally
|
||||
// (but not consistently) run longer than the interval between job runs.
|
||||
@@ -676,6 +762,22 @@ func WithIdentifier(id uuid.UUID) JobOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext sets the parent context for the job.
|
||||
// If you set the first argument of your Task func to be a context.Context,
|
||||
// gocron will pass in the provided context to the job and will cancel the
|
||||
// context on shutdown. If you cancel the context the job will no longer be
|
||||
// scheduled as well. This allows you to both control the job via a context
|
||||
// and listen for and handle cancellation within your job.
|
||||
func WithContext(ctx context.Context) JobOption {
|
||||
return func(j *internalJob, _ time.Time) error {
|
||||
if ctx == nil {
|
||||
return ErrWithContextNil
|
||||
}
|
||||
j.parentCtx = ctx
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// -----------------------------------------------
|
||||
// ------------- Job Event Listeners -------------
|
||||
@@ -698,6 +800,19 @@ func BeforeJobRuns(eventListenerFunc func(jobID uuid.UUID, jobName string)) Even
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeJobRunsSkipIfBeforeFuncErrors is used to listen for when a job is about to run and
|
||||
// then runs the provided function. If the provided function returns an error, the job will be
|
||||
// rescheduled and the current run will be skipped.
|
||||
func BeforeJobRunsSkipIfBeforeFuncErrors(eventListenerFunc func(jobID uuid.UUID, jobName string) error) EventListener {
|
||||
return func(j *internalJob) error {
|
||||
if eventListenerFunc == nil {
|
||||
return ErrEventListenerFuncNil
|
||||
}
|
||||
j.beforeJobRunsSkipIfBeforeFuncErrors = eventListenerFunc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AfterJobRuns is used to listen for when a job has run
|
||||
// without an error, and then run the provided function.
|
||||
func AfterJobRuns(eventListenerFunc func(jobID uuid.UUID, jobName string)) EventListener {
|
||||
@@ -759,7 +874,8 @@ type jobSchedule interface {
|
||||
var _ jobSchedule = (*cronJob)(nil)
|
||||
|
||||
type cronJob struct {
|
||||
cronSchedule cron.Schedule
|
||||
crontab string
|
||||
cronSchedule Cron
|
||||
}
|
||||
|
||||
func (j *cronJob) next(lastRun time.Time) time.Time {
|
||||
@@ -803,7 +919,7 @@ func (d dailyJob) next(lastRun time.Time) time.Time {
|
||||
}
|
||||
firstPass = false
|
||||
|
||||
startNextDay := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day()+int(d.interval), 0, 0, 0, lastRun.Nanosecond(), lastRun.Location())
|
||||
startNextDay := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day()+int(d.interval), 0, 0, 0, 0, lastRun.Location())
|
||||
return d.nextDay(startNextDay, firstPass)
|
||||
}
|
||||
|
||||
@@ -811,7 +927,7 @@ func (d dailyJob) nextDay(lastRun time.Time, firstPass bool) time.Time {
|
||||
for _, at := range d.atTimes {
|
||||
// sub the at time hour/min/sec onto the lastScheduledRun's values
|
||||
// to use in checks to see if we've got our next run time
|
||||
atDate := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day(), at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
|
||||
atDate := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day(), at.Hour(), at.Minute(), at.Second(), 0, lastRun.Location())
|
||||
|
||||
if firstPass && atDate.After(lastRun) {
|
||||
// checking to see if it is after i.e. greater than,
|
||||
@@ -857,7 +973,7 @@ func (w weeklyJob) nextWeekDayAtTime(lastRun time.Time, firstPass bool) time.Tim
|
||||
for _, at := range w.atTimes {
|
||||
// sub the at time hour/min/sec onto the lastScheduledRun's values
|
||||
// to use in checks to see if we've got our next run time
|
||||
atDate := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day()+int(weekDayDiff), at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
|
||||
atDate := time.Date(lastRun.Year(), lastRun.Month(), lastRun.Day()+int(weekDayDiff), at.Hour(), at.Minute(), at.Second(), 0, lastRun.Location())
|
||||
|
||||
if firstPass && atDate.After(lastRun) {
|
||||
// checking to see if it is after i.e. greater than,
|
||||
@@ -925,7 +1041,7 @@ func (m monthlyJob) nextMonthDayAtTime(lastRun time.Time, days []int, firstPass
|
||||
for _, at := range m.atTimes {
|
||||
// sub the day, and the at time hour/min/sec onto the lastScheduledRun's values
|
||||
// to use in checks to see if we've got our next run time
|
||||
atDate := time.Date(lastRun.Year(), lastRun.Month(), day, at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
|
||||
atDate := time.Date(lastRun.Year(), lastRun.Month(), day, at.Hour(), at.Minute(), at.Second(), 0, lastRun.Location())
|
||||
|
||||
if atDate.Month() != lastRun.Month() {
|
||||
// this check handles if we're setting a day not in the current month
|
||||
@@ -1089,12 +1205,14 @@ func (j job) RunNow() error {
|
||||
defer cancel()
|
||||
resp := make(chan error, 1)
|
||||
|
||||
t := time.NewTimer(100 * time.Millisecond)
|
||||
select {
|
||||
case j.runJobRequest <- runJobRequest{
|
||||
id: j.id,
|
||||
outChan: resp,
|
||||
}:
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Stop()
|
||||
case <-t.C:
|
||||
return ErrJobRunNowFailed
|
||||
}
|
||||
var err error
|
||||
|
||||
8
vendor/github.com/go-co-op/gocron/v2/monitor.go
generated
vendored
8
vendor/github.com/go-co-op/gocron/v2/monitor.go
generated
vendored
@@ -26,3 +26,11 @@ type Monitor interface {
|
||||
// to handle instantiating and recording the value
|
||||
RecordJobTiming(startTime, endTime time.Time, id uuid.UUID, name string, tags []string)
|
||||
}
|
||||
|
||||
// MonitorStatus extends RecordJobTiming with the job status.
|
||||
type MonitorStatus interface {
|
||||
Monitor
|
||||
// RecordJobTimingWithStatus will provide details about the job, its status, error and the timing and expects the underlying implementation
|
||||
// to handle instantiating and recording the value
|
||||
RecordJobTimingWithStatus(startTime, endTime time.Time, id uuid.UUID, name string, tags []string, status JobStatus, err error)
|
||||
}
|
||||
|
||||
125
vendor/github.com/go-co-op/gocron/v2/scheduler.go
generated
vendored
125
vendor/github.com/go-co-op/gocron/v2/scheduler.go
generated
vendored
@@ -5,11 +5,12 @@ import (
|
||||
"context"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var _ Scheduler = (*scheduler)(nil)
|
||||
@@ -21,6 +22,10 @@ type Scheduler interface {
|
||||
// NewJob creates a new job in the Scheduler. The job is scheduled per the provided
|
||||
// definition when the Scheduler is started. If the Scheduler is already running
|
||||
// the job will be scheduled when the Scheduler is started.
|
||||
// If you set the first argument of your Task func to be a context.Context,
|
||||
// gocron will pass in a context (either the default Job context, or one
|
||||
// provided via WithContext) to the job and will cancel the context on shutdown.
|
||||
// This allows you to listen for and handle cancellation within your job.
|
||||
NewJob(JobDefinition, Task, ...JobOption) (Job, error)
|
||||
// RemoveByTags removes all jobs that have at least one of the provided tags.
|
||||
RemoveByTags(...string)
|
||||
@@ -133,9 +138,10 @@ func NewScheduler(options ...SchedulerOption) (Scheduler, error) {
|
||||
|
||||
jobsIn: make(chan jobIn),
|
||||
jobsOutForRescheduling: make(chan uuid.UUID),
|
||||
jobUpdateNextRuns: make(chan uuid.UUID),
|
||||
jobsOutCompleted: make(chan uuid.UUID),
|
||||
jobOutRequest: make(chan jobOutRequest, 1000),
|
||||
done: make(chan error),
|
||||
done: make(chan error, 1),
|
||||
}
|
||||
|
||||
s := &scheduler{
|
||||
@@ -171,7 +177,8 @@ func NewScheduler(options ...SchedulerOption) (Scheduler, error) {
|
||||
select {
|
||||
case id := <-s.exec.jobsOutForRescheduling:
|
||||
s.selectExecJobsOutForRescheduling(id)
|
||||
|
||||
case id := <-s.exec.jobUpdateNextRuns:
|
||||
s.updateNextScheduled(id)
|
||||
case id := <-s.exec.jobsOutCompleted:
|
||||
s.selectExecJobsOutCompleted(id)
|
||||
|
||||
@@ -233,20 +240,34 @@ func (s *scheduler) stopScheduler() {
|
||||
for _, j := range s.jobs {
|
||||
j.stop()
|
||||
}
|
||||
for id, j := range s.jobs {
|
||||
for _, j := range s.jobs {
|
||||
<-j.ctx.Done()
|
||||
|
||||
j.ctx, j.cancel = context.WithCancel(s.shutdownCtx)
|
||||
s.jobs[id] = j
|
||||
}
|
||||
var err error
|
||||
if s.started {
|
||||
t := time.NewTimer(s.exec.stopTimeout + 1*time.Second)
|
||||
select {
|
||||
case err = <-s.exec.done:
|
||||
case <-time.After(s.exec.stopTimeout + 1*time.Second):
|
||||
t.Stop()
|
||||
case <-t.C:
|
||||
err = ErrStopExecutorTimedOut
|
||||
}
|
||||
}
|
||||
for id, j := range s.jobs {
|
||||
oldCtx := j.ctx
|
||||
if j.parentCtx == nil {
|
||||
j.parentCtx = s.shutdownCtx
|
||||
}
|
||||
j.ctx, j.cancel = context.WithCancel(j.parentCtx)
|
||||
|
||||
// also replace the old context with the new one in the parameters
|
||||
if len(j.parameters) > 0 && j.parameters[0] == oldCtx {
|
||||
j.parameters[0] = j.ctx
|
||||
}
|
||||
|
||||
s.jobs[id] = j
|
||||
}
|
||||
|
||||
s.stopErrCh <- err
|
||||
s.started = false
|
||||
s.logger.Debug("gocron: scheduler stopped")
|
||||
@@ -261,14 +282,7 @@ func (s *scheduler) selectAllJobsOutRequest(out allJobsOutRequest) {
|
||||
}
|
||||
slices.SortFunc(outJobs, func(a, b Job) int {
|
||||
aID, bID := a.ID().String(), b.ID().String()
|
||||
switch {
|
||||
case aID < bID:
|
||||
return -1
|
||||
case aID > bID:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return strings.Compare(aID, bID)
|
||||
})
|
||||
select {
|
||||
case <-s.shutdownCtx.Done():
|
||||
@@ -329,7 +343,7 @@ func (s *scheduler) selectExecJobsOutForRescheduling(id uuid.UUID) {
|
||||
return
|
||||
}
|
||||
|
||||
scheduleFrom := j.lastRun
|
||||
var scheduleFrom time.Time
|
||||
if len(j.nextScheduled) > 0 {
|
||||
// always grab the last element in the slice as that is the furthest
|
||||
// out in the future and the time from which we want to calculate
|
||||
@@ -360,6 +374,15 @@ func (s *scheduler) selectExecJobsOutForRescheduling(id uuid.UUID) {
|
||||
}
|
||||
}
|
||||
|
||||
if slices.Contains(j.nextScheduled, next) {
|
||||
// if the next value is a duplicate of what's already in the nextScheduled slice, for example:
|
||||
// - the job is being rescheduled off the same next run value as before
|
||||
// increment to the next, next value
|
||||
for slices.Contains(j.nextScheduled, next) {
|
||||
next = j.next(next)
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any existing timer to prevent leaks
|
||||
if j.timer != nil {
|
||||
j.timer.Stop()
|
||||
@@ -384,6 +407,22 @@ func (s *scheduler) selectExecJobsOutForRescheduling(id uuid.UUID) {
|
||||
s.jobs[id] = j
|
||||
}
|
||||
|
||||
func (s *scheduler) updateNextScheduled(id uuid.UUID) {
|
||||
j, ok := s.jobs[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var newNextScheduled []time.Time
|
||||
for _, t := range j.nextScheduled {
|
||||
if t.Before(s.now()) {
|
||||
continue
|
||||
}
|
||||
newNextScheduled = append(newNextScheduled, t)
|
||||
}
|
||||
j.nextScheduled = newNextScheduled
|
||||
s.jobs[id] = j
|
||||
}
|
||||
|
||||
func (s *scheduler) selectExecJobsOutCompleted(id uuid.UUID) {
|
||||
j, ok := s.jobs[id]
|
||||
if !ok {
|
||||
@@ -646,8 +685,6 @@ func (s *scheduler) addOrUpdateJob(id uuid.UUID, definition JobDefinition, taskW
|
||||
j.id = id
|
||||
}
|
||||
|
||||
j.ctx, j.cancel = context.WithCancel(s.shutdownCtx)
|
||||
|
||||
if taskWrapper == nil {
|
||||
return nil, ErrNewJobTaskNil
|
||||
}
|
||||
@@ -662,10 +699,6 @@ func (s *scheduler) addOrUpdateJob(id uuid.UUID, definition JobDefinition, taskW
|
||||
return nil, ErrNewJobTaskNotFunc
|
||||
}
|
||||
|
||||
if err := s.verifyParameterType(taskFunc, tsk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
j.name = runtime.FuncForPC(taskFunc.Pointer()).Name()
|
||||
j.function = tsk.function
|
||||
j.parameters = tsk.parameters
|
||||
@@ -684,6 +717,28 @@ func (s *scheduler) addOrUpdateJob(id uuid.UUID, definition JobDefinition, taskW
|
||||
}
|
||||
}
|
||||
|
||||
if j.parentCtx == nil {
|
||||
j.parentCtx = s.shutdownCtx
|
||||
}
|
||||
j.ctx, j.cancel = context.WithCancel(j.parentCtx)
|
||||
|
||||
if !taskFunc.IsZero() && taskFunc.Type().NumIn() > 0 {
|
||||
// if the first parameter is a context.Context and params have no context.Context, add current ctx to the params
|
||||
if taskFunc.Type().In(0) == reflect.TypeOf((*context.Context)(nil)).Elem() {
|
||||
if len(tsk.parameters) == 0 {
|
||||
tsk.parameters = []any{j.ctx}
|
||||
j.parameters = []any{j.ctx}
|
||||
} else if _, ok := tsk.parameters[0].(context.Context); !ok {
|
||||
tsk.parameters = append([]any{j.ctx}, tsk.parameters...)
|
||||
j.parameters = append([]any{j.ctx}, j.parameters...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.verifyParameterType(taskFunc, tsk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := definition.setup(&j, s.location, s.exec.clock.Now()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -741,20 +796,27 @@ func (s *scheduler) StopJobs() error {
|
||||
return nil
|
||||
case s.stopCh <- struct{}{}:
|
||||
}
|
||||
|
||||
t := time.NewTimer(s.exec.stopTimeout + 2*time.Second)
|
||||
select {
|
||||
case err := <-s.stopErrCh:
|
||||
t.Stop()
|
||||
return err
|
||||
case <-time.After(s.exec.stopTimeout + 2*time.Second):
|
||||
case <-t.C:
|
||||
return ErrStopSchedulerTimedOut
|
||||
}
|
||||
}
|
||||
|
||||
func (s *scheduler) Shutdown() error {
|
||||
s.shutdownCancel()
|
||||
|
||||
t := time.NewTimer(s.exec.stopTimeout + 2*time.Second)
|
||||
select {
|
||||
case err := <-s.stopErrCh:
|
||||
|
||||
t.Stop()
|
||||
return err
|
||||
case <-time.After(s.exec.stopTimeout + 2*time.Second):
|
||||
case <-t.C:
|
||||
return ErrStopSchedulerTimedOut
|
||||
}
|
||||
}
|
||||
@@ -809,6 +871,8 @@ func WithDistributedElector(elector Elector) SchedulerOption {
|
||||
// WithDistributedLocker sets the locker to be used by multiple
|
||||
// Scheduler instances to ensure that only one instance of each
|
||||
// job is run.
|
||||
// To disable this global locker for specific jobs, see
|
||||
// WithDisabledDistributedJobLocker.
|
||||
func WithDistributedLocker(locker Locker) SchedulerOption {
|
||||
return func(s *scheduler) error {
|
||||
if locker == nil {
|
||||
@@ -945,3 +1009,14 @@ func WithMonitor(monitor Monitor) SchedulerOption {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMonitorStatus sets the metrics provider to be used by the Scheduler.
|
||||
func WithMonitorStatus(monitor MonitorStatus) SchedulerOption {
|
||||
return func(s *scheduler) error {
|
||||
if monitor == nil {
|
||||
return ErrWithMonitorNil
|
||||
}
|
||||
s.exec.monitorStatus = monitor
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
11
vendor/github.com/go-co-op/gocron/v2/util.go
generated
vendored
11
vendor/github.com/go-co-op/gocron/v2/util.go
generated
vendored
@@ -3,12 +3,11 @@ package gocron
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func callJobFuncWithParams(jobFunc any, params ...any) error {
|
||||
@@ -63,12 +62,8 @@ func requestJobCtx(ctx context.Context, id uuid.UUID, ch chan jobOutRequest) *in
|
||||
}
|
||||
|
||||
func removeSliceDuplicatesInt(in []int) []int {
|
||||
m := make(map[int]struct{})
|
||||
|
||||
for _, i := range in {
|
||||
m[i] = struct{}{}
|
||||
}
|
||||
return maps.Keys(m)
|
||||
slices.Sort(in)
|
||||
return slices.Compact(in)
|
||||
}
|
||||
|
||||
func convertAtTimesToDateTime(atTimes AtTimes, location *time.Location) ([]time.Time, error) {
|
||||
|
||||
Reference in New Issue
Block a user