Skip to content

Commit 3e16791

Browse files
authored
More fixes for socket issue (#2710)
* more typing fixes * try to redo typing a bit and genericize to make better * use genericized cluster options for cluster as well
1 parent 7e27f72 commit 3e16791

File tree

6 files changed

+48
-32
lines changed

6 files changed

+48
-32
lines changed

packages/client/lib/client/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export interface RedisClientOptions<
2121
F extends RedisFunctions = RedisFunctions,
2222
S extends RedisScripts = RedisScripts,
2323
RESP extends RespVersions = RespVersions,
24-
TYPE_MAPPING extends TypeMapping = TypeMapping
24+
TYPE_MAPPING extends TypeMapping = TypeMapping,
25+
SocketOptions extends RedisSocketOptions = RedisSocketOptions
2526
> extends CommanderConfig<M, F, S, RESP> {
2627
/**
2728
* `redis[s]://[[username][:password]@][host][:port][/db-number]`
@@ -31,7 +32,7 @@ export interface RedisClientOptions<
3132
/**
3233
* Socket connection properties
3334
*/
34-
socket?: RedisSocketOptions;
35+
socket?: SocketOptions;
3536
/**
3637
* ACL username ([see ACL guide](https://redis.io/topics/acl))
3738
*/

packages/client/lib/client/socket.ts

+25-18
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,9 @@ type NetOptions = {
99
tls?: false;
1010
};
1111

12-
type TcpOptions = NetOptions & Omit<
13-
net.TcpNetConnectOpts,
14-
'timeout' | 'onread' | 'readable' | 'writable' | 'port'
15-
> & {
16-
port?: number;
17-
};
18-
19-
type IpcOptions = NetOptions & Omit<
20-
net.IpcNetConnectOpts,
21-
'timeout' | 'onread' | 'readable' | 'writable'
22-
>;
23-
24-
type TlsOptions = {
25-
tls: true;
26-
} & tls.ConnectionOptions;
27-
2812
type ReconnectStrategyFunction = (retries: number, cause: Error) => false | Error | number;
2913

30-
export type RedisSocketOptions = {
14+
type RedisSocketOptionsCommon = {
3115
/**
3216
* Connection timeout (in milliseconds)
3317
*/
@@ -39,7 +23,30 @@ export type RedisSocketOptions = {
3923
* 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.
4024
*/
4125
reconnectStrategy?: false | number | ReconnectStrategyFunction;
42-
} & (TcpOptions | IpcOptions | TlsOptions);
26+
}
27+
28+
type RedisTcpOptions = RedisSocketOptionsCommon & NetOptions & Omit<
29+
net.TcpNetConnectOpts,
30+
'timeout' | 'onread' | 'readable' | 'writable' | 'port'
31+
> & {
32+
port?: number;
33+
};
34+
35+
type RedisTlsOptions = RedisSocketOptionsCommon & tls.ConnectionOptions & {
36+
tls: true;
37+
host: string;
38+
}
39+
40+
type RedisIpcOptions = RedisSocketOptionsCommon & Omit<
41+
net.IpcNetConnectOpts,
42+
'timeout' | 'onread' | 'readable' | 'writable'
43+
> & {
44+
tls: false;
45+
}
46+
47+
export type RedisTcpSocketOptions = RedisTcpOptions | RedisTlsOptions;
48+
49+
export type RedisSocketOptions = RedisTcpSocketOptions | RedisIpcOptions;
4350

4451
export type RedisSocketInitiator = () => void | Promise<unknown>;
4552

packages/client/lib/cluster/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots';
88
import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command';
99
import { PubSubListener } from '../client/pub-sub';
1010
import { ErrorReply } from '../errors';
11+
import { RedisTcpSocketOptions } from '../client/socket';
1112

1213
interface ClusterCommander<
1314
M extends RedisModules,
@@ -21,7 +22,7 @@ interface ClusterCommander<
2122
}
2223

2324
export type RedisClusterClientOptions = Omit<
24-
RedisClientOptions,
25+
RedisClientOptions<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisTcpSocketOptions>,
2526
keyof ClusterCommander<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping/*, CommandPolicies*/>
2627
>;
2728

packages/client/lib/sentinel/index.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { setTimeout } from 'node:timers/promises';
1414
import RedisSentinelModule from './module'
1515
import { RedisVariadicArgument } from '../commands/generic-transformers';
1616
import { WaitQueue } from './wait-queue';
17+
import { TcpNetConnectOpts } from 'node:net';
18+
import { RedisTcpSocketOptions } from '../client/socket';
1719

1820
interface ClientInfo {
1921
id: number;
@@ -578,8 +580,8 @@ class RedisSentinelInternal<
578580
}
579581

580582
readonly #name: string;
581-
readonly #nodeClientOptions: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>;
582-
readonly #sentinelClientOptions: RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING>;
583+
readonly #nodeClientOptions: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
584+
readonly #sentinelClientOptions: RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
583585
readonly #scanInterval: number;
584586
readonly #passthroughClientErrorEvents: boolean;
585587

@@ -624,12 +626,12 @@ class RedisSentinelInternal<
624626
this.#scanInterval = options.scanInterval ?? 0;
625627
this.#passthroughClientErrorEvents = options.passthroughClientErrorEvents ?? false;
626628

627-
this.#nodeClientOptions = options.nodeClientOptions ? Object.assign({} as RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>, options.nodeClientOptions) : {};
629+
this.#nodeClientOptions = options.nodeClientOptions ? Object.assign({} as RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>, options.nodeClientOptions) : {};
628630
if (this.#nodeClientOptions.url !== undefined) {
629631
throw new Error("invalid nodeClientOptions for Sentinel");
630632
}
631633

632-
this.#sentinelClientOptions = options.sentinelClientOptions ? Object.assign({} as RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING>, options.sentinelClientOptions) : {};
634+
this.#sentinelClientOptions = options.sentinelClientOptions ? Object.assign({} as RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>, options.sentinelClientOptions) : {};
633635
this.#sentinelClientOptions.modules = RedisSentinelModule;
634636

635637
if (this.#sentinelClientOptions.url !== undefined) {
@@ -754,7 +756,8 @@ class RedisSentinelInternal<
754756
await this.#reset();
755757
continue;
756758
}
757-
this.#trace("attemping to send command to " + client.options?.socket?.host + ":" + client.options?.socket?.port)
759+
const sockOpts = client.options?.socket as TcpNetConnectOpts | undefined;
760+
this.#trace("attemping to send command to " + sockOpts?.host + ":" + sockOpts?.port)
758761

759762
try {
760763
/*
@@ -1198,7 +1201,8 @@ class RedisSentinelInternal<
11981201

11991202
if (replicaCloseSet.has(str) || !replica.isOpen) {
12001203
if (replica.isOpen) {
1201-
this.#trace(`destroying replica client to ${replica.options?.socket?.host}:${replica.options?.socket?.port}`);
1204+
const sockOpts = replica.options?.socket as TcpNetConnectOpts | undefined;
1205+
this.#trace(`destroying replica client to ${sockOpts?.host}:${sockOpts?.port}`);
12021206
replica.destroy()
12031207
}
12041208
if (!removedSet.has(str)) {

packages/client/lib/sentinel/types.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { CommandOptions } from '../client/commands-queue';
33
import { CommandSignature, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types';
44
import COMMANDS from '../commands';
55
import RedisSentinel, { RedisSentinelClient } from '.';
6+
import { RedisTcpSocketOptions } from '../client/socket';
67

78
export interface RedisNode {
89
host: string;
@@ -31,11 +32,11 @@ export interface RedisSentinelOptions<
3132
/**
3233
* The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with
3334
*/
34-
nodeClientOptions?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>;
35+
nodeClientOptions?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
3536
/**
3637
* The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with
3738
*/
38-
sentinelClientOptions?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>;
39+
sentinelClientOptions?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
3940
/**
4041
* The number of clients connected to the master node
4142
*/

packages/client/lib/sentinel/utils.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Command, RedisFunction, RedisScript, RespVersions } from '../RESP/types';
2-
import { RedisSocketOptions } from '../client/socket';
2+
import { RedisSocketOptions, RedisTcpSocketOptions } from '../client/socket';
33
import { functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
44
import { NamespaceProxySentinel, NamespaceProxySentinelClient, NodeInfo, ProxySentinel, ProxySentinelClient, RedisNode } from './types';
55

@@ -27,9 +27,11 @@ export function createNodeList(nodes: Array<NodeInfo>) {
2727
}
2828

2929
export function clientSocketToNode(socket: RedisSocketOptions): RedisNode {
30+
const s = socket as RedisTcpSocketOptions;
31+
3032
return {
31-
host: socket.host!,
32-
port: socket.port!
33+
host: s.host!,
34+
port: s.port!
3335
}
3436
}
3537

0 commit comments

Comments
 (0)