Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/thebigredgeek/microlock
A dead simple distributed locking library for Node.js and Etcd
https://github.com/thebigredgeek/microlock
distributed-locks etcd nodejs
Last synced: 15 days ago
JSON representation
A dead simple distributed locking library for Node.js and Etcd
- Host: GitHub
- URL: https://github.com/thebigredgeek/microlock
- Owner: thebigredgeek
- License: mit
- Created: 2016-07-16T06:33:58.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2024-04-06T23:58:10.000Z (7 months ago)
- Last Synced: 2024-10-14T18:39:32.023Z (27 days ago)
- Topics: distributed-locks, etcd, nodejs
- Language: JavaScript
- Homepage:
- Size: 79.1 KB
- Stars: 93
- Watchers: 3
- Forks: 8
- Open Issues: 14
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Microlock
A dead simple distributed locking library for Node.js and [etcd](http://github.com/coreos/etcd) (via [node-etcd](https://github.com/stianeikeland/node-etcd))[![NPM](https://nodei.co/npm/microlock.png)](https://nodei.co/npm/microlock/)
[![CircleCI](https://circleci.com/gh/thebigredgeek/microlock.svg?style=shield)](https://circleci.com/gh/thebigredgeek/microlock/tree/master)
## What is Etcd?
[Etcd](https://github.com/coreos/etcd) is a distributed key-value store, built by the [CoreOS](https://coreos.com/) 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](https://raft.github.io/). 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**```bash
$ npm install microlock
```## Basic usage
### ES5
```javascript
var os = require('os');
var Etcd = require('node-etcd');
var Microlock = require('microlock');var key = 'foo'; //name of the lock
var id = os.hostname(); //id of *this* node
var ttl = 5; //5 second lease on lockvar etcd = new Etcd();
var foo = new Microlock.default(etcd, key, id, ttl);foo.lock().then(function () {
// foo is locked by this node// do some stuff...
// release the lock
return foo.unlock();
}, function (e) {
if (e instanceof Microlock.AlreadyLockedError) {
// foo is already locked by a different node
}
});
```### ES2015 (with babel)
```javascript
import { hostname } from 'os';
import Etcd from 'node-etcd';
import Microlock, { AlreadyLockedError } from 'microlock';const key = 'foo'; //name of the lock
const id = hostname(); //id of *this* node
const ttl = 5; //5 second lease on lockconst etcd = new Etcd();
const foo = new Microlock(etcd, key, id, ttl);foo.lock().then(() => {
// foo is locked by this node// do some stuff...
// release the lock
return foo.unlock();
}, (e) => {
if (e instanceof AlreadyLockedError) {
// foo is already locked by a different node
}
});
```### ES2016/2017 (with babel)
```javascriptimport { hostname } from 'os';
import Etcd from 'node-etcd';
import Microlock, { AlreadyLockedError } from 'microlock';async function main () {
const key = 'foo'; //name of the lock
const id = hostname(); //id of *this* node
const ttl = 5; //5 second lease on lockconst etcd = new Etcd();
const foo = new Microlock(etcd, key, id, ttl);try {
await foo.lock();
// foo is locked by this node// do some stuff...
// release the lock
await foo.unlock();
} catch (e) {
if (e instanceof AlreadyLockedError) {
// foo is already locked by a different node
}
}
}main();
```## 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.```javascript
var Etcd = require('node-etcd');
var Microlock = require('microlock');var etcd = new Etcd();
var foo = new Microlock.default(microlock, 'foo', 'bar');
```### .lock()
Attempts to lock the `key` for the `node_id`.```javascript
foo.lock().then(function () {
// foo is locked by this node
}, function (e) {
if (e instanceof Microlock.AlreadyLockedError) {
// foo is already locked by a different node
}
});
```### .unlock()
Attempts to release the `key` for the `node_id`.```javascript
foo.unlock().then(function () {
// foo is unlocked
}, function (e) {
if (e instanceof Microlock.LockNotOwnedError) {
// foo is not locked by `node_id`
}
})
```### .renew()
Attempts to renew the lock on `key` for the `node_id`.```javascript
foo.renew().then(function () {
// foo lease is renewed... ttl is refreshed
}, function (e) {
if (e instanceof Microlock.LockNotOwnedError) {
// foo is not locked by `node_id`
}
})
```### .destroy()
Unbinds listeners/watchers from this client## Events
### unlock
Emits when the key is unlocked (node agnostic)```javascript
foo.on(Microlock.events.unlocked, function () {
//handle unlocked with constant
});foo.on('unlocked', function () {
//handle unlocked with string
});
```### locked
Emits when the key is locked (node agnostic)```javascript
foo.on(Microlock.events.locked, function () {
//handle locked with constant
});foo.on('locked', function () {
//handle locked with string
});
```## Contributing
### Installing packages
```bash
$ make install
```### Building
```bash
$ make
```### Linting
```bash
$ make lint
```### Running unit tests
```bash
$ make unit
```### Running integration tests
**[Docker Compose](https://docs.docker.com/compose/) is required**
```bash
$ make integration etcd_image_version=v2.2.2
```You can use whatever version you'd like to test against in the command above.