Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance the locker documentation #480

Merged
merged 2 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()`.
Expand Down
46 changes: 46 additions & 0 deletions lockers/redislock/README.md
Original file line number Diff line number Diff line change
@@ -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()
}
```
File renamed without changes.
9 changes: 7 additions & 2 deletions scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}