diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..ab22b7c --- /dev/null +++ b/index.d.ts @@ -0,0 +1,42 @@ +import { Redis } from 'ioredis'; + +/** + * Release the lock only if it has the same lockValue as acquireLock sets it. + * This will not release an already released token. + */ +type ReleaseFunction = () => Promise; + +interface acquireOptions { + /** + * Time interval at which attempt to acquire the lock. + * + * @default 100 + */ + retryTimeMillis?: number; + + /** + * Time span after which the acquired lock times out and is released. + */ + timeoutMillis?: number; + + /** + * Time span after which will not attempt to acquire the lock, and the `lock` function will fail. + */ + failAfterMillis?: number; +} + +/** + * Acquire mutex lock on the given resource name. + * If the lock is already acquired, wait until it's free and acquire it. + * + * @param client ioredis instance. + * @param lockName the name of the lock to be acquired. + * @param options lock acquire options. + * + * @returns a promise that resolves with release function. + */ +export function lock( + client: Redis, + lockName: string, + options: acquireOptions +): Promise; diff --git a/index.js b/index.js index 0b99ab5..2eeebf1 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,5 @@ const crypto = require('crypto'); -/** - * Acquire mutex lock on the given resource name. If the lock is already acquired, wait until it's free and acquire it. - * - * @param {import('ioredis').Redis} client - * @param {String} lockName - * @param {Object} [options] - * @param {Number} [options.retryTimeMillis=100] - * @param {Number} [options.timeoutMillis] - * @param {Number} [options.failAfterMillis] - * - * @returns {Promise} release function - */ async function lock(client, lockName, { retryTimeMillis = 100, timeoutMillis, failAfterMillis } = {}) { const lockValue = crypto.randomBytes(50).toString('hex'); const lockKey = `@simple-redis-mutex:lock-${lockName}`; @@ -56,14 +44,13 @@ async function lock(client, lockName, { retryTimeMillis = 100, timeoutMillis, fa attempt(); }); - /** - * Release the lock only if it has the same lockValue as acquireLock sets it. - * This will prevent the release of an already released token. - * - * @returns {Promise} - */ function releaseLock() { - // Script source: https://redis.io/commands/set#patterns - Redis official docs + /* + * Release the lock only if it has the same lockValue as acquireLock sets it. + * This will prevent the release of an already released lock. + * + * Script source: https://redis.io/commands/set#patterns - Redis official docs + */ const luaReleaseScript = ` if redis.call("get", KEYS[1]) == ARGV[1] then @@ -73,7 +60,8 @@ async function lock(client, lockName, { retryTimeMillis = 100, timeoutMillis, fa end `; - return client.eval(luaReleaseScript, 1, lockKey, lockValue); + // After calling the script, make sure to return void promise. + return client.eval(luaReleaseScript, 1, lockKey, lockValue).then(() => {}); } await acquireLock;