Skip to content

Commit 0e8b749

Browse files
Merge pull request #2 from golang-cz/updates
2 parents bbbb66f + c85514c commit 0e8b749

File tree

11 files changed

+170
-105
lines changed

11 files changed

+170
-105
lines changed

_example/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.PHONY: fmt check-fmt lint vet test
2+
3+
run:
4+
rerun -watch . .. -run sh -c 'go run .'
5+
6+
update:
7+
go get github.com/golang-cz/looper@latest

_example/go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module testslog
2+
3+
go 1.22
4+
5+
toolchain go1.24.2
6+
7+
replace github.com/golang-cz/looper => ../
8+
9+
require github.com/golang-cz/looper v0.0.0-00010101000000-000000000000

_example/go.sum

Whitespace-only changes.

_example/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
"time"
7+
8+
"github.com/golang-cz/looper"
9+
)
10+
11+
func main() {
12+
l := looper.New(nil)
13+
14+
j := &looper.Job{
15+
JobFn: job,
16+
Name: "jj",
17+
Timeout: 10 * time.Second,
18+
WaitAfterSuccess: 4 * time.Second,
19+
WaitAfterError: 4 * time.Second,
20+
Active: true,
21+
}
22+
23+
ctx := context.Background()
24+
25+
if err := l.AddJob(ctx, j); err != nil {
26+
log.Fatal(err)
27+
}
28+
29+
l.Start()
30+
31+
select {}
32+
}
33+
34+
func job(ctx context.Context) error {
35+
log.Println("job start")
36+
37+
time.Sleep(2 * time.Second)
38+
39+
log.Println("job end")
40+
41+
return nil
42+
}

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
module github.com/golang-cz/looper
22

3-
go 1.20
3+
go 1.22
44

55
require (
66
github.com/go-redsync/redsync/v4 v4.11.0
7-
github.com/redis/go-redis/v9 v9.3.0
7+
github.com/redis/go-redis/v9 v9.10.0
88
)
99

1010
require (
11-
github.com/cespare/xxhash/v2 v2.2.0 // indirect
11+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
1212
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
1313
github.com/hashicorp/errwrap v1.1.0 // indirect
1414
github.com/hashicorp/go-multierror v1.1.1 // indirect

go.sum

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
2+
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
23
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
3-
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
4-
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
4+
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
5+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
6+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
57
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
68
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
79
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
10+
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
811
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
12+
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
913
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
14+
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
1015
github.com/go-redsync/redsync/v4 v4.11.0 h1:OPEcAxHBb95EzfwCKWM93ksOwHd5bTce2BD4+R14N6k=
1116
github.com/go-redsync/redsync/v4 v4.11.0/go.mod h1:ZfayzutkgeBmEmBlUR3j+rF6kN44UUGtEdfzhBFZTPc=
1217
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
18+
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
1319
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
1420
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
1521
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
1622
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
1723
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
18-
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
19-
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
24+
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
25+
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
2026
github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo=
27+
github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
2128
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
29+
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
2230
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
31+
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=

locker.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,35 @@ var (
1515
)
1616

1717
// Lock if an error is returned by lock, the job will not be scheduled.
18-
type locker interface {
19-
lock(ctx context.Context, key string, timeout time.Duration) (lock, error)
18+
type Locker interface {
19+
Lock(ctx context.Context, key string, timeout time.Duration) (Lock, error)
2020
}
2121

22-
// lock represents an obtained lock
23-
type lock interface {
24-
unlock(ctx context.Context) error
22+
// Lock represents an obtained Lock
23+
type Lock interface {
24+
Unlock(ctx context.Context) error
25+
}
26+
27+
// Nop locker
28+
29+
func newNopLocker() Locker {
30+
return &nopLocker{}
31+
}
32+
33+
// Locker
34+
var _ Locker = (*nopLocker)(nil)
35+
36+
type nopLocker struct{}
37+
38+
func (r *nopLocker) Lock(_ context.Context, _ string, _ time.Duration) (Lock, error) {
39+
return &nopLock{}, nil
40+
}
41+
42+
// Lock
43+
var _ Lock = (*nopLock)(nil)
44+
45+
type nopLock struct{}
46+
47+
func (r *nopLock) Unlock(ctx context.Context) error {
48+
return nil
2549
}

postgresql.go renamed to locker_postgresql/postgresql.go

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
package looper
1+
package lockerpostgresql
22

33
import (
44
"context"
55
"database/sql"
66
"fmt"
77
"time"
8+
9+
"github.com/golang-cz/looper"
810
)
911

1012
const defaultTableName = "looper_lock"
1113

1214
// PostgresLocker provides an implementation of the Locker interface using
1315
// a PostgreSQL table for storage.
14-
func PostgresLocker(ctx context.Context, db *sql.DB, tableName string) (locker, error) {
16+
func PostgresLocker(ctx context.Context, db *sql.DB, tableName string) (looper.Locker, error) {
1517
if err := db.PingContext(ctx); err != nil {
16-
return nil, fmt.Errorf("%w: %v", ErrFailedToConnectToLocker, err)
18+
return nil, fmt.Errorf("%w: %v", looper.ErrFailedToConnectToLocker, err)
1719
}
1820

1921
if tableName == "" {
@@ -34,7 +36,7 @@ func PostgresLocker(ctx context.Context, db *sql.DB, tableName string) (locker,
3436
}
3537

3638
// Locker
37-
var _ locker = (*postgresLocker)(nil)
39+
var _ looper.Locker = (*postgresLocker)(nil)
3840

3941
type postgresLocker struct {
4042
db *sql.DB
@@ -55,7 +57,7 @@ func createLockTable(ctx context.Context, db *sql.DB, table string) error {
5557
),
5658
).Scan(&tableExists)
5759
if err != nil {
58-
return fmt.Errorf("%w: %v", ErrFailedToCheckLockExistence, err)
60+
return fmt.Errorf("%w: %v", looper.ErrFailedToCheckLockExistence, err)
5961
}
6062

6163
if !tableExists {
@@ -69,59 +71,50 @@ func createLockTable(ctx context.Context, db *sql.DB, table string) error {
6971
table,
7072
))
7173
if err != nil {
72-
return fmt.Errorf("%w: %v", ErrFailedToCreateLockTable, err)
74+
return fmt.Errorf("%w: %v", looper.ErrFailedToCreateLockTable, err)
7375
}
7476
}
7577

7678
return nil
7779
}
7880

79-
func (p *postgresLocker) lock(
80-
ctx context.Context,
81-
key string,
82-
timeout time.Duration,
83-
) (lock, error) {
81+
func (p *postgresLocker) Lock(ctx context.Context, key string, timeout time.Duration) (looper.Lock, error) {
8482
// Create a row in the lock table to acquire the lock
85-
_, err := p.db.ExecContext(
86-
ctx,
87-
fmt.Sprintf(`
83+
q := fmt.Sprintf(`
8884
INSERT INTO %s (job_name)
8985
VALUES ('%s');`,
90-
p.table,
91-
key,
92-
))
93-
if err != nil {
94-
var createdAt time.Time
95-
err := p.db.QueryRowContext(
96-
ctx,
97-
fmt.Sprintf(`
86+
p.table,
87+
key,
88+
)
89+
if _, err := p.db.ExecContext(ctx, q); err != nil {
90+
q := fmt.Sprintf(`
9891
SELECT created_at
9992
FROM %s
10093
WHERE job_name = '%s';`,
101-
p.table,
102-
key,
103-
)).Scan(&createdAt)
104-
if err != nil {
105-
return nil, ErrFailedToCheckLockExistence
94+
p.table,
95+
key,
96+
)
97+
98+
var createdAt time.Time
99+
if err := p.db.QueryRowContext(ctx, q).Scan(&createdAt); err != nil {
100+
return nil, looper.ErrFailedToCheckLockExistence
106101
}
107102

108103
if createdAt.Before(time.Now().Add(-timeout)) {
109-
_, err := p.db.ExecContext(
110-
ctx,
111-
fmt.Sprintf(`
104+
q := fmt.Sprintf(`
112105
DELETE FROM %s
113106
WHERE job_name = '%s';`,
114-
p.table,
115-
key,
116-
))
117-
if err != nil {
118-
return nil, ErrFailedToReleaseLock
107+
p.table,
108+
key,
109+
)
110+
if _, err := p.db.ExecContext(ctx, q); err != nil {
111+
return nil, looper.ErrFailedToReleaseLock
119112
}
120113

121-
return p.lock(ctx, key, timeout)
114+
return p.Lock(ctx, key, timeout)
122115
}
123116

124-
return nil, ErrFailedToObtainLock
117+
return nil, looper.ErrFailedToObtainLock
125118
}
126119

127120
pl := &postgresLock{
@@ -134,15 +127,15 @@ func (p *postgresLocker) lock(
134127
}
135128

136129
// Lock
137-
var _ lock = (*postgresLock)(nil)
130+
var _ looper.Lock = (*postgresLock)(nil)
138131

139132
type postgresLock struct {
140133
db *sql.DB
141134
table string
142135
key string
143136
}
144137

145-
func (p *postgresLock) unlock(ctx context.Context) error {
138+
func (p *postgresLock) Unlock(ctx context.Context) error {
146139
// Release the lock by deleting the row
147140
_, err := p.db.ExecContext(
148141
ctx,
@@ -153,7 +146,7 @@ func (p *postgresLock) unlock(ctx context.Context) error {
153146
p.key,
154147
))
155148
if err != nil {
156-
return ErrFailedToReleaseLock
149+
return looper.ErrFailedToReleaseLock
157150
}
158151

159152
return nil

redis.go renamed to locker_redis/redis.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package looper
1+
package lockerredis
22

33
import (
44
"context"
@@ -8,6 +8,8 @@ import (
88
"github.com/go-redsync/redsync/v4"
99
"github.com/go-redsync/redsync/v4/redis/goredis/v9"
1010
"github.com/redis/go-redis/v9"
11+
12+
"github.com/golang-cz/looper"
1113
)
1214

1315
// redisOptions := &redis.Options{
@@ -23,10 +25,10 @@ import (
2325

2426
// RedisLocker provides an implementation of the Locker interface using
2527
// redis for storage.
26-
func RedisLocker(ctx context.Context, rc redis.UniversalClient) (locker, error) {
28+
func RedisLocker(ctx context.Context, rc redis.UniversalClient) (looper.Locker, error) {
2729
err := rc.Ping(ctx).Err()
2830
if err != nil {
29-
return nil, fmt.Errorf("%s: %w", ErrFailedToConnectToLocker, err)
31+
return nil, fmt.Errorf("%s: %w", looper.ErrFailedToConnectToLocker, err)
3032
}
3133

3234
pool := goredis.NewPool(rc)
@@ -38,21 +40,21 @@ func RedisLocker(ctx context.Context, rc redis.UniversalClient) (locker, error)
3840
}
3941

4042
// Locker
41-
var _ locker = (*redisLocker)(nil)
43+
var _ looper.Locker = (*redisLocker)(nil)
4244

4345
type redisLocker struct {
4446
rs *redsync.Redsync
4547
}
4648

47-
func (r *redisLocker) lock(ctx context.Context, key string, timeout time.Duration) (lock, error) {
49+
func (r *redisLocker) Lock(ctx context.Context, key string, timeout time.Duration) (looper.Lock, error) {
4850
options := []redsync.Option{
4951
redsync.WithTries(1),
5052
redsync.WithExpiry(timeout + time.Second),
5153
}
5254
mu := r.rs.NewMutex(key, options...)
5355
err := mu.LockContext(ctx)
5456
if err != nil {
55-
return nil, ErrFailedToObtainLock
57+
return nil, looper.ErrFailedToObtainLock
5658
}
5759

5860
rl := &redisLock{
@@ -63,20 +65,20 @@ func (r *redisLocker) lock(ctx context.Context, key string, timeout time.Duratio
6365
}
6466

6567
// Lock
66-
var _ lock = (*redisLock)(nil)
68+
var _ looper.Lock = (*redisLock)(nil)
6769

6870
type redisLock struct {
6971
mu *redsync.Mutex
7072
}
7173

72-
func (r *redisLock) unlock(ctx context.Context) error {
74+
func (r *redisLock) Unlock(ctx context.Context) error {
7375
unlocked, err := r.mu.UnlockContext(ctx)
7476
if err != nil {
75-
return ErrFailedToReleaseLock
77+
return looper.ErrFailedToReleaseLock
7678
}
7779

7880
if !unlocked {
79-
return ErrFailedToReleaseLock
81+
return looper.ErrFailedToReleaseLock
8082
}
8183

8284
return nil

0 commit comments

Comments
 (0)