Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions packages/client/lib/cluster/cluster-slots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export default class RedisClusterSlots<
this._randomNodeIterator = undefined;
}

async #discover(rootNode: RedisClusterClientOptions) {
async #discover(rootNode: RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>) {
this.clientSideCache?.clear();
this.clientSideCache?.disable();

Expand Down Expand Up @@ -509,16 +509,21 @@ export default class RedisClusterSlots<
}
}

async #getShards(rootNode: RedisClusterClientOptions) {
async #getShards(rootNode: RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>) {
const options = this.#clientOptionsDefaults(rootNode)!;
options.socket ??= {};
options.socket.reconnectStrategy = false;
options.RESP = this.#options.RESP;
options.commandOptions = undefined;
options.maintNotifications = 'disabled';

// TODO: find a way to avoid type casting
const client = await this.#clientFactory(options as RedisClientOptions<M, F, S, RESP, {}>)

const clientOptions: RedisClientOptions<M, F, S, RESP, {}> = {
...options,
socket: {
...options.socket,
reconnectStrategy: false
},
RESP: this.#options.RESP,
commandOptions: undefined,
maintNotifications: 'disabled'
};

const client = await this.#clientFactory<{}>(clientOptions)
.on('error', err => this.#emit('error', err))
.connect();

Expand All @@ -540,7 +545,7 @@ export default class RedisClusterSlots<
}
}

#nodeClientOptions(node: NodeAddress & { address: string }): RedisClusterClientOptions {
#nodeClientOptions(node: NodeAddress & { address: string }): RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING> {
return {
socket: this.#getNodeAddress(node.address) ?? {
host: node.host,
Expand All @@ -549,7 +554,9 @@ export default class RedisClusterSlots<
};
}

#clientOptionsDefaults(options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>) {
#clientOptionsDefaults(options: RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>;
#clientOptionsDefaults(options?: RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING> | undefined;
#clientOptionsDefaults(options?: RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>) {
if (!this.#options.defaults) return options;

let socket;
Expand Down Expand Up @@ -614,13 +621,16 @@ export default class RedisClusterSlots<
const address = node.address;
const emit = this.#emit;
let wasReady = false;
const client = this.#clientFactory( this.#clientOptionsDefaults({
const clientOptions: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING> = {
...this.#clientOptionsDefaults({
clientSideCache: this.clientSideCache,
himportRegistry: this.#himportRegistry,
RESP: this.#options.RESP,
socket,
readonly,
}));
}),
RESP: this.#options.RESP,
};
const client = this.#clientFactory<TYPE_MAPPING>(clientOptions);
client._setIdentity(ClientRole.CLUSTER_NODE, this.#clusterClientId);
client
.on('error', error => emit('node-error', error, clientInfo))
Expand Down
12 changes: 12 additions & 0 deletions packages/client/lib/cluster/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ describe('Cluster', () => {
assert.equal(cluster.nodeByAddress.size, numberOfMasters + numberOfMasters * numberOfReplicas);
}, GLOBAL.CLUSTERS.WITH_REPLICAS);

testUtils.testWithCluster('node clients inherit configured cluster RESP version', async cluster => {
for (const master of cluster.masters) {
assert.ok(master.client instanceof RedisClient);
assert.equal(master.client.options.RESP, cluster._options?.RESP ?? 3);
}
}, {
...GLOBAL.CLUSTERS.OPEN,
clusterConfiguration: {
minimizeConnections: false
}
});

testUtils.testWithCluster('getMasters should be backwards competiable (without `minimizeConnections`)', async cluster => {
const masters = cluster.getMasters();
assert.ok(Array.isArray(masters));
Expand Down
22 changes: 15 additions & 7 deletions packages/client/lib/cluster/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ interface ClusterCommander<
keyPrefix?: RedisArgument;
}

export type RedisClusterClientOptions = Omit<
RedisClientOptions<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisTcpSocketOptions>,
keyof ClusterCommander<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping/*, CommandPolicies*/>
export type RedisClusterClientOptions<
M extends RedisModules = RedisModules,
F extends RedisFunctions = RedisFunctions,
S extends RedisScripts = RedisScripts,
RESP extends RespVersions = RespVersions,
TYPE_MAPPING extends TypeMapping = TypeMapping
> = Omit<
RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>,
keyof ClusterCommander<M, F, S, RESP, TYPE_MAPPING>
>;

export interface RedisClusterOptions<
Expand All @@ -75,15 +81,15 @@ export interface RedisClusterOptions<
* not inherited by the connections made to the discovered nodes. Settings that should apply to
* every connection (e.g. credentials, TLS) must be specified via `defaults`.
*/
rootNodes: Array<RedisClusterClientOptions>;
rootNodes: Array<RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>>;
/**
* Default values used for every client in the cluster. Use this to specify global values,
* for example: ACL credentials, timeouts, TLS configuration etc.
*
* The connections to the discovered cluster nodes are created from these defaults (plus the
* discovered host and port) — they do not inherit any other settings from `rootNodes`.
*/
defaults?: Partial<RedisClusterClientOptions>;
defaults?: Partial<RedisClusterClientOptions<M, F, S, RESP, TYPE_MAPPING>>;
/**
* When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes.
* Useful for short-term or PubSub-only connections.
Expand Down Expand Up @@ -285,7 +291,9 @@ export default class RedisCluster<
RedisCluster.#SingleEntryCache.set(config, Cluster);
}

return (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => {
return (
options?: Omit<RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>, keyof Exclude<typeof config, undefined>>
) => {
// returning a "proxy" to prevent the namespaces._self to leak between "proxies"
return Object.create(new Cluster(options)) as RedisClusterType<M, F, S, RESP, TYPE_MAPPING/*, POLICIES*/>;
};
Expand Down Expand Up @@ -317,7 +325,7 @@ export default class RedisCluster<
TYPE_MAPPING extends TypeMapping = {},
// POLICIES extends CommandPolicies = {}
>(options?: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING/*, POLICIES*/>) {
return RedisCluster.factory(options)(options);
return RedisCluster.factory<M, F, S, RESP, TYPE_MAPPING>(options)(options);
}

readonly _options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING/*, POLICIES*/>;
Expand Down