@valkey/client
Version:
The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.
434 lines (433 loc) • 23 kB
JavaScript
"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _ValkeyClusterSlots_instances, _a, _ValkeyClusterSlots_SLOTS, _ValkeyClusterSlots_options, _ValkeyClusterSlots_Client, _ValkeyClusterSlots_emit, _ValkeyClusterSlots_isOpen, _ValkeyClusterSlots_discoverWithRootNodes, _ValkeyClusterSlots_resetSlots, _ValkeyClusterSlots_discover, _ValkeyClusterSlots_getShards, _ValkeyClusterSlots_getNodeAddress, _ValkeyClusterSlots_clientOptionsDefaults, _ValkeyClusterSlots_initiateSlotNode, _ValkeyClusterSlots_createClient, _ValkeyClusterSlots_createNodeClient, _ValkeyClusterSlots_runningValkeycoverPromise, _ValkeyClusterSlots_rediscover, _ValkeyClusterSlots_destroy, _ValkeyClusterSlots_execOnNodeClient, _ValkeyClusterSlots_iterateAllNodes, _ValkeyClusterSlots_randomNodeIterator, _ValkeyClusterSlots_slotNodesIterator, _ValkeyClusterSlots_initiatePubSubClient, _ValkeyClusterSlots_initiateShardedPubSubClient;
Object.defineProperty(exports, "__esModule", { value: true });
const client_1 = require("../client");
const errors_1 = require("../errors");
const util_1 = require("util");
const pub_sub_1 = require("../client/pub-sub");
// We need to use 'require', because it's not possible with Typescript to import
// function that are exported as 'module.exports = function`, without esModuleInterop
// set to true.
const calculateSlot = require("cluster-key-slot");
class ValkeyClusterSlots {
get isOpen() {
return __classPrivateFieldGet(this, _ValkeyClusterSlots_isOpen, "f");
}
constructor(options, emit) {
_ValkeyClusterSlots_instances.add(this);
_ValkeyClusterSlots_options.set(this, void 0);
_ValkeyClusterSlots_Client.set(this, void 0);
_ValkeyClusterSlots_emit.set(this, void 0);
Object.defineProperty(this, "slots", {
enumerable: true,
configurable: true,
writable: true,
value: new Array(__classPrivateFieldGet(_a, _a, "f", _ValkeyClusterSlots_SLOTS))
});
Object.defineProperty(this, "shards", {
enumerable: true,
configurable: true,
writable: true,
value: new Array()
});
Object.defineProperty(this, "masters", {
enumerable: true,
configurable: true,
writable: true,
value: new Array()
});
Object.defineProperty(this, "replicas", {
enumerable: true,
configurable: true,
writable: true,
value: new Array()
});
Object.defineProperty(this, "nodeByAddress", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "pubSubNode", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
_ValkeyClusterSlots_isOpen.set(this, false);
_ValkeyClusterSlots_runningValkeycoverPromise.set(this, void 0);
_ValkeyClusterSlots_randomNodeIterator.set(this, void 0);
__classPrivateFieldSet(this, _ValkeyClusterSlots_options, options, "f");
__classPrivateFieldSet(this, _ValkeyClusterSlots_Client, client_1.default.extend(options), "f");
__classPrivateFieldSet(this, _ValkeyClusterSlots_emit, emit, "f");
}
async connect() {
if (__classPrivateFieldGet(this, _ValkeyClusterSlots_isOpen, "f")) {
throw new Error("Cluster already open");
}
__classPrivateFieldSet(this, _ValkeyClusterSlots_isOpen, true, "f");
try {
await __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_discoverWithRootNodes).call(this);
}
catch (err) {
__classPrivateFieldSet(this, _ValkeyClusterSlots_isOpen, false, "f");
throw err;
}
}
nodeClient(node) {
return node.client ?? __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_createNodeClient).call(this, node);
}
async rediscover(startWith) {
__classPrivateFieldSet(this, _ValkeyClusterSlots_runningValkeycoverPromise, __classPrivateFieldGet(this, _ValkeyClusterSlots_runningValkeycoverPromise, "f") ?? __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_rediscover).call(this, startWith).finally(() => (__classPrivateFieldSet(this, _ValkeyClusterSlots_runningValkeycoverPromise, undefined, "f"))), "f");
return __classPrivateFieldGet(this, _ValkeyClusterSlots_runningValkeycoverPromise, "f");
}
quit() {
return __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_destroy).call(this, (client) => client.quit());
}
disconnect() {
return __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_destroy).call(this, (client) => client.disconnect());
}
getClient(firstKey, isReadonly) {
if (!firstKey) {
return this.nodeClient(this.getRandomNode());
}
const slotNumber = calculateSlot(firstKey);
if (!isReadonly) {
return this.nodeClient(this.slots[slotNumber].master);
}
return this.nodeClient(this.getSlotRandomNode(slotNumber));
}
getRandomNode() {
__classPrivateFieldSet(this, _ValkeyClusterSlots_randomNodeIterator, __classPrivateFieldGet(this, _ValkeyClusterSlots_randomNodeIterator, "f") ?? __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_iterateAllNodes).call(this), "f");
return __classPrivateFieldGet(this, _ValkeyClusterSlots_randomNodeIterator, "f").next().value;
}
getSlotRandomNode(slotNumber) {
const slot = this.slots[slotNumber];
if (!slot.replicas?.length) {
return slot.master;
}
slot.nodesIterator ?? (slot.nodesIterator = __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_slotNodesIterator).call(this, slot));
return slot.nodesIterator.next().value;
}
getMasterByAddress(address) {
const master = this.nodeByAddress.get(address);
if (!master)
return;
return this.nodeClient(master);
}
getPubSubClient() {
return this.pubSubNode
? this.pubSubNode.client
: __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_initiatePubSubClient).call(this);
}
async executeUnsubscribeCommand(unsubscribe) {
const client = await this.getPubSubClient();
await unsubscribe(client);
if (!client.isPubSubActive && client.isOpen) {
await client.disconnect();
this.pubSubNode = undefined;
}
}
getShardedPubSubClient(channel) {
const { master } = this.slots[calculateSlot(channel)];
return master.pubSubClient ?? __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_initiateShardedPubSubClient).call(this, master);
}
async executeShardedUnsubscribeCommand(channel, unsubscribe) {
const { master } = this.slots[calculateSlot(channel)];
if (!master.pubSubClient)
return Promise.resolve();
const client = await master.pubSubClient;
await unsubscribe(client);
if (!client.isPubSubActive && client.isOpen) {
await client.disconnect();
master.pubSubClient = undefined;
}
}
}
_a = ValkeyClusterSlots, _ValkeyClusterSlots_options = new WeakMap(), _ValkeyClusterSlots_Client = new WeakMap(), _ValkeyClusterSlots_emit = new WeakMap(), _ValkeyClusterSlots_isOpen = new WeakMap(), _ValkeyClusterSlots_runningValkeycoverPromise = new WeakMap(), _ValkeyClusterSlots_randomNodeIterator = new WeakMap(), _ValkeyClusterSlots_instances = new WeakSet(), _ValkeyClusterSlots_discoverWithRootNodes = async function _ValkeyClusterSlots_discoverWithRootNodes() {
let start = Math.floor(Math.random() * __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").rootNodes.length);
for (let i = start; i < __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").rootNodes.length; i++) {
if (await __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_discover).call(this, __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").rootNodes[i]))
return;
}
for (let i = 0; i < start; i++) {
if (await __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_discover).call(this, __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").rootNodes[i]))
return;
}
throw new errors_1.RootNodesUnavailableError();
}, _ValkeyClusterSlots_resetSlots = function _ValkeyClusterSlots_resetSlots() {
this.slots = new Array(__classPrivateFieldGet(_a, _a, "f", _ValkeyClusterSlots_SLOTS));
this.shards = [];
this.masters = [];
this.replicas = [];
__classPrivateFieldSet(this, _ValkeyClusterSlots_randomNodeIterator, undefined, "f");
}, _ValkeyClusterSlots_discover = async function _ValkeyClusterSlots_discover(rootNode) {
__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_resetSlots).call(this);
const addressesInUse = new Set();
try {
const shards = await __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_getShards).call(this, rootNode), promises = [], eagerConnect = __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").minimizeConnections !== true;
for (const { from, to, master, replicas } of shards) {
const shard = {
master: __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_initiateSlotNode).call(this, master, false, eagerConnect, addressesInUse, promises),
};
if (__classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").useReplicas) {
shard.replicas = replicas.map((replica) => __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_initiateSlotNode).call(this, replica, true, eagerConnect, addressesInUse, promises));
}
this.shards.push(shard);
for (let i = from; i <= to; i++) {
this.slots[i] = shard;
}
}
if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) {
if (util_1.types.isPromise(this.pubSubNode.client)) {
promises.push(this.pubSubNode.client.then((client) => client.disconnect()));
this.pubSubNode = undefined;
}
else {
promises.push(this.pubSubNode.client.disconnect());
const channelsListeners = this.pubSubNode.client.getPubSubListeners(pub_sub_1.PubSubType.CHANNELS), patternsListeners = this.pubSubNode.client.getPubSubListeners(pub_sub_1.PubSubType.PATTERNS);
if (channelsListeners.size || patternsListeners.size) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_initiatePubSubClient).call(this, {
[pub_sub_1.PubSubType.CHANNELS]: channelsListeners,
[pub_sub_1.PubSubType.PATTERNS]: patternsListeners,
}));
}
}
}
for (const [address, node] of this.nodeByAddress.entries()) {
if (addressesInUse.has(address))
continue;
if (node.client) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_execOnNodeClient).call(this, node.client, (client) => client.disconnect()));
}
const { pubSubClient } = node;
if (pubSubClient) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_execOnNodeClient).call(this, pubSubClient, (client) => client.disconnect()));
}
this.nodeByAddress.delete(address);
}
await Promise.all(promises);
return true;
}
catch (err) {
__classPrivateFieldGet(this, _ValkeyClusterSlots_emit, "f").call(this, "error", err);
return false;
}
}, _ValkeyClusterSlots_getShards = async function _ValkeyClusterSlots_getShards(rootNode) {
const client = new (__classPrivateFieldGet(this, _ValkeyClusterSlots_Client, "f"))(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_clientOptionsDefaults).call(this, rootNode, true));
client.on("error", (err) => __classPrivateFieldGet(this, _ValkeyClusterSlots_emit, "f").call(this, "error", err));
await client.connect();
try {
// using `CLUSTER SLOTS` and not `CLUSTER SHARDS` to support older versions
return await client.clusterSlots();
}
finally {
await client.disconnect();
}
}, _ValkeyClusterSlots_getNodeAddress = function _ValkeyClusterSlots_getNodeAddress(address) {
switch (typeof __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").nodeAddressMap) {
case "object":
return __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").nodeAddressMap[address];
case "function":
return __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").nodeAddressMap(address);
}
}, _ValkeyClusterSlots_clientOptionsDefaults = function _ValkeyClusterSlots_clientOptionsDefaults(options, disableReconnect) {
let result;
if (__classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").defaults) {
let socket;
if (__classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").defaults.socket) {
socket = options?.socket
? {
...__classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").defaults.socket,
...options.socket,
}
: __classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").defaults.socket;
}
else {
socket = options?.socket;
}
result = {
...__classPrivateFieldGet(this, _ValkeyClusterSlots_options, "f").defaults,
...options,
socket,
};
}
else {
result = options;
}
if (disableReconnect) {
result ?? (result = {});
result.socket ?? (result.socket = {});
result.socket.reconnectStrategy = false;
}
return result;
}, _ValkeyClusterSlots_initiateSlotNode = function _ValkeyClusterSlots_initiateSlotNode({ id, ip, port }, readonly, eagerConnent, addressesInUse, promises) {
const address = `${ip}:${port}`;
addressesInUse.add(address);
let node = this.nodeByAddress.get(address);
if (!node) {
node = {
id,
host: ip,
port,
address,
readonly,
client: undefined,
};
if (eagerConnent) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_createNodeClient).call(this, node));
}
this.nodeByAddress.set(address, node);
}
(readonly ? this.replicas : this.masters).push(node);
return node;
}, _ValkeyClusterSlots_createClient = async function _ValkeyClusterSlots_createClient(node, readonly = node.readonly) {
const client = new (__classPrivateFieldGet(this, _ValkeyClusterSlots_Client, "f"))(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_clientOptionsDefaults).call(this, {
socket: __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_getNodeAddress).call(this, node.address) ?? {
host: node.host,
port: node.port,
},
readonly,
}));
client.on("error", (err) => __classPrivateFieldGet(this, _ValkeyClusterSlots_emit, "f").call(this, "error", err));
await client.connect();
return client;
}, _ValkeyClusterSlots_createNodeClient = function _ValkeyClusterSlots_createNodeClient(node) {
const promise = __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_createClient).call(this, node)
.then((client) => {
node.client = client;
return client;
})
.catch((err) => {
node.client = undefined;
throw err;
});
node.client = promise;
return promise;
}, _ValkeyClusterSlots_rediscover = async function _ValkeyClusterSlots_rediscover(startWith) {
if (await __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_discover).call(this, startWith.options))
return;
return __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_discoverWithRootNodes).call(this);
}, _ValkeyClusterSlots_destroy = async function _ValkeyClusterSlots_destroy(fn) {
__classPrivateFieldSet(this, _ValkeyClusterSlots_isOpen, false, "f");
const promises = [];
for (const { master, replicas } of this.shards) {
if (master.client) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_execOnNodeClient).call(this, master.client, fn));
}
if (master.pubSubClient) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_execOnNodeClient).call(this, master.pubSubClient, fn));
}
if (replicas) {
for (const { client } of replicas) {
if (client) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_execOnNodeClient).call(this, client, fn));
}
}
}
}
if (this.pubSubNode) {
promises.push(__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_execOnNodeClient).call(this, this.pubSubNode.client, fn));
this.pubSubNode = undefined;
}
__classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_resetSlots).call(this);
this.nodeByAddress.clear();
await Promise.allSettled(promises);
}, _ValkeyClusterSlots_execOnNodeClient = function _ValkeyClusterSlots_execOnNodeClient(client, fn) {
return util_1.types.isPromise(client) ? client.then(fn) : fn(client);
}, _ValkeyClusterSlots_iterateAllNodes = function* _ValkeyClusterSlots_iterateAllNodes() {
let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length));
if (i < this.masters.length) {
do {
yield this.masters[i];
} while (++i < this.masters.length);
for (const replica of this.replicas) {
yield replica;
}
}
else {
i -= this.masters.length;
do {
yield this.replicas[i];
} while (++i < this.replicas.length);
}
while (true) {
for (const master of this.masters) {
yield master;
}
for (const replica of this.replicas) {
yield replica;
}
}
}, _ValkeyClusterSlots_slotNodesIterator = function* _ValkeyClusterSlots_slotNodesIterator(slot) {
let i = Math.floor(Math.random() * (1 + slot.replicas.length));
if (i < slot.replicas.length) {
do {
yield slot.replicas[i];
} while (++i < slot.replicas.length);
}
while (true) {
yield slot.master;
for (const replica of slot.replicas) {
yield replica;
}
}
}, _ValkeyClusterSlots_initiatePubSubClient = async function _ValkeyClusterSlots_initiatePubSubClient(toResubscribe) {
const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), node = index < this.masters.length
? this.masters[index]
: this.replicas[index - this.masters.length];
this.pubSubNode = {
address: node.address,
client: __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_createClient).call(this, node, true)
.then(async (client) => {
if (toResubscribe) {
await Promise.all([
client.extendPubSubListeners(pub_sub_1.PubSubType.CHANNELS, toResubscribe[pub_sub_1.PubSubType.CHANNELS]),
client.extendPubSubListeners(pub_sub_1.PubSubType.PATTERNS, toResubscribe[pub_sub_1.PubSubType.PATTERNS]),
]);
}
this.pubSubNode.client = client;
return client;
})
.catch((err) => {
this.pubSubNode = undefined;
throw err;
}),
};
return this.pubSubNode.client;
}, _ValkeyClusterSlots_initiateShardedPubSubClient = function _ValkeyClusterSlots_initiateShardedPubSubClient(master) {
const promise = __classPrivateFieldGet(this, _ValkeyClusterSlots_instances, "m", _ValkeyClusterSlots_createClient).call(this, master, true)
.then((client) => {
client.on("server-sunsubscribe", async (channel, listeners) => {
try {
await this.rediscover(client);
const redirectTo = await this.getShardedPubSubClient(channel);
redirectTo.extendPubSubChannelListeners(pub_sub_1.PubSubType.SHARDED, channel, listeners);
}
catch (err) {
__classPrivateFieldGet(this, _ValkeyClusterSlots_emit, "f").call(this, "sharded-shannel-moved-error", err, channel, listeners);
}
});
master.pubSubClient = client;
return client;
})
.catch((err) => {
master.pubSubClient = undefined;
throw err;
});
master.pubSubClient = promise;
return promise;
};
_ValkeyClusterSlots_SLOTS = { value: 16384 };
exports.default = ValkeyClusterSlots;