Skip to content

Commit

Permalink
Merge branch 'master' into 42technologies/master
Browse files Browse the repository at this point in the history
  • Loading branch information
TheDeveloper committed Sep 1, 2021
2 parents a087209 + 24f5ce4 commit d4fcea5
Show file tree
Hide file tree
Showing 13 changed files with 2,493 additions and 124 deletions.
9 changes: 9 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
env:
commonjs: true
es2021: true
node: true
extends:
- airbnb-base
parserOptions:
ecmaVersion: 12
rules: {}
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
Changelog
---
# [1.0.0](https://github.com/TheDeveloper/warlock/compare/v0.2.0...v1.0.0) (2021-04-02)

**This is a major release and contain breaking changes. Please read this changelog before upgrading.**

### BREAKING CHANGES

* Drop support for node 6.

### Bug Fixes
* Switch deprecated [node-redis-scripty](https://github.com/TheDeveloper/scripty) to [node-redis-script](https://github.com/TheDeveloper/node-redis-script).
* Updated deps.

# v0.2.0

Expand Down
35 changes: 16 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
warlock
=======

[![Travis](https://travis-ci.org/TheDeveloper/warlock.svg?branch=master)](https://travis-ci.org/TheDeveloper/warlock)
[![Dependency Status](https://david-dm.org/thedeveloper/warlock.svg)](https://david-dm.org/thedeveloper/warlock)
[![Join the chat at https://gitter.im/TheDeveloper/warlock](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/TheDeveloper/warlock?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Battle-hardened distributed locking using redis.
Expand All @@ -19,19 +17,18 @@ Battle-hardened distributed locking using redis.
## Usage

```javascript

var Warlock = require('node-redis-warlock');
var redis = require('redis');
const Warlock = require('node-redis-warlock');
const Redis = require('redis');

// Establish a redis client and pass it to warlock
var redis = redis.createClient();
var warlock = Warlock(redis);
const redis = Redis.createClient();
const warlock = Warlock(redis);

// Set a lock
var key = 'test-lock';
var ttl = 10000; // Lifetime of the lock
const key = 'test-lock';
const ttl = 10000; // Lifetime of the lock

warlock.lock(key, ttl, function(err, unlock){
warlock.lock(key, ttl, (err, unlock) => {
if (err) {
// Something went wrong and we weren't able to set a lock
return;
Expand All @@ -51,24 +48,24 @@ warlock.lock(key, ttl, function(err, unlock){
});

// set a lock optimistically
var key = 'opt-lock';
var ttl = 10000;
var maxAttempts = 4; // Max number of times to try setting the lock before erroring
var wait = 1000; // Time to wait before another attempt if lock already in place
warlock.optimistic(key, ttl, maxAttempts, wait, function(err, unlock) {});
const key = 'opt-lock';
const ttl = 10000;
const maxAttempts = 4; // Max number of times to try setting the lock before erroring
const wait = 1000; // Time to wait before another attempt if lock already in place
warlock.optimistic(key, ttl, maxAttempts, wait, (err, unlock) => {});

// unlock using the lock id
var key = 'test-lock-2';
var ttl = 10000;
var lockId;
let lockId;

warlock.lock(key, ttl, function(err, _, id) {
warlock.lock(key, ttl, (err, _, id) => {
lockId = id;
});

// each client who knows the lockId can release the lock
warlock.unlock(key, lockId, function(err, result) {
if(result == 1) {
warlock.unlock(key, lockId, (err, result) => {
if (result == 1) {
// unlocked successfully
}
});
Expand Down
13 changes: 13 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
redis:
image: redis:3.2-alpine
ports:
- 6386:6379
container_name: warlock-redis

monitor:
image: redis:3.2-alpine
links:
- redis
entrypoint: "/bin/sh"
command: >
"-c" "redis-cli -h warlock-redis -p 6379 monitor"
10 changes: 10 additions & 0 deletions lib/del.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const path = require('path');

const _path = path.resolve(__dirname, './lua/parityDel.lua');
const src = require('fs').readFileSync(_path, { encoding: 'utf-8' });
const { createScript } = require('node-redis-script');

exports.createScript = (redis) => {
const opts = { redis };
return createScript(opts, src);
};
2 changes: 1 addition & 1 deletion lib/lua/parityDel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
-- Delete a key if content is equal
--
-- KEYS[1] - key
-- KEYS[2] - content
-- ARGV[1] - content
local key = KEYS[1]
local content = ARGV[1]

Expand Down
58 changes: 28 additions & 30 deletions lib/warlock.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
var crypto = require('crypto');
var Scripty = require('node-redis-scripty');
var UUID = require('uuid');
const UUID = require('uuid');
const { createScript } = require('./del');

module.exports = function(redis){
var warlock = {};
module.exports = function (redis) {
const warlock = {};

var scripty = new Scripty(redis);
const parityDel = createScript(redis);

warlock.makeKey = function(key) {
return key + ':lock';
warlock.makeKey = function (key) {
return `${key}:lock`;
};

/**
Expand All @@ -17,64 +16,63 @@ module.exports = function(redis){
* @param {integer} ttl Time in milliseconds for the lock to live.
* @param {Function} cb
*/
warlock.lock = function(key, ttl, cb) {
cb = cb || function(){};
warlock.lock = function (key, ttl, cb) {
cb = cb || function () {};

if (typeof key !== 'string') {
return cb(new Error('lock key must be string'));
}

var id;
let id;
UUID.v1(null, (id = new Buffer(16)));
id = id.toString('base64');
redis.set(
warlock.makeKey(key), id,
'PX', ttl, 'NX',
function(err, lockSet) {
(err, lockSet) => {
if (err) return cb(err);

var unlock = warlock.unlock.bind(warlock, key, id);
let unlock = warlock.unlock.bind(warlock, key, id);
if (!lockSet) unlock = false;

return cb(err, unlock, id);
}
},
);

return key;
};

warlock.unlock = function(key, id, cb) {
cb = cb || function(){};
warlock.unlock = async (key, id, cb) => {
cb = cb || function () {};

if (typeof key !== 'string') {
return cb(new Error('lock key must be string'));
}

scripty.loadScriptFile(
'parityDel',
__dirname + '/lua/parityDel.lua',
function(err, parityDel){
if (err) return cb(err);

return parityDel.run(1, warlock.makeKey(key), id, cb);
}
);
const numKeys = 1;
const _key = warlock.makeKey(key);
try {
const result = await parityDel(numKeys, _key, id);
cb(null, result);
} catch (e) {
cb(e);
}
};

/**
* Set a lock optimistically (retries until reaching maxAttempts).
*/
warlock.optimistic = function(key, ttl, maxAttempts, wait, cb) {
var attempts = 0;
warlock.optimistic = function (key, ttl, maxAttempts, wait, cb) {
let attempts = 0;

var tryLock = function() {
var tryLock = function () {
attempts += 1;
warlock.lock(key, ttl, function(err, unlock) {
warlock.lock(key, ttl, (err, unlock) => {
if (err) return cb(err);

if (typeof unlock !== 'function') {
if (attempts >= maxAttempts) {
var e = new Error('unable to obtain lock');
const e = new Error('unable to obtain lock');
e.maxAttempts = maxAttempts;
e.key = key;
e.ttl = ttl;
Expand Down
Loading

0 comments on commit d4fcea5

Please sign in to comment.