You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
fix #2679 - fix socket types, and clean some code
This commit is contained in:
@@ -1,31 +1,32 @@
|
|||||||
# `createClient` configuration
|
# `createClient` configuration
|
||||||
|
|
||||||
| Property | Default | Description |
|
| Property | Default | Description |
|
||||||
|--------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------------------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) |
|
| url | | `redis[s]://[[username][:password]@][host][:port][/db-number]` (see [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details) |
|
||||||
| socket | | Socket connection properties. Unlisted [`net.connect`](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) properties (and [`tls.connect`](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)) are also supported |
|
| socket | | Socket connection properties. Unlisted [`net.connect`](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) properties (and [`tls.connect`](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)) are also supported |
|
||||||
| socket.port | `6379` | Redis server port |
|
| socket.port | `6379` | Redis server port |
|
||||||
| socket.host | `'localhost'` | Redis server hostname |
|
| socket.host | `'localhost'` | Redis server hostname |
|
||||||
| socket.family | `0` | IP Stack version (one of `4 \| 6 \| 0`) |
|
| socket.family | `0` | IP Stack version (one of `4 \| 6 \| 0`) |
|
||||||
| socket.path | | Path to the UNIX Socket |
|
| socket.path | | Path to the UNIX Socket |
|
||||||
| socket.connectTimeout | `5000` | Connection Timeout (in milliseconds) |
|
| socket.connectTimeout | `5000` | Connection timeout (in milliseconds) |
|
||||||
| socket.noDelay | `true` | Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) |
|
| socket.noDelay | `true` | Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay) |
|
||||||
| socket.keepAlive | `5000` | Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay) functionality |
|
| socket.keepAlive | `true` | Toggle [`keep-alive`](https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay) functionality |
|
||||||
| socket.tls | | See explanation and examples [below](#TLS) |
|
| socket.keepAliveInitialDelay | `5000` | If set to a positive number, it sets the initial delay before the first keepalive probe is sent on an idle socket |
|
||||||
| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic |
|
| socket.tls | | See explanation and examples [below](#TLS) |
|
||||||
| username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) |
|
| socket.reconnectStrategy | `retries => Math.min(retries * 50, 500)` | A function containing the [Reconnect Strategy](#reconnect-strategy) logic |
|
||||||
| password | | ACL password or the old "--requirepass" password |
|
| username | | ACL username ([see ACL guide](https://redis.io/topics/acl)) |
|
||||||
| name | | Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) |
|
| password | | ACL password or the old "--requirepass" password |
|
||||||
| database | | Redis database number (see [`SELECT`](https://redis.io/commands/select) command) |
|
| name | | Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname)) |
|
||||||
| modules | | Included [Redis Modules](../README.md#packages) |
|
| database | | Redis database number (see [`SELECT`](https://redis.io/commands/select) command) |
|
||||||
| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) |
|
| modules | | Included [Redis Modules](../README.md#packages) |
|
||||||
| functions | | Function definitions (see [Functions](../README.md#functions)) |
|
| scripts | | Script definitions (see [Lua Scripts](../README.md#lua-scripts)) |
|
||||||
| commandsQueueMaxLength | | Maximum length of the client's internal command queue |
|
| functions | | Function definitions (see [Functions](../README.md#functions)) |
|
||||||
| disableOfflineQueue | `false` | Disables offline queuing, see [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) |
|
| commandsQueueMaxLength | | Maximum length of the client's internal command queue |
|
||||||
| readonly | `false` | Connect in [`READONLY`](https://redis.io/commands/readonly) mode |
|
| disableOfflineQueue | `false` | Disables offline queuing, see [FAQ](./FAQ.md#what-happens-when-the-network-goes-down) |
|
||||||
| legacyMode | `false` | Maintain some backwards compatibility (see the [Migration Guide](./v3-to-v4.md)) |
|
| readonly | `false` | Connect in [`READONLY`](https://redis.io/commands/readonly) mode |
|
||||||
| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) |
|
| legacyMode | `false` | Maintain some backwards compatibility (see the [Migration Guide](./v3-to-v4.md)) |
|
||||||
| pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) |
|
| isolationPoolOptions | | See the [Isolated Execution Guide](./isolated-execution.md) |
|
||||||
|
| pingInterval | | Send `PING` command at interval (in ms). Useful with ["Azure Cache for Redis"](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout) |
|
||||||
|
|
||||||
## Reconnect Strategy
|
## Reconnect Strategy
|
||||||
|
|
||||||
|
@@ -1,5 +1,27 @@
|
|||||||
# v4 to v5 migration guide
|
# v4 to v5 migration guide
|
||||||
|
|
||||||
|
## Client Configuration
|
||||||
|
|
||||||
|
### Keep Alive
|
||||||
|
|
||||||
|
To better align with Node.js build-in [`net`](https://nodejs.org/api/net.html) and [`tls`](https://nodejs.org/api/tls.html) modules, the `keepAlive` option has been split into 2 options: `keepAlive` (`boolean`) and `keepAliveInitialDelay` (`number`). The defaults remain `true` and `5000`.
|
||||||
|
|
||||||
|
### Legacy Mode
|
||||||
|
|
||||||
|
In the previous version, you could access "legacy" mode by creating a client and passing in `{ legacyMode: true }`. Now, you can create one off of an existing client by calling the `.legacy()` function. This allows easier access to both APIs and enables better TypeScript support.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// use `client` for the current API
|
||||||
|
const client = createClient();
|
||||||
|
await client.set('key', 'value');
|
||||||
|
|
||||||
|
// use `legacyClient` for the "legacy" API
|
||||||
|
const legacyClient = client.legacy();
|
||||||
|
legacyClient.set('key', 'value', (err, reply) => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Command Options
|
## Command Options
|
||||||
|
|
||||||
In v4, command options are passed as a first optional argument:
|
In v4, command options are passed as a first optional argument:
|
||||||
@@ -59,22 +81,6 @@ for await (const keys of client.scanIterator()) {
|
|||||||
|
|
||||||
for more information, see the [Scan Iterators guide](./scan-iterators.md).
|
for more information, see the [Scan Iterators guide](./scan-iterators.md).
|
||||||
|
|
||||||
## Legacy Mode
|
|
||||||
|
|
||||||
In the previous version, you could access "legacy" mode by creating a client and passing in `{ legacyMode: true }`. Now, you can create one off of an existing client by calling the `.legacy()` function. This allows easier access to both APIs and enables better TypeScript support.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// use `client` for the current API
|
|
||||||
const client = createClient();
|
|
||||||
await client.set('key', 'value');
|
|
||||||
|
|
||||||
// use `legacyClient` for the "legacy" API
|
|
||||||
const legacyClient = client.legacy();
|
|
||||||
legacyClient.set('key', 'value', (err, reply) => {
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Isolation Pool
|
## Isolation Pool
|
||||||
|
|
||||||
[TODO](./blocking-commands.md).
|
[TODO](./blocking-commands.md).
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import COMMANDS from '../commands';
|
import COMMANDS from '../commands';
|
||||||
import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket';
|
import RedisSocket, { RedisSocketOptions } from './socket';
|
||||||
import RedisCommandsQueue, { CommandOptions } from './commands-queue';
|
import RedisCommandsQueue, { CommandOptions } from './commands-queue';
|
||||||
import { EventEmitter } from 'node:events';
|
import { EventEmitter } from 'node:events';
|
||||||
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
||||||
@@ -244,7 +244,7 @@ export default class RedisClient<
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (protocol === 'rediss:') {
|
if (protocol === 'rediss:') {
|
||||||
(parsed.socket as RedisTlsSocketOptions).tls = true;
|
parsed!.socket!.tls = true;
|
||||||
} else if (protocol !== 'redis:') {
|
} else if (protocol !== 'redis:') {
|
||||||
throw new TypeError('Invalid protocol');
|
throw new TypeError('Invalid protocol');
|
||||||
}
|
}
|
||||||
|
@@ -1,84 +1,65 @@
|
|||||||
import { EventEmitter } from 'node:events';
|
import { EventEmitter, once } from 'node:events';
|
||||||
import * as net from 'node:net';
|
import net from 'node:net';
|
||||||
import * as tls from 'node:tls';
|
import tls from 'node:tls';
|
||||||
import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors';
|
import { ConnectionTimeoutError, ClientClosedError, SocketClosedUnexpectedlyError, ReconnectStrategyError } from '../errors';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { setTimeout } from 'node:timers/promises';
|
||||||
import { RedisArgument } from '../RESP/types';
|
import { RedisArgument } from '../RESP/types';
|
||||||
|
|
||||||
export interface RedisSocketCommonOptions {
|
type NetOptions = {
|
||||||
|
tls?: false;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TcpOptions = NetOptions & Omit<
|
||||||
|
net.TcpNetConnectOpts,
|
||||||
|
'timeout' | 'onread' | 'readable' | 'writable' | 'port'
|
||||||
|
> & {
|
||||||
|
port?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type IpcOptions = NetOptions & Omit<
|
||||||
|
net.IpcNetConnectOpts,
|
||||||
|
'timeout' | 'onread' | 'readable' | 'writable'
|
||||||
|
>;
|
||||||
|
|
||||||
|
type TlsOptions = {
|
||||||
|
tls: true;
|
||||||
|
} & tls.ConnectionOptions;
|
||||||
|
|
||||||
|
type ReconnectStrategyFunction = (retries: number, cause: Error) => false | Error | number;
|
||||||
|
|
||||||
|
export type RedisSocketOptions = {
|
||||||
/**
|
/**
|
||||||
* Connection Timeout (in milliseconds)
|
* Connection timeout (in milliseconds)
|
||||||
*/
|
*/
|
||||||
connectTimeout?: number;
|
connectTimeout?: number;
|
||||||
/**
|
|
||||||
* Toggle [`Nagle's algorithm`](https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay)
|
|
||||||
*/
|
|
||||||
noDelay?: boolean;
|
|
||||||
/**
|
|
||||||
* Toggle [`keep-alive`](https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay)
|
|
||||||
*/
|
|
||||||
keepAlive?: number | false;
|
|
||||||
/**
|
/**
|
||||||
* When the socket closes unexpectedly (without calling `.close()`/`.destroy()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported:
|
* When the socket closes unexpectedly (without calling `.close()`/`.destroy()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported:
|
||||||
* 1. `false` -> do not reconnect, close the client and flush the command queue.
|
* 1. `false` -> do not reconnect, close the client and flush the command queue.
|
||||||
* 2. `number` -> wait for `X` milliseconds before reconnecting.
|
* 2. `number` -> wait for `X` milliseconds before reconnecting.
|
||||||
* 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error.
|
* 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error.
|
||||||
* Defaults to `retries => Math.min(retries * 50, 500)`
|
|
||||||
*/
|
*/
|
||||||
reconnectStrategy?: false | number | ((retries: number, cause: Error) => false | Error | number);
|
reconnectStrategy?: false | number | ReconnectStrategyFunction;
|
||||||
}
|
} & (TcpOptions | IpcOptions | TlsOptions);
|
||||||
|
|
||||||
export interface RedisNetConnectOpts extends Omit<Partial<net.TcpNetConnectOpts>, 'keepAlive'>, Partial<net.IpcNetConnectOpts>, RedisSocketCommonOptions {
|
|
||||||
tls?: false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface RedisTlsSocketOptions extends Partial<tls.ConnectionOptions>, RedisSocketCommonOptions {
|
|
||||||
tls: true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RedisSocketOptions = RedisNetConnectOpts | RedisTlsSocketOptions
|
|
||||||
|
|
||||||
interface CreateSocketReturn<T> {
|
|
||||||
connectEvent: string;
|
|
||||||
socket: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RedisSocketInitiator = () => void | Promise<unknown>;
|
export type RedisSocketInitiator = () => void | Promise<unknown>;
|
||||||
|
|
||||||
export default class RedisSocket extends EventEmitter {
|
export default class RedisSocket extends EventEmitter {
|
||||||
static #initiateOptions(options?: RedisSocketOptions): RedisSocketOptions {
|
readonly #initiator;
|
||||||
options ??= {};
|
readonly #connectTimeout;
|
||||||
if (!(options as net.IpcSocketConnectOpts).path) {
|
readonly #reconnectStrategy;
|
||||||
(options as net.TcpSocketConnectOpts).port ??= 6379;
|
readonly #socketFactory;
|
||||||
(options as net.TcpSocketConnectOpts).host ??= 'localhost';
|
|
||||||
}
|
|
||||||
|
|
||||||
options.connectTimeout ??= 5000;
|
|
||||||
options.keepAlive ??= 5000;
|
|
||||||
options.noDelay ??= true;
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions {
|
|
||||||
return (options as RedisTlsSocketOptions).tls === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly #initiator: RedisSocketInitiator;
|
|
||||||
|
|
||||||
readonly #options: RedisSocketOptions;
|
|
||||||
|
|
||||||
#socket?: net.Socket | tls.TLSSocket;
|
#socket?: net.Socket | tls.TLSSocket;
|
||||||
|
|
||||||
#isOpen = false;
|
#isOpen = false;
|
||||||
|
|
||||||
get isOpen(): boolean {
|
get isOpen() {
|
||||||
return this.#isOpen;
|
return this.#isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
#isReady = false;
|
#isReady = false;
|
||||||
|
|
||||||
get isReady(): boolean {
|
get isReady() {
|
||||||
return this.#isReady;
|
return this.#isReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,28 +69,101 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.#initiator = initiator;
|
this.#initiator = initiator;
|
||||||
this.#options = RedisSocket.#initiateOptions(options);
|
this.#connectTimeout = options?.connectTimeout ?? 5000;
|
||||||
|
this.#reconnectStrategy = this.#createReconnectStrategy(options);
|
||||||
|
this.#socketFactory = this.#createSocketFactory(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
#reconnectStrategy(retries: number, cause: Error) {
|
#createReconnectStrategy(options?: RedisSocketOptions): ReconnectStrategyFunction {
|
||||||
if (this.#options.reconnectStrategy === false) {
|
const strategy = options?.reconnectStrategy;
|
||||||
return false;
|
if (strategy === false || typeof strategy === 'number') {
|
||||||
} else if (typeof this.#options.reconnectStrategy === 'number') {
|
return () => strategy;
|
||||||
return this.#options.reconnectStrategy;
|
|
||||||
} else if (this.#options.reconnectStrategy) {
|
|
||||||
try {
|
|
||||||
const retryIn = this.#options.reconnectStrategy(retries, cause);
|
|
||||||
if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') {
|
|
||||||
throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retryIn;
|
|
||||||
} catch (err) {
|
|
||||||
this.emit('error', err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.min(retries * 50, 500);
|
if (strategy) {
|
||||||
|
return (retries, cause) => {
|
||||||
|
try {
|
||||||
|
const retryIn = strategy(retries, cause);
|
||||||
|
if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') {
|
||||||
|
throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`);
|
||||||
|
}
|
||||||
|
return retryIn;
|
||||||
|
} catch (err) {
|
||||||
|
this.emit('error', err);
|
||||||
|
return Math.min(retries * 50, 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return retries => Math.min(retries * 50, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
#createSocketFactory(options?: RedisSocketOptions) {
|
||||||
|
// TLS
|
||||||
|
if (options?.tls === true) {
|
||||||
|
const withDefaults: tls.ConnectionOptions = {
|
||||||
|
...options,
|
||||||
|
port: options?.port ?? 6379,
|
||||||
|
// https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed"
|
||||||
|
// @types/node is... incorrect...
|
||||||
|
// @ts-expect-error
|
||||||
|
noDelay: options?.noDelay ?? true,
|
||||||
|
// https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed"
|
||||||
|
// @types/node is... incorrect...
|
||||||
|
// @ts-expect-error
|
||||||
|
keepAlive: options?.keepAlive ?? true,
|
||||||
|
// https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed"
|
||||||
|
// @types/node is... incorrect...
|
||||||
|
// @ts-expect-error
|
||||||
|
keepAliveInitialDelay: options?.keepAliveInitialDelay ?? 5000,
|
||||||
|
timeout: undefined,
|
||||||
|
onread: undefined,
|
||||||
|
readable: true,
|
||||||
|
writable: true
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
create() {
|
||||||
|
return tls.connect(withDefaults);
|
||||||
|
},
|
||||||
|
event: 'secureConnect'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPC
|
||||||
|
if (options && 'path' in options) {
|
||||||
|
const withDefaults: net.IpcNetConnectOpts = {
|
||||||
|
...options,
|
||||||
|
timeout: undefined,
|
||||||
|
onread: undefined,
|
||||||
|
readable: true,
|
||||||
|
writable: true
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
create() {
|
||||||
|
return net.createConnection(withDefaults);
|
||||||
|
},
|
||||||
|
event: 'connect'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCP
|
||||||
|
const withDefaults: net.TcpNetConnectOpts = {
|
||||||
|
...options,
|
||||||
|
port: options?.port ?? 6379,
|
||||||
|
noDelay: options?.noDelay ?? true,
|
||||||
|
keepAlive: options?.keepAlive ?? true,
|
||||||
|
keepAliveInitialDelay: options?.keepAliveInitialDelay ?? 5000,
|
||||||
|
timeout: undefined,
|
||||||
|
onread: undefined,
|
||||||
|
readable: true,
|
||||||
|
writable: true
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
create() {
|
||||||
|
return net.createConnection(withDefaults);
|
||||||
|
},
|
||||||
|
event: 'connect'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#shouldReconnect(retries: number, cause: Error) {
|
#shouldReconnect(retries: number, cause: Error) {
|
||||||
@@ -164,56 +218,37 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
}
|
}
|
||||||
} while (this.#isOpen && !this.#isReady);
|
} while (this.#isOpen && !this.#isReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async #createSocket(): Promise<net.Socket | tls.TLSSocket> {
|
||||||
|
const socket = this.#socketFactory.create();
|
||||||
|
|
||||||
#createSocket(): Promise<net.Socket | tls.TLSSocket> {
|
let onTimeout;
|
||||||
return new Promise((resolve, reject) => {
|
if (this.#connectTimeout !== undefined) {
|
||||||
const { connectEvent, socket } = RedisSocket.#isTlsSocket(this.#options) ?
|
onTimeout = () => socket.destroy(new ConnectionTimeoutError());
|
||||||
this.#createTlsSocket() :
|
socket.once('timeout', onTimeout);
|
||||||
this.#createNetSocket();
|
socket.setTimeout(this.#connectTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.#options.connectTimeout) {
|
if (this.#isSocketUnrefed) {
|
||||||
socket.setTimeout(this.#options.connectTimeout, () => socket.destroy(new ConnectionTimeoutError()));
|
socket.unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#isSocketUnrefed) {
|
await once(socket, this.#socketFactory.event);
|
||||||
socket.unref();
|
|
||||||
}
|
|
||||||
|
|
||||||
socket
|
if (onTimeout) {
|
||||||
.setNoDelay(this.#options.noDelay)
|
socket.removeListener('timeout', onTimeout);
|
||||||
.once('error', reject)
|
}
|
||||||
.once(connectEvent, () => {
|
|
||||||
socket
|
|
||||||
.setTimeout(0)
|
|
||||||
// https://github.com/nodejs/node/issues/31663
|
|
||||||
.setKeepAlive(this.#options.keepAlive !== false, this.#options.keepAlive || 0)
|
|
||||||
.off('error', reject)
|
|
||||||
.once('error', (err: Error) => this.#onSocketError(err))
|
|
||||||
.once('close', hadError => {
|
|
||||||
if (!hadError && this.#isOpen && this.#socket === socket) {
|
|
||||||
this.#onSocketError(new SocketClosedUnexpectedlyError());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('drain', () => this.emit('drain'))
|
|
||||||
.on('data', data => this.emit('data', data));
|
|
||||||
|
|
||||||
resolve(socket);
|
socket
|
||||||
});
|
.once('error', err => this.#onSocketError(err))
|
||||||
});
|
.once('close', hadError => {
|
||||||
}
|
if (hadError || !this.#isOpen || this.#socket !== socket) return;
|
||||||
|
this.#onSocketError(new SocketClosedUnexpectedlyError());
|
||||||
|
})
|
||||||
|
.on('drain', () => this.emit('drain'))
|
||||||
|
.on('data', data => this.emit('data', data));
|
||||||
|
|
||||||
#createNetSocket(): CreateSocketReturn<net.Socket> {
|
return socket;
|
||||||
return {
|
|
||||||
connectEvent: 'connect',
|
|
||||||
socket: net.connect(this.#options as net.NetConnectOpts) // TODO
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#createTlsSocket(): CreateSocketReturn<tls.TLSSocket> {
|
|
||||||
return {
|
|
||||||
connectEvent: 'secureConnect',
|
|
||||||
socket: tls.connect(this.#options as tls.ConnectionOptions) // TODO
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#onSocketError(err: Error): void {
|
#onSocketError(err: Error): void {
|
||||||
@@ -229,11 +264,11 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
write(iterator: IterableIterator<Array<RedisArgument>>): void {
|
write(iterable: Iterable<Array<RedisArgument>>) {
|
||||||
if (!this.#socket) return;
|
if (!this.#socket) return;
|
||||||
|
|
||||||
this.#socket.cork();
|
this.#socket.cork();
|
||||||
for (const args of iterator) {
|
for (const args of iterable) {
|
||||||
for (const toWrite of args) {
|
for (const toWrite of args) {
|
||||||
this.#socket.write(toWrite);
|
this.#socket.write(toWrite);
|
||||||
}
|
}
|
||||||
@@ -282,12 +317,12 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
this.emit('end');
|
this.emit('end');
|
||||||
}
|
}
|
||||||
|
|
||||||
ref(): void {
|
ref() {
|
||||||
this.#isSocketUnrefed = false;
|
this.#isSocketUnrefed = false;
|
||||||
this.#socket?.ref();
|
this.#socket?.ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
unref(): void {
|
unref() {
|
||||||
this.#isSocketUnrefed = true;
|
this.#isSocketUnrefed = true;
|
||||||
this.#socket?.unref();
|
this.#socket?.unref();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user