@drift-labs/common
Version:
Common functions for Drift
647 lines • 23.1 kB
JavaScript
"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