Skip to content

Commit 1b6d0d1

Browse files
authored
Fix deadlock retrier (#117)
* pglock: speed up retries on transaction deadlocks * pglock: upgrade pgx version used for testing
1 parent d5c1348 commit 1b6d0d1

File tree

5 files changed

+21
-216
lines changed

5 files changed

+21
-216
lines changed

client.go

+9-11
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func (c *Client) AcquireContext(ctx context.Context, name string, opts ...LockOp
182182
if err := ctx.Err(); err != nil {
183183
return nil, ErrNotAcquired
184184
}
185-
err := c.retry(ctx, func() error { return c.tryAcquire(ctx, l) })
185+
err := c.retry(func() error { return c.tryAcquire(ctx, l) })
186186
switch {
187187
case l.failIfLocked && errors.Is(err, ErrNotAcquired):
188188
c.log.Debug("not acquired, exit")
@@ -303,7 +303,7 @@ func (c *Client) Release(l *Lock) error {
303303
func (c *Client) ReleaseContext(ctx context.Context, l *Lock) error {
304304
l.heartbeatCancel()
305305
l.heartbeatWG.Wait()
306-
err := c.retry(ctx, func() error { return c.storeRelease(ctx, l) })
306+
err := c.retry(func() error { return c.storeRelease(ctx, l) })
307307
return err
308308
}
309309

@@ -377,7 +377,7 @@ func (c *Client) SendHeartbeat(ctx context.Context, l *Lock) error {
377377
if l.isReleased {
378378
return ErrLockAlreadyReleased
379379
}
380-
err := c.retry(ctx, func() error { return c.storeHeartbeat(ctx, l) })
380+
err := c.retry(func() error { return c.storeHeartbeat(ctx, l) })
381381
if err != nil {
382382
l.isReleased = true
383383
return fmt.Errorf("cannot send heartbeat (%v): %w", l.name, err)
@@ -444,7 +444,7 @@ func (c *Client) GetDataContext(ctx context.Context, name string) ([]byte, error
444444
// holding it first.
445445
func (c *Client) GetContext(ctx context.Context, name string) (*Lock, error) {
446446
var l *Lock
447-
err := c.retry(ctx, func() error {
447+
err := c.retry(func() error {
448448
var err error
449449
l, err = c.getLock(ctx, name)
450450
return err
@@ -486,19 +486,17 @@ func (c *Client) getNextRVN(ctx context.Context, db *sql.DB) (int64, error) {
486486

487487
const maxRetries = 1024
488488

489-
func (c *Client) retry(ctx context.Context, f func() error) error {
490-
retryPeriod := c.heartbeatFrequency
491-
if retryPeriod == 0 {
492-
retryPeriod = c.leaseDuration
493-
}
489+
func (c *Client) retry(f func() error) error {
494490
var err error
495491
for i := 0; i < maxRetries; i++ {
496492
err = f()
497493
if failedPrecondition := (&FailedPreconditionError{}); err == nil || !errors.As(err, &failedPrecondition) {
498494
break
499495
}
500496
c.log.Debug("bad transaction, retrying: %v", err)
501-
waitFor(ctx, retryPeriod)
497+
if isContextError(err) {
498+
break
499+
}
502500
}
503501
return err
504502
}
@@ -511,7 +509,7 @@ func (c *Client) GetAllLocks() ([]*ReadOnlyLock, error) {
511509
// GetAllLocksContext returns all known locks in a read-only fashion.
512510
func (c *Client) GetAllLocksContext(ctx context.Context) ([]*ReadOnlyLock, error) {
513511
var locks []*ReadOnlyLock
514-
err := c.retry(ctx, func() error {
512+
err := c.retry(func() error {
515513
var err error
516514
locks, err = c.getAllLocks(ctx)
517515
return err

client_internal_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func TestRetry(t *testing.T) {
6464
&FailedPreconditionError{errors.New("failed precondition")},
6565
&OtherError{errors.New("other error")},
6666
}
67-
err := c.retry(context.Background(), func() error {
67+
err := c.retry(func() error {
6868
var err error
6969
err, errs = errs[0], errs[1:]
7070
return err
@@ -78,7 +78,7 @@ func TestRetry(t *testing.T) {
7878
log: &flatLogger{log.New(io.Discard, "", 0)},
7979
}
8080
var retries int
81-
err := c.retry(context.Background(), func() error {
81+
err := c.retry(func() error {
8282
retries++
8383
return &FailedPreconditionError{errors.New("failed precondition")}
8484
})

client_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
"time"
3232

3333
"cirello.io/pglock"
34-
_ "github.com/jackc/pgx/v4/stdlib"
34+
_ "github.com/jackc/pgx/v5/stdlib"
3535
"github.com/lib/pq"
3636
"golang.org/x/sync/errgroup"
3737
)

go.mod

+3-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@ module cirello.io/pglock
22

33
require (
44
github.com/DATA-DOG/go-sqlmock v1.5.2
5-
github.com/jackc/pgx/v4 v4.18.3
5+
github.com/jackc/pgx/v5 v5.7.1
66
github.com/lib/pq v1.10.9
77
golang.org/x/sync v0.8.0
88
)
99

1010
require (
11-
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
12-
github.com/jackc/pgconn v1.14.3 // indirect
13-
github.com/jackc/pgio v1.0.0 // indirect
1411
github.com/jackc/pgpassfile v1.0.0 // indirect
15-
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
16-
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
17-
github.com/jackc/pgtype v1.14.4 // indirect
12+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
13+
github.com/jackc/puddle/v2 v2.2.2 // indirect
1814
golang.org/x/crypto v0.28.0 // indirect
1915
golang.org/x/text v0.19.0 // indirect
2016
)

0 commit comments

Comments
 (0)