From 893e8730b774d67ad05d15138c035305a4dac144 Mon Sep 17 00:00:00 2001 From: Mark Nachazel Date: Sun, 16 Mar 2025 19:52:54 -0400 Subject: [PATCH] update types, lock down client options Signed-off-by: Mark Nachazel --- packages/backend-defaults/config.d.ts | 61 +++++++++++++++++++ .../entrypoints/cache/CacheManager.test.ts | 4 +- .../src/entrypoints/cache/CacheManager.ts | 20 +++++- .../src/entrypoints/cache/types.ts | 4 +- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/packages/backend-defaults/config.d.ts b/packages/backend-defaults/config.d.ts index 438790a177..0729bad456 100644 --- a/packages/backend-defaults/config.d.ts +++ b/packages/backend-defaults/config.d.ts @@ -559,6 +559,67 @@ export interface Config { connection: string; /** An optional default TTL (in milliseconds, if given as a number). */ defaultTtl?: number | HumanDuration | string; + redis?: { + /** + * An optional Redis client configuration. These options are passed to the `@keyv/redis` client. + */ + client?: { + /** + * Namespace for the current instance. + */ + namespace?: string; + /** + * Separator to use between namespace and key. + */ + keyPrefixSeparator?: string; + /** + * Number of keys to delete in a single batch. + */ + clearBatchSize?: number; + /** + * Enable Unlink instead of using Del for clearing keys. This is more performant but may not be supported by all Redis versions. + */ + useUnlink?: boolean; + /** + * Whether to allow clearing all keys when no namespace is set. + * If set to true and no namespace is set, iterate() will return all keys. + * Defaults to `false`. + */ + noNamespaceAffectsAll?: boolean; + }; + /** + * An optional Redis cluster configuration. + */ + cluster?: { + /** + * Cluster configuration options to be passed to the `@keyv/redis` client (and node-redis under the hood) + * https://github.com/redis/node-redis/blob/master/docs/clustering.md + * + * @visibility secret + */ + rootNodes: Array; + /** + * Cluster node default configuration options to be passed to the `@keyv/redis` client (and node-redis under the hood) + * https://github.com/redis/node-redis/blob/master/docs/clustering.md + * + * @visibility secret + */ + defaults?: Partial; + /** + * When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes. + * Useful for short-term or PubSub-only connections. + */ + minimizeConnections?: boolean; + /** + * When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes. + */ + useReplicas?: boolean; + /** + * The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors. + */ + maxCommandRedirections?: number; + }; + }; } | { store: 'memcache'; diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts index fc7ed34fc4..3a7886098f 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts @@ -226,7 +226,9 @@ describe('CacheManager store options', () => { cache: { store: 'redis', connection: 'redis://localhost:6379', - cluster: {}, + redis: { + cluster: {}, + }, }, }, }, diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts index e4f0f5f408..ab8b03eb3b 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts @@ -141,7 +141,16 @@ export class CacheManager { const redisOptions: RedisCacheStoreOptions = {}; const redisConfig = config.getConfig(storeConfigPath); - redisOptions.client = redisConfig.getOptional('client'); + redisOptions.client = { + namespace: redisConfig.getOptionalString('client.namespace'), + keyPrefixSeparator: + redisConfig.getOptionalString('client.keyPrefixSeparator') || ':', + clearBatchSize: redisConfig.getOptionalNumber('client.clearBatchSize'), + useUnlink: redisConfig.getOptionalBoolean('client.useUnlink'), + noNamespaceAffectsAll: redisConfig.getOptionalBoolean( + 'client.noNamespaceAffectsAll', + ), + }; if (redisConfig.has('cluster')) { const clusterConfig = redisConfig.getConfig('cluster'); @@ -156,6 +165,13 @@ export class CacheManager { redisOptions.cluster = { rootNodes: clusterConfig.get('rootNodes'), defaults: clusterConfig.getOptional('defaults'), + minimizeConnections: clusterConfig.getOptionalBoolean( + 'minimizeConnections', + ), + useReplicas: clusterConfig.getOptionalBoolean('useReplicas'), + maxCommandRedirections: clusterConfig.getOptionalNumber( + 'maxCommandRedirections', + ), }; } @@ -207,7 +223,7 @@ export class CacheManager { private createRedisStoreFactory(): StoreFactory { const KeyvRedis = require('@keyv/redis').default; const { createCluster } = require('@keyv/redis'); - const stores: Record = {}; + const stores: Record = {}; return (pluginId, defaultTtl) => { if (!stores[pluginId]) { diff --git a/packages/backend-defaults/src/entrypoints/cache/types.ts b/packages/backend-defaults/src/entrypoints/cache/types.ts index 4d32f007b0..d21fb5d44b 100644 --- a/packages/backend-defaults/src/entrypoints/cache/types.ts +++ b/packages/backend-defaults/src/entrypoints/cache/types.ts @@ -16,7 +16,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { HumanDuration, durationToMilliseconds } from '@backstage/types'; -import { RedisClusterOptions, RedisClientOptions } from '@keyv/redis'; +import { RedisClusterOptions, KeyvRedisOptions } from '@keyv/redis'; /** * Options for Redis cache store. @@ -24,7 +24,7 @@ import { RedisClusterOptions, RedisClientOptions } from '@keyv/redis'; * @public */ export type RedisCacheStoreOptions = { - client?: RedisClientOptions; + client?: KeyvRedisOptions; cluster?: RedisClusterOptions; };