diff --git a/pkg/dbconn/metadatalock.go b/pkg/dbconn/metadatalock.go index 933348f..493d0bf 100644 --- a/pkg/dbconn/metadatalock.go +++ b/pkg/dbconn/metadatalock.go @@ -2,6 +2,7 @@ package dbconn import ( "context" + "database/sql" "errors" "fmt" "time" @@ -20,6 +21,8 @@ type MetadataLock struct { cancel context.CancelFunc closeCh chan error refreshInterval time.Duration + ticker *time.Ticker + dbConn *sql.DB } func NewMetadataLock(ctx context.Context, dsn string, lockName string, logger loggers.Advanced, optionFns ...func(*MetadataLock)) (*MetadataLock, error) { @@ -46,6 +49,7 @@ func NewMetadataLock(ctx context.Context, dsn string, lockName string, logger lo if err != nil { return nil, err } + mdl.dbConn = dbConn // Function to acquire the lock getLock := func() error { @@ -76,8 +80,8 @@ func NewMetadataLock(ctx context.Context, dsn string, lockName string, logger lo ctx, mdl.cancel = context.WithCancel(ctx) mdl.closeCh = make(chan error) go func() { - ticker := time.NewTicker(mdl.refreshInterval) - defer ticker.Stop() + mdl.ticker = time.NewTicker(mdl.refreshInterval) + defer mdl.ticker.Stop() for { select { case <-ctx.Done(): @@ -85,7 +89,7 @@ func NewMetadataLock(ctx context.Context, dsn string, lockName string, logger lo logger.Warnf("releasing metadata lock: %s", lockName) mdl.closeCh <- dbConn.Close() return - case <-ticker.C: + case <-mdl.ticker.C: if err = getLock(); err != nil { logger.Errorf("could not refresh metadata lock: %s", err) } @@ -98,6 +102,21 @@ func NewMetadataLock(ctx context.Context, dsn string, lockName string, logger lo } func (m *MetadataLock) Close() error { + // Handle odd race situation here where the cancel func is nil somehow + if m.cancel == nil { + // Make a best effort to cleanup + if m.ticker != nil { + m.ticker.Stop() + } + if m.closeCh != nil { + close(m.closeCh) + } + if m.dbConn != nil { + return m.dbConn.Close() + } + return nil + } + // Cancel the background refresh runner m.cancel()