UNPKG

@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
"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;