UNPKG

redis-smq-common

Version:

Provides essential components and utilities shared across RedisSMQ packages.

217 lines 7.72 kB
import fs from 'fs'; import { resolve } from 'path'; import { async } from '../../async/index.js'; import { env } from '../../env/index.js'; import { CallbackEmptyReplyError, PanicError } from '../../errors/index.js'; import { EventEmitter } from '../../event/index.js'; import { UnknownRedisServerVersionError, UnsupportedRedisServerVersionError, } from '../errors/index.js'; const dir = env.getCurrentDir(); export var ELuaScriptName; (function (ELuaScriptName) { ELuaScriptName["LPOPRPUSH"] = "LPOPRPUSH"; ELuaScriptName["ZPOPRPUSH"] = "ZPOPRPUSH"; ELuaScriptName["ZPOPLPUSH"] = "ZPOPLPUSH"; })(ELuaScriptName || (ELuaScriptName = {})); const luaScriptMap = { [ELuaScriptName.LPOPRPUSH]: resolve(dir, '../lua-scripts/lpoprpush.lua'), [ELuaScriptName.ZPOPRPUSH]: resolve(dir, '../lua-scripts/zpoprpush.lua'), [ELuaScriptName.ZPOPLPUSH]: resolve(dir, '../lua-scripts/zpoplpush.lua'), }; const minimalSupportedVersion = [4, 0, 0]; export class RedisClientAbstract extends EventEmitter { static scripts = {}; static redisServerVersion = null; connectionClosed = true; init() { async.series([ (cb) => this.validateRedisServerSupport(cb), (cb) => this.loadBuiltInScriptFiles(cb), ], (err) => { if (err) this.emit('error', err); else this.emit('ready'); }); } validateRedisVersion(major, feature = 0, minor = 0) { if (!RedisClientAbstract.redisServerVersion) { this.emit('error', new UnknownRedisServerVersionError()); return false; } return (RedisClientAbstract.redisServerVersion[0] > major || (RedisClientAbstract.redisServerVersion[0] === major && RedisClientAbstract.redisServerVersion[1] >= feature && RedisClientAbstract.redisServerVersion[2] >= minor)); } validateRedisServerSupport(cb) { const validate = (cb) => { const [major, feature, minor] = minimalSupportedVersion; if (!this.validateRedisVersion(major, feature, minor)) cb(new UnsupportedRedisServerVersionError()); else cb(); }; if (!RedisClientAbstract.redisServerVersion) { this.updateServerVersion((err) => { if (err) cb(err); else validate(cb); }); } else validate(cb); } sscanAll(key, options, cb) { const result = new Set(); const iterate = (cursor) => { this.sscan(key, cursor, options, (err, reply) => { if (err) cb(err); else if (!reply) cb(new CallbackEmptyReplyError()); else { reply.items.forEach((i) => result.add(i)); if (reply.cursor === '0') cb(null, [...result]); else iterate(reply.cursor); } }); }; iterate('0'); } hscanAll(key, options, cb) { const result = {}; const iterate = (cursor) => { this.hscan(key, cursor, options, (err, reply) => { if (err) cb(err); else if (!reply) cb(new CallbackEmptyReplyError()); else { Object.assign(result, reply.result); if (reply.cursor === '0') cb(null, result); else iterate(reply.cursor); } }); }; iterate('0'); } zpoprpush(source, destination, cb) { this.runScript(ELuaScriptName.ZPOPRPUSH, [source, destination], [], (err, res) => { if (err) cb(err); else cb(null, typeof res === 'string' ? res : null); }); } zpoplpush(source, destination, cb) { this.runScript(ELuaScriptName.ZPOPLPUSH, [source, destination], [], (err, res) => { if (err) cb(err); else cb(null, typeof res === 'string' ? res : null); }); } lpoprpush(source, destination, cb) { if (this.validateRedisVersion(6, 2)) { this.lmove(source, destination, 'LEFT', 'RIGHT', cb); } else { this.runScript(ELuaScriptName.LPOPRPUSH, [source, destination], [], (err, res) => { if (err) cb(err); else cb(null, typeof res === 'string' ? res : null); }); } } updateServerVersion(cb) { if (!RedisClientAbstract.redisServerVersion) { this.getInfo((err, res) => { if (err) cb(err); else if (!res) cb(new CallbackEmptyReplyError()); else { RedisClientAbstract.redisServerVersion = res .split('\r\n')[1] .split(':')[1] .split('.') .map((i) => Number(i)); cb(); } }); } else cb(); } loadBuiltInScriptFiles(cb) { this.loadScriptFiles(luaScriptMap, (err) => cb(err)); } loadScriptFiles(scriptMap, cb) { const scriptsToLoad = {}; const loadedScripts = {}; for (const name in scriptMap) { const sha = RedisClientAbstract.scripts[name]; if (sha) { loadedScripts[name] = sha; } else { scriptsToLoad[name] = scriptMap[name]; } } if (Object.keys(scriptsToLoad).length === 0) { return cb(null, loadedScripts); } async.eachIn(scriptsToLoad, (scripts, name, taskCallback) => { const scriptsArr = Array.isArray(scripts) ? scripts : [scripts]; async.map(scriptsArr, (file, mapCallback) => fs.readFile(file, 'utf8', mapCallback), 10, (err, contents) => { if (err) return taskCallback(err); if (!contents) return cb(new CallbackEmptyReplyError()); const scriptContent = contents.join('\n'); this.loadScript(scriptContent, (err, sha) => { if (err) return taskCallback(err); if (!sha) return taskCallback(new CallbackEmptyReplyError()); loadedScripts[name] = sha; RedisClientAbstract.scripts[name] = sha; taskCallback(); }); }); }, (err) => { if (err) return cb(err); cb(null, loadedScripts); }); } getScriptId(name) { const id = RedisClientAbstract.scripts[name]; if (!id) { return new PanicError({ message: `ID of script [${name}] is missing`, }); } return id; } runScript(scriptName, keys, args, cb) { const sha = this.getScriptId(scriptName); if (sha instanceof Error) cb(sha); else { this.evalsha(sha, [keys.length, ...keys, ...args], (err, res) => { if (err) cb(err); else cb(null, res); }); } } } //# sourceMappingURL=redis-client-abstract.js.map