nslock

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

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

Go to latest
Published: Sep 8, 2020 License: MIT Imports: 6 Imported by: 0

README

go-nslock

Go Report Card codecov Build Status LICENSE

go-nslock is a pure Go implementation of namespace lock.

Install

go get github.com/meowdada/go-nslock

What is namespace lock ?

Namespace lock is a kind of lock which only locks on a specific namespace, which usually is a key consisting of a string or an array of strings.

The main purpose of the namespace lock is to maintenance the consistency of a system by locking resources in proper way.

For example, say we have an application that allow user to apply CRUD operations on objects which are stored in our backend (database, cloud or something else...). Once there are multiple operations, such like ADD, UPDATE or DELETE on the same object at the sametime, how can we determine the eventually result would be. By using namespace lock, we can make these operations atomic without blocking other objects with different namespace. This could improve the performance significantly compared to using a single lock.

Usage

In brief, you have to create a nslock.Map instance to manages all the namespace locks. In most cases, create one would be enough.

Then, whenever you want to acquire a namespace lock, you'll need to create a lock instance and do the Lock or RLock depends on your need. And remember to reclaim these locks by Unlock or RUnlock when you're no longer need it or it might be blocked forerver.

Manually lock and unlock

// Creates a namespace lock manager.
m := NewMap()

// Creates a new lock instance with given namespace.
ctx, namespace := context.Background(), "myNamespace"
ins := m.New(ctx, namespace)

// Defines timeout that denotes how long can this thread blocking wait for fetching this locker.
timeout := time.Second

// Peform try locking operation.
if err := ins.Lock(timeout); err != nil {
    // Your logic for getting lock failed...
    return ...
}
defer ins.Unlock()

Automatically lock and unlock

// Creates a namespace lock manager.
m := NewMap()

// Creates a new lock instance with given namespace.
ctx, namespace := context.Background(), "myNamespace"
ins := m.New(ctx, namespace)

// Defines timeout that denotes how long can this thread blocking wait for fetching this locker.
timeout := time.Second

callback := func() error {
    // Your business logic to do when the namespaces are locked.
    return ...
}

// Perform try locking operation.
if err := ins.LockFn(timeout, callback); err != nil {
    // Handle the error, it could be an error that failed to fetch a lock or the error value returned by the callback function.
}

Note that the timeout value of any lock operations should be reasonable one. If the value is too small such as microsecond, it might lead to impossible to fetch a lock because the creation time of internal data structure when fetching lock always consume more than a microsecond. The minimum value depends on your hardware, but in generally 10 millisecond would be a minimum safe choice.

Contributing

Any contributions are welcome.

References

Inspired by awesome minio subprojects.

And we use these two packages internally for sharding and retry capabilities.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type GetLockErr

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

GetLockErr implements error interface. And it appends the information of the get lock request.

func (GetLockErr) Error

func (e GetLockErr) Error() string

Error implements error interface.

type Instance

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

Instance is a lock instance which is able to lock/unlock multiple namespaces at once.

Note that you can only create a valid instance by invoking Map.New(ctx, keys...).

func (*Instance) Lock

func (ins *Instance) Lock(timeout time.Duration) error

Lock locks all underlying namespaces with a write lock.

func (*Instance) LockFn

func (ins *Instance) LockFn(timeout time.Duration, fn func() error) error

LockFn locks all underlying namespaces with a write lock and automatically unlock this locker when LockFn returned. It will keep contesting for the successful lock until it succeed or being timeout.

func (*Instance) RLock

func (ins *Instance) RLock(timeout time.Duration) error

RLock locks underlying namespace with a read lock.

func (*Instance) RLockFn

func (ins *Instance) RLockFn(timeout time.Duration, fn func() error) error

RLockFn locks all underlying namespaces with a read lock and automatically unlock this locker when LockFn returned. It will keep contesting for the successful lock until it succeed or being timeout.

func (*Instance) RUnlock

func (ins *Instance) RUnlock()

RUnlock unlocks the read lock of underlying namespace.

func (*Instance) Unlock

func (ins *Instance) Unlock()

Unlock unlocks the write lock of underlying namespace.

type Map

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

Map is a namespace locks manager.

func NewMap

func NewMap() *Map

NewMap returns an initialized map instance.

func (*Map) New

func (m *Map) New(ctx context.Context, keys ...string) RWLocker

New returns a namespace lock instance managed by this map.

type RWLocker

type RWLocker interface {
	LockFn(timeout time.Duration, fn func() error) error
	RLockFn(timeout time.Duration, fn func() error) error
	Lock(timeout time.Duration) error
	Unlock()
	RLock(timeout time.Duration) error
	RUnlock()
}

RWLocker is a locker interface that provides read-write lock capabilities.

Jump to

Keyboard shortcuts

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