UNPKG

@drift-labs/common

Version:

Common functions for Drift

647 lines 23.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RedisClient = exports.getRedisClient = exports.getRedisClusterClient = exports.RedisClientPrefix = void 0; const __1 = require(".."); const ioredis_1 = __importDefault(require("ioredis")); const BULK_WRITE_CHUNK_SIZE = 500; const BULK_READ_CHUNK_SIZE = 1000; const CHUNK_SLEEP_TIME = 100; var RedisClientPrefix; (function (RedisClientPrefix) { RedisClientPrefix["EXCHANGE"] = ""; RedisClientPrefix["USER_MAP"] = "usermap-server:"; RedisClientPrefix["DLOB"] = "dlob:"; RedisClientPrefix["DLOB_HELIUS"] = "dlob-helius:"; })(RedisClientPrefix || (exports.RedisClientPrefix = RedisClientPrefix = {})); function isWrite() { return function (_target, propertyKey, descriptor) { const method = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { if (process.env.DISABLE_CACHE_WRITE) { console.log(`DISABLE_CACHE_WRITE=true :: Skipping ${methodName}`); return; } // Run the decorated method and return the value const returnValue = method.apply(this, args); return returnValue; }; }; } function isRead(_target, _propertyKey, descriptor) { return descriptor; } const getTlsConfiguration = () => { if (process.env.RUNNING_LOCAL === 'true' && process.env.LOCAL_CACHE === 'true') { console.log('Redis: Running LOCAL with LOCAL cache'); return undefined; } if (process.env.RUNNING_LOCAL === 'true') { console.log('Redis: Running LOCAL with REMOTE cache'); return { checkServerIdentity: () => { return undefined; }, }; } console.log('Redis: Making a tls connection'); return {}; }; const getNatMap = () => { var _a; if (process.env.RUNNING_LOCAL === 'true' && process.env.LOCAL_CACHE === 'false') { console.log('Redis: Getting NATMAP for remote connection'); const natMap = ((_a = process.env) === null || _a === void 0 ? void 0 : _a.NAT_MAP) ? JSON.parse(process.env.NAT_MAP) : false; console.log(`NATMAP: ${process.env.NAT_MAP}`); if (!natMap) { throw new Error('NATMAP not found. When running the openElasticacheTunnel script copy the output into your terminal'); } return natMap; } return {}; }; const getRedisClusterClient = (host, port, prefix, opts) => { if (host && port) { console.log(`Connecting to configured cluster redis:: ${host}:${port}`); const redisClient = new ioredis_1.default.Cluster([ { host, port: parseInt(port, 10), }, ], { keyPrefix: prefix, natMap: getNatMap(), clusterRetryStrategy: (times) => { const delay = Math.min(times * 1000, 10000); console.log(`Reconnecting to Redis in ${delay}ms... (retries: ${times})`); return delay; }, dnsLookup: (address, callback) => callback(null, address), redisOptions: { reconnectOnError: (err) => { const targetError = 'ECONNREFUSED'; if (err.message.includes(targetError)) { console.log(`Redis error: ${targetError}. Attempting to reconnect...`); return true; } return false; }, maxRetriesPerRequest: null, tls: getTlsConfiguration(), }, ...(opts !== null && opts !== void 0 ? opts : {}), }); redisClient.on('connect', () => { console.log('Connected to Redis.'); }); redisClient.on('error', (err) => { console.error('Redis error:', err); }); redisClient.on('reconnecting', () => { console.log('Reconnecting to Redis...'); }); return redisClient; } throw Error('Missing redis client configuration'); }; exports.getRedisClusterClient = getRedisClusterClient; const getRedisClient = (host, port, prefix, opts) => { if (host && port) { console.log(`Connecting to configured redis:: ${host}:${port}`); const redisClient = new ioredis_1.default({ host, port: parseInt(port, 10), keyPrefix: prefix, retryStrategy: (times) => { const delay = Math.min(times * 1000, 10000); console.log(`Reconnecting to Redis in ${delay}ms... (retries: ${times})`); return delay; }, reconnectOnError: (err) => { const targetError = 'ECONNREFUSED'; if (err.message.includes(targetError)) { console.log(`Redis error: ${targetError}. Attempting to reconnect...`); return true; } return false; }, maxRetriesPerRequest: null, tls: getTlsConfiguration(), ...(opts !== null && opts !== void 0 ? opts : {}), }); redisClient.on('connect', () => { console.log('Connected to Redis.'); }); redisClient.on('error', (err) => { console.error('Redis error:', err); }); redisClient.on('reconnecting', () => { console.log('Reconnecting to Redis...'); }); return redisClient; } throw Error('Missing redis client configuration'); }; exports.getRedisClient = getRedisClient; /** * Wrapper around the redis client. * * You can hover over the underlying redis client methods for explanations of the methods, but will also include links to DOCS for some important concepts below: * * zRange, zRangeByScore etc.: * - All of the "z" methods are methods that use sorted sets. * - Sorted sets are explained here : https://redis.io/docs/data-types/sorted-sets/ */ class RedisClient { constructor(_a) { var _b, _c; var { host = (_b = process.env.ELASTICACHE_HOST) !== null && _b !== void 0 ? _b : 'localhost', port = (_c = process.env.ELASTICACHE_PORT) !== null && _c !== void 0 ? _c : '6379', prefix = RedisClientPrefix.EXCHANGE, opts, cluster = process.env.LOCAL_CACHE === 'true' && process.env.RUNNING_LOCAL === 'true', } = _a; this.prefix = prefix; if (cluster) { this.client = (0, exports.getRedisClusterClient)(host, port, prefix, opts); return; } this.client = (0, exports.getRedisClient)(host, port, prefix, opts); } /** * Should avoid using this unless necessary. * @returns */ forceGetClient() { return this.client; } getPrefix() { return this.prefix; } get connected() { var _a; return ((_a = this === null || this === void 0 ? void 0 : this.client) === null || _a === void 0 ? void 0 : _a.status) === 'ready'; } async connect() { if (process.env.CI_TEST) return; if (this.connected) return; await (0, __1.sleep)(100); if (this.client.status === 'connecting') { return this.connectionPromise; } if (this.client.status === 'connect') { return this.connectionPromise; } if (this.client.status === 'ready') { return true; } this.client.on('error', (error) => console.log(`'Redis Client Error :: ', ${error === null || error === void 0 ? void 0 : error.message}`)); try { await this.client.connect(); } catch (e) { console.error(e); } return true; } disconnect() { if (process.env.CI_TEST) return; this.client.disconnect(); } assertConnected() { if (!this.connected) { throw 'Redis client not connected'; } } /** * IMPORTANT NOTE: non-expiring keys will not be cleared up by the eviction policy, so they must be cleared manually * @param key * @param value */ async set(key, value) { this.assertConnected(); this.client.set(key, JSON.stringify(value)); } async setRaw(key, value) { this.assertConnected(); this.client.set(key, value); } /** * IMPORTANT NOTE: non-expiring keys will not be cleared up by the eviction policy, so they must be cleared manually * @param key * @param value */ async mset(values) { this.assertConnected(); if (values.length === 0) return; const chunkedValues = __1.COMMON_UTILS.chunks(values, BULK_WRITE_CHUNK_SIZE); for (const valuesChunk of chunkedValues) { for (const [key, val] of valuesChunk) { await this.client.set(key, JSON.stringify(val)); } if (chunkedValues.length > 0) { await (0, __1.sleep)(CHUNK_SLEEP_TIME); } } } async setExpiring(key, value, expirySeconds) { this.assertConnected(); const resp = await this.client.setex(key, expirySeconds, JSON.stringify(value)); return resp; } async msetExpiring(values, expirySeconds) { if (!values) return; if (values.length === 0) return; this.assertConnected(); const chunkedValues = __1.COMMON_UTILS.chunks(values, BULK_WRITE_CHUNK_SIZE); for (const valuesChunk of chunkedValues) { for (const [key, val] of valuesChunk) { this.client.setex(key, expirySeconds, JSON.stringify(val)); } if (chunkedValues.length > 0) { await (0, __1.sleep)(CHUNK_SLEEP_TIME); } } } async expireKey(key, expirySeconds) { this.assertConnected(); const resp = await this.client.expire(key, expirySeconds); return resp; } async get(key) { this.assertConnected(); const value = await this.client.get(key); if (value) { return JSON.parse(value); } return undefined; } async getRaw(key) { this.assertConnected(); const resp = await this.client.get(key); return resp; } async mget(keys) { this.assertConnected(); if (keys.length === 0) return []; const chunkedValues = __1.COMMON_UTILS.chunks(keys, BULK_READ_CHUNK_SIZE); const rawValues = []; const chunkPromises = chunkedValues.map(async (valuesChunk, index) => { const chunkResults = await Promise.all(valuesChunk.map((key) => this.client.get(key))); if (index < chunkedValues.length - 1) { await (0, __1.sleep)(20); } return chunkResults; }); const allChunks = await Promise.all(chunkPromises); for (const chunk of allChunks) { rawValues.push(...chunk); } const parsedValues = rawValues.map((value) => { if (value) { return JSON.parse(value); } return undefined; }); return parsedValues; } async smembers(key) { const resp = await this.client.smembers(key); return resp; } async zRange(key, min, max) { const resp = await this.client.zrange(key, min, max); return resp; } async zRevRange(key, start, stop, withScores) { const resp = withScores ? this.client.zrevrange(key, start, stop, withScores) : this.client.zrevrange(key, start, stop); return resp; } async zRangeByScore(key, min, max) { const resp = await this.client.zrangebyscore(key, min, max); return resp; } async zRevRangeByScore(key, max, min) { const resp = await this.client.zrevrangebyscore(key, max, min); return resp; } async zRank(key, member) { const resp = await this.client.zrank(key, member); return resp; } async zRevRank(key, member) { const resp = await this.client.zrevrank(key, member); return resp; } async zRem(key, member) { const resp = await this.client.zrem(key, member); return resp; } async zRemRange(key, start, stop) { const resp = await this.client.zremrangebyrank(key, start, stop); return resp; } async zRemRangeByScore(key, start, stop) { const resp = await this.client.zremrangebyscore(key, start, stop); return resp; } async zCount(key, start, stop) { const resp = await this.client.zcount(key, start, stop); return resp; } async zCard(key) { const resp = await this.client.zcard(key); return resp; } async zAdd(key, ...scoreMembers) { const resp = await this.client.zadd(key, ...scoreMembers); return resp; } async lPush(key, ...elements) { const resp = await this.client.lpush(key, ...elements); return resp; } async rPush(key, ...elements) { const resp = await this.client.rpush(key, ...elements); return resp; } async lTrim(key, start, stop) { const resp = await this.client.ltrim(key, start, stop); return resp; } async lRem(key, count, element) { const resp = await this.client.lrem(key, count, element); return resp; } async lSet(key, index, element) { const resp = await this.client.lset(key, index, element); return resp; } async lRange(key, start, stop) { const resp = await this.client.lrange(key, start, stop); return resp; } async lLen(key) { const resp = await this.client.llen(key); return resp; } async lIndex(key, index) { const resp = await this.client.lindex(key, index); return resp; } async publish(key, value) { const resp = await this.client.publish(key, JSON.stringify(value)); return resp; } async subscribe(key) { const resp = await this.client.subscribe(key); return resp; } async unsubscribe(key) { const resp = await this.client.unsubscribe(key); return resp; } /** * Clears the entire cache of the current DB (not the other DBs in the redis instance) */ async flush() { this.assertConnected(); return this.client.flushdb(); } async delete(...keys) { this.assertConnected(); const chunkedValues = __1.COMMON_UTILS.chunks(keys, BULK_WRITE_CHUNK_SIZE); let count = 0; for (const valuesChunk of chunkedValues) { for (const key of valuesChunk) { await this.client.del(key); count++; } if (chunkedValues.length > 0) { await (0, __1.sleep)(20); } } return count; } } exports.RedisClient = RedisClient; __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "set", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "setRaw", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [Array]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "mset", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "setExpiring", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [Array, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "msetExpiring", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "expireKey", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "get", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "getRaw", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [Array]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "mget", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "smembers", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRange", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number, String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRevRange", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRangeByScore", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRevRangeByScore", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRank", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRevRank", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRem", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRemRange", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zRemRangeByScore", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zCount", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zCard", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "zAdd", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lPush", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "rPush", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lTrim", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lRem", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lSet", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lRange", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lLen", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String, Number]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "lIndex", null); __decorate([ isWrite(), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "publish", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], RedisClient.prototype, "flush", null); __decorate([ isRead, __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], RedisClient.prototype, "delete", null); //# sourceMappingURL=redisClient.js.map