Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,5 @@ coordination/orchestration/*
claude-flow
# Removed Windows wrapper files per user request
hive-mind-prompt-*.txt
infrastructure/
.playwright-*
11 changes: 11 additions & 0 deletions packages/api/src/cache/cacheConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ const cacheConfig = {
REDIS_USE_ALTERNATIVE_DNS_LOOKUP: isEnabled(process.env.REDIS_USE_ALTERNATIVE_DNS_LOOKUP),
/** Enable redis cluster without the need of multiple URIs */
USE_REDIS_CLUSTER: isEnabled(process.env.USE_REDIS_CLUSTER ?? 'false'),
/**
* Force single-key operations for Redis commands even in non-cluster mode.
* Required for AWS ElastiCache Serverless which internally shards data but
* doesn't support the Redis Cluster protocol. This avoids CROSSSLOT errors
* when using batch operations on a sharded backend.
*
* Note: When USE_REDIS_CLUSTER=true, single-key operations are always used.
* This option only affects behavior when USE_REDIS_CLUSTER=false.
* @default false
*/
REDIS_SINGLE_KEY_OPS: isEnabled(process.env.REDIS_SINGLE_KEY_OPS ?? 'false'),
CI: isEnabled(process.env.CI),
DEBUG_MEMORY_CACHE: isEnabled(process.env.DEBUG_MEMORY_CACHE),

Expand Down
15 changes: 11 additions & 4 deletions packages/api/src/cache/redisUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,24 @@ export async function batchDeleteKeys(
}

const size = chunkSize ?? cacheConfig.REDIS_DELETE_CHUNK_SIZE;
const mode = cacheConfig.USE_REDIS_CLUSTER ? 'cluster' : 'single-node';
// Use single-key operations if cluster mode OR if explicitly enabled for sharded backends like ElastiCache Serverless
const useSingleKeyOps = cacheConfig.USE_REDIS_CLUSTER || cacheConfig.REDIS_SINGLE_KEY_OPS;
const mode = cacheConfig.USE_REDIS_CLUSTER
? 'cluster'
: cacheConfig.REDIS_SINGLE_KEY_OPS
? 'single-key-ops'
: 'single-node';
const deletePromises = [];

if (cacheConfig.USE_REDIS_CLUSTER) {
// Cluster mode: Delete each key individually in parallel chunks to avoid CROSSSLOT errors
if (useSingleKeyOps) {
// Single-key mode: Delete each key individually in parallel chunks to avoid CROSSSLOT errors
// Required for Redis Cluster or sharded backends like AWS ElastiCache Serverless
for (let i = 0; i < keys.length; i += size) {
const chunk = keys.slice(i, i + size);
deletePromises.push(Promise.all(chunk.map((key) => client.del(key))));
}
} else {
// Single-node mode: Batch delete chunks using DEL with array
// Batch mode: Delete chunks using DEL with array (only safe for true single-node Redis)
for (let i = 0; i < keys.length; i += size) {
const chunk = keys.slice(i, i + size);
deletePromises.push(client.del(chunk));
Expand Down
Loading