Skip to content

Commit

Permalink
feat: uses ioredis 5 and ESM (#23)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: ioredis@5 is now supported by default, removed bluebird. Module now uses ESM format, but doesn't use async load and node should be able to load it if need be with recent versions even in cjs

* feat: uses ioredis 5 and ESM
* chore: update readme
  • Loading branch information
AVVS authored Jan 19, 2025
1 parent 3201bd5 commit 302116a
Show file tree
Hide file tree
Showing 16 changed files with 5,581 additions and 3,920 deletions.
File renamed without changes.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ lib-cov
*.out
*.pid
*.gz
*.tsbuildinfo

pids
logs
Expand All @@ -15,4 +16,4 @@ npm-debug.log
node_modules

.DS_Store
lib
lib
5 changes: 1 addition & 4 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

"$(npm x mdep bin commitlint)" --edit $1
"`npm x -- mdep bin commitlint`" --edit $1
1 change: 0 additions & 1 deletion .husky/prepare-commit-msg
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

case "$2,$3" in
merge,)
Expand Down
6 changes: 2 additions & 4 deletions .mdeprc.js → .mdeprc.cjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
module.exports = {
nycCoverage: false,
coverage: false,
auto_compose: true,
node: "16",
node: "22",
parallel: 1,
services: ['redis'],
test_framework: "mocha -r @swc-node/register -R spec",
test_framework: "mocha",
tests: "./spec/**/*.spec.ts"
}
5 changes: 5 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"import": "@swc-node/register/esm-register",
"extension": "ts",
"reporter": "spec"
}
8 changes: 4 additions & 4 deletions .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ global_job_config:
prologue:
commands:
- set -e
- sem-version node 16
- curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6
- sem-version node 22
- corepack enable pnpm
- checkout
- cache restore node-$(checksum pnpm-lock.yaml)
- pnpm i --prefer-offline
- cache store node-$(checksum pnpm-lock.yaml) ~/.pnpm-store
- pnpm i --frozen-lockfile --prefer-offline --ignore-scripts
- cache store node-$(checksum pnpm-lock.yaml) $(pnpm store path)

blocks:
- name: tests
Expand Down
143 changes: 65 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@ WATCH/MULTI. Refer to [Implementation](#implementation) and
* [redislock.LockReleaseError](#redislocklockreleaseerror)
* [redislock.LockExtendError](#redislocklockextenderror)
* [Class: Lock](#class-lock)
* [lock.acquire(key, \[fn\])](#lockacquirekey-fn)
* [lock.release(\[fn\])](#lockreleasefn)
* [lock.extend(time, \[fn\])](#lockextendtime-fn)
* [lock.acquire(key)](#lockacquirekey-fn)
* [lock.release()](#lockreleasefn)
* [lock.extend(time)](#lockextendtime-fn)
* [Tests](#tests)

## Installation

Using npm, you can install redislock with `npm install ioredis-lock -S`.

Note: since version 3.4.0 it's possible to use this with node@4 once again
Using npm, you can install redislock with `npm i @microfleet/ioredis-lock -S`.

## Overview

Expand All @@ -39,17 +37,19 @@ object specifying the following three options:
* delay: Time in milliseconds to wait between each attempt (default: 50 ms)

``` javascript
const Promise = require('bluebird');
const Redis = require('ioredis');
import Bluebird from 'bluebird'
import { Redis } from 'ioredis'
import { createLock } from '@microfleet/ioredis-lock'

const client = new Redis();
const lock = require('ioredis-lock').createLock(client, {
const lock = createLock(client, {
timeout: 20000,
retries: 3,
delay: 100,
});

// this uses bind feature of `bluebird`
Promise
Bluebird
.bind(lock)
.call('acquire', 'app:feature:lock')
.catch(err => {
Expand All @@ -62,52 +62,30 @@ Promise
.then(() => {
// all good
});
});
```

Supports promises, thanks to bluebird, out of the box:

```javascript
const Redis = require('ioredis');
const client = new Redis();
const lock = require('ioredis-lock').createLock(client);

const LockAcquisitionError = redislock.LockAcquisitionError;
const LockReleaseError = redislock.LockReleaseError;

lock.acquire('app:feature:lock').then(() => {
// Lock has been acquired
return lock.release();
}).then(() => {
// Lock has been released
}).catch(LockAcquisitionError, (err) => {
// The lock could not be acquired
}).catch(LockReleaseError, (err) => {
// The lock could not be released
});
```
import Bluebird from 'bluebird'
import { Redis } from 'ioredis'
import { createLock, LockAcquisitionError, LockReleaseError } from '@microfleet/ioredis-lock'

And an example with co:

```javascript
const co = require('co');
const Redis = require('ioredis');
const client = new Redis();
const lock = require('ioredis-lock').createLock(client);
const lock = createLock(client);

co(function *(){
try {
yield lock.acquire('app:feature:lock');
} catch (e) {
// Failed to acquire the lock
}

try {
yield lock.release();
} catch (e) {
// Failed to release
}
})();
Bluebird
.resolve(lock.acquire('app:feature:lock'))
.then(() => {
// Lock has been acquired
return lock.release();
}).then(() => {
// Lock has been released
}).catch(LockAcquisitionError, (err) => {
// The lock could not be acquired
}).catch(LockReleaseError, (err) => {
// The lock could not be released
});
```

## Implementation
Expand Down Expand Up @@ -180,7 +158,7 @@ following three keys, as outlined at the start of the documentation: timeout,
retries and delay.

``` javascript
var lock = redislock.createLock(client, {
const lock = redislock.createLock(client, {
timeout: 10000,
retries: 3,
delay: 100
Expand All @@ -195,11 +173,10 @@ Returns an array of currently active/acquired locks.
// Create 3 locks, but only acquire 2
redislock.createLock(client);

redislock.createLock(client).acquire('app:lock1', function(err) {
redislock.createLock(client).acquire('app:lock2', function(err) {
const locks = redislock.getAcquiredLocks(); // [lock, lock]
});
});
await redislock.createLock(client).acquire('app:lock1')
await redislock.createLock(client).acquire('app:lock2')

const locks = redislock.getAcquiredLocks(); // [lock, lock]
```

#### redislock.LockAcquisitionError
Expand All @@ -222,7 +199,7 @@ could not be extended.
The lock class exposed by redislock. Each instance is assigned a UUID v1 string
as an id, and is configured to work with the given redis client

#### lock.acquire[key, [fn]]
#### lock.acquire(key)

Attempts to acquire a lock, given a key, and an optional callback function.
If the initial lock fails, additional attempts will be made for the
Expand All @@ -233,29 +210,40 @@ LockAcquisitionError.

``` javascript
const lock = redislock.createLock(client);
lock.acquire('example:lock', function(err) {
if (err) return console.log(err.message); // 'Lock already held'
});

try {
await lock.acquire('example:lock')
} catch (err) {
console.log(err.message); // 'Lock already held'
}

```

#### lock.release([fn])

Attempts to release the lock, and accepts an optional callback function. The callback is invoked with an error on failure, and returns a promise if no callback is supplied. If invoked in the context of a promise, it may throw a LockReleaseError.

```js
const lock = redislock.createLock(client);
lock.acquire('app:lock', err => {
if (err) return;

setTimeout(() => {
lock.release(err => {
if (err) return console.log(err.message); // 'Lock on app:lock has expired'
});
}, 20000);
});
import { setTimeout } from 'node:timers/promises'

const lock = redislock.createLock(client)

try {
await lock.acquire('app:lock')
await setTimeout(20e3)

try {
await lock.release();
} catch (err) {
console.log(err.message); // 'Lock on app:lock has expired'
}

} catch (err) {
throw err
}
```

#### lock.extend(time, [fn])
#### lock.extend(time)

Attempts to extend the timeout of a lock, and accepts an optional callback
function. The callback is invoked with an error on failure, and returns a
Expand All @@ -264,15 +252,14 @@ it may throw a LockExtendError.

``` javascript
const lock = redislock.createLock(client);
lock.acquire('app:lock', function(err) {
if (err) return;

setTimeout(function() {
lock.extend(20000, function(err) {
if (err) return console.log(err.message); // 'Lock on app:lock has expired'
});
}, 20000)
});
await lock.acquire('app:lock')

await setTimeout(20e3)
try {
await lock.extend(20000)
} catch (err) {
console.log(err.message); // 'Lock on app:lock has expired'
}
```

## Tests
Expand Down
58 changes: 32 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
"name": "@microfleet/ioredis-lock",
"version": "4.0.0",
"description": "Node distributed locking using redis with ioredis adapter",
"type": "module",
"main": "lib/redislock.js",
"types": "./lib/redislock.d.ts",
"exports": {
".": {
"import": "./lib/redislock.js",
"default": "./lib/redislock.js",
"types": "./lib/redislock.d.ts"
}
},
"publishConfig": {
"access": "public"
},
Expand All @@ -22,49 +32,45 @@
"Bernhard Weisshuhn <[email protected]>"
],
"license": "MIT",
"main": "lib/redislock.js",
"homepage": "https://github.com/microfleet/ioredis-lock",
"repository": {
"type": "git",
"url": "https://github.com/microfleet/ioredis-lock.git"
},
"dependencies": {
"bluebird": "^3.7.2",
"uuid": "^8.3.2"
"uuid": "^11.0.5"
},
"peerDependencies": {
"ioredis": "~4.x.x"
"ioredis": "~4.x.x || ~5.x.x"
},
"devDependencies": {
"@makeomatic/deploy": "^12.5.0",
"@swc-node/register": "^1.4.2",
"@types/bluebird": "^3.5.33",
"@types/ioredis": "^4.22.3",
"@types/mocha": "^9.1.0",
"@types/node": "^17.0.14",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"codecov": "^3.8.3",
"@makeomatic/deploy": "^13.1.0",
"@oxc-resolver/binding-linux-arm64-musl": "^3.0.3",
"@swc-node/register": "^1.10.9",
"@swc/core-linux-arm64-musl": "^1.10.7",
"@types/mocha": "^10.0.10",
"@types/node": "^22.10.7",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"cross-env": "^7.0.3",
"eslint": "^8.8.0",
"eslint-config-makeomatic": "^5.0.4",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-promise": "^6.0.0",
"ioredis": "^4.26.0",
"mocha": "^9.2.0",
"semantic-release": "^19.0.2",
"typescript": "^4.2.3"
"eslint": "^8.57.1",
"eslint-config-makeomatic": "^6.0.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-promise": "^7.2.1",
"ioredis": "^5.4.2",
"mocha": "^11.0.1",
"semantic-release": "^24.2.1",
"typescript": "^5.7.3"
},
"engine": {
"node": ">= 12.18.0"
"node": ">= 22.13.0"
},
"scripts": {
"lint": "eslint ./src",
"test": "npm run lint && mdep test run",
"test": "pnpm lint && pnpm compile && mdep test run",
"compile": "tsc --build ./tsconfig.build.json",
"prepublishOnly": "npm run compile",
"pretest": "npm run compile",
"prepublishOnly": "pnpm compile",
"semantic-release": "semantic-release"
},
"files": [
Expand Down
Loading

0 comments on commit 302116a

Please sign in to comment.