1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-10 11:43:01 +03:00
Files
node-redis/packages/client/lib/sentinel/utils.ts
Bobby I. f01f1014cb 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>
2025-05-19 15:11:47 +03:00

99 lines
3.5 KiB
TypeScript

import { ArrayReply, Command, RedisFunction, RedisScript, RespVersions, UnwrapReply } from '../RESP/types';
import { BasicCommandParser } from '../client/parser';
import { RedisSocketOptions, RedisTcpSocketOptions } from '../client/socket';
import { functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
import { NamespaceProxySentinel, NamespaceProxySentinelClient, ProxySentinel, ProxySentinelClient, RedisNode } from './types';
/* TODO: should use map interface, would need a transform reply probably? as resp2 is list form, which this depends on */
export function parseNode(node: Record<string, string>): RedisNode | undefined{
if (node.flags.includes("s_down") || node.flags.includes("disconnected") || node.flags.includes("failover_in_progress")) {
return undefined;
}
return { host: node.ip, port: Number(node.port) };
}
export function createNodeList(nodes: UnwrapReply<ArrayReply<Record<string, string>>>) {
var nodeList: Array<RedisNode> = [];
for (const nodeData of nodes) {
const node = parseNode(nodeData)
if (node === undefined) {
continue;
}
nodeList.push(node);
}
return nodeList;
}
export function clientSocketToNode(socket: RedisSocketOptions): RedisNode {
const s = socket as RedisTcpSocketOptions;
return {
host: s.host!,
port: s.port!
}
}
export function createCommand<T extends ProxySentinel | ProxySentinelClient>(command: Command, resp: RespVersions) {
const transformReply = getTransformReply(command, resp);
return async function (this: T, ...args: Array<unknown>) {
const parser = new BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self._execute(
command.IS_READ_ONLY,
client => client._executeCommand(command, parser, this.commandOptions, transformReply)
);
};
}
export function createFunctionCommand<T extends NamespaceProxySentinel | NamespaceProxySentinelClient>(name: string, fn: RedisFunction, resp: RespVersions) {
const prefix = functionArgumentsPrefix(name, fn);
const transformReply = getTransformReply(fn, resp);
return async function (this: T, ...args: Array<unknown>) {
const parser = new BasicCommandParser();
parser.push(...prefix);
fn.parseCommand(parser, ...args);
return this._self._execute(
fn.IS_READ_ONLY,
client => client._executeCommand(fn, parser, this._self.commandOptions, transformReply)
);
}
};
export function createModuleCommand<T extends NamespaceProxySentinel | NamespaceProxySentinelClient>(command: Command, resp: RespVersions) {
const transformReply = getTransformReply(command, resp);
return async function (this: T, ...args: Array<unknown>) {
const parser = new BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self._execute(
command.IS_READ_ONLY,
client => client._executeCommand(command, parser, this._self.commandOptions, transformReply)
);
}
};
export function createScriptCommand<T extends ProxySentinel | ProxySentinelClient>(script: RedisScript, resp: RespVersions) {
const prefix = scriptArgumentsPrefix(script);
const transformReply = getTransformReply(script, resp);
return async function (this: T, ...args: Array<unknown>) {
const parser = new BasicCommandParser();
parser.push(...prefix);
script.parseCommand(parser, ...args);
return this._self._execute(
script.IS_READ_ONLY,
client => client._executeScript(script, parser, this.commandOptions, transformReply)
);
};
}