Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
memcache-client
Advanced tools
NodeJS memcached client with the most efficient ASCII protocol parser.
Primary developed to be used at @WalmartLabs to power the http://www.walmart.com eCommerce site.
Buffer
, string
, numeric
, and JSON
values$ npm i memcache-client --save
zstd.ts
is used by default, for that implementation to work, zstd
is required to be an executable program in the OSimport {
MemcacheClient,
MultiRetrievalResponse,
MultiCasRetrievalResponse,
StatsCommandResponse,
} from "memcache-client";
import assert from "node:assert";
const server = "localhost:11211";
// create a normal client
const client = new MemcacheClient({ server });
// Create a client that ignores NOT_STORED response (for McRouter AllAsync mode)
const mrClient = new MemcacheClient({ server, ignoreNotStored: true });
// You can specify maxConnections by using an object for server
// Default maxConnections is 1
const mClient = new MemcacheClient({ server: { server, maxConnections: 5 } });
// with callback
client.set("key", "data", (err, r) => {
assert.deepEqual(r, ["STORED"]);
});
client.get("key", (err, data) => {
assert.equal(data?.value, "data");
});
// with callback - use generic to provide type of data.value
client.get<string>("key", (err, data) => {
assert.equal(data?.value, "data"); // data?.value is string instead of unknown
});
// with promise
client.set("key", "data").then((r) => assert.deepEqual(r, ["STORED"]));
client.get("key").then((data) => assert.equal(data?.value, "data"));
// with promise - use generic to provide type of data.value
client.get<string>("key").then((data) => assert.equal(data?.value, "data"));
// concurrency using promise
Promise.all([client.set("key1", "data1"), client.set("key2", "data2")]).then((r) =>
assert.deepEqual(r, [["STORED"], ["STORED"]])
);
Promise.all([client.get("key1"), client.get("key2")]).then((r) => {
assert.equal(r[0].value, "data1");
assert.equal(r[1].value, "data2");
});
// get multiple keys
// NOTE: For being able to correctly type the result of getting multiple keys with a single call,
// use the helper type MultiRetrievalResponse or MultiCasRetrievalResponse
// depending of the executed function, and send the desire type
// use MultiRetrievalResponse for client.get
client.get<MultiRetrievalResponse<string>>(["key1", "key2"]).then((results) => {
assert.equal(results["key1"].value, "data1");
assert.equal(results["key2"].value, "data2");
});
// use MultiCasRetrievalResponse for client.gets
client.gets<MultiCasRetrievalResponse<string>>(["key1", "key2"]).then((results) => {
assert.equal(results["key1"].value, "data1");
assert.equal(results["key2"].value, "data2");
});
// gets and cas (check and set)
client.gets("key1").then((v) => client.cas("key1", "casData", { casUniq: v.casUniq }));
// enable compression (if data size >= 100 bytes)
const data = Buffer.alloc(500);
client.set("key", data, { compress: true }).then((r) => assert.deepEqual(r, ["STORED"]));
// fire and forget
client.set("key", data, { noreply: true });
// send any arbitrary command (\r\n will be appended automatically)
// NOTE: client.cmd can accept a generic same way as client.get and client.gets
// there is already a type for "stats" command
client.cmd<StatsCommandResponse>("stats").then((r) => {
console.log(r.STAT);
});
client.set("foo", "10", { noreply: true });
client.cmd<string>("incr foo 5").then((v) => assert.equal(+v, 15));
// you can also send arbitary command with noreply option (noreply will be appended automatically)
client.cmd("incr foo 5", { noreply: true });
// send any arbitrary data (remember \r\n)
client.send("set foo 0 0 5\r\nhello\r\n").then((r) => assert.deepEqual(r, ["STORED"]));
// disconnect from the memcached server(s)
client.shutdown();
All take an optional callback
. If it's not provided then all return a Promise
.
client.get<ReturnValueType>(key, [callback])
or client.get([key1, key2], [callback])
client.gets<ReturnValueType>(key, [callback])
or client.gets([key1, key2], [callback])
client.set(key, data, [options], [callback])
client.add(key, data, [options], [callback])
client.replace(key, data, [options], [callback])
client.append(key, data, [options], [callback])
client.prepend(key, data, [options], [callback])
client.cas(key, data, options, [callback])
client.delete(key, [options], [callback])
client.incr(key, value, [options], [callback])
client.decr(key, value, [options], [callback])
client.touch(key, exptime, [options], [callback])
client.version([callback])
For all store commands,
set
,add
,replace
,append
,prepend
, andcas
, the data can be aBuffer
,string
,number
, or aJSON
object.
The client constructor takes the following values in options
.
const options = {
server: { server: "host:port", maxConnections: 3 },
ignoreNotStored: true, // ignore NOT_STORED response
lifetime: 100, // TTL 100 seconds
cmdTimeout: 3000, // command timeout in milliseconds
connectTimeout: 8000, // connect to server timeout in ms
keepAlive: 120000, // keepalive initial delay in ms, or `false` to disable
noDelay: true, // whether to enable TCP_NODELAY on connections
compressor: require("custom-compressor"),
logger: require("./custom-logger"),
Promise,
tls: {}
};
const client = new MemcacheClient(options);
server
- required A string in host:port
format, or an object:{ server: "host:port", maxConnections: 3 }
Default
maxConnections
is1
ignoreNotStored
- optional If set to true, then will not treat NOT_STORED
reply from any store commands as error. Use this for Mcrouter AllAsyncRoute mode.lifetime
- optional Your cache TTL in seconds to use for all entries. DEFAULT: 60 seconds.noDelay
- optional Whether to enable TCP_NODELAY
on connections to decrease latency. DEFAULT: falsecmdTimeout
- optional Command timeout in milliseconds. DEFAULT: 5000 ms.
connectTimeout
- optional Custom self connect to server timeout in milliseconds. It's disabled if set to 0. DEFAULT: 0
connecting
set to true
keepAlive
- optional Initial delay (in milliseconds) between the last data packet received on a connection and when a keepalive probe should be sent, or false
to disable the SO_KEEPALIVE
socket option entirely. DEFAULT: 1 minute (60000 milliseconds)keepDangleSocket
- optional After connectTimeout
trigger, do not destroy the socket but keep listening for errors on it. DEFAULT: falsedangleSocketWaitTimeout
- optional How long to wait for errors on dangle socket before destroying it. DEFAULT: 5 minutes (30000 milliseconds)compressor
- optional a custom compressor for compressing the data. See data compression for more details.logger
- optional Custom logger like this:
module.exports = {
debug: (msg) => console.log(msg),
info: (msg) => console.log(msg),
warn: (msg) => console.warn(msg),
error: (msg) => console.error(msg)
};
Promise
- optional Internally this module will try to find bluebird
in your node_modules
and fallback to global.Promise
. You can set this option to force the Promise to use.tls
- optional If set, defines the TLS options to make the client connect to server in TLS modeconnectTimeout
Note that the connectTimeout
option is a custom timeout this client adds. It will preempt
the system's connect timeout, for which you typically get back a connect ETIMEDOUT
error.
Since from NodeJS there's no way to change the system's connect timeout, which is usually
fairly long, this option allows you to set a shorter timeout. When it triggers, the client
will shutdown the connection and destroys the socket, and rejects with an error. The error's
message will be "connect timeout"
and has the field connecting
set to true.
If you want to let the system connect timeout to take place, then set this option to 0 to completely disable it, or set it to a high value like 10 minutes in milliseconds (60000).
If you set a small custom connectTimeout
and do not want to destroy the socket after it
triggers, then you will end up with a dangling socket.
To enable keeping the dangling socket, set the option keepDangleSocket
to true
.
The client will automatically add a new error handler for the socket in case the system's
ETIMEDOUT
eventually comes back. The client also sets a timeout to eventually destroy the
socket in case the system never comes back with anything.
To control the dangling wait timeout, use the option dangleSocketWaitTimeout
. It's default
to 5 minutes.
The client will emit the event dangle-wait
with the following data:
{ type: "wait", socket }
{ type: "timeout" }
{ type: "error", err }
Generally it's better to just destroy the socket instead of leaving it dangling.
If you have multiple redundant servers, you can pass them to the client with the server
option:
{
server: {
servers: [
{
server: "name1.domain.com:11211",
maxConnections: 3
},
{
server: "name2.domain.com:11211",
maxConnections: 3
}
],
config: {
retryFailedServerInterval: 1000, // milliseconds - how often to check failed servers
failedServerOutTime: 30000, // (ms) how long a failed server should be out before retrying it
keepLastServer: false
}
}
}
You can also pass in server.config
with the following options:
retryFailedServerInterval
- (ms) how often to check failed servers. Default 10000 ms (10 secs)failedServerOutTime
- (ms) how long a failed server should be out before retrying it. Default 60000 ms (1 min).keepLastServer
- (boolean) Keep at least one server even if it failed connection. Default true
.If the memcached server is configured with TLS, you can make the client connect to it via specifying the tls
ConnectionOptions.
For production environments, the server should be using a TLS certificate that is signed by a trusted public CA. In this case you can simply do the following to create the client:
const client = new MemcacheClient({server: "{server_hostname}:11211", tls: {}});
client.set("key", "value");
If the server requires client certificate authentication, you can do the following:
import Fs from "fs";
const client = new MemcacheClient({server: "{server_hostname}:11211", tls: {
key: Fs.readFileSync("client-key.pem"),
cert: Fs.readFileSync("client-cert.pem"),
}});
client.set("key", "value");
If you are running the server with a self-signed certificate (i.e. for local developments), you can create the client by specifying the CA certificate and disable hostname verification as follows:
import Fs from "fs";
const client = new MemcacheClient({server: "localhost:11211", tls: {
ca: Fs.readFileSync("ca-cert.pem"),
checkServerIdentity: () => {return undefined;}
}});
client.set("key", "value");
The client supports automatic compression/decompression of the data you set. It's turned off by default.
To enable this, you need to:
compress
flag when calling the store commandsBy default, the client is modeled to use node-zstd version 2's APIs, specifically, it requires a compressor with these two methods:
compressSync(value)
decompressSync(value)
Both must take and return Buffer
data.
If you just add node-zstd version 2 to your dependencies, then you can start setting the compress
flag when calling the store commands to enable compression.
If you want to use another major version of node-zstd or another compressor that doesn't offer the two APIs expected above, then you need to create a wrapper compressor and pass it to the client constructor.
noreply
Almost all commands take a noreply
field for options
, which if set to true, then the command is fire & forget for the memcached server.
Obviously this doesn't apply to commands like get
and gets
, which exist to retrieve from the server.
lifetime
and compress
For all store commands, set
, add
, replace
, append
, prepend
, and cas
, they take:
lifetime
field that specify the TTL time in seconds for the entry. If this is not set, then will try to use client options.lifetime
or 60 seconds.compress
field, which if set to true, will cause any data with size >= 100 bytes to be compressed.
casUniq
For the cas
command, options
must contain a casUniq
value that you received from an gets
command you called earlier.
client.send<ReturnValueType>(data, [options], [callback])
client.xsend(data, [options])
client.cmd<ReturnValueType>(data, [options], [callback])
client.store(cmd, key, value, [optons], [callback])
client.retrieve<ReturnValueType>(cmd, key, [options], [callback])
client.xretrieve(cmd, key)
client.shutdown()
Apache-2.0 © Joel Chen
FAQs
NodeJS memcached client
The npm package memcache-client receives a total of 138 weekly downloads. As such, memcache-client popularity was classified as not popular.
We found that memcache-client demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.