From 53b59289a6c5232f3fe918fda1381912cd3ce861 Mon Sep 17 00:00:00 2001 From: Jared Wray Date: Wed, 9 Oct 2024 12:12:01 -0700 Subject: [PATCH] valkey - adding in docs and createKeyv helper function (#1158) --- packages/valkey/README.md | 38 +++++++++++++++++++++--------------- packages/valkey/package.json | 2 +- packages/valkey/src/index.ts | 18 +++++++++-------- packages/valkey/src/types.ts | 2 +- packages/valkey/test/test.ts | 31 +++++++++++++++++------------ 5 files changed, 53 insertions(+), 38 deletions(-) diff --git a/packages/valkey/README.md b/packages/valkey/README.md index bd1b199e7..d809740b6 100644 --- a/packages/valkey/README.md +++ b/packages/valkey/README.md @@ -13,25 +13,31 @@ Valkey is the open source replacement to Redis which decided do a [dual license] We are using the [iovalkey](https://www.npmjs.com/package/iovalkey) which is a Node.js client for Valkey based on the `ioredis` client. -## Install +# Install ```shell npm install --save keyv @keyv/valkey ``` -## Usage +# Usage + +This is using the helper `createKeyv` function to create a Keyv instance with the Valkey storage adapter: ```js -import Keyv from 'keyv'; -import KeyvValkey from '@keyv/valkey'; +import {createKeyv} from '@keyv/valkey'; -const keyv = new Keyv(new KeyvValkey('redis://user:pass@localhost:6379')); +const keyv = createKeyv('redis://localhost:6379'); keyv.on('error', handleConnectionError); +await keyv.set('foo', 'bar'); +console.log(await keyv.get('foo')); // 'bar' ``` -e.g: +If you want to specify the `KeyvValkey` class directly, you can do so: ```js +import Keyv from 'keyv'; +import KeyvValkey from '@keyv/valkey'; + const keyv = new Keyv(new KeyvValkey('redis://user:pass@localhost:6379', { disable_resubscribing: true })); ``` @@ -68,32 +74,32 @@ const redis = new Redis.Cluster('redis://user:pass@localhost:6379'); const KeyvValkey = new KeyvValkey(redis); const keyv = new Keyv({ store: KeyvValkey }); ``` -## Options +# Options -### useRedisSets +## useSets -The `useRedisSets` option lets you decide whether to use Redis sets for key management. By default, this option is set to `true`. +The `useSets` option lets you decide whether to use Redis sets for key management. By default, this option is set to `true`. -When `useRedisSets` is enabled (`true`): +When `useSets` is enabled (`true`): - A namespace for the Redis sets is created, and all created keys are added to this. This allows for group management of keys. - When a key is deleted, it's removed not only from the main storage but also from the Redis set. - When clearing all keys (using the `clear` function), all keys in the Redis set are looked up for deletion. The set itself is also deleted. -**Note**: In high-performance scenarios, enabling `useRedisSets` might lead to memory leaks. If you're running a high-performance application or service, it is recommended to set `useRedisSets` to `false`. +**Note**: In high-performance scenarios, enabling `useSets` might lead to memory leaks. If you're running a high-performance application or service, it is recommended to set `useSets` to `false`. -If you decide to set `useRedisSets` as `false`, keys will be handled individually and Redis sets won't be utilized. +If you decide to set `useSets` as `false`, keys will be handled individually and Redis sets won't be utilized. -However, please note that setting `useRedisSets` to `false` could lead to performance issues in production when using the `clear` function, as it will need to iterate over all keys to delete them. +However, please note that setting `useSets` to `false` could lead to performance issues in production when using the `clear` function, as it will need to iterate over all keys to delete them. -#### Example +## Example -Here's how you can use the `useRedisSets` option: +Here's how you can use the `useSets` option: ```js import Keyv from 'keyv'; -const keyv = new Keyv(new KeyvValkey('redis://user:pass@localhost:6379', { useRedisSets: false })); +const keyv = new Keyv(new KeyvValkey('redis://user:pass@localhost:6379', { useSets: false })); ``` ## License diff --git a/packages/valkey/package.json b/packages/valkey/package.json index d62248d70..6ec28d92b 100644 --- a/packages/valkey/package.json +++ b/packages/valkey/package.json @@ -66,7 +66,7 @@ }, "devDependencies": { "@keyv/test-suite": "*", - "keyv": "^5.0.3", + "keyv": "*", "rimraf": "^6.0.1", "timekeeper": "^2.3.1", "tsd": "^0.31.2", diff --git a/packages/valkey/src/index.ts b/packages/valkey/src/index.ts index bde73a61e..6e4dbe762 100644 --- a/packages/valkey/src/index.ts +++ b/packages/valkey/src/index.ts @@ -1,11 +1,13 @@ import EventEmitter from 'events'; import Redis from 'iovalkey'; -import {type KeyvStoreAdapter, type StoredData} from 'keyv'; +import {Keyv, type KeyvStoreAdapter, type StoredData} from 'keyv'; import { type KeyvValkeyOptions, type KeyvUriOptions, } from './types.js'; +export const createKeyv = (uri: KeyvValkeyOptions | KeyvUriOptions, options?: KeyvValkeyOptions) => new Keyv({store: new KeyvValkey(uri, options)}); + class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { ttlSupport = true; namespace?: string; @@ -14,7 +16,7 @@ class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { constructor(uri: KeyvValkeyOptions | KeyvUriOptions, options?: KeyvValkeyOptions) { super(); this.opts = {}; - this.opts.useRedisSets = true; + this.opts.useSets = true; this.opts.dialect = 'redis'; if (typeof uri !== 'string' && uri.options && ('family' in uri.options || uri.isCluster)) { @@ -25,8 +27,8 @@ class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { this.redis = new Redis(options.uri!, options); } - if (options !== undefined && options.useRedisSets === false) { - this.opts.useRedisSets = false; + if (options !== undefined && options.useSets === false) { + this.opts.useSets = false; } this.redis.on('error', (error: Error) => this.emit('error', error)); @@ -37,7 +39,7 @@ class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { } _getKeyName = (key: string): string => { - if (!this.opts.useRedisSets) { + if (!this.opts.useSets) { return `sets:${this._getNamespace()}:${key}`; } @@ -75,7 +77,7 @@ class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { } }; - if (this.opts.useRedisSets) { + if (this.opts.useSets) { const trx = await this.redis.multi(); await set(trx); await trx.sadd(this._getNamespace(), key); @@ -90,7 +92,7 @@ class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { let items = 0; const unlink = async (redis: any) => redis.unlink(key); - if (this.opts.useRedisSets) { + if (this.opts.useSets) { const trx = this.redis.multi(); await unlink(trx); await trx.srem(this._getNamespace(), key); @@ -111,7 +113,7 @@ class KeyvValkey extends EventEmitter implements KeyvStoreAdapter { } async clear() { - if (this.opts.useRedisSets) { + if (this.opts.useSets) { const keys: string[] = await this.redis.smembers(this._getNamespace()); if (keys.length > 0) { await Promise.all([ diff --git a/packages/valkey/src/types.ts b/packages/valkey/src/types.ts index 301401a25..e2ca39af4 100644 --- a/packages/valkey/src/types.ts +++ b/packages/valkey/src/types.ts @@ -5,7 +5,7 @@ export type KeyvValkeyOptions = { } & { uri?: string; dialect?: string; - useRedisSets?: boolean; + useSets?: boolean; }; export type KeyvUriOptions = string | KeyvValkeyOptions | Redis | Cluster; diff --git a/packages/valkey/test/test.ts b/packages/valkey/test/test.ts index 0d537a70d..f844d235a 100644 --- a/packages/valkey/test/test.ts +++ b/packages/valkey/test/test.ts @@ -3,7 +3,7 @@ import tk from 'timekeeper'; import keyvTestSuite, {keyvIteratorTests} from '@keyv/test-suite'; import Keyv from 'keyv'; import Redis from 'iovalkey'; -import KeyvValkey from '../src/index.js'; +import KeyvValkey, {createKeyv} from '../src/index.js'; // eslint-disable-next-line @typescript-eslint/naming-convention const REDIS_HOST = 'localhost:6370'; @@ -123,8 +123,8 @@ test.it('should handle KeyvOptions with family option', t => { t.expect(keyv.redis instanceof Redis).toBeTruthy(); }); -test.it('set method should use Redis sets when useRedisSets is false', async t => { - const options = {useRedisSets: false}; +test.it('set method should use Redis sets when useSets is false', async t => { + const options = {useSets: false}; const keyv = new KeyvValkey(options); await keyv.set('foo', 'bar'); @@ -133,8 +133,8 @@ test.it('set method should use Redis sets when useRedisSets is false', async t = t.expect(value).toBe('bar'); }); -test.it('clear method when useRedisSets is false', async t => { - const options = {useRedisSets: false}; +test.it('clear method when useSets is false', async t => { + const options = {useSets: false}; const keyv = new KeyvValkey(options); await keyv.set('foo', 'bar'); @@ -148,22 +148,22 @@ test.it('clear method when useRedisSets is false', async t => { t.expect(value2).toBe(undefined); }); -test.it('clear method when useRedisSets is false and empty keys should not error', async t => { - const options = {useRedisSets: false}; +test.it('clear method when useSets is false and empty keys should not error', async t => { + const options = {useSets: false}; const keyv = new KeyvValkey(options); t.expect(await keyv.clear()).toBeUndefined(); }); -test.it('when passing in ioredis set the options.useRedisSets', t => { - const options = {useRedisSets: false}; +test.it('when passing in ioredis set the options.useSets', t => { + const options = {useSets: false}; const redis = new Redis(redisURI); const keyv = new KeyvValkey(redis, options); - t.expect(keyv.opts.useRedisSets).toBe(false); + t.expect(keyv.opts.useSets).toBe(false); }); -test.it('del should work when not using useRedisSets', async t => { - const options = {useRedisSets: false}; +test.it('del should work when not using useSets', async t => { + const options = {useSets: false}; const redis = new Redis(redisURI); const keyv = new KeyvValkey(redis, options); @@ -175,3 +175,10 @@ test.it('del should work when not using useRedisSets', async t => { t.expect(value).toBe(undefined); }); + +test.it('can create a full keyv instance with a uri', async t => { + const keyv = createKeyv(redisURI); + t.expect(keyv).toBeTruthy(); + await keyv.set('foo222', 'bar222'); + t.expect(await keyv.get('foo222')).toBe('bar222'); +});