@keyv/memcache

Memcache storage adapter for Keyv using the memcache client

build codecov GitHub license npm

Features

  • Built on the memcache package with a fully Promise-based API and TypeScript types
  • TTL support (millisecond input, converted to seconds for memcache)
  • Namespace support for key isolation across multiple Keyv instances
  • Multiple nodes with consistent hashing (KetamaHash)
  • SASL authentication support
  • AWS ElastiCache Auto Discovery support
  • Retry and backoff support for failed commands
  • createKeyv helper for quick setup

Table of Contents

Install

npm install --save @keyv/memcache

Keyv Compression is not Supported

This package does not support compression. If you need compression, please use the @keyv/redis or another service package instead.

Expiration Granularity

Expiry is enforced server-side by Memcached's exptime, which is second-granular. TTLs are accepted in milliseconds and rounded up to whole seconds, so Memcached itself can return a value for up to ~1 second past a sub-second (or just-elapsed) deadline before it evicts it. Keyv filters expired reads at its own layer by default (checkExpired defaults to true), using the absolute expires stored with the value, so reads are millisecond-precise out of the box even though Memcached's eviction is coarser. If you prefer to trust Memcached's exptime alone (and skip the extra decode on reads), opt out:

const keyv = new Keyv({ store: new KeyvMemcache('localhost:11211'), checkExpired: false });

Quick Start with createKeyv

The createKeyv helper creates a Keyv instance with a Memcache store in a single call:

import { createKeyv } from '@keyv/memcache';

const keyv = createKeyv('localhost:11211');
keyv.on('error', handleConnectionError);

// set a value
await keyv.set('foo', 'bar', 6000);

// get a value
const value = await keyv.get('foo');

// delete a value
await keyv.delete('foo');

You can also pass an options object:

import { createKeyv } from '@keyv/memcache';

const keyv = createKeyv({ nodes: ['localhost:11211'] });

Usage

import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

const memcache = new KeyvMemcache('localhost:11211');
const keyv = new Keyv({ store: memcache });
keyv.on('error', handleConnectionError);

//set
await keyv.set("foo","bar", 6000) //Expiring time is optional

//get
const obj = await keyv.get("foo");

//delete
await keyv.delete("foo");

//clear
await keyv.clear();

//disconnect
await memcache.disconnect();

Usage with Namespaces

The namespace lives on the store adapter, which prefixes every key with namespace:. Because the namespace is held on the adapter, give each namespace its own KeyvMemcache instance rather than sharing a single store between multiple Keyv instances:

import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

const keyv1 = new Keyv({ store: new KeyvMemcache('localhost:11211'), namespace: "namespace1" });
const keyv2 = new Keyv({ store: new KeyvMemcache('localhost:11211'), namespace: "namespace2" });

//set
await keyv1.set("foo","bar1", 6000) //Expiring time is optional
await keyv2.set("foo","bar2", 6000) //Expiring time is optional

//get
const obj1 = await keyv1.get("foo"); //will return bar1
const obj2 = await keyv2.get("foo"); //will return bar2

You can also set the namespace directly on the store, which is handy when you only need a single namespace:

const memcache = new KeyvMemcache('localhost:11211');
memcache.namespace = "namespace1";
const keyv = new Keyv({ store: memcache });

Note: clear() always flushes the entire Memcached server because Memcached cannot enumerate keys, so it is not scoped to a namespace.

Options

The KeyvMemcacheOptions type extends MemcacheOptions from the memcache package with a namespace property:

Option Type Default Description
namespace string undefined Key prefix for namespace isolation
nodes (string | MemcacheNode)[] ['localhost:11211'] Array of memcache server URIs or MemcacheNode instances
timeout number 5000 Operation timeout in milliseconds
keepAlive boolean true Keep the connection alive
keepAliveDelay number 1000 Keep-alive delay in milliseconds
retries number 0 Number of retry attempts for failed commands (0 to disable)
retryDelay number 100 Base delay in milliseconds between retries
retryBackoff function fixed delay Function to calculate backoff delay between retries
retryOnlyIdempotent boolean true Only retry idempotent commands to prevent double-execution
sasl { username, password, mechanism? } undefined SASL PLAIN authentication credentials
autoDiscover AutoDiscoverOptions undefined AWS ElastiCache Auto Discovery configuration
import KeyvMemcache from '@keyv/memcache';

const memcache = new KeyvMemcache({
  nodes: ['server1:11211', 'server2:11211'],
  timeout: 3000,
  retries: 2,
  retryDelay: 200,
});

Multiple Nodes

The adapter supports connecting to multiple memcache servers. Keys are distributed across nodes using consistent hashing (KetamaHash):

import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

const memcache = new KeyvMemcache({
  nodes: ['server1:11211', 'server2:11211', 'server3:11211'],
});
const keyv = new Keyv({ store: memcache });

Node URIs support multiple formats:

  • Simple: localhost:11211
  • With protocol: memcache://localhost:11211
  • IPv6: [::1]:11211

SASL Authentication

To connect to a memcache server that requires SASL authentication:

import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

const memcache = new KeyvMemcache({
  nodes: ['localhost:11211'],
  sasl: {
    username: 'myuser',
    password: 'mypassword',
  },
});
const keyv = new Keyv({ store: memcache });

AWS ElastiCache Auto Discovery

When using AWS ElastiCache, you can enable auto discovery to automatically detect cluster topology changes:

import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

const memcache = new KeyvMemcache({
  nodes: ['my-cluster.cfg.use1.cache.amazonaws.com:11211'],
  autoDiscover: {
    enabled: true,
    pollingInterval: 60000, // poll every 60 seconds (default)
  },
});
const keyv = new Keyv({ store: memcache });
Option Type Default Description
enabled boolean — Enable auto discovery
pollingInterval number 60000 How often to poll for topology changes (ms)
configEndpoint string first node The .cfg endpoint for discovery
useLegacyCommand boolean false Use legacy command for engine versions < 1.4.14

API

constructor(uri?, options?)

Creates a new KeyvMemcache instance.

  • uri — A memcache server URI string (e.g., 'localhost:11211') or a KeyvMemcacheOptions object. Defaults to 'localhost:11211' if not provided.
  • options — Optional KeyvMemcacheOptions object. When both uri and options are objects, they are merged together.

The namespace property is extracted from the resolved options and used for key prefixing. All remaining options are passed directly to the underlying Memcache client.

import KeyvMemcache from '@keyv/memcache';

// Using a URI string
const memcache = new KeyvMemcache('localhost:11211');

// Using an options object
const memcache2 = new KeyvMemcache({ nodes: ['localhost:11211'], timeout: 3000 });

// Using multiple nodes
const memcache3 = new KeyvMemcache({
  nodes: ['server1:11211', 'server2:11211', 'server3:11211'],
  namespace: 'myapp',
});

// Using a URI string with additional options
const memcache4 = new KeyvMemcache('localhost:11211', { namespace: 'myapp' });

.get(key)

Retrieves a value from the memcache server. Returns the stored value, or undefined if the key does not exist or has expired.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('foo', 'bar');
const result = await memcache.get('foo'); // 'bar'

.getMany(keys)

Retrieves multiple values from the memcache server. Returns an array of values corresponding to each key, with undefined for any key that does not exist.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('key1', 'value1');
await memcache.set('key2', 'value2');
const results = await memcache.getMany(['key1', 'key2', 'key3']); // ['value1', 'value2', undefined]

.set(key, value, ttl?)

Stores a value in the memcache server. The optional ttl parameter is in milliseconds and is converted to seconds internally.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('foo', 'bar'); // no expiration
await memcache.set('foo', 'bar', 5000); // expires in 5 seconds

.setMany(entries)

Stores multiple values in the memcache server. Each entry is a KeyvEntry object ({ key: string, value: Value, ttl?: number }), where Value is inferred from the entries provided. Returns a boolean[] indicating whether each entry was set successfully.

const memcache = new KeyvMemcache('localhost:11211');
const results = await memcache.setMany([
  { key: 'key1', value: 'value1' },
  { key: 'key2', value: 'value2', ttl: 5000 },
]); // [true, true]

.delete(key)

Deletes a key from the memcache server. Returns true if the key was deleted.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('foo', 'bar');
const deleted = await memcache.delete('foo'); // true

.deleteMany(keys)

Deletes multiple keys from the memcache server. Returns a boolean[] indicating whether each key was deleted.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('key1', 'value1');
await memcache.set('key2', 'value2');
const results = await memcache.deleteMany(['key1', 'key2']); // [true, true]

.clear()

Flushes all data from the memcache server. Note: this clears the entire server, not just keys within the current namespace.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.clear();

.has(key)

Checks whether a key exists in the memcache server. Returns false on any error.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('foo', 'bar');
const exists = await memcache.has('foo'); // true
const missing = await memcache.has('baz'); // false

.hasMany(keys)

Checks whether multiple keys exist in the memcache server. Returns an array of booleans corresponding to each key.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.set('key1', 'value1');
await memcache.set('key2', 'value2');
const results = await memcache.hasMany(['key1', 'key2', 'key3']); // [true, true, false]

.disconnect()

Gracefully disconnects from the memcache server.

const memcache = new KeyvMemcache('localhost:11211');
await memcache.disconnect();

.formatKey(key)

Formats a key by prepending the namespace if one is set. If no namespace is set, the key is returned as-is.

const memcache = new KeyvMemcache('localhost:11211');
memcache.formatKey('foo'); // 'foo'

memcache.namespace = 'myapp';
memcache.formatKey('foo'); // 'myapp:foo'

Properties

The following public properties are available on a KeyvMemcache instance:

Property Type Description
client Memcache The underlying memcache client instance for advanced use.
namespace string | undefined The namespace used to prefix keys. Can be read and set directly.
nodes (string | MemcacheNode)[] The configured memcache nodes (read-only).
timeout number | undefined The configured operation timeout in milliseconds (read-only).
keepAlive boolean | undefined The configured keep-alive setting (read-only).
retries number | undefined The configured number of retry attempts (read-only).
retryDelay number | undefined The configured base retry delay in milliseconds (read-only).

Works with Memcached and Google Cloud

Using Memcached

  1. Install Memcached and start an instance
import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

//set the server to the correct address and port
const memcache = new KeyvMemcache("localhost:11211");
const keyv = new Keyv({ store: memcache});

Using Google Cloud

  1. Go to https://cloud.google.com/ and sign up.
  2. Go to the memcached configuration page in the google cloud console by navigating to Memorystore > Memcached.
  3. On the memcached page (Eg. https://console.cloud.google.com/memorystore/memcached/instances?project=example), Click Create Instance
  4. Fill in all mandatory fields as needed. You will need to set up a private service connection.
  5. To set up a private service connection, click the Set Up Connection button.
  6. Once required fields are complete, click the Create button to create the instance.
  7. Google provides further documentation for connecting to and managing your Memcached instance here.
import Keyv from 'keyv';
import KeyvMemcache from '@keyv/memcache';

const memcache = new KeyvMemcache("insert the internal google memcached discovery endpoint");
const keyv = new Keyv({ store: memcache});

Breaking Changes from v2 to v6

Underlying Client Changed from memjs to memcache

The underlying memcache client has been replaced from memjs to memcache. This brings a fully Promise-based API, built-in TypeScript types, and removes the need for the buffer dependency.

client Property Type Changed

The client property on KeyvMemcache is now an instance of Memcache from the memcache package instead of memjs.Client. If you were accessing client directly, you will need to update your code to use the new API.

KeyvMemcacheOptions Type Changed

The options type no longer extends memjs.ClientOptions. It now extends MemcacheOptions from the memcache package. Options such as logger, username, password passed directly are no longer supported. Use the memcache package options format instead:

// Before (v2)
const memcache = new KeyvMemcache('user:pass@localhost:11211', { logger: { log: console.log } });

// After (v6)
const memcache = new KeyvMemcache('localhost:11211', { sasl: { username: 'user', password: 'pass' } });

disconnect() Method Added

A new disconnect() method is available to gracefully close the connection to the memcache server:

await memcache.disconnect();

buffer Dependency Removed

The buffer polyfill dependency has been removed. Values are now handled as strings instead of Buffers.

License

MIT © Jared Wray