bentocache
Version:
Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers
209 lines (206 loc) • 6.21 kB
JavaScript
import {
CacheBusMessageType
} from "../../chunk-HVHH5I26.js";
import {
BaseDriver
} from "../../chunk-BO75WXSS.js";
// src/drivers/redis.ts
import { Redis as IoRedis } from "ioredis";
import { RedisTransport } from "@boringnode/bus/transports/redis";
// src/bus/encoders/binary_encoder.ts
var BinaryEncoder = class {
#busIdLength;
/**
* We assume the bus ID is a string of length 24 by default.
* Because this is the default length of a cuid
*/
constructor(busIdLength = 24) {
this.#busIdLength = busIdLength;
}
busMessageTypeToNum(type) {
if (type === CacheBusMessageType.Set) return 1;
if (type === CacheBusMessageType.Clear) return 2;
if (type === CacheBusMessageType.Delete) return 3;
if (type === CacheBusMessageType.Expire) return 4;
throw new Error(`Unknown message type: ${type}`);
}
numToBusMessageType(num) {
if (num === 1) return CacheBusMessageType.Set;
if (num === 2) return CacheBusMessageType.Clear;
if (num === 3) return CacheBusMessageType.Delete;
if (num === 4) return CacheBusMessageType.Expire;
throw new Error(`Unknown message type: ${num}`);
}
/**
* Encode the given message into a Buffer
*/
encode(message) {
const payload = message.payload;
const totalKeysLength = payload.keys.reduce(
(sum, key) => sum + 4 + Buffer.byteLength(key, "utf8"),
0
);
const namespaceKeyLength = payload.namespace ? Buffer.byteLength(payload.namespace, "utf8") : 0;
const totalLength = this.#busIdLength + 1 + 4 + namespaceKeyLength + totalKeysLength;
const buffer = Buffer.alloc(totalLength);
buffer.write(message.busId, 0, this.#busIdLength, "utf8");
buffer.writeUInt8(this.busMessageTypeToNum(payload.type), this.#busIdLength);
let offset = this.#busIdLength + 1;
buffer.writeUInt32BE(namespaceKeyLength, offset);
offset += 4;
if (payload.namespace) {
buffer.write(payload.namespace, offset, namespaceKeyLength, "utf8");
offset += namespaceKeyLength;
}
for (const key of payload.keys) {
const keyLength = Buffer.byteLength(key, "utf8");
buffer.writeUInt32BE(keyLength, offset);
offset += 4;
buffer.write(key, offset, keyLength, "utf8");
offset += keyLength;
}
return buffer;
}
/**
* Decode the given Buffer into a CacheBusMessage
*/
decode(data) {
let offset = 0;
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary");
const busId = buffer.toString("utf8", offset, this.#busIdLength);
offset += this.#busIdLength;
const typeValue = buffer.readUInt8(offset++);
const type = this.numToBusMessageType(typeValue);
const namespaceKeyLength = buffer.readUInt32BE(offset);
offset += 4;
const namespace = namespaceKeyLength ? buffer.toString("utf8", offset, offset + namespaceKeyLength) : "";
offset += namespaceKeyLength;
const keys = [];
while (offset < buffer.length) {
const keyLength = buffer.readUInt32BE(offset);
offset += 4;
const key = buffer.toString("utf8", offset, offset + keyLength);
offset += keyLength;
keys.push(key);
}
return { busId, payload: { keys, type, namespace } };
}
};
// src/drivers/redis.ts
function redisDriver(options) {
return { options, factory: (config) => new RedisDriver(config) };
}
function redisBusDriver(options) {
return {
options,
factory: () => {
return new RedisTransport(
{ ...options.connection, useMessageBuffer: true },
new BinaryEncoder()
);
}
};
}
var RedisDriver = class _RedisDriver extends BaseDriver {
type = "l2";
#connection;
constructor(config) {
super(config);
if (config.connection instanceof IoRedis) {
this.#connection = config.connection;
return;
}
this.#connection = new IoRedis(config.connection);
}
getConnection() {
return this.#connection;
}
/**
* Returns a new instance of the driver namespaced
*/
namespace(namespace) {
return new _RedisDriver({
...this.config,
connection: this.#connection,
prefix: this.createNamespacePrefix(namespace)
});
}
/**
* Get a value from the cache
*/
async get(key) {
const result = await this.#connection.get(this.getItemKey(key));
return result ?? void 0;
}
/**
* Get the value of a key and delete it
*
* Returns the value if the key exists, undefined otherwise
*/
async pull(key) {
const value = await this.#connection.getdel(this.getItemKey(key));
return value ?? void 0;
}
/**
* Put a value in the cache
* Returns true if the value was set, false otherwise
*/
async set(key, value, ttl) {
key = this.getItemKey(key);
if (!ttl) {
const result2 = await this.#connection.set(key, value);
return result2 === "OK";
}
const result = await this.#connection.set(key, value, "PX", ttl);
return result === "OK";
}
/**
* Remove all items from the cache
*/
async clear() {
let cursor = "0";
const COUNT = 1e3;
const prefix = this.prefix && `${this.prefix}:`;
const connectionKeyPrefix = this.#connection.options.keyPrefix;
do {
const [newCursor, keys] = await this.#connection.scan(
cursor,
"MATCH",
`${connectionKeyPrefix}${prefix}*`,
"COUNT",
COUNT
);
if (keys.length)
this.#connection.unlink(keys.map((key) => key.slice(connectionKeyPrefix?.length)));
cursor = newCursor;
} while (cursor !== "0");
}
/**
* Delete a key from the cache
* Returns true if the key was deleted, false otherwise
*/
async delete(key) {
const deletedKeys = await this.#connection.unlink(this.getItemKey(key));
return deletedKeys > 0;
}
/**
* Delete multiple keys from the cache
*/
async deleteMany(keys) {
if (keys.length === 0) return true;
await this.#connection.unlink(keys.map((key) => this.getItemKey(key)));
return true;
}
/**
* Closes the connection to the cache
*/
async disconnect() {
this.#connection.disconnect();
}
};
export {
RedisDriver,
redisBusDriver,
redisDriver
};
//# sourceMappingURL=redis.js.map