Skip to content

Commit

Permalink
Fix for goroutine leak (#7)
Browse files Browse the repository at this point in the history
* Fix context leak
* make unlocker channel buffered

Co-authored-by: Ankur Singh <[email protected]>
  • Loading branch information
ankur-a and ankur-cashfree authored Jul 5, 2021
1 parent f748fb9 commit eb4a2f4
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

# IDE meta
.idea
.idea
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ go 1.13
require (
github.com/go-sql-driver/mysql v1.5.0
github.com/stretchr/testify v1.6.1
go.uber.org/goleak v1.1.10
)
23 changes: 23 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,34 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 3 additions & 0 deletions lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (l Lock) refresher(duration time.Duration, cancelFunc context.CancelFunc) {
case <-time.After(duration):
deadline := time.Now().Add(duration)
contextDeadline, deadlineCancelFunc := context.WithDeadline(context.Background(), deadline)

// try refresh, else cancel
err := l.conn.PingContext(contextDeadline)
if err != nil {
Expand All @@ -43,8 +44,10 @@ func (l Lock) refresher(duration time.Duration, cancelFunc context.CancelFunc) {
l.Release()
return
}
deadlineCancelFunc() // to avoid context leak
case <-l.unlocker:
cancelFunc()
return
}
}
}
4 changes: 1 addition & 3 deletions locker_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ type lockerOpt func(locker *MysqlLocker)
type MysqlLocker struct {
db *sql.DB
refreshInterval time.Duration
unlocker chan (struct{})
}

// NewMysqlLocker returns an instance of locker which can be used to obtain locks
func NewMysqlLocker(db *sql.DB, lockerOpts ...lockerOpt) *MysqlLocker {
locker := &MysqlLocker{
db: db,
refreshInterval: DefaultRefreshInterval,
unlocker: make(chan (struct{})),
}

for _, opt := range lockerOpts {
Expand Down Expand Up @@ -93,7 +91,7 @@ func (l MysqlLocker) ObtainTimeoutContext(ctx context.Context, key string, timeo
lock := &Lock{
key: key,
conn: dbConn,
unlocker: make(chan struct{}),
unlocker: make(chan struct{}, 1),
lostLockContext: cancellableContext,
cancelFunc: cancelFunc,
}
Expand Down
9 changes: 9 additions & 0 deletions locker_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ import (

_ "github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/assert"
"go.uber.org/goleak"
)

func TestMain(m *testing.M) {
// ignoring go routines spawned for db connection maintenance
goleak.VerifyTestMain(m, goleak.IgnoreTopFunction("database/sql.(*DB).connectionOpener"),
goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"),
goleak.IgnoreTopFunction("database/sql.(*DB).connectionResetter"),
)
}

func setupDB(t *testing.T) *sql.DB {
db, err := sql.Open("mysql", "root@tcp(localhost:3306)/")
assert.NoError(t, err, "failed to setup db")
Expand Down

0 comments on commit eb4a2f4

Please sign in to comment.