Microlock
A dead simple distributed locking library for Node.js and etcd (via node-etcd)
What is Etcd?
Etcd is a distributed key-value store, built by the CoreOS team, that provides strong guarantees around consistency and partition tolerance. Data is duplicated to all nodes in a given cluster and remains consistent between node failures. Cluster leaders are elected via the Raft consensus algorithm. Etcd provides operations for atomic value swapping/removal based on criteria and TTL for values, making it a perfect media for distributed locks.
What is a distributed lock?
A distributed lock is a mechanism that provides serialized flow control on a context that is acted on by more than one process. These processes typically operate on different machines via Service Oriented Architecture. Each process uses an object called a distributed lock to "lock" access to the shared context, aliased by a key, so that only one process, each aliased by a node id, can act on it at a time, thereby ensuring consistency and preventing race conditions.
Why not Redlock?
Redis is great for a lot of things. Caching, keeping processes stateless, and fast access to simply structured data are all cases where Redis shines. However, implementing a distributed lock with Redis via Redlock has several caveats that are unsuitable for many cases. Namely, if you need strong guarantees that a lock will not be acquired by multiple nodes at once even in the event of failure, Redlock isn't a viable option.
Notes
Microlock is currently compatible with Etd 2.2.x. Work on support for Etcd 2.2.x - 3.x is in progress.
Install
Requires NodeJS >= 4.0
$ npm install microlock
Basic usage
ES5
var os = ;var Etcd = ;var Microlock = ; var key = 'foo'; //name of the lockvar id = oshostname; //id of *this* nodevar ttl = 5; //5 second lease on lock var etcd = ;var foo = etcd key id ttl; foo;
ES2015 (with babel)
;;; const key = 'foo'; //name of the lockconst id = ; //id of *this* nodeconst ttl = 5; //5 second lease on lock const etcd = ;const foo = etcd key id ttl; foo;
ES2016/2017 (with babel)
;;; { const key = 'foo'; //name of the lock const id = ; //id of *this* node const ttl = 5; //5 second lease on lock const etcd = ; const foo = etcd key id ttl; try await foo; // foo is locked by this node // do some stuff... // release the lock await foo; catch e if e instanceof AlreadyLockedError // foo is already locked by a different node } ;
Methods
All methods (except destroy) return promises, making it easy to use features like async/await with ES2016/ES2017 via Babel.
Microlock(etcd, key, node_id, [ttl = 1])
Creates a microlock client for a lock key.
var Etcd = ;var Microlock = ; var etcd = ;var foo = microlock 'foo' 'bar';
.lock()
Attempts to lock the key
for the node_id
.
foo;
.unlock()
Attempts to release the key
for the node_id
.
foo
.renew()
Attempts to renew the lock on key
for the node_id
.
foo
.destroy()
Unbinds listeners/watchers from this client
Events
unlock
Emits when the key is unlocked (node agnostic)
foo; foo;
locked
Emits when the key is locked (node agnostic)
foo; foo;
Contributing
Installing packages
$ make install
Building
$ make
Linting
$ make lint
Running unit tests
$ make unit
Running integration tests
Docker Compose is required
$ make integration etcd_image_version=v2.2.2
You can use whatever version you'd like to test against in the command above.