Skip to content

Commit a29acc4

Browse files
committed
Add graceful Redis failure handling with aggressive timeouts
- Wrap all Redis operations in try-catch blocks to prevent app crashes - Add aggressive timeout configurations (200ms command, 500ms connect) - Disable retries and offline queue for fast-fail behavior - Remove unnecessary try-catch blocks from db.ts (now handled in RedisCache) - App continues functioning without cache when Redis is unavailable ⚠️ UNTESTED - needs verification in staging environment
1 parent a71e477 commit a29acc4

File tree

2 files changed

+46
-37
lines changed

2 files changed

+46
-37
lines changed

src/RedisCache.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,57 @@ class RedisCache implements Cache {
99
private redis_: Redis;
1010

1111
constructor(options: RedisOptions) {
12-
this.redis_ = new Redis(options);
12+
this.redis_ = new Redis({
13+
...options,
14+
connectTimeout: 500, // 500ms to connect
15+
commandTimeout: 200, // 200ms per command
16+
retryStrategy: (times) => {
17+
// Stop retrying after 2 attempts
18+
if (times > 2) return null;
19+
return 50; // Wait only 50ms between retries
20+
},
21+
maxRetriesPerRequest: 0, // Don't retry - fail immediately
22+
enableOfflineQueue: false, // Don't queue commands when disconnected
23+
});
24+
25+
this.redis_.on('error', (err) => {
26+
console.error('Redis error (app will continue without cache):', err.message);
27+
});
1328
}
1429

1530
private static key_ = ({ collection, id }: Selector): string => {
1631
return `${collection}/${id}`;
1732
};
1833

1934
async get(selector: Selector): Promise<object | null> {
20-
const data = await this.redis_.get(RedisCache.key_(selector));
21-
if (!data) return null;
22-
23-
return JSON.parse(data);
35+
try {
36+
const data = await this.redis_.get(RedisCache.key_(selector));
37+
if (!data) return null;
38+
return JSON.parse(data);
39+
} catch (err) {
40+
console.error('Redis GET failed, continuing without cache:', err);
41+
return null;
42+
}
2443
}
2544

2645
async set(selector: Selector, value: object | null): Promise<void> {
27-
if (!value) {
28-
await this.redis_.del(RedisCache.key_(selector));
29-
return;
46+
try {
47+
if (!value) {
48+
await this.redis_.del(RedisCache.key_(selector));
49+
return;
50+
}
51+
await this.redis_.setex(RedisCache.key_(selector), RedisCache.DEFAULT_TTL, JSON.stringify(value));
52+
} catch (err) {
53+
console.error('Redis SET failed, continuing without cache:', err);
3054
}
31-
32-
await this.redis_.setex(RedisCache.key_(selector), RedisCache.DEFAULT_TTL, JSON.stringify(value));
3355
}
3456

3557
async remove(selector: Selector): Promise<void> {
36-
await this.redis_.del(RedisCache.key_(selector));
58+
try {
59+
await this.redis_.del(RedisCache.key_(selector));
60+
} catch (err) {
61+
console.error('Redis DEL failed, continuing without cache:', err);
62+
}
3763
}
3864
}
3965

src/db.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,12 @@ class Db {
6060

6161
const cacheSelector: Selector = { ...selector, collection: fCollection };
6262

63-
try {
64-
const cached = await this.cache_.get(cacheSelector);
63+
const cached = await this.cache_.get(cacheSelector);
6564

66-
if (cached) return {
67-
type: 'success',
68-
value: cached as T,
69-
};
70-
} catch (e) {
71-
console.error(e);
72-
}
65+
if (cached) return {
66+
type: 'success',
67+
value: cached as T,
68+
};
7369

7470
const doc = await firestore
7571
.collection(fCollection)
@@ -82,12 +78,8 @@ class Db {
8278
message: `Document "${selector.id}" not found.`,
8379
};
8480

85-
try {
86-
// Update the cache
87-
await this.cache_.set(cacheSelector, doc.data());
88-
} catch (e) {
89-
console.error(e);
90-
}
81+
// Update the cache
82+
await this.cache_.set(cacheSelector, doc.data());
9183

9284
return {
9385
type: 'success',
@@ -120,12 +112,7 @@ class Db {
120112
await docRef.set(value, { mergeFields: keysToReplace });
121113
} else {
122114
await docRef.set(value);
123-
124-
try {
125-
await this.cache_.set(cacheSelector, value);
126-
} catch (e) {
127-
console.error(e);
128-
}
115+
await this.cache_.set(cacheSelector, value);
129116
}
130117

131118
return {
@@ -143,11 +130,7 @@ class Db {
143130

144131
const cacheSelector: Selector = { ...selector, collection: fCollection };
145132

146-
try {
147-
await this.cache_.remove(cacheSelector);
148-
} catch (e) {
149-
console.error(e);
150-
}
133+
await this.cache_.remove(cacheSelector);
151134

152135
await firestore
153136
.collection(fCollection)

0 commit comments

Comments
 (0)