UNPKG

hfxbus

Version:

Redis backed high frequency exchange bus

193 lines (192 loc) 6.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = __importDefault(require("fs")); const ioredis_1 = __importStar(require("ioredis")); const path_1 = __importDefault(require("path")); const { crc16 } = require('crc'); require('../lib/add-streams-to-ioredis')(ioredis_1.default); const XRETRY_LUA = fs_1.default.readFileSync(path_1.default.join(__dirname, '../lib/scripts/xretry.lua')).toString(); class ConnectionManager { constructor({ standalone, cluster, startupNodes, nodes, }) { this.clients = {}; this.keyPrefix = 'hfxbus'; this.staticRoutes = {}; this.standalone = standalone; this.cluster = cluster; this.startupNodes = startupNodes; this.nodes = nodes; if (this.standalone) { this.keyPrefix = this.standalone.keyPrefix || 'hfxbus'; Reflect.deleteProperty(this.standalone, 'keyPrefix'); } else if (this.nodes) { this.keyPrefix = this.nodes.keyPrefix || 'hfxbus'; Reflect.deleteProperty(this.nodes, 'keyPrefix'); this.nodes.nodes.forEach((node, index) => { if (node.staticRoutes) { for (let staticRoute of node.staticRoutes) { if (typeof staticRoute === 'string') { staticRoute = crc16(staticRoute); } this.staticRoutes[staticRoute] = index; } } }); } else { this.keyPrefix = this.cluster.keyPrefix || 'hfxbus'; Reflect.deleteProperty(this.cluster, 'keyPrefix'); } } static cluster(startupNodes, cluster) { return new ConnectionManager({ startupNodes, cluster: { enablePipelining: false, ...cluster, }, }); } static standalone(standalone) { return new ConnectionManager({ standalone: { enablePipelining: true, ...standalone, }, }); } static nodes(nodes) { nodes.nodes = nodes.nodes.map((node, index) => { if (!('sequence' in node)) { node.sequence = index; } return node; }).sort((a, b) => a.sequence - b.sequence); return new ConnectionManager({ nodes: { enablePipelining: true, ...nodes, }, }); } getClientByRoute(key, route) { if (!this.nodes) { return this.getClient(key); } const routeHash = crc16(route); let index = this.staticRoutes[routeHash]; if (index === undefined) { index = routeHash % this.nodes.nodes.length; } const clientKey = `${key}-${index}`; if (!(clientKey in this.clients)) { const client = new ioredis_1.default(this.nodes.nodes[index]); client.enablePipelining = this.nodes.enablePipelining; this.addClient(clientKey, client); } return this.clients[clientKey]; } getClients(key) { if (!this.nodes) { return [this.getClient(key)]; } return this.nodes.nodes.map((node, index) => { const clientKey = `${key}-${index}`; if (!(clientKey in this.clients)) { const client = new ioredis_1.default(node); client.enablePipelining = this.nodes.enablePipelining; this.addClient(clientKey, client); } return this.clients[clientKey]; }); } getClient(key) { if (!(key in this.clients)) { let client; if (this.standalone) { client = new ioredis_1.default(this.standalone); client.enablePipelining = this.standalone.enablePipelining; } else { client = new ioredis_1.Cluster(this.startupNodes, this.cluster); client.enablePipelining = this.cluster.enablePipelining; } this.addClient(key, client); } return this.clients[key]; } getKeyPrefix() { return this.keyPrefix; } async stop({ maxWait, force, } = {}) { const promises = []; for (const key in this.clients) { const client = this.clients[key]; if (client.stopped && !force) { continue; } client.stopped = true; if (force) { client.disconnect(); continue; } promises.push(new Promise((resolve, reject) => { if (client.usedBy === 0) { client.quit().then(() => resolve()).catch(reject); return undefined; } let timeout = null; if (maxWait) { timeout = setTimeout(() => { client.disconnect(); }, maxWait); } client.once('free', () => { client.quit().then(() => { if (timeout) { clearTimeout(timeout); } resolve(); }).catch(reject); }).emit('stopped'); return undefined; })); } if (!force) { await Promise.all(promises); } this.clients = {}; } addClient(key, client) { this.clients[key] = client; client.keyPrefix = this.keyPrefix; client.setMaxListeners(Infinity); client.usedBy = 0; client.stopped = false; client.defineCommand('xretry', { lua: XRETRY_LUA, numberOfKeys: 6, }); client.once('close', () => { delete this.clients[key]; }).on('use', () => { client.usedBy++; }).on('release', () => { client.usedBy--; if (client.usedBy === 0) { client.emit('free'); } }); } } exports.ConnectionManager = ConnectionManager;