UNPKG

memcache-client

Version:
242 lines 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MemcacheClient = void 0; const tslib_1 = require("tslib"); const assert_1 = tslib_1.__importDefault(require("assert")); const optional_require_1 = require("optional-require"); const Promise = (0, optional_require_1.optionalRequire)("bluebird", { default: global.Promise, }); const Zstd = (0, optional_require_1.optionalRequire)("zstd.ts"); const nodeify_1 = tslib_1.__importDefault(require("./nodeify")); const value_packer_1 = tslib_1.__importDefault(require("./value-packer")); const null_logger_1 = tslib_1.__importDefault(require("./null-logger")); const defaults_1 = tslib_1.__importDefault(require("./defaults")); const redundant_servers_1 = tslib_1.__importDefault(require("./redundant-servers")); const events_1 = tslib_1.__importDefault(require("events")); class MemcacheClient extends events_1.default { constructor(options) { super(); (0, assert_1.default)(options.server, "Must provide options.server"); this.options = options; this.socketID = 1; this._packer = new value_packer_1.default(options.compressor || Zstd); this._logger = options.logger !== undefined ? options.logger : null_logger_1.default; this.options.cmdTimeout = options.cmdTimeout || defaults_1.default.CMD_TIMEOUT_MS; this._servers = new redundant_servers_1.default(this, options.server); this.Promise = options.Promise || Promise; } shutdown() { this._servers.shutdown(); } // // Allows you to send any arbitrary data you want to the server. // You are responsible for making sure the data contains properly // formed memcached ASCII protocol commands and data. // Any responses from the server will be parsed by the client // and returned as best as it could. // // If data is a function, then it will be called with socket which you can // use to write any data you want else it will be passed to socket.write. // // DO NOT send multiple commands in a single call. Bad things will happen. // // Set options.noreply if you want to fire and forget. Note that this // doesn't apply if you send a command like get/gets/stats, which don't // have the noreply option. // send(data, options, callback) { if (typeof options === "function") { callback = options; options = {}; } else if (options === undefined) { options = {}; } return this._callbackSend(data, options, callback); } // the promise only version of send xsend(data, options) { return this._servers.doCmd((c) => this._send(c, data, options || {})); } // a convenient method to send a single line as a command to the server // with \r\n appended for you automatically cmd(data, options, callback) { return this.send((socket) => { let line = data; if (options === null || options === void 0 ? void 0 : options.noreply) { line += " noreply"; } socket === null || socket === void 0 ? void 0 : socket.write(`${line}\r\n`); }, options, callback); } // "set" means "store this data". set(key, value, options, callback) { options = options || {}; if (options.ignoreNotStored === undefined) { options.ignoreNotStored = this.options.ignoreNotStored; } // it's tricky to threat optional object as callback return this.store("set", key, value, options, callback); } // "add" means "store this data, but only if the server *doesn't* already // hold data for this key". add(key, value, options, callback) { return this.store("add", key, value, options, callback); } // "replace" means "store this data, but only if the server *does* // already hold data for this key". replace(key, value, options, callback) { return this.store("replace", key, value, options, callback); } // "append" means "add this data to an existing key after existing data". append(key, value, options, callback) { return this.store("append", key, value, options, callback); } // "prepend" means "add this data to an existing key before existing data". prepend(key, value, options, callback) { return this.store("prepend", key, value, options, callback); } // "cas" is a check and set operation which means "store this data but // only if no one else has updated since I last fetched it." // // cas unique must be passed in options.casUniq // cas(key, value, options, callback) { (0, assert_1.default)(options === null || options === void 0 ? void 0 : options.casUniq, "Must provide options.casUniq for cas store command"); return this.store("cas", key, value, options, callback); } // delete key, fire & forget with options.noreply delete(key, options, callback) { return this.cmd(`delete ${key}`, options, callback); } // incr key by value, fire & forget with options.noreply incr(key, value, options, callback) { return this.cmd(`incr ${key} ${value}`, options, callback); } // decrease key by value, fire & forget with options.noreply decr(key, value, options, callback) { return this.cmd(`decr ${key} ${value}`, options, callback); } // touch key with exp time, fire & forget with options.noreply touch(key, exptime, options, callback) { return this.cmd(`touch ${key} ${exptime}`, options, callback); } // get version of server version(callback) { return this.cmd(`version`, {}, callback); } // a generic API for issuing one of the store commands store(cmd, key, value, options, callback) { if (typeof options === "function") { callback = options; options = {}; } else if (options === undefined) { options = {}; } const lifetime = options.lifetime !== undefined ? options.lifetime : this.options.lifetime || 60; const casUniq = options.casUniq ? ` ${options.casUniq}` : ""; const noreply = options.noreply ? ` noreply` : ""; // // store commands // <command name> <key> <flags> <exptime> <bytes> [noreply]\r\n // const _data = (socket) => { const packed = this._packer.pack(value, (options === null || options === void 0 ? void 0 : options.compress) === true); const bytes = Buffer.byteLength(packed.data); socket === null || socket === void 0 ? void 0 : socket.write(Buffer.concat([ Buffer.from(`${cmd} ${key} ${packed.flag} ${lifetime} ${bytes}${casUniq}${noreply}\r\n`), Buffer.isBuffer(packed.data) ? packed.data : Buffer.from(packed.data), Buffer.from("\r\n"), ])); }; return this._callbackSend(_data, options, callback); } get(key, options, callback) { return this.retrieve("get", key, options, callback); } gets(key, options, callback) { return this.retrieve("gets", key, options, callback); } // A generic API for issuing get or gets command retrieve(cmd, key, options, callback) { if (typeof options === "function") { callback = options; options = {}; } return (0, nodeify_1.default)(this.xretrieve(cmd, key, options), callback); } // the promise only version of retrieve xretrieve(cmd, key, options) { // // get <key>*\r\n // gets <key>*\r\n // // - <key>* means one or more key strings separated by whitespace. // return Array.isArray(key) ? this.xsend(`${cmd} ${key.join(" ")}\r\n`, options) : this.xsend(`${cmd} ${key}\r\n`, options).then((r) => r[key]); } // // Internal methods // _send(conn, data, options) { var _a; try { // send data to connection if (typeof data === "function") { data(conn.socket); } else { (_a = conn.socket) === null || _a === void 0 ? void 0 : _a.write(data); } // if no reply wanted then just return if (options.noreply) { return this.Promise.resolve(); } // queue up context to listen for reply return new this.Promise((resolve, reject) => { const context = { error: null, results: {}, callback: (err, result) => { if (err) { if (options.ignoreNotStored === true && err.message === "NOT_STORED") { return resolve("ignore NOT_STORED"); } return reject(err); } if (result) { return resolve(result); } else if (context.error) { return reject(context.error); } else { return resolve(context.results); } }, }; conn.queueCommand(context); }); } catch (err) { return this.Promise.reject(err); } } // internal send that expects all params passed (even if they are undefined) _callbackSend(data, options, callback) { return (0, nodeify_1.default)(this.xsend(data, options), callback); } _unpackValue(result) { // // VALUE <key> <flags> <bytes> [<cas unique>]\r\n // result.flag = +result.cmdTokens[2]; return this._packer.unpack(result); } } exports.MemcacheClient = MemcacheClient; //# sourceMappingURL=client.js.map