From a8b81bdd01329252466eb1dd608b2a92b960c3ae Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Tue, 7 Nov 2023 14:43:20 +0200 Subject: [PATCH 01/11] Send client user-agent during connection, via CLIENT SETINFO (#2645) * Add SETINFO support to client connection, with the ability to disable sending the user agent if the end user desires. * Also enables modifying the user-agent with a tag to enable distinguishing different usages. --- packages/client/lib/client/index.spec.ts | 40 +++++++++++++++++++++ packages/client/lib/client/index.ts | 39 +++++++++++++++++++- packages/client/lib/commands/CLIENT_INFO.ts | 7 +++- packages/client/lib/test-utils.ts | 3 +- packages/client/tsconfig.json | 3 +- tsconfig.base.json | 3 +- 6 files changed, 90 insertions(+), 5 deletions(-) diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 4c2899a9b7..3278d27775 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -10,6 +10,8 @@ import { once } from 'events'; import { ClientKillFilters } from '../commands/CLIENT_KILL'; import { promisify } from 'util'; +import {version} from '../../package.json'; + export const SQUARE_SCRIPT = defineScript({ SCRIPT: 'return ARGV[1] * ARGV[1];', NUMBER_OF_KEYS: 0, @@ -118,6 +120,44 @@ describe('Client', () => { ...GLOBAL.SERVERS.PASSWORD, disableClientSetup: true }); + + testUtils.testWithClient('should set default lib name and version', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, 'node-redis'); + assert.equal(clientInfo.libVer, version); + }, { + ...GLOBAL.SERVERS.PASSWORD, + minimumDockerVersion: [7, 2] + }); + + testUtils.testWithClient('disable sending lib name and version', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, ''); + assert.equal(clientInfo.libVer, ''); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + disableClientInfo: true + }, + minimumDockerVersion: [7, 2] + }); + + testUtils.testWithClient('send client name tag', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, 'node-redis(test)'); + assert.equal(clientInfo.libVer, version); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + clientInfoTag: "test" + }, + minimumDockerVersion: [7, 2] + }); }); describe('authentication', () => { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 800894e06b..4c3964c7aa 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -11,11 +11,13 @@ import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; import { attachCommands, attachExtensions, fCallArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; -import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError, ErrorReply } from '../errors'; import { URL } from 'url'; import { TcpSocketConnectOpts } from 'net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; +import {version} from '../../package.json'; + export interface RedisClientOptions< M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, @@ -66,6 +68,14 @@ export interface RedisClientOptions< * Useful with Redis deployments that do not use TCP Keep-Alive. */ pingInterval?: number; + /** + * If set to true, disables sending client identifier (user-agent like message) to the redis server + */ + disableClientInfo?: boolean; + /** + * Tag to append to library name that is sent to the Redis server + */ + clientInfoTag?: string; } type WithCommands = { @@ -274,6 +284,33 @@ export default class RedisClient< ); } + if (!this.#options?.disableClientInfo) { + promises.push( + this.#queue.addCommand( + [ 'CLIENT', 'SETINFO', 'LIB-VER', version], + { asap: true } + ).catch(err => { + if (!(err instanceof ErrorReply)) { + throw err; + } + }) + ); + + promises.push( + this.#queue.addCommand( + [ + 'CLIENT', 'SETINFO', 'LIB-NAME', + this.#options?.clientInfoTag ? `node-redis(${this.#options.clientInfoTag})` : 'node-redis' + ], + { asap: true } + ).catch(err => { + if (!(err instanceof ErrorReply)) { + throw err; + } + }) + ); + } + if (this.#options?.name) { promises.push( this.#queue.addCommand( diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 7f6b6e1884..fd823542f8 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -31,6 +31,9 @@ export interface ClientInfoReply { user?: string; // 6.0 redir?: number; // 6.2 resp?: number; // 7.0 + // 7.2 + libName?: string; + libVer?: string; } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; @@ -62,7 +65,9 @@ export function transformReply(rawReply: string): ClientInfoReply { totMem: Number(map['tot-mem']), events: map.events, cmd: map.cmd, - user: map.user + user: map.user, + libName: map['lib-name'], + libVer: map['lib-ver'], }; if (map.laddr !== undefined) { diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 65d526f601..a9db70c860 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -4,7 +4,8 @@ import { promiseTimeout } from './utils'; const utils = new TestUtils({ dockerImageName: 'redis', - dockerImageVersionArgument: 'redis-version' + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '7.2' }); export default utils; diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 3271cf400a..c71595c570 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -5,7 +5,8 @@ }, "include": [ "./index.ts", - "./lib/**/*.ts" + "./lib/**/*.ts", + "./package.json" ], "exclude": [ "./lib/test-utils.ts", diff --git a/tsconfig.base.json b/tsconfig.base.json index 68325e51dc..1157be947b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -4,7 +4,8 @@ "declaration": true, "allowJs": true, "useDefineForClassFields": true, - "esModuleInterop": false + "esModuleInterop": false, + "resolveJsonModule": true }, "ts-node": { "files": true From 68d835d7a228cc1cf6b0a18b960fbdbb2cce2e4a Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 20 Nov 2023 13:16:36 -0500 Subject: [PATCH 02/11] fix #2632 - handle socket close in "socket initiator" phase (#2653) --- packages/client/lib/client/socket.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/client/socket.ts b/packages/client/lib/client/socket.ts index b079a51fea..b701f6ea97 100644 --- a/packages/client/lib/client/socket.ts +++ b/packages/client/lib/client/socket.ts @@ -199,7 +199,7 @@ export default class RedisSocket extends EventEmitter { .off('error', reject) .once('error', (err: Error) => this.#onSocketError(err)) .once('close', hadError => { - if (!hadError && this.#isReady && this.#socket === socket) { + if (!hadError && this.#isOpen && this.#socket === socket) { this.#onSocketError(new SocketClosedUnexpectedlyError()); } }) @@ -229,10 +229,11 @@ export default class RedisSocket extends EventEmitter { } #onSocketError(err: Error): void { + const wasReady = this.#isReady; this.#isReady = false; this.emit('error', err); - if (!this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; + if (!wasReady || !this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number') return; this.emit('reconnecting'); this.#connect().catch(() => { From e91509a3e1b91400beb7e442f90ef2b7d3a9c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20Ate=C5=9F=20Uzun?= Date: Mon, 20 Nov 2023 21:40:06 +0300 Subject: [PATCH 03/11] fix `GRAPH.EXPLAIN` - fix transform typo in `transformReply` --- packages/graph/lib/commands/EXPLAIN.ts | 2 +- packages/graph/lib/commands/PROFILE.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts index 419ff62b11..ebea9ca900 100644 --- a/packages/graph/lib/commands/EXPLAIN.ts +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, query: string): Array { return ['GRAPH.EXPLAIN', key, query]; } -export declare function transfromReply(): Array; +export declare function transformReply(): Array; diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts index 473c526e67..c964452f49 100644 --- a/packages/graph/lib/commands/PROFILE.ts +++ b/packages/graph/lib/commands/PROFILE.ts @@ -6,4 +6,4 @@ export function transformArguments(key: string, query: string): Array { return ['GRAPH.PROFILE', key, query]; } -export declare function transfromReply(): Array; +export declare function transformReply(): Array; From 623b56b5ca43a0ebe749da044e7a36ad3042d53b Mon Sep 17 00:00:00 2001 From: Clubsandwich Date: Tue, 21 Nov 2023 03:40:38 +0900 Subject: [PATCH 04/11] fix `cluster.sUnsubscribe` - make `listener` optional --- packages/client/lib/cluster/index.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts index 7e486a376b..49ac293d6c 100644 --- a/packages/client/lib/cluster/index.ts +++ b/packages/client/lib/cluster/index.ts @@ -20,12 +20,12 @@ export interface RedisClusterOptions< S extends RedisScripts = Record > extends RedisExtensions { /** - * Should contain details for some of the cluster nodes that the client will use to discover + * Should contain details for some of the cluster nodes that the client will use to discover * the "cluster topology". We recommend including details for at least 3 nodes here. */ rootNodes: Array; /** - * Default values used for every client in the cluster. Use this to specify global values, + * Default values used for every client in the cluster. Use this to specify global values, * for example: ACL credentials, timeouts, TLS configuration etc. */ defaults?: Partial; @@ -45,7 +45,7 @@ export interface RedisClusterOptions< /** * Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to * Useful when the cluster is running on another network - * + * */ nodeAddressMap?: NodeAddressMap; } @@ -98,7 +98,7 @@ export default class RedisCluster< readonly #options: RedisClusterOptions; readonly #slots: RedisClusterSlots; - + get slots() { return this.#slots.slots; } @@ -310,7 +310,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this.#slots.executeUnsubscribeCommand(client => + return this.#slots.executeUnsubscribeCommand(client => client.UNSUBSCRIBE(channels, listener, bufferMode) ); } @@ -333,7 +333,7 @@ export default class RedisCluster< listener?: PubSubListener, bufferMode?: T ) { - return this.#slots.executeUnsubscribeCommand(client => + return this.#slots.executeUnsubscribeCommand(client => client.PUNSUBSCRIBE(patterns, listener, bufferMode) ); } @@ -344,7 +344,7 @@ export default class RedisCluster< channels: string | Array, listener: PubSubListener, bufferMode?: T - ) { + ) { const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16, firstChannel = Array.isArray(channels) ? channels[0] : channels; let client = await this.#slots.getShardedPubSubClient(firstChannel); @@ -371,7 +371,7 @@ export default class RedisCluster< SUNSUBSCRIBE( channels: string | Array, - listener: PubSubListener, + listener?: PubSubListener, bufferMode?: T ) { return this.#slots.executeShardedUnsubscribeCommand( @@ -391,7 +391,7 @@ export default class RedisCluster< } nodeClient(node: ShardNode) { - return this.#slots.nodeClient(node); + return this.#slots.nodeClient(node); } getRandomNode() { From 0ee3278085420b2406fb34f804d2fea25e870d39 Mon Sep 17 00:00:00 2001 From: Savvas Papageorgiadis <50584606+papsavas@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:41:44 +0200 Subject: [PATCH 05/11] chore(search): export languages (#2651) --- packages/search/lib/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/search/lib/index.ts b/packages/search/lib/index.ts index 296136021e..0f84c11466 100644 --- a/packages/search/lib/index.ts +++ b/packages/search/lib/index.ts @@ -1,5 +1,5 @@ export { default } from './commands'; -export { RediSearchSchema, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; -export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE'; +export { RediSearchSchema, RedisSearchLanguages, SchemaFieldTypes, SchemaTextFieldPhonetics, SearchReply, VectorAlgorithms } from './commands'; +export { AggregateGroupByReducers, AggregateSteps } from './commands/AGGREGATE'; export { SearchOptions } from './commands/SEARCH'; From 9ed9cb5be9454706765b17386a2683f9221c680e Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Mon, 20 Nov 2023 20:42:35 +0200 Subject: [PATCH 06/11] export missing graph types --- packages/graph/lib/graph.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/graph/lib/graph.ts b/packages/graph/lib/graph.ts index 5baff1dae2..a95338bd8f 100644 --- a/packages/graph/lib/graph.ts +++ b/packages/graph/lib/graph.ts @@ -126,11 +126,11 @@ type GraphValue = null | string | number | boolean | Array | { longitude: string; }; -type GraphReply = Omit & { +export type GraphReply = Omit & { data?: Array; }; -type GraphClientType = RedisClientType<{ +export type GraphClientType = RedisClientType<{ graph: { query: typeof import('./commands/QUERY'), roQuery: typeof import('./commands/RO_QUERY') From debd2770028bf17552b4d00b0f42b6f9e3c26a71 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:48:13 -0500 Subject: [PATCH 07/11] Release client@1.5.12 --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index cbb36ab772..aa1ce57b11 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@redis/client", - "version": "1.5.11", + "version": "1.5.12", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 0419b600b8698b5aee96283e5665d46015dfc9c0 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:48:43 -0500 Subject: [PATCH 08/11] Release graph@1.1.1 --- packages/graph/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graph/package.json b/packages/graph/package.json index 9d48d96367..95cce6b8a8 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@redis/graph", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a227cb0cefaf231ad449a21d7e1e12b1518055b6 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:49:13 -0500 Subject: [PATCH 09/11] Release search@1.1.6 --- packages/search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/search/package.json b/packages/search/package.json index 48f4e5cdec..9fb1d0e002 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@redis/search", - "version": "1.1.5", + "version": "1.1.6", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5e4165eb6a15eee8a698a8c68ef1f94868ee6927 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:49:56 -0500 Subject: [PATCH 10/11] upgrade subpackages --- package-lock.json | 12 ++++++------ package.json | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 344044b134..9b5fd2a017 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,10 @@ ], "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.11", - "@redis/graph": "1.1.0", + "@redis/client": "1.5.12", + "@redis/graph": "1.1.1", "@redis/json": "1.0.6", - "@redis/search": "1.1.5", + "@redis/search": "1.1.6", "@redis/time-series": "1.0.5" }, "devDependencies": { @@ -8838,7 +8838,7 @@ }, "packages/client": { "name": "@redis/client", - "version": "1.5.11", + "version": "1.5.12", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", @@ -8868,7 +8868,7 @@ }, "packages/graph": { "name": "@redis/graph", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -8906,7 +8906,7 @@ }, "packages/search": { "name": "@redis/search", - "version": "1.1.5", + "version": "1.1.6", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/package.json b/package.json index 2982a32a5e..b58cb8f278 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.11", - "@redis/graph": "1.1.0", + "@redis/client": "1.5.12", + "@redis/graph": "1.1.1", "@redis/json": "1.0.6", - "@redis/search": "1.1.5", + "@redis/search": "1.1.6", "@redis/time-series": "1.0.5" }, "devDependencies": { From d6d2064c72b99d34fc88318f3979177e3c89acd4 Mon Sep 17 00:00:00 2001 From: Leibale Date: Mon, 20 Nov 2023 13:50:15 -0500 Subject: [PATCH 11/11] Release redis@4.6.11 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b5fd2a017..1557c39877 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "redis", - "version": "4.6.10", + "version": "4.6.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "redis", - "version": "4.6.10", + "version": "4.6.11", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/package.json b/package.json index b58cb8f278..2924233d9c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redis", "description": "A modern, high performance Redis client", - "version": "4.6.10", + "version": "4.6.11", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts",