Skip to content

Commit

Permalink
pglock: not log context cancelation on heartbeats (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
ucirello authored Mar 7, 2024
1 parent 23984ec commit 17e3f3d
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
10 changes: 7 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,11 @@ func (c *Client) heartbeat(ctx context.Context, l *Lock) {
c.log.Debug("heartbeat started: %v", l.name)
defer c.log.Debug("heartbeat stopped: %v", l.name)
for {
if err := ctx.Err(); err != nil {
return
} else if err := c.SendHeartbeat(ctx, l); err != nil {
if err := c.SendHeartbeat(ctx, l); err != nil && !isContextError(err) {
defer c.log.Error("heartbeat missed: %v", err)
return
} else if err := ctx.Err(); err != nil {
return
}
waitFor(ctx, c.heartbeatFrequency)
}
Expand Down Expand Up @@ -618,3 +618,7 @@ func waitFor(ctx context.Context, d time.Duration) {
case <-ctx.Done():
}
}

func isContextError(err error) bool {
return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
}
68 changes: 68 additions & 0 deletions client_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ limitations under the License.
package pglock

import (
"bytes"
"context"
"database/sql"
"errors"
"fmt"
"io"
"log"
"net"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -331,3 +333,69 @@ func Test_waitFor(t *testing.T) {
}
})
}

func Test_heartbeatLogging(t *testing.T) {
t.Run("cancelled", func(t *testing.T) {
db, err := sql.Open("postgres", "")
if err != nil {
t.Fatal("cannot connect to test database server:", err)
}
logger := &testBufferLogger{}
client, _ := New(db, WithHeartbeatFrequency(0), WithLogger(logger))
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal("cannot create mock:", err)
}
client.db = db
ctx, cancel := context.WithCancel(context.Background())
fakeLock := &Lock{
heartbeatContext: ctx,
heartbeatCancel: cancel,
leaseDuration: time.Minute,
}
hbCtx, hbCancel := context.WithCancel(context.Background())
hbCancel()
mock.ExpectQuery(`SELECT nextval\('locks_rvn'\)`).WillReturnError(hbCtx.Err())
fakeLock.heartbeatWG.Add(1)
client.heartbeat(hbCtx, fakeLock)
t.Log(logger.buf.String())
if strings.Contains(logger.buf.String(), context.Canceled.Error()) {
t.Fatal("must not log context cancellations")
}
})
t.Run("errored", func(t *testing.T) {
db, err := sql.Open("postgres", "")
if err != nil {
t.Fatal("cannot connect to test database server:", err)
}
logger := &testBufferLogger{}
client, _ := New(db, WithHeartbeatFrequency(0), WithLogger(logger))
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal("cannot create mock:", err)
}
client.db = db
ctx, cancel := context.WithCancel(context.Background())
fakeLock := &Lock{
heartbeatContext: ctx,
heartbeatCancel: cancel,
leaseDuration: time.Minute,
}
errExpected := errors.New("expected error")
mock.ExpectQuery(`SELECT nextval\('locks_rvn'\)`).WillReturnError(errExpected)
fakeLock.heartbeatWG.Add(1)
client.heartbeat(context.Background(), fakeLock)
t.Log(logger.buf.String())
if !strings.Contains(logger.buf.String(), errExpected.Error()) {
t.Fatal("expected error missing")
}
})
}

type testBufferLogger struct {
buf bytes.Buffer
}

func (t *testBufferLogger) Println(v ...interface{}) {
fmt.Fprintln(&t.buf, v...)
}

0 comments on commit 17e3f3d

Please sign in to comment.