Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/content/docs/drivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Verrou supports multiple drivers to store the locks. No matter which driver you

## Redis

You will need to install `ioredis` to use this driver.
You will need to install `ioredis` to use this driver. Both ioredis v5 and v6 are supported. Note that ioredis v6 requires Node.js 20 or newer.

The Redis driver can be used with many different providers:

Expand Down Expand Up @@ -55,7 +55,7 @@ const lockFactory = new LockFactory(store)

| Option | Description | Default |
|--------------|--------------------------|---------|
| `connection` | An instance of `ioredis` | N/A |
| `connection` | An ioredis `Redis` or `Cluster` instance | N/A |

### Implementation details

Expand Down
2 changes: 1 addition & 1 deletion packages/verrou/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"peerDependencies": {
"@aws-sdk/client-dynamodb": "^3.484.0",
"ioredis": "^5.3.2"
"ioredis": "^5.3.2 || ^6.0.0"
},
"peerDependenciesMeta": {
"@aws-sdk/client-dynamodb": {
Expand Down
6 changes: 2 additions & 4 deletions packages/verrou/src/drivers/redis.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { Redis as IoRedis } from 'ioredis'

import { E_LOCK_NOT_OWNED } from '../errors.js'
import type { LockStore, RedisStoreOptions } from '../types/main.js'
import type { LockStore, RedisConnection, RedisStoreOptions } from '../types/main.js'

/**
* Create a new Redis store
Expand All @@ -14,7 +12,7 @@ export class RedisStore implements LockStore {
/**
* IORedis connection instance
*/
#connection: IoRedis
#connection: RedisConnection

constructor(options: RedisStoreOptions) {
this.#connection = options.connection
Expand Down
20 changes: 17 additions & 3 deletions packages/verrou/src/types/drivers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Knex } from 'knex'
import type { Kysely } from 'kysely'
import type { Redis as IoRedis } from 'ioredis'
import type { DynamoDBClient } from '@aws-sdk/client-dynamodb'

/**
Expand Down Expand Up @@ -42,14 +41,29 @@ export interface KyselyOptions extends DatabaseOptions {
connection: Kysely<any>
}

/**
* The subset of the ioredis API used by the Redis store.
*
* Typed structurally rather than against the `Redis` class so that
* any ioredis version (v5, v6), reply mapping (`legacy`, `resp3`)
* or `Cluster` instance is accepted.
*/
export interface RedisConnection {
set(key: string, value: string, px: 'PX', ms: number, nx: 'NX'): Promise<'OK' | null>
setnx(key: string, value: string): Promise<number>
eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>
del(key: string): Promise<number>
get(key: string): Promise<string | null>
}

/**
* Options for the Redis store
*/
export type RedisStoreOptions = {
/**
* The Redis connection
* The Redis connection. Any ioredis `Redis` or `Cluster` instance
*/
connection: IoRedis
connection: RedisConnection
}

/**
Expand Down
7 changes: 7 additions & 0 deletions packages/verrou/tests/drivers/redis.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Redis } from 'ioredis'
import { test } from '@japa/runner'
import type { Cluster } from 'ioredis'

import { RedisStore } from '../../src/drivers/redis.js'
import type { RedisConnection } from '../../src/types/main.js'
import { REDIS_CREDENTIALS } from '../../test_helpers/index.js'
import { registerStoreTestSuite } from '../../src/test_suite.js'

Expand All @@ -21,6 +23,11 @@ test.group('Redis Driver', (group) => {
createStore: () => new RedisStore({ connection: ioredis }),
})

test('accepts Redis and Cluster instances as connection', ({ expectTypeOf }) => {
expectTypeOf<Redis>().toMatchTypeOf<RedisConnection>()
expectTypeOf<Cluster>().toMatchTypeOf<RedisConnection>()
})

test('null ttl', async ({ assert }) => {
const store = new RedisStore({ connection: ioredis })
await store.save('foo', 'bar', null)
Expand Down