diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 97d42a685f4..c6e9023b1b3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,7 +45,7 @@ jobs: version: "8.2" - tag: "8.4.0" version: "8.4" - - tag: "8.8-m03" + - tag: "8.8-rc1" version: "8.8" steps: - uses: actions/checkout@v4 diff --git a/examples/README.md b/examples/README.md index ce0595e8486..5e82b3a82bc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,7 +14,6 @@ This folder contains example scripts showing how to use Node Redis in different | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io). | | `cas-cad-digest.js` | Atomic compare-and-set (CAS) and compare-and-delete (CAD) using digests for single-key optimistic concurrency control. | | `dump-and-restore.js` | Demonstrates the use of the [`DUMP`](https://redis.io/commands/dump/) and [`RESTORE`](https://redis.io/commands/restore/) commands | -| `gcra-rate-limiting.js` | Demonstrates the [`GCRA`](https://redis.io/commands/gcra/) command for server-side rate limiting with optional token cost (`TOKENS`). | | `get-server-time.js` | Get the time from the Redis server. | | `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys. | diff --git a/examples/gcra-rate-limiting.js b/examples/gcra-rate-limiting.js deleted file mode 100644 index c28eed50488..00000000000 --- a/examples/gcra-rate-limiting.js +++ /dev/null @@ -1,29 +0,0 @@ -// Rate limit requests with the Redis GCRA command (Redis 8.8+). - -import { createClient } from 'redis'; - -const client = createClient(); -await client.connect(); - -const key = 'rate-limit:user:42'; -await client.del(key); - -const maxBurst = 2; -const tokensPerPeriod = 5; -const periodSeconds = 1; - -console.log('Basic rate limiting (5 requests/second with burst=2)'); -for (let i = 1; i <= 5; i++) { - const { limited, maxRequests, availableRequests, retryAfter, fullBurstAfter } = - await client.gcra(key, maxBurst, tokensPerPeriod, periodSeconds); - - console.log( - `Attempt ${i}: limited=${limited}, max=${maxRequests}, available=${availableRequests}, retryAfter=${retryAfter}, fullBurstAfter=${fullBurstAfter}` - ); -} - -console.log('\nWeighted request using TOKENS=2'); -const weighted = await client.gcra(key, maxBurst, tokensPerPeriod, periodSeconds, 2); -console.log(weighted); - -await client.close(); diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 342f89f0cd9..f05be8f77ed 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -5,7 +5,7 @@ export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); export const GLOBAL = { diff --git a/packages/client/lib/commands/GCRA.spec.ts b/packages/client/lib/commands/GCRA.spec.ts deleted file mode 100644 index 3e49214907f..00000000000 --- a/packages/client/lib/commands/GCRA.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { strict as assert } from 'node:assert'; -import testUtils, { GLOBAL } from '../test-utils'; -import GCRA from './GCRA'; -import { parseArgs } from './generic-transformers'; - -describe('GCRA', () => { - testUtils.isVersionGreaterThanHook([8, 8]); - - describe('transformArguments', () => { - it('with required arguments', () => { - assert.deepEqual( - parseArgs(GCRA, 'key', 15, 30, 60), - ['GCRA', 'key', '15', '30', '60'] - ); - }); - - it('with a fractional period', () => { - assert.deepEqual( - parseArgs(GCRA, 'key', 15, 30, 0.5), - ['GCRA', 'key', '15', '30', '0.5'] - ); - }); - - it('with TOKENS', () => { - assert.deepEqual( - parseArgs(GCRA, 'key', 15, 30, 60, 3), - ['GCRA', 'key', '15', '30', '60', 'TOKENS', '3'] - ); - }); - }); - - function assertReplyShape(reply: { - limited: boolean; - maxRequests: number; - availableRequests: number; - retryAfter: number; - fullBurstAfter: number; - }, expectedMaxRequests: number) { - assert.ok(reply.limited === true || reply.limited === false); - assert.equal(reply.maxRequests, expectedMaxRequests); - assert.ok(reply.availableRequests >= 0); - assert.ok(reply.retryAfter >= -1); - assert.ok(reply.fullBurstAfter >= 0); - } - - testUtils.testWithClient('gcra allows one request then limits the next with zero burst', async client => { - const first = await client.gcra('gcra:single-token', 0, 1, 1); - const second = await client.gcra('gcra:single-token', 0, 1, 1); - - assertReplyShape(first, 1); - assertReplyShape(second, 1); - assert.notEqual(first.limited, second.limited); - - assert.ok(first.retryAfter === -1 || second.retryAfter === -1); - assert.ok(first.retryAfter >= 0 || second.retryAfter >= 0); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('gcra supports weighted requests using TOKENS', async client => { - const key = 'gcra:weighted'; - - const first = await client.gcra(key, 10, 10, 1, 10); - const second = await client.gcra(key, 10, 10, 1, 10); - - assertReplyShape(first, 11); - assertReplyShape(second, 11); - assert.notEqual(first.limited, second.limited); - }, GLOBAL.SERVERS.OPEN); - - testUtils.testWithClient('gcra returns the same reply shape on RESP3', async client => { - const first = await client.gcra('gcra:resp3', 0, 1, 1); - const second = await client.gcra('gcra:resp3', 0, 1, 1); - - assertReplyShape(first, 1); - assertReplyShape(second, 1); - assert.notEqual(first.limited, second.limited); - }, GLOBAL.SERVERS.OPEN_RESP_3); -}); diff --git a/packages/client/lib/commands/GCRA.ts b/packages/client/lib/commands/GCRA.ts deleted file mode 100644 index 494216a73d7..00000000000 --- a/packages/client/lib/commands/GCRA.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { CommandParser } from '../client/parser'; -import { BooleanReply, Command, NumberReply, RedisArgument, TuplesReply, UnwrapReply } from '../RESP/types'; -import { transformDoubleArgument } from './generic-transformers'; - -export type GCRARawReply = TuplesReply<[ - limited: NumberReply<0 | 1>, - maxRequests: NumberReply, - availableRequests: NumberReply, - retryAfter: NumberReply, - fullBurstAfter: NumberReply -]>; - -export interface GCRAReply { - limited: BooleanReply; - maxRequests: NumberReply; - availableRequests: NumberReply; - retryAfter: NumberReply; - fullBurstAfter: NumberReply; -} - -function transformGCRAReply(reply: UnwrapReply): GCRAReply { - return { - limited: (reply[0] as unknown as number === 1) as unknown as BooleanReply, - maxRequests: reply[1], - availableRequests: reply[2], - retryAfter: reply[3], - fullBurstAfter: reply[4] - }; -} - -export default { - IS_READ_ONLY: false, - parseCommand( - parser: CommandParser, - key: RedisArgument, - maxBurst: number, - tokensPerPeriod: number, - period: number, - tokens?: number - ) { - parser.push('GCRA'); - parser.pushKey(key); - parser.push( - maxBurst.toString(), - tokensPerPeriod.toString(), - transformDoubleArgument(period) - ); - - if (tokens !== undefined) { - parser.push('TOKENS', tokens.toString()); - } - }, - transformReply: transformGCRAReply -} as const satisfies Command; diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index 0c60cb58d5e..cfa8949386b 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -93,7 +93,6 @@ import EVAL_RO from './EVAL_RO'; import EVAL from './EVAL'; import EVALSHA_RO from './EVALSHA_RO'; import EVALSHA from './EVALSHA'; -import GCRA from './GCRA'; import GEOADD from './GEOADD'; import GEODIST from './GEODIST'; import GEOHASH from './GEOHASH'; @@ -1557,34 +1556,6 @@ export default { * Returns information about the function that is currently running and information about the available execution engines */ functionStats: FUNCTION_STATS, - /** - * Rate limit via GCRA (Generic Cell Rate Algorithm). - * `tokensPerPeriod` are allowed per `period` at a sustained rate, which implies - * a minimum emission interval of `period / tokensPerPeriod` seconds between requests. - * `maxBurst` allows occasional spikes by permitting up to `maxBurst` additional - * tokens to be consumed at once. - * @param key - Key associated with the rate limit bucket - * @param maxBurst - Maximum number of extra tokens allowed as burst (min 0) - * @param tokensPerPeriod - Number of tokens allowed per period (min 1) - * @param period - Period in seconds as a float for sustained rate calculation (min 1.0, max 1e12) - * @param tokens - Optional request cost (weight). If omitted, defaults to 1 - * @see https://redis.io/commands/gcra/ - */ - GCRA, - /** - * Rate limit via GCRA (Generic Cell Rate Algorithm). - * `tokensPerPeriod` are allowed per `period` at a sustained rate, which implies - * a minimum emission interval of `period / tokensPerPeriod` seconds between requests. - * `maxBurst` allows occasional spikes by permitting up to `maxBurst` additional - * tokens to be consumed at once. - * @param key - Key associated with the rate limit bucket - * @param maxBurst - Maximum number of extra tokens allowed as burst (min 0) - * @param tokensPerPeriod - Number of tokens allowed per period (min 1) - * @param period - Period in seconds as a float for sustained rate calculation (min 1.0, max 1e12) - * @param tokens - Optional request cost (weight). If omitted, defaults to 1 - * @see https://redis.io/commands/gcra/ - */ - gcra: GCRA, /** * Adds geospatial items to the specified key * @param key - Key to add the geospatial items to diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 8b5df3a0a74..951a00a38a1 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -175,7 +175,7 @@ export class SentinelFramework extends DockerBase { dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 0acc7675ba6..8d045bd6ca8 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -10,7 +10,7 @@ const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 6d4e566916a..240e772c84a 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -7,7 +7,7 @@ export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index bd836cd38b3..23d7ce2e5c1 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -5,7 +5,7 @@ export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 7cbcdef2cfe..28374d67552 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -6,7 +6,7 @@ export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); export const GLOBAL = { diff --git a/packages/test-utils/lib/test-utils.ts b/packages/test-utils/lib/test-utils.ts index b37fde7d343..69e9834023f 100644 --- a/packages/test-utils/lib/test-utils.ts +++ b/packages/test-utils/lib/test-utils.ts @@ -4,7 +4,7 @@ export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index ccc3e77ef3d..d1e0c6684d3 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -5,7 +5,7 @@ export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageTagArgument: 'redis-tag', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: { tag: '8.8-m03', version: '8.8' } + defaultDockerVersion: { tag: '8.8-rc1', version: '8.8' } }); export const GLOBAL = {