1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-06 02:15:48 +03:00

Release 4.0.0-rc.1 (#1648)

* update workflows & README

* add .deepsource.toml

* fix client.quit, add error events on cluster, fix some "deepsource.io" warnings

* Release 4.0.0-rc.1
This commit is contained in:
Leibale Eidelman
2021-09-06 16:02:53 -04:00
committed by GitHub
parent e9ebdbd1bb
commit 77664c31ff
14 changed files with 106 additions and 93 deletions

9
.deepsource.toml Normal file
View File

@@ -0,0 +1,9 @@
version = 1
[[analyzers]]
name = "javascript"
enabled = true
[analyzers.meta]
environment = ["nodejs"]
dialect = "typescript"

View File

@@ -3,7 +3,8 @@ name: Benchmark
on:
push:
branches:
- v4
- master
- v4.0
jobs:
benchmark:

View File

@@ -3,7 +3,8 @@ name: Documentation
on:
push:
branches:
- v4
- master
- v4.0
jobs:
documentation:

View File

@@ -3,7 +3,8 @@ name: Tests
on:
push:
branches:
- v4
- master
- v4.0
jobs:
tests:

View File

@@ -6,8 +6,8 @@
</p>
<div align="center">
<a href="https://coveralls.io/github/NodeRedis/node-redis?branch=v4">
<img src="https://coveralls.io/repos/github/NodeRedis/node-redis/badge.svg?branch=v4" alt="Coverage Status"/>
<a href="https://coveralls.io/github/NodeRedis/node-redis">
<img src="https://coveralls.io/repos/github/NodeRedis/node-redis/badge.svg" alt="Coverage Status"/>
</a>
<a href="https://www.npmjs.com/package/redis/v/next">
<img src="https://img.shields.io/npm/dm/redis.svg" alt="Downloads"/>

View File

@@ -298,9 +298,10 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
}
QUIT(): Promise<void> {
return this.#socket.quit(async () => {
this.#queue.addEncodedCommand(encodeCommand(['QUIT']));
return this.#socket.quit(() => {
const promise = this.#queue.addEncodedCommand(encodeCommand(['QUIT']));
this.#tick();
return promise;
});
}

View File

@@ -17,54 +17,40 @@ interface SlotNodes<M extends RedisModules, S extends RedisLuaScripts> {
clientIterator: IterableIterator<RedisClientType<M, S>> | undefined;
}
type OnError = (err: unknown) => void;
export default class RedisClusterSlots<M extends RedisModules, S extends RedisLuaScripts> {
readonly #options: RedisClusterOptions;
readonly #onError: OnError;
readonly #nodeByUrl = new Map<string, ClusterNode<M, S>>();
readonly #slots: Array<SlotNodes<M, S>> = [];
constructor(options: RedisClusterOptions) {
constructor(options: RedisClusterOptions, onError: OnError) {
this.#options = options;
this.#onError = onError;
}
async connect(): Promise<void> {
for (const rootNode of this.#options.rootNodes) {
try {
await this.#discoverNodes(rootNode);
return;
} catch (err) {
console.error(err);
// this.emit('error', err);
}
if (await this.#discoverNodes(rootNode)) return;
}
throw new Error('None of the root nodes is available');
}
async discover(startWith: RedisClientType<M, S>): Promise<void> {
try {
await this.#discoverNodes(startWith.options?.socket);
return;
} catch (err) {
console.error(err);
// this.emit('error', err);
}
if (await this.#discoverNodes(startWith.options?.socket)) return;
for (const { client } of this.#nodeByUrl.values()) {
if (client === startWith) continue;
try {
await this.#discoverNodes(client.options?.socket);
return;
} catch (err) {
console.error(err);
// this.emit('error', err);
}
if (await this.#discoverNodes(client.options?.socket)) return;
}
throw new Error('None of the cluster nodes is available');
}
async #discoverNodes(socketOptions?: RedisSocketOptions): Promise<void> {
async #discoverNodes(socketOptions?: RedisSocketOptions): Promise<boolean> {
const client = RedisClient.create({
socket: socketOptions
});
@@ -73,8 +59,14 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisLu
try {
await this.#reset(await client.clusterNodes());
return true;
} catch (err) {
this.#onError(err);
return false;
} finally {
await client.disconnect(); // TODO: catch error from disconnect?
if (client.isOpen) {
await client.disconnect();
}
}
}
@@ -102,7 +94,6 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisLu
for (const [url, { client }] of this.#nodeByUrl.entries()) {
if (clientsInUse.has(url)) continue;
// TODO: ignore error from `.disconnect`?
promises.push(client.disconnect());
this.#nodeByUrl.delete(url);
}

View File

@@ -5,6 +5,7 @@ import RedisClusterSlots, { ClusterNode } from './cluster-slots';
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander';
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
import { EventEmitter } from 'events';
export interface RedisClusterOptions<M = RedisModules, S = RedisLuaScripts> {
rootNodes: Array<RedisSocketOptions>;
@@ -17,7 +18,7 @@ export interface RedisClusterOptions<M = RedisModules, S = RedisLuaScripts> {
export type RedisClusterType<M extends RedisModules, S extends RedisLuaScripts> =
WithPlugins<M, S> & RedisCluster;
export default class RedisCluster<M extends RedisModules = RedisModules, S extends RedisLuaScripts = RedisLuaScripts> {
export default class RedisCluster<M extends RedisModules = RedisModules, S extends RedisLuaScripts = RedisLuaScripts> extends EventEmitter {
static #extractFirstKey(command: RedisCommand, originalArgs: Array<unknown>, redisArgs: Array<string>): string | undefined {
if (command.FIRST_KEY_INDEX === undefined) {
return undefined;
@@ -83,8 +84,10 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
readonly #Multi: new (...args: ConstructorParameters<typeof RedisMultiCommand>) => RedisMultiCommandType<M, S>;
constructor(options: RedisClusterOptions<M, S>) {
super();
this.#options = options;
this.#slots = new RedisClusterSlots(options);
this.#slots = new RedisClusterSlots(options, err => this.emit('error', err));
this.#Multi = RedisMultiCommand.extend(options);
}

View File

@@ -97,12 +97,12 @@ export function transformCommandArguments<T = unknown>(
export function encodeCommand(args: Array<string>): string {
const encoded = [
`*${args.length}`,
`$${Buffer.byteLength(args[0])}`,
`$${Buffer.byteLength(args[0]).toString()}`,
args[0]
];
for (let i = 1; i < args.length; i++) {
encoded.push(`$${Buffer.byteLength(args[i])}`, args[i]);
encoded.push(`$${Buffer.byteLength(args[i]).toString()}`, args[i]);
}
return encoded.join('\r\n') + '\r\n';

View File

@@ -12,6 +12,7 @@ export interface QueueCommandOptions {
interface CommandWaitingToBeSent extends CommandWaitingForReply {
encodedCommand: string;
byteLength: number;
chainId?: symbol;
abort?: {
signal: any; // TODO: `AbortSignal` type is incorrect
@@ -130,6 +131,7 @@ export default class RedisCommandsQueue {
return new Promise((resolve, reject) => {
const node = new LinkedList.Node<CommandWaitingToBeSent>({
encodedCommand,
byteLength: Buffer.byteLength(encodedCommand),
chainId: options?.chainId,
resolve,
reject
@@ -156,7 +158,7 @@ export default class RedisCommandsQueue {
this.#waitingToBeSent.pushNode(node);
}
this.#waitingToBeSentCommandsLength += encodedCommand.length;
this.#waitingToBeSentCommandsLength += node.value.byteLength;
});
}
@@ -230,8 +232,12 @@ export default class RedisCommandsQueue {
}
this.#pubSubState[inProgressKey] += channelsCounter;
const encodedCommand = encodeCommand(commandArgs),
byteLength = Buffer.byteLength(encodedCommand);
this.#waitingToBeSent.push({
encodedCommand: encodeCommand(commandArgs),
encodedCommand,
byteLength,
channelsCounter,
resolve: () => {
this.#pubSubState[inProgressKey] -= channelsCounter;
@@ -243,6 +249,7 @@ export default class RedisCommandsQueue {
reject();
}
});
this.#waitingToBeSentCommandsLength += byteLength;
});
}
@@ -268,7 +275,7 @@ export default class RedisCommandsQueue {
lastCommandChainId: symbol | undefined;
for (const command of this.#waitingToBeSent) {
encoded.push(command.encodedCommand);
size += command.encodedCommand.length;
size += command.byteLength;
if (size > recommendedSize) {
lastCommandChainId = command.chainId;
break;

View File

@@ -19,7 +19,7 @@ export function transformArguments(
isKeyString = typeof key === 'string';
if (isKeyString) {
args.push(key as string);
args.push(key);
} else {
args.push('""');
}

View File

@@ -1,5 +1,4 @@
import { strict as assert } from 'assert';
import { isObject } from 'util';
import {
transformReplyBoolean,
transformReplyBooleanArray,

98
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "redis",
"version": "4.0.0-rc.0",
"version": "4.0.0-rc.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "redis",
"version": "4.0.0-rc.0",
"version": "4.0.0-rc.1",
"license": "MIT",
"dependencies": {
"cluster-key-slot": "1.1.0",
@@ -17,7 +17,7 @@
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/mocha": "^9.0.0",
"@types/node": "^16.7.8",
"@types/node": "^16.7.10",
"@types/sinon": "^10.0.2",
"@types/which": "^2.0.1",
"@types/yallist": "^4.0.1",
@@ -642,9 +642,9 @@
}
},
"node_modules/@octokit/graphql": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz",
"integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==",
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"dev": true,
"dependencies": {
"@octokit/request": "^5.6.0",
@@ -653,18 +653,18 @@
}
},
"node_modules/@octokit/openapi-types": {
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz",
"integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz",
"integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==",
"dev": true
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz",
"integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==",
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz",
"integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==",
"dev": true,
"dependencies": {
"@octokit/types": "^6.24.0"
"@octokit/types": "^6.26.0"
},
"peerDependencies": {
"@octokit/core": ">=2"
@@ -730,12 +730,12 @@
}
},
"node_modules/@octokit/types": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz",
"integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==",
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz",
"integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==",
"dev": true,
"dependencies": {
"@octokit/openapi-types": "^9.5.0"
"@octokit/openapi-types": "^10.0.0"
}
},
"node_modules/@sindresorhus/is": {
@@ -855,9 +855,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "16.7.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz",
"integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==",
"version": "16.7.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz",
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==",
"dev": true
},
"node_modules/@types/parse-json": {
@@ -1845,9 +1845,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.3.822",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz",
"integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==",
"version": "1.3.827",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz",
"integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==",
"dev": true
},
"node_modules/emoji-regex": {
@@ -4773,9 +4773,9 @@
}
},
"node_modules/shiki": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz",
"integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==",
"version": "0.9.10",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz",
"integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==",
"dev": true,
"dependencies": {
"json5": "^2.2.0",
@@ -6164,9 +6164,9 @@
}
},
"@octokit/graphql": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.7.0.tgz",
"integrity": "sha512-diY0qMPyQjfu4rDu3kDhJ9qIZadIm4IISO3RJSv9ajYUWJUCO0AykbgzLcg1xclxtXgzY583u3gAv66M6zz5SA==",
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"dev": true,
"requires": {
"@octokit/request": "^5.6.0",
@@ -6175,18 +6175,18 @@
}
},
"@octokit/openapi-types": {
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz",
"integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-10.0.0.tgz",
"integrity": "sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==",
"dev": true
},
"@octokit/plugin-paginate-rest": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz",
"integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==",
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz",
"integrity": "sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==",
"dev": true,
"requires": {
"@octokit/types": "^6.24.0"
"@octokit/types": "^6.26.0"
}
},
"@octokit/plugin-request-log": {
@@ -6244,12 +6244,12 @@
}
},
"@octokit/types": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz",
"integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==",
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.26.0.tgz",
"integrity": "sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==",
"dev": true,
"requires": {
"@octokit/openapi-types": "^9.5.0"
"@octokit/openapi-types": "^10.0.0"
}
},
"@sindresorhus/is": {
@@ -6360,9 +6360,9 @@
"dev": true
},
"@types/node": {
"version": "16.7.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.8.tgz",
"integrity": "sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==",
"version": "16.7.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.10.tgz",
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==",
"dev": true
},
"@types/parse-json": {
@@ -7108,9 +7108,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.822",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz",
"integrity": "sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q==",
"version": "1.3.827",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz",
"integrity": "sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A==",
"dev": true
},
"emoji-regex": {
@@ -9284,9 +9284,9 @@
}
},
"shiki": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.8.tgz",
"integrity": "sha512-499zQUTjcNTVwwiaPrWldUTXIV3T9HZWxDwE82bY+9GE7P2uD6hpHUTXNbTof3iOG6WT+/062+OMbl0lDoG8WQ==",
"version": "0.9.10",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.10.tgz",
"integrity": "sha512-xeM7Oc6hY+6iW5O/T5hor8ul7mEprzyl5y4r5zthEHToQNw7MIhREMgU3r2gKDB0NaMLNrkcEQagudCdzE13Lg==",
"dev": true,
"requires": {
"json5": "^2.2.0",

View File

@@ -1,6 +1,6 @@
{
"name": "redis",
"version": "4.0.0-rc.0",
"version": "4.0.0-rc.1",
"description": "A high performance Redis client.",
"keywords": [
"database",
@@ -35,7 +35,7 @@
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/mocha": "^9.0.0",
"@types/node": "^16.7.8",
"@types/node": "^16.7.10",
"@types/sinon": "^10.0.2",
"@types/which": "^2.0.1",
"@types/yallist": "^4.0.1",