}~n+Zm1glLsniSaukF#9y*lG1XYQg4G7PEhMymy6eZ!Q(OOvPAzL$~?nu-z
z2qvMF>uR{C9JRE4;k3GraK?inYoM*cBR
zn;{fipOms~d(r6#EF!u6fIesNn+YQ<*E!Y2sjw~y;eH@vfzek48WurVqN}
zrIQ3dH&Tiau=khMPU!jM4!-ayE)CmZ-3snLdf}?eTz@ThQ!rSKJRuacdH*0R2-4IHi9
zXD33#J@-;I5pxup&;IU+gq_E&cuS;7_--Qv-dINly}SuOAw|0Y##qy5YbeJ9--;T=
z_VrI8bHa_P>Y_s61KiClswdunf61+!$Wlz4VvyYtkJ8!oNWcq@vEKesafT{K1v9
z?sp*LJMv^$CR{-^YDN#a#oMKe_FHb?eva_JEj)^zq-Q;=Jzdj*5`bDj)yWv@@Q|~q
zx_7!Vi%6zcJxu@
zSWf;EM{3mVzun>Ki9wY~9y`xF#V&dX|1Bf_u!apKOp!AkU>tc<&fRk^_4yJCxz`st
zqb|aH8}Z=7PzK=NYdo$R*jvZ|cR%y#b!4UX8zSsKk)Um|7#n*Y^#*6HO!>ZcGH%(%
z4v$4P?ej8#WL>;cyVFLCJh@i2!Jtdys^@0f!>v*`)#OX1Y&hHvlpZE}9j68OH6Cv{~EH61iCx!BLLo)*MT0(VlD;vch{ss->-*k7KHdf@#b&%C_;InJeEjbLDE5
zNnra=snb=-eS5BN3J;3FZ1HY2$v`XA0Ct5{x`biejw-;OVy(OlL1owPwP+sq1|Z9|
z01!IeVa_QvdVt2n8^UVzUtYPaS*N{~E}C?w!DTa~r7r
Qu;*I=oT;rzy%8b)KL^VBVE_OC
literal 0
HcmV?d00001
From eb4dfe670086a9a4ac184fc525c4a6135f94fe4c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 29 Apr 2025 10:43:10 -0500
Subject: [PATCH 2/5] go 1.23 & golangci-lint v2 (#843)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Roesler
---
.github/workflows/go_test.yml | 6 +--
.golangci.bck.yaml | 42 ++++++++++++++++++++
.golangci.yaml | 73 ++++++++++++++++++++---------------
.pre-commit-config.yaml | 4 +-
go.mod | 2 +-
scheduler_test.go | 20 ++--------
6 files changed, 92 insertions(+), 55 deletions(-)
create mode 100644 .golangci.bck.yaml
diff --git a/.github/workflows/go_test.yml b/.github/workflows/go_test.yml
index 2aba0d2..042191f 100644
--- a/.github/workflows/go_test.yml
+++ b/.github/workflows/go_test.yml
@@ -12,8 +12,6 @@
strategy:
matrix:
go-version:
- - "1.21"
- - "1.22"
- "1.23"
- "1.24"
name: lint and test
@@ -26,8 +24,8 @@
with:
go-version: ${{ matrix.go-version }}
- name: golangci-lint
- uses: golangci/golangci-lint-action@v6.5.0
+ uses: golangci/golangci-lint-action@v7.0.0
with:
- version: v1.64.5
+ version: v2.1.5
- name: test
run: make test_ci
diff --git a/.golangci.bck.yaml b/.golangci.bck.yaml
new file mode 100644
index 0000000..e4f0cc1
--- /dev/null
+++ b/.golangci.bck.yaml
@@ -0,0 +1,42 @@
+run:
+ timeout: 5m
+ issues-exit-code: 1
+ tests: true
+
+issues:
+ max-same-issues: 100
+ include:
+ - EXC0012
+ - EXC0014
+ exclude-dirs:
+ - local
+ exclude-rules:
+ - path: example_test.go
+ linters:
+ - revive
+ text: "seems to be unused"
+ fix: true
+
+linters:
+ enable:
+ - bodyclose
+ - copyloopvar
+ - gofumpt
+ - goimports
+ - gosimple
+ - govet
+ - ineffassign
+ - misspell
+ - revive
+ - staticcheck
+ - typecheck
+ - unused
+ - whitespace
+
+output:
+ formats:
+ - format: colored-line-number
+ print-issued-lines: true
+ print-linter-name: true
+ path-prefix: ""
+ sort-results: true
diff --git a/.golangci.yaml b/.golangci.yaml
index e4f0cc1..33ebebf 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -1,42 +1,51 @@
+version: "2"
run:
- timeout: 5m
issues-exit-code: 1
tests: true
-
-issues:
- max-same-issues: 100
- include:
- - EXC0012
- - EXC0014
- exclude-dirs:
- - local
- exclude-rules:
- - path: example_test.go
- linters:
- - revive
- text: "seems to be unused"
- fix: true
-
+output:
+ formats:
+ text:
+ path: stdout
+ print-linter-name: true
+ print-issued-lines: true
+ path-prefix: ""
linters:
enable:
- bodyclose
- copyloopvar
- - gofumpt
- - goimports
- - gosimple
- - govet
- - ineffassign
- misspell
- revive
- - staticcheck
- - typecheck
- - unused
- whitespace
-
-output:
- formats:
- - format: colored-line-number
- print-issued-lines: true
- print-linter-name: true
- path-prefix: ""
- sort-results: true
+ exclusions:
+ generated: lax
+ presets:
+ - common-false-positives
+ - legacy
+ - std-error-handling
+ rules:
+ - linters:
+ - revive
+ path: example_test.go
+ text: seems to be unused
+ - linters:
+ - revive
+ text: package-comments
+ paths:
+ - local
+ - third_party$
+ - builtin$
+ - examples$
+issues:
+ max-same-issues: 100
+ fix: true
+formatters:
+ enable:
+ - gofumpt
+ - goimports
+ exclusions:
+ generated: lax
+ paths:
+ - local
+ - third_party$
+ - builtin$
+ - examples$
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9e57a00..125f461 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.5.0
+ rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
@@ -12,7 +12,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/golangci/golangci-lint
- rev: v1.64.5
+ rev: v2.1.5
hooks:
- id: golangci-lint
- repo: https://github.com/TekWizely/pre-commit-golang
diff --git a/go.mod b/go.mod
index 45ddf58..9d544da 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/go-co-op/gocron/v2
-go 1.21.0
+go 1.23.0
require (
github.com/google/uuid v1.6.0
diff --git a/scheduler_test.go b/scheduler_test.go
index 9f84562..4ef276d 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -1626,10 +1626,7 @@ func TestScheduler_WithDistributed(t *testing.T) {
func(t *testing.T) {
timeout := time.Now().Add(1 * time.Second)
var notLeaderCount int
- for {
- if time.Now().After(timeout) {
- break
- }
+ for !time.Now().After(timeout) {
select {
case <-notLeader:
notLeaderCount++
@@ -1650,10 +1647,7 @@ func TestScheduler_WithDistributed(t *testing.T) {
func(_ *testing.T) {
timeout := time.Now().Add(1 * time.Second)
var notLockedCount int
- for {
- if time.Now().After(timeout) {
- break
- }
+ for !time.Now().After(timeout) {
select {
case <-notLocked:
notLockedCount++
@@ -1675,10 +1669,7 @@ func TestScheduler_WithDistributed(t *testing.T) {
func(_ *testing.T) {
timeout := time.Now().Add(1 * time.Second)
var notLockedCount int
- for {
- if time.Now().After(timeout) {
- break
- }
+ for !time.Now().After(timeout) {
select {
case <-notLocked:
notLockedCount++
@@ -1702,10 +1693,7 @@ func TestScheduler_WithDistributed(t *testing.T) {
func(_ *testing.T) {
timeout := time.Now().Add(1 * time.Second)
var notLockedCount int
- for {
- if time.Now().After(timeout) {
- break
- }
+ for !time.Now().After(timeout) {
select {
case <-notLocked:
notLockedCount++
From 17f65124cc5ac8104d0998c809b76e941bcc4613 Mon Sep 17 00:00:00 2001
From: John Roesler
Date: Wed, 30 Apr 2025 14:15:15 -0500
Subject: [PATCH 3/5] remove golangci v1 config backup
---
.golangci.bck.yaml | 42 ------------------------------------------
1 file changed, 42 deletions(-)
delete mode 100644 .golangci.bck.yaml
diff --git a/.golangci.bck.yaml b/.golangci.bck.yaml
deleted file mode 100644
index e4f0cc1..0000000
--- a/.golangci.bck.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-run:
- timeout: 5m
- issues-exit-code: 1
- tests: true
-
-issues:
- max-same-issues: 100
- include:
- - EXC0012
- - EXC0014
- exclude-dirs:
- - local
- exclude-rules:
- - path: example_test.go
- linters:
- - revive
- text: "seems to be unused"
- fix: true
-
-linters:
- enable:
- - bodyclose
- - copyloopvar
- - gofumpt
- - goimports
- - gosimple
- - govet
- - ineffassign
- - misspell
- - revive
- - staticcheck
- - typecheck
- - unused
- - whitespace
-
-output:
- formats:
- - format: colored-line-number
- print-issued-lines: true
- print-linter-name: true
- path-prefix: ""
- sort-results: true
From c9b34bdce1e2a5251911878be90c2c430eaed433 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 May 2025 14:27:06 -0500
Subject: [PATCH 4/5] Bump golangci/golangci-lint-action from 7.0.0 to 8.0.0
(#847)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/go_test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/go_test.yml b/.github/workflows/go_test.yml
index 042191f..7773f95 100644
--- a/.github/workflows/go_test.yml
+++ b/.github/workflows/go_test.yml
@@ -24,7 +24,7 @@
with:
go-version: ${{ matrix.go-version }}
- name: golangci-lint
- uses: golangci/golangci-lint-action@v7.0.0
+ uses: golangci/golangci-lint-action@v8.0.0
with:
version: v2.1.5
- name: test
From 4fb3b987637d059463250ed4de26342f6441fef3 Mon Sep 17 00:00:00 2001
From: John Roesler
Date: Fri, 16 May 2025 14:15:33 -0500
Subject: [PATCH 5/5] chore: document the limitations with the locker design
(#848)
---
README.md | 2 ++
distributed.go | 15 +++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/README.md b/README.md
index d15e59f..43f46c5 100644
--- a/README.md
+++ b/README.md
@@ -121,6 +121,8 @@ other instances checking to see if a new leader needs to be elected.
- [**Locker**](https://pkg.go.dev/github.com/go-co-op/gocron/v2#WithDistributedLocker):
A locker can be used to lock each run of a job to a single instance of gocron.
Locker can be at job or scheduler, if it is defined both at job and scheduler then locker of job will take precedence.
+ - See Notes in the doc for [Locker](https://pkg.go.dev/github.com/go-co-op/gocron/v2#Locker) for
+ details and limitations of the locker design.
- Implementations: [go-co-op lockers](https://github.com/go-co-op?q=-lock&type=all&language=&sort=)
(don't see what you need? request on slack to get a repo created to contribute it!)
diff --git a/distributed.go b/distributed.go
index 1617c62..98e906e 100644
--- a/distributed.go
+++ b/distributed.go
@@ -18,6 +18,21 @@ type Elector interface {
// locker implementation handles time splay between schedulers.
// The lock key passed is the job's name - which, if not set, defaults to the
// go function's name, e.g. "pkg.myJob" for func myJob() {} in pkg
+//
+// Notes: The locker and scheduler do not handle synchronization of run times across
+// schedulers.
+//
+// 1. If you are using duration based jobs (DurationJob), you can utilize the JobOption
+// WithStartAt to set a start time for the job to the nearest time rounded to your
+// duration. For example, if you have a job that runs every 5 minutes, you can set
+// the start time to the nearest 5 minute e.g. 12:05, 12:10.
+//
+// 2. For all jobs, the implementation is still vulnerable to clockskew between scheduler
+// instances. This may result in a single scheduler instance running the majority of the
+// jobs.
+//
+// For distributed jobs, consider utilizing the Elector option if these notes are not acceptable
+// to your use case.
type Locker interface {
// Lock if an error is returned by lock, the job will not be scheduled.
Lock(ctx context.Context, key string) (Lock, error)