You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Client Side Caching (#2947)
* CSC POC ontop of Parser * add csc file that weren't merged after patch * address review comments * nits to try and fix github * last change from review * Update client-side cache and improve documentation * Add client side caching RESP3 validation * Add documentation for RESP and unstableResp3 options * Add comprehensive cache statistics The `CacheStats` class provides detailed metrics like hit/miss counts, load success/failure counts, total load time, and eviction counts. It also offers derived metrics such as hit/miss rates, load failure rate, and average load penalty. The design is inspired by Caffeine. `BasicClientSideCache` now uses a `StatsCounter` to accumulate these statistics, exposed via a new `stats()` method. The previous `cacheHits()` and `cacheMisses()` methods have been removed. A `recordStats` option (default: true) in `ClientSideCacheConfig` allows disabling statistics collection. --------- Co-authored-by: Shaya Potter <shaya@redislabs.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import { RedisVariadicArgument } from '../commands/generic-transformers';
|
||||
import { WaitQueue } from './wait-queue';
|
||||
import { TcpNetConnectOpts } from 'node:net';
|
||||
import { RedisTcpSocketOptions } from '../client/socket';
|
||||
import { BasicPooledClientSideCache, PooledClientSideCacheProvider } from '../client/cache';
|
||||
|
||||
interface ClientInfo {
|
||||
id: number;
|
||||
@@ -301,6 +302,10 @@ export default class RedisSentinel<
|
||||
#masterClientCount = 0;
|
||||
#masterClientInfo?: ClientInfo;
|
||||
|
||||
get clientSideCache() {
|
||||
return this._self.#internal.clientSideCache;
|
||||
}
|
||||
|
||||
constructor(options: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>) {
|
||||
super();
|
||||
|
||||
@@ -617,7 +622,7 @@ class RedisSentinelInternal<
|
||||
|
||||
readonly #name: string;
|
||||
readonly #nodeClientOptions: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
|
||||
readonly #sentinelClientOptions: RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
|
||||
readonly #sentinelClientOptions: RedisClientOptions<typeof RedisSentinelModule, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisTcpSocketOptions>;
|
||||
readonly #scanInterval: number;
|
||||
readonly #passthroughClientErrorEvents: boolean;
|
||||
|
||||
@@ -650,9 +655,22 @@ class RedisSentinelInternal<
|
||||
|
||||
#trace: (msg: string) => unknown = () => { };
|
||||
|
||||
#clientSideCache?: PooledClientSideCacheProvider;
|
||||
get clientSideCache() {
|
||||
return this.#clientSideCache;
|
||||
}
|
||||
|
||||
#validateOptions(options?: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>) {
|
||||
if (options?.clientSideCache && options?.RESP !== 3) {
|
||||
throw new Error('Client Side Caching is only supported with RESP3');
|
||||
}
|
||||
}
|
||||
|
||||
constructor(options: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>) {
|
||||
super();
|
||||
|
||||
this.#validateOptions(options);
|
||||
|
||||
this.#name = options.name;
|
||||
|
||||
this.#sentinelRootNodes = Array.from(options.sentinelRootNodes);
|
||||
@@ -662,11 +680,21 @@ class RedisSentinelInternal<
|
||||
this.#scanInterval = options.scanInterval ?? 0;
|
||||
this.#passthroughClientErrorEvents = options.passthroughClientErrorEvents ?? false;
|
||||
|
||||
this.#nodeClientOptions = options.nodeClientOptions ? Object.assign({} as RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>, options.nodeClientOptions) : {};
|
||||
this.#nodeClientOptions = (options.nodeClientOptions ? {...options.nodeClientOptions} : {}) as RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>;
|
||||
if (this.#nodeClientOptions.url !== undefined) {
|
||||
throw new Error("invalid nodeClientOptions for Sentinel");
|
||||
}
|
||||
|
||||
if (options.clientSideCache) {
|
||||
if (options.clientSideCache instanceof PooledClientSideCacheProvider) {
|
||||
this.#clientSideCache = this.#nodeClientOptions.clientSideCache = options.clientSideCache;
|
||||
} else {
|
||||
const cscConfig = options.clientSideCache;
|
||||
this.#clientSideCache = this.#nodeClientOptions.clientSideCache = new BasicPooledClientSideCache(cscConfig);
|
||||
// this.#clientSideCache = this.#nodeClientOptions.clientSideCache = new PooledNoRedirectClientSideCache(cscConfig);
|
||||
}
|
||||
}
|
||||
|
||||
this.#sentinelClientOptions = options.sentinelClientOptions ? Object.assign({} as RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>, options.sentinelClientOptions) : {};
|
||||
this.#sentinelClientOptions.modules = RedisSentinelModule;
|
||||
|
||||
@@ -904,6 +932,8 @@ class RedisSentinelInternal<
|
||||
|
||||
this.#isReady = false;
|
||||
|
||||
this.#clientSideCache?.onPoolClose();
|
||||
|
||||
if (this.#scanTimer) {
|
||||
clearInterval(this.#scanTimer);
|
||||
this.#scanTimer = undefined;
|
||||
@@ -952,6 +982,8 @@ class RedisSentinelInternal<
|
||||
|
||||
this.#isReady = false;
|
||||
|
||||
this.#clientSideCache?.onPoolClose();
|
||||
|
||||
if (this.#scanTimer) {
|
||||
clearInterval(this.#scanTimer);
|
||||
this.#scanTimer = undefined;
|
||||
|
Reference in New Issue
Block a user