gomysqllock

package module
v0.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2021 License: Apache-2.0 Imports: 5 Imported by: 0

README

go-mysql-lock

GoDoc Azure DevOps builds Azure DevOps coverage Go Report Card

go-mysql-lock provides locking primitive based on MySQL's GET_LOCK Lock names are strings and MySQL enforces a maximum length on lock names of 64 characters.

Use cases

Though there are mature locking primitives provided by systems like Zookeeper and etcd, when you have an application which is primarily dependent on MySQL for its uptime and health, added resiliency provided by systems just mentioned doesn't add much benefit. go-mysql-lock helps when you have multiple application instances which are backed by a common mysql instance and you want only one of those application instances to hold a lock and do certain tasks.

Installation
go get github.com/sanketplus/go-mysql-lock
Example:
package main

import (
    "context"
    "database/sql"
    
    _ "github.com/go-sql-driver/mysql"
    "github.com/sanketplus/go-mysql-lock"
)

func main() {
	db, _ := sql.Open("mysql", "root@tcp(localhost:3306)/dyno_test")

	locker := gomysqllock.NewMysqlLocker(db)

	lock, _ := locker.Obtain("foo")
	lock.Release()
}

Features

Customizable Refresh Period

Once the lock is obtained, a goroutine periodically (default every 1 second) keeps pinging on connection since the lock is valid on a connection(session). To configure the refresh interval

locker := gomysqllock.NewMysqlLocker(db, gomysqllock.WithRefreshInterval(time.Millisecond*500))
Obtain Lock With Context

By default, an attempt to obtain a lock is backed by background context. That means the Obtain call would block indefinitely. Optionally, an Obtain call can be made with user given context which will get cancelled with the given context.

The following call will give up after a second if the lock was not obtained.

locker := gomysqllock.NewMysqlLocker(db)
ctxShort, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
lock, err := locker.ObtainContext(ctxShort, "key")
Obtain Lock With (MySQL) Timeout

MySQL has the ability to timeout and return if the lock can't be acquired in a given number of seconds. This timeout can be specified when using ObtainTimeout and ObtainTimeoutContext. On timeout, ErrMySQLTimeout is returned, and the lock is not obtained.

The following call will give up after a second if the lock was not obtained, using MySQL timeout option:

locker := gomysqllock.NewMysqlLocker(db)
lock, err := locker.ObtainTimeout("key", 1)
Know When The Lock is Lost

Obtained lock has a context which is cancelled if the lock is lost. This is determined while a goroutine keeps pinging the connection. If there is an error while pinging, assuming connection has an error, the context is cancelled. And the lock owner gets notified of the lost lock.

context := lock.GetContext()
Compatibility

This library is tested (automatically) against MySQL 8 and MariaDB 10.1, and it should work for MariaDB versions >= 10.1 and MySQL versions >= 5.6.

Note that GET_LOCK function won't lock indefinitely on MariaDB 10.1 / MySQL 5.6 and older, as 0 or negative value for timeouts are not accepted in those versions. This means that in MySQL <= 5.6 / MariaDB <= 10.1 you can't use Obtain or ObtainContext. To achieve a similar goal, you can use ObtainTimeout (and ObtainTimeoutContext) using a very high timeout value.

Documentation

Index

Constants

View Source
const DefaultRefreshInterval = time.Second

DefaultRefreshInterval is the periodic duration with which a connection is refreshed/pinged

Variables

View Source
var ErrGetLockContextCancelled = errors.New("context cancelled while trying to obtain lock")

ErrGetLockContextCancelled is returned when user given context is cancelled while trying to obtain the lock

View Source
var ErrMySQLInternalError = errors.New("internal mysql error acquiring the lock")

ErrMySQLInternalError is returned when MySQL is returning a generic internal error

View Source
var ErrMySQLTimeout = errors.New("(mysql) timeout while acquiring the lock")

ErrMySQLTimeout is returned when the MySQL server can't acquire the lock in the specified timeout

Functions

func WithRefreshInterval

func WithRefreshInterval(d time.Duration) lockerOpt

WithRefreshInterval sets the duration for refresh interval for each obtained lock

Types

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

Lock denotes an acquired lock and presents two methods, one for getting the context which is cancelled when the lock is lost/released and other for Releasing the lock

func (Lock) GetContext

func (l Lock) GetContext() context.Context

GetContext returns a context which is cancelled when the lock is lost or released

func (Lock) Release

func (l Lock) Release() error

Release unlocks the lock

type MysqlLocker

type MysqlLocker struct {
	// contains filtered or unexported fields
}

MysqlLocker is the client which provide APIs to obtain lock

func NewMysqlLocker

func NewMysqlLocker(db *sql.DB, lockerOpts ...lockerOpt) *MysqlLocker

NewMysqlLocker returns an instance of locker which can be used to obtain locks

func (MysqlLocker) Obtain

func (l MysqlLocker) Obtain(key string) (*Lock, error)

Obtain tries to acquire lock (with no MySQL timeout) with background context. This call is expected to block is lock is already held

func (MysqlLocker) ObtainContext

func (l MysqlLocker) ObtainContext(ctx context.Context, key string) (*Lock, error)

ObtainContext tries to acquire lock and gives up when the given context is cancelled

func (MysqlLocker) ObtainTimeout added in v0.0.5

func (l MysqlLocker) ObtainTimeout(key string, timeout int) (*Lock, error)

ObtainTimeout tries to acquire lock with background context and a MySQL timeout. This call is expected to block is lock is already held

func (MysqlLocker) ObtainTimeoutContext added in v0.0.5

func (l MysqlLocker) ObtainTimeoutContext(ctx context.Context, key string, timeout int) (*Lock, error)

ObtainTimeoutContext tries to acquire lock and gives up when the given context is cancelled

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL