mastercache
Version:
Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers
151 lines (130 loc) • 3.56 kB
text/typescript
import { Redis as IoRedis } from 'ioredis';
import type { RedisOptions as IoRedisOptions } from 'ioredis';
import { RedisTransport } from '@boringnode/bus/transports/redis';
import { BaseDriver } from './base-driver';
import { BinaryEncoder } from '../bus/encoders/binary-encoder';
import type {
BusOptions,
CreateBusDriverResult,
CreateDriverResult,
L2CacheDriver,
RedisConfig,
} from '../types/main';
/**
* Create a new bus redis driver. It leverages the Pub/sub capabilities of Redis
* to sending messages between your different processes.
*/
export function redisBusDriver(
options: { connection: IoRedisOptions } & BusOptions,
): CreateBusDriverResult {
return {
options,
factory: () => {
return new RedisTransport(
{ ...options.connection, useMessageBuffer: true },
new BinaryEncoder(),
);
},
};
}
/**
* Caching driver for Redis
*/
export class RedisDriver extends BaseDriver implements L2CacheDriver {
type = 'l2' as const;
#connection: IoRedis;
declare config: RedisConfig;
constructor(config: RedisConfig) {
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: string) {
return new RedisDriver({
...this.config,
connection: this.#connection,
prefix: this.createNamespacePrefix(namespace),
});
}
/**
* Get a value from the cache
*/
async get(key: string) {
const result = await this.#connection.get(this.getItemKey(key));
return result ?? undefined;
}
/**
* Get the value of a key and delete it
*
* Returns the value if the key exists, undefined otherwise
*/
async pull(key: string) {
const value = await this.#connection.getdel(this.getItemKey(key));
return value ?? undefined;
}
/**
* Put a value in the cache
* Returns true if the value was set, false otherwise
*/
async set(key: string, value: string, ttl?: number) {
key = this.getItemKey(key);
if (!ttl) {
const result = await this.#connection.set(key, value);
return result === 'OK';
}
const result = await this.#connection.set(key, value, 'PX', ttl);
return result === 'OK';
}
/**
* Check if a key exists in the cache
*/
async has(key: string) {
const exists = await this.#connection.exists(this.getItemKey(key));
return exists > 0;
}
/**
* Remove all items from the cache
*/
async clear() {
const keys = await this.#connection.keys(`${this.prefix}*`);
if (keys.length) {
await this.#connection.del(keys);
}
}
/**
* Delete a key from the cache
* Returns true if the key was deleted, false otherwise
*/
async delete(key: string) {
const deletedKeys = await this.#connection.del(this.getItemKey(key));
return deletedKeys > 0;
}
/**
* Delete multiple keys from the cache
*/
async deleteMany(keys: string[]) {
await this.#connection.del(keys.map((key) => this.getItemKey(key)));
return true;
}
/**
* Closes the connection to the cache
*/
async disconnect() {
await this.#connection.quit();
}
}
/**
* Create a new cache redis driver
*/
export function redisDriver(options: RedisConfig): CreateDriverResult<RedisDriver> {
return { options, factory: (config: RedisConfig) => new RedisDriver(config) };
}