Skip to content

Commit 850116d

Browse files
author
Weyoss
committed
refactor(redis-smq): improve redis-keys module with better organization and documentation
1 parent 77a923d commit 850116d

File tree

1 file changed

+189
-74
lines changed

1 file changed

+189
-74
lines changed

packages/redis-smq/src/common/redis-keys/redis-keys.ts

Lines changed: 189 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,173 +10,288 @@
1010
import { IQueueParams } from '../../lib/index.js';
1111
import { RedisKeysInvalidKeyError } from './errors/index.js';
1212

13-
// Key segments separator
14-
const keySegmentSeparator = ':';
13+
/**
14+
* Redis key configuration constants
15+
*/
16+
const REDIS_KEY_CONFIG = {
17+
/** Key segments separator */
18+
SEGMENT_SEPARATOR: ':',
1519

16-
// Keys version
17-
const keyVersion = `800.26`;
20+
/** Keys version */
21+
VERSION: '800.26',
1822

19-
// Keys prefix
20-
const keyPrefix = `redis-smq-${keyVersion}`;
23+
/** Global namespace identifier */
24+
GLOBAL_NAMESPACE: 'global',
25+
} as const;
2126

22-
// Namespaces
23-
const globalNamespace = 'global';
27+
/**
28+
* Redis key prefix with version
29+
*/
30+
const KEY_PREFIX = `redis-smq-${REDIS_KEY_CONFIG.VERSION}`;
2431

32+
/**
33+
* Enum for Redis key types
34+
*/
2535
enum ERedisKey {
26-
KEY_QUEUE_PENDING = 1,
27-
KEY_QUEUE_PRIORITY_PENDING,
28-
KEY_QUEUE_DL,
29-
KEY_QUEUE_PROCESSING,
30-
KEY_QUEUE_ACKNOWLEDGED,
31-
KEY_QUEUE_SCHEDULED,
32-
KEY_QUEUE_DELAYED,
33-
KEY_QUEUE_REQUEUED,
34-
KEY_QUEUE_CONSUMERS,
35-
KEY_QUEUE_PROCESSING_QUEUES,
36-
KEY_QUEUE_WORKERS_LOCK,
37-
KEY_QUEUE_RATE_LIMIT_COUNTER,
38-
KEY_QUEUE_PROPERTIES,
39-
KEY_QUEUE_MESSAGES,
40-
KEY_QUEUE_MESSAGE_IDS,
41-
KEY_QUEUE_CONSUMER_GROUPS,
42-
KEY_QUEUES,
43-
KEY_CONSUMER_QUEUES,
44-
KEY_CONSUMER_HEARTBEAT,
45-
KEY_NS_QUEUES,
46-
KEY_NAMESPACES,
47-
KEY_EXCHANGE_BINDINGS,
48-
KEY_FANOUT_EXCHANGES,
49-
KEY_MESSAGE,
36+
// Queue related keys
37+
QUEUE_PENDING = 1,
38+
QUEUE_PRIORITY_PENDING,
39+
QUEUE_DL,
40+
QUEUE_PROCESSING,
41+
QUEUE_ACKNOWLEDGED,
42+
QUEUE_SCHEDULED,
43+
QUEUE_DELAYED,
44+
QUEUE_REQUEUED,
45+
QUEUE_CONSUMERS,
46+
QUEUE_PROCESSING_QUEUES,
47+
QUEUE_WORKERS_LOCK,
48+
QUEUE_RATE_LIMIT_COUNTER,
49+
QUEUE_PROPERTIES,
50+
QUEUE_MESSAGES,
51+
QUEUE_MESSAGE_IDS,
52+
QUEUE_CONSUMER_GROUPS,
53+
54+
// Global keys
55+
QUEUES,
56+
CONSUMER_QUEUES,
57+
CONSUMER_HEARTBEAT,
58+
NS_QUEUES,
59+
NAMESPACES,
60+
EXCHANGE_BINDINGS,
61+
FANOUT_EXCHANGES,
62+
MESSAGE,
5063
}
5164

52-
function makeNamespacedKeys<T extends Record<string, ERedisKey>>(
65+
/**
66+
* Type for key mapping objects
67+
*/
68+
type TRedisKeyMap = Record<string, ERedisKey>;
69+
70+
/**
71+
* Creates namespaced Redis keys from a key mapping
72+
*
73+
* @param keys - Object mapping key names to ERedisKey values
74+
* @param namespace - Namespace for the keys
75+
* @param rest - Additional key segments
76+
* @returns Record with the same keys but values as formatted Redis key strings
77+
*/
78+
function makeNamespacedKeys<T extends TRedisKeyMap>(
5379
keys: T,
5480
namespace: string,
5581
...rest: (string | number)[]
5682
): Record<Extract<keyof T, string>, string> {
5783
const result: Record<string, string> = {};
58-
for (const k in keys) {
59-
result[k] = [keyPrefix, namespace, keys[k], ...rest].join(
60-
keySegmentSeparator,
84+
85+
for (const keyName in keys) {
86+
result[keyName] = [KEY_PREFIX, namespace, keys[keyName], ...rest].join(
87+
REDIS_KEY_CONFIG.SEGMENT_SEPARATOR,
6188
);
6289
}
90+
6391
return result;
6492
}
6593

94+
/**
95+
* Redis keys utility functions
96+
*/
6697
export const redisKeys = {
98+
/**
99+
* Get keys for a specific namespace
100+
*
101+
* @param ns - Namespace
102+
* @returns Namespace-specific keys
103+
*/
67104
getNamespaceKeys(ns: string) {
68105
const keys = {
69-
keyNamespaceQueues: ERedisKey.KEY_NS_QUEUES,
106+
keyNamespaceQueues: ERedisKey.NS_QUEUES,
70107
};
71108
return {
72109
...makeNamespacedKeys(keys, ns),
73110
};
74111
},
75112

113+
/**
114+
* Get keys for a specific queue
115+
*
116+
* @param queueParams - Queue parameters
117+
* @param consumerGroupId - Optional consumer group ID
118+
* @returns Queue-specific keys
119+
*/
76120
getQueueKeys(queueParams: IQueueParams, consumerGroupId: string | null) {
77121
const queueKeys = {
78-
keyQueueDL: ERedisKey.KEY_QUEUE_DL,
79-
keyQueueProcessingQueues: ERedisKey.KEY_QUEUE_PROCESSING_QUEUES,
80-
keyQueueAcknowledged: ERedisKey.KEY_QUEUE_ACKNOWLEDGED,
81-
keyQueueScheduled: ERedisKey.KEY_QUEUE_SCHEDULED,
82-
keyQueueRequeued: ERedisKey.KEY_QUEUE_REQUEUED,
83-
keyQueueDelayed: ERedisKey.KEY_QUEUE_DELAYED,
84-
keyQueueConsumers: ERedisKey.KEY_QUEUE_CONSUMERS,
85-
keyQueueRateLimitCounter: ERedisKey.KEY_QUEUE_RATE_LIMIT_COUNTER,
86-
keyQueueProperties: ERedisKey.KEY_QUEUE_PROPERTIES,
87-
keyQueueMessages: ERedisKey.KEY_QUEUE_MESSAGES,
88-
keyQueueMessageIds: ERedisKey.KEY_QUEUE_MESSAGE_IDS,
89-
keyQueueConsumerGroups: ERedisKey.KEY_QUEUE_CONSUMER_GROUPS,
90-
keyQueueWorkersLock: ERedisKey.KEY_QUEUE_WORKERS_LOCK,
122+
keyQueueDL: ERedisKey.QUEUE_DL,
123+
keyQueueProcessingQueues: ERedisKey.QUEUE_PROCESSING_QUEUES,
124+
keyQueueAcknowledged: ERedisKey.QUEUE_ACKNOWLEDGED,
125+
keyQueueScheduled: ERedisKey.QUEUE_SCHEDULED,
126+
keyQueueRequeued: ERedisKey.QUEUE_REQUEUED,
127+
keyQueueDelayed: ERedisKey.QUEUE_DELAYED,
128+
keyQueueConsumers: ERedisKey.QUEUE_CONSUMERS,
129+
keyQueueRateLimitCounter: ERedisKey.QUEUE_RATE_LIMIT_COUNTER,
130+
keyQueueProperties: ERedisKey.QUEUE_PROPERTIES,
131+
keyQueueMessages: ERedisKey.QUEUE_MESSAGES,
132+
keyQueueMessageIds: ERedisKey.QUEUE_MESSAGE_IDS,
133+
keyQueueConsumerGroups: ERedisKey.QUEUE_CONSUMER_GROUPS,
134+
keyQueueWorkersLock: ERedisKey.QUEUE_WORKERS_LOCK,
91135
};
136+
92137
const pendingKeys = {
93-
keyQueuePending: ERedisKey.KEY_QUEUE_PENDING,
94-
keyQueuePriorityPending: ERedisKey.KEY_QUEUE_PRIORITY_PENDING,
138+
keyQueuePending: ERedisKey.QUEUE_PENDING,
139+
keyQueuePriorityPending: ERedisKey.QUEUE_PRIORITY_PENDING,
95140
};
141+
96142
const payload = [queueParams.name];
143+
const pendingPayload = [
144+
...payload,
145+
...(consumerGroupId ? [consumerGroupId] : []),
146+
];
147+
97148
return {
98149
...makeNamespacedKeys(queueKeys, queueParams.ns, ...payload),
99-
...makeNamespacedKeys(
100-
pendingKeys,
101-
queueParams.ns,
102-
...payload,
103-
...(consumerGroupId ? [consumerGroupId] : []),
104-
),
150+
...makeNamespacedKeys(pendingKeys, queueParams.ns, ...pendingPayload),
105151
};
106152
},
107153

154+
/**
155+
* Get keys for a specific message
156+
*
157+
* @param messageId - Message ID
158+
* @returns Message-specific keys
159+
*/
108160
getMessageKeys(messageId: string) {
109-
const exchangeKeys = {
110-
keyMessage: ERedisKey.KEY_MESSAGE,
161+
const messageKeys = {
162+
keyMessage: ERedisKey.MESSAGE,
111163
};
112164
return {
113-
...makeNamespacedKeys(exchangeKeys, globalNamespace, messageId),
165+
...makeNamespacedKeys(
166+
messageKeys,
167+
REDIS_KEY_CONFIG.GLOBAL_NAMESPACE,
168+
messageId,
169+
),
114170
};
115171
},
116172

173+
/**
174+
* Get keys for a fanout exchange
175+
*
176+
* @param bindingKey - Exchange binding key
177+
* @returns Exchange-specific keys
178+
*/
117179
getFanOutExchangeKeys(bindingKey: string) {
118180
const exchangeKeys = {
119-
keyExchangeBindings: ERedisKey.KEY_EXCHANGE_BINDINGS,
181+
keyExchangeBindings: ERedisKey.EXCHANGE_BINDINGS,
120182
};
121183
return {
122-
...makeNamespacedKeys(exchangeKeys, globalNamespace, bindingKey),
184+
...makeNamespacedKeys(
185+
exchangeKeys,
186+
REDIS_KEY_CONFIG.GLOBAL_NAMESPACE,
187+
bindingKey,
188+
),
123189
};
124190
},
125191

192+
/**
193+
* Get keys for a consumer instance
194+
*
195+
* @param instanceId - Consumer instance ID
196+
* @returns Consumer-specific keys
197+
*/
126198
getConsumerKeys(instanceId: string) {
127199
const consumerKeys = {
128-
keyConsumerQueues: ERedisKey.KEY_CONSUMER_QUEUES,
129-
keyConsumerHeartbeat: ERedisKey.KEY_CONSUMER_HEARTBEAT,
200+
keyConsumerQueues: ERedisKey.CONSUMER_QUEUES,
201+
keyConsumerHeartbeat: ERedisKey.CONSUMER_HEARTBEAT,
130202
};
131203
return {
132-
...makeNamespacedKeys(consumerKeys, globalNamespace, instanceId),
204+
...makeNamespacedKeys(
205+
consumerKeys,
206+
REDIS_KEY_CONFIG.GLOBAL_NAMESPACE,
207+
instanceId,
208+
),
133209
};
134210
},
135211

212+
/**
213+
* Get keys for a queue consumer
214+
*
215+
* @param queueParams - Queue parameters
216+
* @param instanceId - Consumer instance ID
217+
* @returns Queue consumer-specific keys
218+
*/
136219
getQueueConsumerKeys(queueParams: IQueueParams, instanceId: string) {
137220
const keys = {
138-
keyQueueProcessing: ERedisKey.KEY_QUEUE_PROCESSING,
221+
keyQueueProcessing: ERedisKey.QUEUE_PROCESSING,
139222
};
140223
return {
141224
...makeNamespacedKeys(keys, queueParams.ns, queueParams.name, instanceId),
142225
};
143226
},
144227

228+
/**
229+
* Get main global keys
230+
*
231+
* @returns Global keys
232+
*/
145233
getMainKeys() {
146234
const mainKeys = {
147-
keyQueues: ERedisKey.KEY_QUEUES,
148-
keyNamespaces: ERedisKey.KEY_NAMESPACES,
149-
keyFanOutExchanges: ERedisKey.KEY_FANOUT_EXCHANGES,
235+
keyQueues: ERedisKey.QUEUES,
236+
keyNamespaces: ERedisKey.NAMESPACES,
237+
keyFanOutExchanges: ERedisKey.FANOUT_EXCHANGES,
150238
};
151-
return makeNamespacedKeys(mainKeys, globalNamespace);
239+
return makeNamespacedKeys(mainKeys, REDIS_KEY_CONFIG.GLOBAL_NAMESPACE);
152240
},
153241

242+
/**
243+
* Validate a namespace string
244+
*
245+
* @param ns - Namespace to validate
246+
* @returns Validated namespace or error
247+
*/
154248
validateNamespace(ns: string): string | RedisKeysInvalidKeyError {
155249
const validated = this.validateRedisKey(ns);
156-
if (validated === globalNamespace) {
250+
251+
if (validated instanceof RedisKeysInvalidKeyError) {
252+
return validated;
253+
}
254+
255+
if (validated === REDIS_KEY_CONFIG.GLOBAL_NAMESPACE) {
157256
return new RedisKeysInvalidKeyError();
158257
}
258+
159259
return validated;
160260
},
161261

262+
/**
263+
* Validate a Redis key string
264+
*
265+
* @param key - Key to validate
266+
* @returns Validated key or error
267+
*/
162268
validateRedisKey(
163269
key: string | null | undefined,
164270
): string | RedisKeysInvalidKeyError {
165271
if (!key || !key.length) {
166272
return new RedisKeysInvalidKeyError();
167273
}
274+
168275
const lowerCase = key.toLowerCase();
276+
// Regex matches valid key patterns, then we check if anything remains
169277
const filtered = lowerCase.replace(
170-
/(?:[a-z][a-z0-9]?)+(?:[-_.]?[a-z0-9])*/,
278+
/(?:[a-z][a-z0-9]?)+(?:[-_.]?[a-z0-9])*/g,
171279
'',
172280
);
281+
173282
if (filtered.length) {
174283
return new RedisKeysInvalidKeyError();
175284
}
285+
176286
return lowerCase;
177287
},
178288

289+
/**
290+
* Get the key segment separator
291+
*
292+
* @returns Key segment separator
293+
*/
179294
getKeySegmentSeparator() {
180-
return keySegmentSeparator;
295+
return REDIS_KEY_CONFIG.SEGMENT_SEPARATOR;
181296
},
182297
};

0 commit comments

Comments
 (0)