UNPKG

@azteam/redis-async

Version:

N/A

256 lines (210 loc) 7.15 kB
import Redis from 'ioredis'; import {timeout} from '@azteam/util'; class RedisAsync { constructor(config) { this.connected = false; this.host = config.host; this.port = config.port; this.prefix = config.prefix; this.subscriptions = {}; this.connect(); } async waitConnection(n = 10) { for (let i = 0; !this.connected || i < n; i += 1) { await timeout(1000); } if (!this.connected) { throw new Error('Redis not connected'); } } parsePrefix(name) { if (this.prefix) { return `${this.prefix}:${name}`; } return name; } connect() { this._alert('connecting', 'Redis connecting...'); this.client = new Redis({ host: this.host, port: this.port, lazyConnect: true, retryStrategy: () => { this.connected = false; this._alert('connect', 'Redis disconnected'); return null; }, }); this.client.connect().catch((err) => { this._alert('error', `Redis Connection Error: ${err}`); }); this.client.on('connect', () => { this.connected = true; this._alert('connect', 'Redis connected'); }); this.client.on('close', () => { this.connected = false; this._alert('end', 'Redis end'); }); this.client.on('error', (err) => { this._alert('error', `Redis Error${err}`); }); } async get(key, defaultValue = null) { const prefixKey = this.parsePrefix(key); if (this.connected) { const data = await this.client.get(prefixKey); if (data) { return JSON.parse(data); } } else { this.connect(); return null; } return defaultValue; } async ttl(key) { const prefixKey = this.parsePrefix(key); return this.client.ttl(prefixKey); } async expire(key, timeSecond = 86400) { const prefixKey = this.parsePrefix(key); return await this.client.expire(prefixKey, timeSecond); } async set(key, data, timeSecond = 86400, count = 0) { const prefixKey = this.parsePrefix(key); if (this.connected) { await this.client.set(prefixKey, JSON.stringify(data)); if (timeSecond > 0) { await this.expire(key, timeSecond); } return true; } this.connect(); if (count < 5) { return this.set(key, data, timeSecond, count + 1); } return false; } async incr(key, timeSecond = 86400, count = 0) { const prefixKey = this.parsePrefix(key); if (this.connected) { const result = await this.client.incr(prefixKey); // Option B: Only set TTL if it's a new key (result is 1) if (result === 1 && timeSecond > 0) { await this.expire(key, timeSecond); } return result; } this.connect(); if (count < 5) { return this.incr(key, timeSecond, count + 1); } return false; } // async scan(pattern, cursor = 0, count = 1000) { // const scanAsync = promisify(this.client.scan).bind(this.client); // return await scanAsync(cursor, 'MATCH', pattern, 'COUNT', count); // } async getKeys(pattern, count = 0) { const regexKey = this.prefix ? `${this.prefix}:*${pattern}*` : `*${pattern}*`; if (this.connected) { const keys = await this.client.keys(regexKey); if (this.prefix) { const prefixPrefix = `${this.prefix}:`; return keys.map((k) => k.replace(prefixPrefix, '')); } return keys; } this.connect(); if (count < 5) { return this.getKeys(pattern, count + 1); } return []; } async remove(key, exact = true, count = 0) { const prefixKey = this.parsePrefix(key); if (this.connected) { if (exact) { await this.client.del(prefixKey); } else { const regexKey = `*${prefixKey}*`; const keys = await this.client.keys(regexKey); if (keys.length > 0) { return this.client.del(...keys); } } } else { this.connect(); if (count < 5) { return this.remove(key, exact, count + 1); } return false; } return false; } async publish(channel, data, count = 0) { const prefixChannel = this.parsePrefix(channel); if (this.connected) { return this.client.publish(prefixChannel, JSON.stringify(data)); } this.connect(); if (count < 5) { return this.publish(channel, data, count + 1); } return false; } async subscribe(channel, callback) { const prefixChannel = this.parsePrefix(channel); if (!this.subscriberClient) { this.subscriberClient = this.client.duplicate(); this.subscriberClient.connect().catch((err) => { this._alert('error', `Redis Subscriber Error: ${err}`); }); this.subscriberClient.on('message', (chan, message) => { let parsedMsg = message; try { parsedMsg = JSON.parse(message); } catch (e) { // ignore if not JSON } if (this.subscriptions[chan]) { this.subscriptions[chan].forEach((cb) => cb(parsedMsg)); } }); } if (!this.subscriptions[prefixChannel]) { this.subscriptions[prefixChannel] = []; await this.subscriberClient.subscribe(prefixChannel); } this.subscriptions[prefixChannel].push(callback); } async unsubscribe(channel, callback) { const prefixChannel = this.parsePrefix(channel); if (!this.subscriptions[prefixChannel]) { return; } if (callback) { this.subscriptions[prefixChannel] = this.subscriptions[prefixChannel].filter((cb) => cb !== callback); } else { this.subscriptions[prefixChannel] = []; } if (this.subscriptions[prefixChannel].length === 0) { delete this.subscriptions[prefixChannel]; if (this.subscriberClient) { await this.subscriberClient.unsubscribe(prefixChannel); } } } setAlertCallback(callback) { this.alertCallback = callback; } _alert(status, msg) { if (typeof this.alertCallback === 'function') { this.alertCallback(status, msg); } else { console.error(status, msg); } } } export default RedisAsync;