rwlock

package module
v0.0.0-...-fb2d9df Latest Latest
Warning

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

Go to latest
Published: Dec 8, 2023 License: MIT Imports: 7 Imported by: 0

README

redis-rwlock

Golang implementation of distributed RW-Lock using Redis.

This implementation was inspired by Redis distributed locking mechanism and uses Redis LUA scripts. Configuration:

Option Default Minumum Description
LockTTL 1 sec 100 msec
RetryCount 200 0 Lock acquisition internally is a non-blocking operation. To imitate normal blocking behavior we make RetryCount attempts to acquire the lock with RetryInterval intervals. If lock is not acquired after all attempts caller receives timeout error.
RetryInterval 10 msec 1 msec Interval between lock acquisition attempts. See RetryCount description.
Context context.Background n/a Execution context. All locking operation use this context. You can use cancelable context for graceful shutdown.
AppID "" n/a Any string which will allow you to debug redis calls and locks to detect interference of multiple apps using the same lock.
ReaderLockToken "read_c2d-75a1-4b5b-a6fb-b0754224c666" n/a This token is used as a value for the global lock key when reader acquires the lock. It should be the same for all readers because when tle lock is released we check if it was acquired by us using this token and raise error if not. But if you have separate groups of readers which should not interfere you can use different token to distinguish them.
Mode ModePreferWriter n/a RWMutex can work in two modes: either reader and writer are equal or readers respect writer and do not acquire lock if writers has declared it's intention to acquire the lock. By default ModePreferWriter is set to keep back compatibility. See [MutexMode](#Mutex Modes)
Mutex Modes

Generally RWMutex is a combination of regular mutex and a counter of readers. Lock is either free or acquired exclusively by the writer or by a number of readers. First reader acquires lock and increments readers counter. Every new reader increments number of readers and does not do anything with the global lock. When reader releases the lock it first decrements readers counter and if reader was the last one it releases the global lock. That's it.

Reader-preferring RWMutex is simplest implementation. If lock is acquired by reader(s) writer will block but new reader will increment counter and keep global lock acquired. In this case readers may never release lock for the writer and continuously read outdated data.

Writer-preferring RWMutex introduce 'Writer Intention'. In this case if lock is acquired by reader(s) and writer tries to acquire the lock it will set an intention. Writer-respecting readers will not increment readers counter and writer will acquire the global lock as soon as all old readers will finish their job.

Implementation

Every lock operation (rlock, runlock, lock, unlock, refresh) is a single LUA script. You can find scripts with comments in lua directory.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrConnection is returned by Locker methods in case of problems with redis.
	ErrConnection = errors.New("redis connection error")

	// ErrTimeout is returned by Locker methods if timeout was specified and was exceeded while waiting for lock.
	ErrTimeout = errors.New("timeout exceeded but lock not acquired")

	// ErrInterrupted is returned by Locker methods if they were interrupted via Context.
	ErrInterrupted = errors.New("interrupted")

	// ErrNotReleased is returned by locker methods if lock was not released.
	ErrNotReleased = errors.New("lock was not released")

	// ErrUnknownMode is return by locker methods in case the lock was set to unknown mode.
	ErrUnknownMode = errors.New("lock is in unknown mode")
)

Functions

This section is empty.

Types

type Locker

type Locker interface {
	// Read executes given function with shared reader access.
	Read(context.Context, func()) error

	// Write executes given function with unique writer access.
	Write(context.Context, func()) error
}

Locker allows to execute given functions at reader or writer access privilege.

func New

func New(
	redisClient *redis.Client,
	keyLock, keyReadersCount, keyWriterIntent string,
	opts Options,
) Locker

New instance of RW-Locker. keyLock, keyReadersCount, keyWriterIntent must be unique keys that will be used by locker implementation.

type Mode

type Mode int

Mode of the lock behavior.

const (
	// ModeUndefined will trigger default option to be used.
	ModeUndefined Mode = iota

	// ModePreferReader makes the writer and reader to have equal priority.
	ModePreferReader

	// ModePreferWriter makes the writer to have higher priority over the reader.
	ModePreferWriter
)

type Options

type Options struct {
	// LockTTL sets lock duration timeout.
	// This allows to release the lock even if whole program crashes.
	// Recommended not to make it too big or too small as it may affect performance.
	// It should be less than RetryCount * RetryInterval in order to avoid undesired ErrTimeouts.
	// Minimum: 100 milliseconds
	// Default: 1 second
	LockTTL time.Duration

	// RetryCount limits number of attempts to acquire lock.
	// Default: 200
	RetryCount int

	// RetryInterval sets interval between attemts to acquire lock.
	// Minimum: 1 millisecond
	// Default: 10 milliseconds
	RetryInterval time.Duration

	// AppID is used as writer lock token prefix.
	// Used for debugging.
	AppID string

	// ReaderLockToken should be the same for all readers group.
	// You can override default token here to create subgroups of readers.
	ReaderLockToken string

	// Mode of the lock behavior.
	// Defaults to writer-preferring behavior in order not to break back compatibility.
	// Default: ModePreferWriter
	Mode Mode
}

Options used to configure locker.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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