@winible/winible-typed 2.109.0 → 2.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,8 @@ class CacheService {
26
26
  constructor() {
27
27
  this.client = null;
28
28
  this.errorHandler = null;
29
+ this.isConnected = false; // Track connection state
30
+ this.hasConnectedBefore = false; // Track if we've ever connected
29
31
  }
30
32
  /**
31
33
  * Set a custom error handler for capturing errors to Sentry or other services
@@ -68,13 +70,31 @@ class CacheService {
68
70
  const username = process.env.ELASTICACHE_USERNAME;
69
71
  const password = process.env.ELASTICACHE_PASSWORD;
70
72
  if (!host) {
71
- throw new Error("ELASTICACHE_HOST environment variable is required");
73
+ const error = new Error("ELASTICACHE_HOST environment variable is required");
74
+ this.handleError(error, null, {
75
+ method: "CacheService.initialize",
76
+ action: "validation_error",
77
+ additionalData: { message: "Missing ELASTICACHE_HOST" },
78
+ });
79
+ return;
72
80
  }
73
81
  if (!username) {
74
- throw new Error("ELASTICACHE_USERNAME environment variable is required");
82
+ const error = new Error("ELASTICACHE_USERNAME environment variable is required");
83
+ this.handleError(error, null, {
84
+ method: "CacheService.initialize",
85
+ action: "validation_error",
86
+ additionalData: { message: "Missing ELASTICACHE_USERNAME" },
87
+ });
88
+ return;
75
89
  }
76
90
  if (!password) {
77
- throw new Error("ELASTICACHE_PASSWORD environment variable is required");
91
+ const error = new Error("ELASTICACHE_PASSWORD environment variable is required");
92
+ this.handleError(error, null, {
93
+ method: "CacheService.initialize",
94
+ action: "validation_error",
95
+ additionalData: { message: "Missing ELASTICACHE_PASSWORD" },
96
+ });
97
+ return;
78
98
  }
79
99
  (0, logger_1.default)("Initializing cache client", {
80
100
  level: "info",
@@ -88,55 +108,76 @@ class CacheService {
88
108
  username,
89
109
  password,
90
110
  connectTimeout: 10000,
111
+ commandTimeout: 5000,
112
+ // enableOfflineQueue: false, // fail immediately when disconnected
113
+ maxRetriesPerRequest: 1,
91
114
  retryStrategy: (times) => {
92
- // Stop retrying after 3 attempts
93
- if (times > 3) {
94
- return null; // Stop retrying - this will cause the connection to fail
95
- }
96
- const delay = Math.min(times * 50, 2000);
115
+ // Retry with exponential backoff, max 30 seconds between attempts
116
+ const delay = Math.min(times * 1000, 30000);
97
117
  return delay;
98
118
  },
99
- maxRetriesPerRequest: parseInt(process.env.ELASTICACHE_MAX_RETRIES, 10) || 3,
100
- tls: process.env.ELASTICACHE_SSL === "false" || process.env.ENVIRONMENT === "local"
101
- ? null : {
102
- rejectUnauthorized: true,
103
- }
104
- });
105
- this.client.on("connect", () => {
106
- (0, logger_1.default)("Cache client connected", {
107
- level: "info",
108
- method: "CacheService",
109
- action: "connected",
110
- });
119
+ tls: process.env.ELASTICACHE_SSL === "false" ||
120
+ process.env.ENVIRONMENT === "local"
121
+ ? null
122
+ : {
123
+ rejectUnauthorized: true,
124
+ },
111
125
  });
112
126
  this.client.on("ready", () => {
113
- (0, logger_1.default)("Cache client ready", {
114
- level: "info",
115
- method: "CacheService",
116
- action: "ready",
117
- });
127
+ // Only log when transitioning from disconnected to connected
128
+ if (!this.isConnected) {
129
+ this.isConnected = true;
130
+ // Only alert Sentry on Reconnection, not initial connection
131
+ if (this.hasConnectedBefore && this.errorHandler) {
132
+ const reconnectInfo = new Error("Cache reconnected successfully");
133
+ reconnectInfo.name = "CacheReconnected";
134
+ this.errorHandler(reconnectInfo, undefined, {
135
+ method: "CacheService",
136
+ action: "reconnected",
137
+ additionalData: { message: "Cache connection restored" },
138
+ });
139
+ }
140
+ else {
141
+ // Mark that we've connected at least once
142
+ this.hasConnectedBefore = true;
143
+ (0, logger_1.default)("Cache client connected successfully", {
144
+ level: "info",
145
+ method: "CacheService",
146
+ action: "connected",
147
+ });
148
+ }
149
+ }
118
150
  });
119
151
  this.client.on("error", (error) => {
120
- (0, logger_1.default)("Cache client error", {
121
- level: "error",
122
- method: "CacheService",
123
- action: "error",
124
- additionalData: { error: error.message },
125
- });
152
+ // Only report error when transitioning from connected to disconnected
153
+ if (this.isConnected) {
154
+ this.isConnected = false;
155
+ this.handleError(error, null, {
156
+ method: "CacheService",
157
+ action: "disconnected",
158
+ additionalData: {
159
+ message: "Cache connection lost",
160
+ error: error.message,
161
+ },
162
+ });
163
+ }
164
+ // Ignore repeated errors while already disconnected
126
165
  });
127
166
  this.client.on("close", () => {
128
- (0, logger_1.default)("Cache client disconnected", {
129
- level: "info",
130
- method: "CacheService",
131
- action: "disconnected",
132
- });
167
+ // Only log when transitioning from connected to disconnected
168
+ if (this.isConnected) {
169
+ this.isConnected = false;
170
+ this.handleError(new Error("Cache Connection Closed"), null, {
171
+ method: "CacheService",
172
+ action: "disconnected",
173
+ additionalData: { message: "Cache client disconnected" },
174
+ });
175
+ }
176
+ // Ignore repeated close events while already disconnected
133
177
  });
134
- // Wait for connection with timeout and throw if it fails
135
- await Promise.race([
136
- this.client.ping(),
137
- new Promise((_, reject) => setTimeout(() => reject(new Error("Connection timeout after 20 seconds")), 20000)),
138
- ]);
139
- (0, logger_1.default)("Cache client connected successfully", {
178
+ await this.client.ping();
179
+ this.isConnected = true;
180
+ (0, logger_1.default)("Cache client initialized successfully", {
140
181
  level: "info",
141
182
  method: "CacheService.initialize",
142
183
  action: "completed",
@@ -146,7 +187,9 @@ class CacheService {
146
187
  this.handleError(error, null, {
147
188
  method: "CacheService.initialize",
148
189
  action: "error",
149
- additionalData: { message: error.message || "Failed to initialize cache client", },
190
+ additionalData: {
191
+ message: error.message || "Failed to initialize cache client",
192
+ },
150
193
  });
151
194
  }
152
195
  }
@@ -156,6 +199,8 @@ class CacheService {
156
199
  * @returns The cached value or null if not found
157
200
  */
158
201
  async get(key) {
202
+ if (!this.isConnected)
203
+ return null;
159
204
  try {
160
205
  const value = await this.client.get(key);
161
206
  if (value === null) {
@@ -179,6 +224,8 @@ class CacheService {
179
224
  * @param ttl - Optional time-to-live in seconds
180
225
  */
181
226
  async set(key, value, ttl) {
227
+ if (!this.isConnected)
228
+ return;
182
229
  try {
183
230
  const serialized = JSON.stringify(value);
184
231
  if (ttl) {
@@ -201,6 +248,8 @@ class CacheService {
201
248
  * @param key - The cache key to delete
202
249
  */
203
250
  async delete(key) {
251
+ if (!this.isConnected)
252
+ return;
204
253
  try {
205
254
  await this.client.del(key);
206
255
  }
@@ -219,6 +268,8 @@ class CacheService {
219
268
  * @returns Array of matching keys
220
269
  */
221
270
  async keys(pattern) {
271
+ if (!this.isConnected)
272
+ return [];
222
273
  try {
223
274
  const keys = [];
224
275
  let cursor = "0";
@@ -244,6 +295,8 @@ class CacheService {
244
295
  * @returns The total number of keys
245
296
  */
246
297
  async dbsize() {
298
+ if (!this.isConnected)
299
+ return 0;
247
300
  try {
248
301
  const size = await this.client.dbsize();
249
302
  return size;
@@ -263,6 +316,8 @@ class CacheService {
263
316
  * @param pattern - Pattern to match (e.g., "user:*", "*:session")
264
317
  */
265
318
  async clearPattern(pattern) {
319
+ if (!this.isConnected)
320
+ return;
266
321
  try {
267
322
  const keys = await this.keys(pattern);
268
323
  if (keys.length > 0) {
@@ -282,7 +337,10 @@ class CacheService {
282
337
  this.handleError(error, null, {
283
338
  method: "CacheService.clearPattern",
284
339
  action: "error",
285
- additionalData: { message: "Failed to clear keys matching pattern", pattern },
340
+ additionalData: {
341
+ message: "Failed to clear keys matching pattern",
342
+ pattern,
343
+ },
286
344
  });
287
345
  }
288
346
  }
@@ -292,6 +350,8 @@ class CacheService {
292
350
  * @returns true if the key exists, false otherwise
293
351
  */
294
352
  async exists(key) {
353
+ if (!this.isConnected)
354
+ return false;
295
355
  try {
296
356
  const result = await this.client.exists(key);
297
357
  return result === 1;
@@ -310,6 +370,8 @@ class CacheService {
310
370
  * @param entries - Array of key-value-ttl tuples
311
371
  */
312
372
  async mset(entries) {
373
+ if (!this.isConnected)
374
+ return;
313
375
  try {
314
376
  const pipeline = this.client.pipeline();
315
377
  entries.forEach(({ key, value, ttl }) => {
@@ -327,7 +389,10 @@ class CacheService {
327
389
  this.handleError(error, null, {
328
390
  method: "CacheService.mset",
329
391
  action: "error",
330
- additionalData: { message: "Failed to set multiple values in cache", count: entries.length },
392
+ additionalData: {
393
+ message: "Failed to set multiple values in cache",
394
+ count: entries.length,
395
+ },
331
396
  });
332
397
  }
333
398
  }
@@ -337,15 +402,20 @@ class CacheService {
337
402
  * @returns Array of cached values (null for missing keys)
338
403
  */
339
404
  async mget(keys) {
405
+ if (!this.isConnected)
406
+ return keys.map(() => null);
340
407
  try {
341
408
  const values = await this.client.mget(...keys);
342
- return values.map((value) => value ? JSON.parse(value) : null);
409
+ return values.map((value) => (value ? JSON.parse(value) : null));
343
410
  }
344
411
  catch (error) {
345
412
  this.handleError(error, null, {
346
413
  method: "CacheService.mget",
347
414
  action: "error",
348
- additionalData: { message: "Failed to get multiple values from cache", count: keys.length },
415
+ additionalData: {
416
+ message: "Failed to get multiple values from cache",
417
+ count: keys.length,
418
+ },
349
419
  });
350
420
  return keys.map(() => null);
351
421
  }
@@ -357,6 +427,8 @@ class CacheService {
357
427
  * @returns The new value after increment
358
428
  */
359
429
  async increment(key, increment = 1) {
430
+ if (!this.isConnected)
431
+ return 0;
360
432
  try {
361
433
  return await this.client.incrby(key, increment);
362
434
  }
@@ -364,7 +436,11 @@ class CacheService {
364
436
  this.handleError(error, null, {
365
437
  method: "CacheService.increment",
366
438
  action: "error",
367
- additionalData: { message: "Failed to increment value in cache", key, increment },
439
+ additionalData: {
440
+ message: "Failed to increment value in cache",
441
+ key,
442
+ increment,
443
+ },
368
444
  });
369
445
  return 0;
370
446
  }
@@ -376,6 +452,8 @@ class CacheService {
376
452
  * @returns The new value after decrement
377
453
  */
378
454
  async decrement(key, decrement = 1) {
455
+ if (!this.isConnected)
456
+ return 0;
379
457
  try {
380
458
  return await this.client.decrby(key, decrement);
381
459
  }
@@ -383,7 +461,11 @@ class CacheService {
383
461
  this.handleError(error, null, {
384
462
  method: "CacheService.decrement",
385
463
  action: "error",
386
- additionalData: { message: "Failed to decrement value in cache", key, decrement },
464
+ additionalData: {
465
+ message: "Failed to decrement value in cache",
466
+ key,
467
+ decrement,
468
+ },
387
469
  });
388
470
  return 0;
389
471
  }
@@ -395,6 +477,8 @@ class CacheService {
395
477
  * @returns true if the timeout was set, false if key doesn't exist
396
478
  */
397
479
  async expire(key, ttl) {
480
+ if (!this.isConnected)
481
+ return false;
398
482
  try {
399
483
  const result = await this.client.expire(key, ttl);
400
484
  return result === 1;
@@ -403,7 +487,11 @@ class CacheService {
403
487
  this.handleError(error, null, {
404
488
  method: "CacheService.expire",
405
489
  action: "error",
406
- additionalData: { message: "Failed to set expiration on key", key, ttl },
490
+ additionalData: {
491
+ message: "Failed to set expiration on key",
492
+ key,
493
+ ttl,
494
+ },
407
495
  });
408
496
  return false;
409
497
  }
@@ -414,6 +502,8 @@ class CacheService {
414
502
  * @returns TTL in seconds, -1 if key exists but has no expire, -2 if key doesn't exist
415
503
  */
416
504
  async ttl(key) {
505
+ if (!this.isConnected)
506
+ return -2;
417
507
  try {
418
508
  return await this.client.ttl(key);
419
509
  }
@@ -426,6 +516,58 @@ class CacheService {
426
516
  return -2;
427
517
  }
428
518
  }
519
+ /**
520
+ * Atomically set a value only if the key doesn't exist (SETNX)
521
+ * Uses Redis SET NX EX command which is atomic - no race conditions possible.
522
+ *
523
+ * @param key - The cache key
524
+ * @param value - The value to cache (will be JSON serialized)
525
+ * @param ttl - Time-to-live in seconds (required for safety)
526
+ * @returns true if the value was set (key didn't exist), false if key already exists
527
+ *
528
+ * @example
529
+ * // Single-use token implementation
530
+ * const wasSet = await cacheService.setIfNotExists('magic_link:abc123', { userId: '123' }, 300);
531
+ * if (!wasSet) {
532
+ * throw new Error('Token already used');
533
+ * }
534
+ *
535
+ * @example
536
+ * // Distributed lock
537
+ * const lockAcquired = await cacheService.setIfNotExists('lock:resource_id', { owner: 'worker-1' }, 30);
538
+ * if (lockAcquired) {
539
+ * try {
540
+ * // Do work while holding lock
541
+ * } finally {
542
+ * await cacheService.delete('lock:resource_id');
543
+ * }
544
+ * }
545
+ */
546
+ async setIfNotExists(key, value, ttl) {
547
+ if (!this.isConnected)
548
+ return false;
549
+ try {
550
+ const serialized = JSON.stringify(value);
551
+ // Atomic operation: SET key value EX ttl NX
552
+ // EX = Set expiry in seconds
553
+ // NX = Only set if key doesn't exist
554
+ // Returns "OK" if set successfully, null if key already exists
555
+ const result = await this.client.set(key, serialized, "EX", ttl, "NX");
556
+ return result === "OK";
557
+ }
558
+ catch (error) {
559
+ this.handleError(error, null, {
560
+ method: "CacheService.setIfNotExists",
561
+ action: "error",
562
+ additionalData: {
563
+ message: "Failed to set value atomically",
564
+ key,
565
+ ttl,
566
+ },
567
+ });
568
+ return false;
569
+ }
570
+ }
429
571
  /**
430
572
  * Disconnect the cache client
431
573
  * Use this for graceful shutdown
@@ -440,8 +582,7 @@ class CacheService {
440
582
  this.client.disconnect();
441
583
  }
442
584
  this.client = null;
443
- // this.isInitialized = false;
444
- // this.initPromise = null;
585
+ this.isConnected = false;
445
586
  (0, logger_1.default)("Cache client disconnected", {
446
587
  level: "info",
447
588
  method: "CacheService.disconnect",
@@ -450,6 +591,8 @@ class CacheService {
450
591
  }
451
592
  }
452
593
  async ping() {
594
+ if (!this.isConnected)
595
+ return "";
453
596
  try {
454
597
  const result = await this.client.ping();
455
598
  return result;
@@ -460,6 +603,7 @@ class CacheService {
460
603
  action: "error",
461
604
  additionalData: { message: "Failed to ping cache client" },
462
605
  });
606
+ return "";
463
607
  }
464
608
  }
465
609
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cacheService.js","sourceRoot":"","sources":["../../../utils/cacheService/cacheService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cacheService.js","sourceRoot":"","sources":["../../../utils/cacheService/cacheService.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winible/winible-typed",
3
- "version": "2.109.0",
3
+ "version": "2.111.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -52,7 +52,7 @@ describe('CacheService', () => {
52
52
  undefined,
53
53
  expect.objectContaining({
54
54
  method: 'CacheService.initialize',
55
- action: 'error',
55
+ action: 'validation_error',
56
56
  additionalData: expect.objectContaining({
57
57
  message: expect.any(String),
58
58
  }),
@@ -73,7 +73,7 @@ describe('CacheService', () => {
73
73
  undefined,
74
74
  expect.objectContaining({
75
75
  method: 'CacheService.initialize',
76
- action: 'error',
76
+ action: 'validation_error',
77
77
  additionalData: expect.objectContaining({
78
78
  message: expect.any(String),
79
79
  }),
@@ -94,7 +94,7 @@ describe('CacheService', () => {
94
94
  undefined,
95
95
  expect.objectContaining({
96
96
  method: 'CacheService.initialize',
97
- action: 'error',
97
+ action: 'validation_error',
98
98
  additionalData: expect.objectContaining({
99
99
  message: expect.any(String),
100
100
  }),
@@ -446,6 +446,142 @@ describe('CacheService', () => {
446
446
  });
447
447
  });
448
448
 
449
+ describe('Atomic Operations', () => {
450
+ describe('setIfNotExists', () => {
451
+ it('should set value if key does not exist', async () => {
452
+ const wasSet = await cacheService.setIfNotExists('atomic:1', { data: 'value1' }, 300);
453
+ expect(wasSet).toBe(true);
454
+
455
+ const value = await cacheService.get('atomic:1');
456
+ expect(value).toEqual({ data: 'value1' });
457
+ });
458
+
459
+ it('should return false if key already exists', async () => {
460
+ // Set initial value
461
+ await cacheService.set('atomic:2', { data: 'original' }, 300);
462
+
463
+ // Try to set with setIfNotExists
464
+ const wasSet = await cacheService.setIfNotExists('atomic:2', { data: 'new' }, 300);
465
+ expect(wasSet).toBe(false);
466
+
467
+ // Verify original value unchanged
468
+ const value = await cacheService.get('atomic:2');
469
+ expect(value).toEqual({ data: 'original' });
470
+ });
471
+
472
+ it('should set value with TTL', async () => {
473
+ const wasSet = await cacheService.setIfNotExists('atomic:ttl', { data: 'temp' }, 1);
474
+ expect(wasSet).toBe(true);
475
+
476
+ // Verify key exists immediately
477
+ let value = await cacheService.get('atomic:ttl');
478
+ expect(value).toEqual({ data: 'temp' });
479
+
480
+ // Wait for expiry
481
+ await new Promise(resolve => setTimeout(resolve, 1100));
482
+
483
+ // Verify key is gone
484
+ value = await cacheService.get('atomic:ttl');
485
+ expect(value).toBeNull();
486
+ });
487
+
488
+ it('should handle concurrent requests atomically', async () => {
489
+ const key = 'atomic:concurrent';
490
+ const promises = Array(10)
491
+ .fill(null)
492
+ .map((_, i) => cacheService.setIfNotExists(key, { request: i }, 300));
493
+
494
+ const results = await Promise.all(promises);
495
+ const successCount = results.filter(r => r === true).length;
496
+
497
+ // Only ONE request should succeed
498
+ expect(successCount).toBe(1);
499
+
500
+ // Verify a value was set
501
+ const value = await cacheService.get<{ request: number }>(key);
502
+ expect(value).toHaveProperty('request');
503
+ expect(typeof value!.request).toBe('number');
504
+ expect(value!.request).toBeGreaterThanOrEqual(0);
505
+ expect(value!.request).toBeLessThan(10);
506
+ });
507
+
508
+ it('should allow setting after key expires', async () => {
509
+ // Set with 1 second TTL
510
+ const firstSet = await cacheService.setIfNotExists('atomic:expire', { data: 'first' }, 1);
511
+ expect(firstSet).toBe(true);
512
+
513
+ // Wait for expiry
514
+ await new Promise(resolve => setTimeout(resolve, 1100));
515
+
516
+ // Should be able to set again after expiration
517
+ const secondSet = await cacheService.setIfNotExists('atomic:expire', { data: 'second' }, 300);
518
+ expect(secondSet).toBe(true);
519
+
520
+ const value = await cacheService.get('atomic:expire');
521
+ expect(value).toEqual({ data: 'second' });
522
+ });
523
+
524
+ it('should work with string values', async () => {
525
+ const wasSet = await cacheService.setIfNotExists('atomic:string', 'simple-value', 300);
526
+ expect(wasSet).toBe(true);
527
+
528
+ const value = await cacheService.get<string>('atomic:string');
529
+ expect(value).toBe('simple-value');
530
+ });
531
+
532
+ it('should work with numeric values', async () => {
533
+ const wasSet = await cacheService.setIfNotExists('atomic:number', 42, 300);
534
+ expect(wasSet).toBe(true);
535
+
536
+ const value = await cacheService.get<number>('atomic:number');
537
+ expect(value).toBe(42);
538
+ });
539
+
540
+ it('should work with array values', async () => {
541
+ const arr = [1, 2, 3, 4, 5];
542
+ const wasSet = await cacheService.setIfNotExists('atomic:array', arr, 300);
543
+ expect(wasSet).toBe(true);
544
+
545
+ const value = await cacheService.get<number[]>('atomic:array');
546
+ expect(value).toEqual(arr);
547
+ });
548
+
549
+ it('should work with complex nested objects', async () => {
550
+ const complex = {
551
+ userId: 'user-123',
552
+ metadata: {
553
+ usedAt: Date.now(),
554
+ source: 'mobile',
555
+ nested: {
556
+ deep: {
557
+ value: true,
558
+ },
559
+ },
560
+ },
561
+ };
562
+
563
+ const wasSet = await cacheService.setIfNotExists('atomic:complex', complex, 300);
564
+ expect(wasSet).toBe(true);
565
+
566
+ const value = await cacheService.get('atomic:complex');
567
+ expect(value).toEqual(complex);
568
+ });
569
+
570
+ it('should return false when trying to overwrite existing key with different TTL', async () => {
571
+ // Set initial value with 300s TTL
572
+ await cacheService.set('atomic:overwrite', { data: 'original' }, 300);
573
+
574
+ // Try to set with different TTL
575
+ const wasSet = await cacheService.setIfNotExists('atomic:overwrite', { data: 'new' }, 600);
576
+ expect(wasSet).toBe(false);
577
+
578
+ // Verify original value and TTL unchanged
579
+ const value = await cacheService.get('atomic:overwrite');
580
+ expect(value).toEqual({ data: 'original' });
581
+ });
582
+ });
583
+ });
584
+
449
585
  describe('Connection Management', () => {
450
586
  it('should disconnect gracefully', async () => {
451
587
  await cacheService.set('test', 'value');
@@ -33,12 +33,14 @@ type ErrorHandler = (
33
33
  class CacheService {
34
34
  private client: Redis | null = null;
35
35
  private errorHandler: any = null;
36
+ private isConnected: boolean = false; // Track connection state
37
+ private hasConnectedBefore: boolean = false; // Track if we've ever connected
36
38
 
37
39
  /**
38
40
  * Set a custom error handler for capturing errors to Sentry or other services
39
41
  * @param handler - Function to handle errors
40
42
  */
41
- setErrorHandler(handler: any): void {
43
+ setErrorHandler(handler: (error: any, req: any, context: any) => void): void {
42
44
  this.errorHandler = handler;
43
45
  }
44
46
 
@@ -56,7 +58,7 @@ class CacheService {
56
58
  }
57
59
  ): void {
58
60
  if (this.errorHandler) {
59
- error.name = context.method
61
+ error.name = context.method;
60
62
  this.errorHandler(error, undefined, context);
61
63
  } else {
62
64
  // Fallback to logger if no error handler is set
@@ -85,13 +87,37 @@ class CacheService {
85
87
  const password = process.env.ELASTICACHE_PASSWORD;
86
88
 
87
89
  if (!host) {
88
- throw new Error("ELASTICACHE_HOST environment variable is required");
90
+ const error = new Error(
91
+ "ELASTICACHE_HOST environment variable is required"
92
+ );
93
+ this.handleError(error, null, {
94
+ method: "CacheService.initialize",
95
+ action: "validation_error",
96
+ additionalData: { message: "Missing ELASTICACHE_HOST" },
97
+ });
98
+ return;
89
99
  }
90
100
  if (!username) {
91
- throw new Error("ELASTICACHE_USERNAME environment variable is required");
101
+ const error = new Error(
102
+ "ELASTICACHE_USERNAME environment variable is required"
103
+ );
104
+ this.handleError(error, null, {
105
+ method: "CacheService.initialize",
106
+ action: "validation_error",
107
+ additionalData: { message: "Missing ELASTICACHE_USERNAME" },
108
+ });
109
+ return;
92
110
  }
93
111
  if (!password) {
94
- throw new Error("ELASTICACHE_PASSWORD environment variable is required");
112
+ const error = new Error(
113
+ "ELASTICACHE_PASSWORD environment variable is required"
114
+ );
115
+ this.handleError(error, null, {
116
+ method: "CacheService.initialize",
117
+ action: "validation_error",
118
+ additionalData: { message: "Missing ELASTICACHE_PASSWORD" },
119
+ });
120
+ return;
95
121
  }
96
122
 
97
123
  logger("Initializing cache client", {
@@ -106,64 +132,85 @@ class CacheService {
106
132
  port,
107
133
  username,
108
134
  password,
109
- connectTimeout: 10000, // 10 seconds timeout for initial connection
135
+ connectTimeout: 10000, // 10s connection timeout
136
+ commandTimeout: 5000, // fail commands taking longer than 5s
137
+ // enableOfflineQueue: false, // fail immediately when disconnected
138
+ maxRetriesPerRequest: 1, // fail fast with only 1 retry attempt
110
139
  retryStrategy: (times: number) => {
111
- // Stop retrying after 3 attempts
112
- if (times > 3) {
113
- return null; // Stop retrying - this will cause the connection to fail
114
- }
115
- const delay = Math.min(times * 50, 2000);
140
+ // Retry with exponential backoff, max 30 seconds between attempts
141
+ const delay = Math.min(times * 1000, 30000);
116
142
  return delay;
117
143
  },
118
- maxRetriesPerRequest: parseInt(process.env.ELASTICACHE_MAX_RETRIES, 10) || 3,
119
- tls: process.env.ELASTICACHE_SSL === "false" || process.env.ENVIRONMENT === "local"
120
- ? null : {
121
- rejectUnauthorized: true,
122
- }
123
- });
124
-
125
- this.client.on("connect", () => {
126
- logger("Cache client connected", {
127
- level: "info",
128
- method: "CacheService",
129
- action: "connected",
130
- });
144
+ tls:
145
+ process.env.ELASTICACHE_SSL === "false" ||
146
+ process.env.ENVIRONMENT === "local"
147
+ ? null
148
+ : {
149
+ rejectUnauthorized: true,
150
+ },
131
151
  });
132
152
 
133
153
  this.client.on("ready", () => {
134
- logger("Cache client ready", {
135
- level: "info",
136
- method: "CacheService",
137
- action: "ready",
138
- });
154
+ // Only log when transitioning from disconnected to connected
155
+ if (!this.isConnected) {
156
+ this.isConnected = true;
157
+
158
+ // Only alert Sentry on Reconnection, not initial connection
159
+ if (this.hasConnectedBefore && this.errorHandler) {
160
+ const reconnectInfo = new Error("Cache reconnected successfully");
161
+ reconnectInfo.name = "CacheReconnected";
162
+
163
+ this.errorHandler(reconnectInfo, undefined, {
164
+ method: "CacheService",
165
+ action: "reconnected",
166
+ additionalData: { message: "Cache connection restored" },
167
+ });
168
+ } else {
169
+ // Mark that we've connected at least once
170
+ this.hasConnectedBefore = true;
171
+
172
+ logger("Cache client connected successfully", {
173
+ level: "info",
174
+ method: "CacheService",
175
+ action: "connected",
176
+ });
177
+ }
178
+ }
139
179
  });
140
180
 
141
181
  this.client.on("error", (error) => {
142
- logger("Cache client error", {
143
- level: "error",
144
- method: "CacheService",
145
- action: "error",
146
- additionalData: { error: error.message },
147
- });
182
+ // Only report error when transitioning from connected to disconnected
183
+ if (this.isConnected) {
184
+ this.isConnected = false;
185
+ this.handleError(error, null, {
186
+ method: "CacheService",
187
+ action: "disconnected",
188
+ additionalData: {
189
+ message: "Cache connection lost",
190
+ error: error.message,
191
+ },
192
+ });
193
+ }
194
+ // Ignore repeated errors while already disconnected
148
195
  });
149
196
 
150
197
  this.client.on("close", () => {
151
- logger("Cache client disconnected", {
152
- level: "info",
153
- method: "CacheService",
154
- action: "disconnected",
155
- });
198
+ // Only log when transitioning from connected to disconnected
199
+ if (this.isConnected) {
200
+ this.isConnected = false;
201
+ this.handleError(new Error("Cache Connection Closed"), null, {
202
+ method: "CacheService",
203
+ action: "disconnected",
204
+ additionalData: { message: "Cache client disconnected" },
205
+ });
206
+ }
207
+ // Ignore repeated close events while already disconnected
156
208
  });
157
209
 
158
- // Wait for connection with timeout and throw if it fails
159
- await Promise.race([
160
- this.client.ping(),
161
- new Promise((_, reject) =>
162
- setTimeout(() => reject(new Error("Connection timeout after 20 seconds")), 20000)
163
- ),
164
- ]);
210
+ await this.client.ping();
211
+ this.isConnected = true;
165
212
 
166
- logger("Cache client connected successfully", {
213
+ logger("Cache client initialized successfully", {
167
214
  level: "info",
168
215
  method: "CacheService.initialize",
169
216
  action: "completed",
@@ -172,7 +219,9 @@ class CacheService {
172
219
  this.handleError(error, null, {
173
220
  method: "CacheService.initialize",
174
221
  action: "error",
175
- additionalData: { message: error.message || "Failed to initialize cache client", },
222
+ additionalData: {
223
+ message: error.message || "Failed to initialize cache client",
224
+ },
176
225
  });
177
226
  }
178
227
  }
@@ -183,6 +232,8 @@ class CacheService {
183
232
  * @returns The cached value or null if not found
184
233
  */
185
234
  async get<T>(key: string): Promise<T | null> {
235
+ if (!this.isConnected) return null;
236
+
186
237
  try {
187
238
  const value = await this.client!.get(key);
188
239
  if (value === null) {
@@ -206,6 +257,8 @@ class CacheService {
206
257
  * @param ttl - Optional time-to-live in seconds
207
258
  */
208
259
  async set<T>(key: string, value: T, ttl?: number): Promise<void> {
260
+ if (!this.isConnected) return;
261
+
209
262
  try {
210
263
  const serialized = JSON.stringify(value);
211
264
  if (ttl) {
@@ -227,6 +280,8 @@ class CacheService {
227
280
  * @param key - The cache key to delete
228
281
  */
229
282
  async delete(key: string): Promise<void> {
283
+ if (!this.isConnected) return;
284
+
230
285
  try {
231
286
  await this.client!.del(key);
232
287
  } catch (error) {
@@ -245,6 +300,8 @@ class CacheService {
245
300
  * @returns Array of matching keys
246
301
  */
247
302
  async keys(pattern: string): Promise<string[]> {
303
+ if (!this.isConnected) return [];
304
+
248
305
  try {
249
306
  const keys: string[] = [];
250
307
  let cursor = "0";
@@ -278,6 +335,8 @@ class CacheService {
278
335
  * @returns The total number of keys
279
336
  */
280
337
  async dbsize(): Promise<number> {
338
+ if (!this.isConnected) return 0;
339
+
281
340
  try {
282
341
  const size = await this.client!.dbsize();
283
342
  return size;
@@ -297,6 +356,8 @@ class CacheService {
297
356
  * @param pattern - Pattern to match (e.g., "user:*", "*:session")
298
357
  */
299
358
  async clearPattern(pattern: string): Promise<void> {
359
+ if (!this.isConnected) return;
360
+
300
361
  try {
301
362
  const keys = await this.keys(pattern);
302
363
  if (keys.length > 0) {
@@ -315,7 +376,10 @@ class CacheService {
315
376
  this.handleError(error, null, {
316
377
  method: "CacheService.clearPattern",
317
378
  action: "error",
318
- additionalData: { message: "Failed to clear keys matching pattern", pattern },
379
+ additionalData: {
380
+ message: "Failed to clear keys matching pattern",
381
+ pattern,
382
+ },
319
383
  });
320
384
  }
321
385
  }
@@ -326,6 +390,8 @@ class CacheService {
326
390
  * @returns true if the key exists, false otherwise
327
391
  */
328
392
  async exists(key: string): Promise<boolean> {
393
+ if (!this.isConnected) return false;
394
+
329
395
  try {
330
396
  const result = await this.client!.exists(key);
331
397
  return result === 1;
@@ -346,6 +412,8 @@ class CacheService {
346
412
  async mset(
347
413
  entries: Array<{ key: string; value: any; ttl?: number }>
348
414
  ): Promise<void> {
415
+ if (!this.isConnected) return;
416
+
349
417
  try {
350
418
  const pipeline = this.client!.pipeline();
351
419
  entries.forEach(({ key, value, ttl }) => {
@@ -361,7 +429,10 @@ class CacheService {
361
429
  this.handleError(error, null, {
362
430
  method: "CacheService.mset",
363
431
  action: "error",
364
- additionalData: { message: "Failed to set multiple values in cache", count: entries.length },
432
+ additionalData: {
433
+ message: "Failed to set multiple values in cache",
434
+ count: entries.length,
435
+ },
365
436
  });
366
437
  }
367
438
  }
@@ -372,16 +443,19 @@ class CacheService {
372
443
  * @returns Array of cached values (null for missing keys)
373
444
  */
374
445
  async mget<T>(keys: string[]): Promise<Array<T | null>> {
446
+ if (!this.isConnected) return keys.map(() => null);
447
+
375
448
  try {
376
449
  const values = await this.client!.mget(...keys);
377
- return values.map((value) =>
378
- value ? (JSON.parse(value) as T) : null
379
- );
450
+ return values.map((value) => (value ? (JSON.parse(value) as T) : null));
380
451
  } catch (error) {
381
452
  this.handleError(error, null, {
382
453
  method: "CacheService.mget",
383
454
  action: "error",
384
- additionalData: { message: "Failed to get multiple values from cache", count: keys.length },
455
+ additionalData: {
456
+ message: "Failed to get multiple values from cache",
457
+ count: keys.length,
458
+ },
385
459
  });
386
460
  return keys.map(() => null);
387
461
  }
@@ -394,13 +468,19 @@ class CacheService {
394
468
  * @returns The new value after increment
395
469
  */
396
470
  async increment(key: string, increment: number = 1): Promise<number> {
471
+ if (!this.isConnected) return 0;
472
+
397
473
  try {
398
474
  return await this.client!.incrby(key, increment);
399
475
  } catch (error) {
400
476
  this.handleError(error, null, {
401
477
  method: "CacheService.increment",
402
478
  action: "error",
403
- additionalData: { message: "Failed to increment value in cache", key, increment },
479
+ additionalData: {
480
+ message: "Failed to increment value in cache",
481
+ key,
482
+ increment,
483
+ },
404
484
  });
405
485
  return 0;
406
486
  }
@@ -413,13 +493,19 @@ class CacheService {
413
493
  * @returns The new value after decrement
414
494
  */
415
495
  async decrement(key: string, decrement: number = 1): Promise<number> {
496
+ if (!this.isConnected) return 0;
497
+
416
498
  try {
417
499
  return await this.client!.decrby(key, decrement);
418
500
  } catch (error) {
419
501
  this.handleError(error, null, {
420
502
  method: "CacheService.decrement",
421
503
  action: "error",
422
- additionalData: { message: "Failed to decrement value in cache", key, decrement },
504
+ additionalData: {
505
+ message: "Failed to decrement value in cache",
506
+ key,
507
+ decrement,
508
+ },
423
509
  });
424
510
  return 0;
425
511
  }
@@ -432,6 +518,8 @@ class CacheService {
432
518
  * @returns true if the timeout was set, false if key doesn't exist
433
519
  */
434
520
  async expire(key: string, ttl: number): Promise<boolean> {
521
+ if (!this.isConnected) return false;
522
+
435
523
  try {
436
524
  const result = await this.client!.expire(key, ttl);
437
525
  return result === 1;
@@ -439,7 +527,11 @@ class CacheService {
439
527
  this.handleError(error, null, {
440
528
  method: "CacheService.expire",
441
529
  action: "error",
442
- additionalData: { message: "Failed to set expiration on key", key, ttl },
530
+ additionalData: {
531
+ message: "Failed to set expiration on key",
532
+ key,
533
+ ttl,
534
+ },
443
535
  });
444
536
  return false;
445
537
  }
@@ -451,6 +543,8 @@ class CacheService {
451
543
  * @returns TTL in seconds, -1 if key exists but has no expire, -2 if key doesn't exist
452
544
  */
453
545
  async ttl(key: string): Promise<number> {
546
+ if (!this.isConnected) return -2;
547
+
454
548
  try {
455
549
  return await this.client!.ttl(key);
456
550
  } catch (error) {
@@ -463,6 +557,64 @@ class CacheService {
463
557
  }
464
558
  }
465
559
 
560
+ /**
561
+ * Atomically set a value only if the key doesn't exist (SETNX)
562
+ * Uses Redis SET NX EX command which is atomic - no race conditions possible.
563
+ *
564
+ * @param key - The cache key
565
+ * @param value - The value to cache (will be JSON serialized)
566
+ * @param ttl - Time-to-live in seconds (required for safety)
567
+ * @returns true if the value was set (key didn't exist), false if key already exists
568
+ *
569
+ * @example
570
+ * // Single-use token implementation
571
+ * const wasSet = await cacheService.setIfNotExists('magic_link:abc123', { userId: '123' }, 300);
572
+ * if (!wasSet) {
573
+ * throw new Error('Token already used');
574
+ * }
575
+ *
576
+ * @example
577
+ * // Distributed lock
578
+ * const lockAcquired = await cacheService.setIfNotExists('lock:resource_id', { owner: 'worker-1' }, 30);
579
+ * if (lockAcquired) {
580
+ * try {
581
+ * // Do work while holding lock
582
+ * } finally {
583
+ * await cacheService.delete('lock:resource_id');
584
+ * }
585
+ * }
586
+ */
587
+ async setIfNotExists<T>(
588
+ key: string,
589
+ value: T,
590
+ ttl: number
591
+ ): Promise<boolean> {
592
+ if (!this.isConnected) return false;
593
+
594
+ try {
595
+ const serialized = JSON.stringify(value);
596
+
597
+ // Atomic operation: SET key value EX ttl NX
598
+ // EX = Set expiry in seconds
599
+ // NX = Only set if key doesn't exist
600
+ // Returns "OK" if set successfully, null if key already exists
601
+ const result = await this.client!.set(key, serialized, "EX", ttl, "NX");
602
+
603
+ return result === "OK";
604
+ } catch (error) {
605
+ this.handleError(error, null, {
606
+ method: "CacheService.setIfNotExists",
607
+ action: "error",
608
+ additionalData: {
609
+ message: "Failed to set value atomically",
610
+ key,
611
+ ttl,
612
+ },
613
+ });
614
+ return false;
615
+ }
616
+ }
617
+
466
618
  /**
467
619
  * Disconnect the cache client
468
620
  * Use this for graceful shutdown
@@ -476,8 +628,7 @@ class CacheService {
476
628
  this.client.disconnect();
477
629
  }
478
630
  this.client = null;
479
- // this.isInitialized = false;
480
- // this.initPromise = null;
631
+ this.isConnected = false;
481
632
  logger("Cache client disconnected", {
482
633
  level: "info",
483
634
  method: "CacheService.disconnect",
@@ -487,6 +638,8 @@ class CacheService {
487
638
  }
488
639
 
489
640
  async ping(): Promise<string> {
641
+ if (!this.isConnected) return "";
642
+
490
643
  try {
491
644
  const result = await this.client!.ping();
492
645
  return result;
@@ -496,6 +649,7 @@ class CacheService {
496
649
  action: "error",
497
650
  additionalData: { message: "Failed to ping cache client" },
498
651
  });
652
+ return "";
499
653
  }
500
654
  }
501
655
  }