From 106289f3e2a3d329a1e4b0b50ca38a51ad1b629f Mon Sep 17 00:00:00 2001 From: John Roesler Date: Tue, 9 May 2023 16:32:09 -0500 Subject: [PATCH 1/2] enhance the locker documentation --- README.md | 18 ++++---- lockers/redislock/README.md | 46 +++++++++++++++++++ .../redislock/docker-compose.yml | 0 3 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 lockers/redislock/README.md rename docker-compose.yml => lockers/redislock/docker-compose.yml (100%) diff --git a/README.md b/README.md index e9a4c70b..45cd39c2 100644 --- a/README.md +++ b/README.md @@ -88,16 +88,16 @@ For more examples, take a look in our [go docs](https://pkg.go.dev/github.com/go There are several options available to restrict how jobs run: -| Mode | Function | Behavior | -|----------------------------|---------------------------|------------------------------------------------------------------------------------------------------| -| Default | | jobs are rescheduled at every interval | -| Job singleton | `SingletonMode()` | a long running job will not be rescheduled until the current run is completed | -| Scheduler limit | `SetMaxConcurrentJobs()` | set a collective maximum number of concurrent jobs running across the scheduler | -| Distributed locking (BETA) | `WithDistributedLocker()` | prevents the same job from being run more than once when running multiple instances of the scheduler | +| Mode | Function | Behavior | +|---------------------|---------------------------|------------------------------------------------------------------------------------------------------| +| Default | | jobs are rescheduled at every interval | +| Job singleton | `SingletonMode()` | a long running job will not be rescheduled until the current run is completed | +| Scheduler limit | `SetMaxConcurrentJobs()` | set a collective maximum number of concurrent jobs running across the scheduler | +| Distributed locking | `WithDistributedLocker()` | prevents the same job from being run more than once when running multiple instances of the scheduler | ## Distributed Locker Implementations -- Redis: [redis.go](lockers/redislock/redislock.go) `go get github.com/go-co-op/gocron/lockers/redislock` +- Redis: [redislock](lockers/redislock/README.md) `go get github.com/go-co-op/gocron/lockers/redislock` ## Tags @@ -125,8 +125,8 @@ s.RunByTag("tag") - Q: I'm running multiple pods on a distributed environment. How can I make a job not run once per pod causing duplication? - We recommend using your own lock solution within the jobs themselves (you could use [Redis](https://redis.io/topics/distlock), for example) - - A2: Currently in BETA (please provide feedback): Use the scheduler option `WithDistributedLocker` and either use an implemented backend - or implement your own and contribute it back in a PR (we hope)! + - A2: Use the scheduler option `WithDistributedLocker` and either use an implemented [backend](lockers) + or implement your own and contribute it back in a PR! - Q: I've removed my job from the scheduler, but how can I stop a long-running job that has already been triggered? - A: We recommend using a means of canceling your job, e.g. a `context.WithCancel()`. diff --git a/lockers/redislock/README.md b/lockers/redislock/README.md new file mode 100644 index 00000000..cb504469 --- /dev/null +++ b/lockers/redislock/README.md @@ -0,0 +1,46 @@ +# redislock + +## install + +``` +go get github.com/go-co-op/gocron/lockers/redislock +``` + +## usage + +Here is an example usage that would be deployed in multiple instances + +```go +package main + +import ( + "time" + + "github.com/go-co-op/gocron" + "github.com/go-co-op/gocron/lockers/redislock" + "github.com/redis/go-redis/v9" +) + +func main() { + redisOptions := &redis.Options{ + Addr: "localhost:6379", + } + redisClient := redis.NewClient(redisOptions) + locker, err := redislock.NewRedisLocker(redisClient) + if err != nil { + // handle the error + } + + s := gocron.NewScheduler(time.UTC) + s.WithDistributedLocker(locker) + + _, err = s.Every("1s").Name("unique_name").Do(func() { + // task to do + }) + if err != nil { + // handle the error + } + + s.StartBlocking() +} +``` \ No newline at end of file diff --git a/docker-compose.yml b/lockers/redislock/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to lockers/redislock/docker-compose.yml From 39980f211d3dcfe19aca1a4a1b54dbec6b49b6c9 Mon Sep 17 00:00:00 2001 From: John Roesler Date: Tue, 9 May 2023 16:36:47 -0500 Subject: [PATCH 2/2] enhance WithDistributedLocker comment --- scheduler.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scheduler.go b/scheduler.go index 454d3de0..e1a8e8bc 100644 --- a/scheduler.go +++ b/scheduler.go @@ -1406,8 +1406,13 @@ func (s *Scheduler) StopBlockingChan() { // Another strategy is to use the Cron or CronWithSeconds methods as they // use the same behavior described above using StartAt. // -// NOTE - the Locker will NOT lock jobs using any of the limiting functions: -// SingletonMode, SingletonModeAll or SetMaxConcurrentJobs +// NOTE - the Locker will NOT lock jobs using the singleton options: +// SingletonMode, or SingletonModeAll +// +// NOTE - beware of potential race conditions when running the Locker +// with SetMaxConcurrentJobs and WaitMode as jobs are not guaranteed +// to be locked when each scheduler's is below its limit and able +// to run the job. func (s *Scheduler) WithDistributedLocker(l Locker) { s.executor.distributedLocker = l }