Skip to content

Commit 3165def

Browse files
authored
✨ tweak: enhance railway redis connection and retry logic
1 parent 53b77fe commit 3165def

File tree

1 file changed

+65
-49
lines changed

1 file changed

+65
-49
lines changed

src/services/redisService.ts

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,54 @@ export class RedisService {
1414
try {
1515
// Check if already connected
1616
if (this.client.isOpen) {
17-
LogEngine.debug('Redis 8-alpine connection already established');
17+
LogEngine.debug('Railway Redis 8-alpine connection already established');
1818
return;
1919
}
2020

21-
LogEngine.debug('Attempting Redis 8-alpine connection...');
21+
LogEngine.warn('EMERGENCY: Attempting Railway Redis 8-alpine connection with extended timeout...');
2222

23-
// Redis 8-alpine optimized connection with shorter timeout
23+
// Emergency connection timeout for Railway Redis issues
2424
const connectPromise = this.client.connect();
2525
const timeoutPromise = new Promise((_, reject) => {
26-
setTimeout(() => reject(new Error('Redis 8-alpine connection timeout after 8 seconds')), 8000);
26+
setTimeout(() => reject(new Error('EMERGENCY: Railway Redis 8-alpine connection timeout after 15 seconds')), 15000);
2727
});
2828

2929
await Promise.race([connectPromise, timeoutPromise]);
3030

31-
// Redis 8-alpine health check
32-
await this.healthCheck();
31+
// Skip health check if Redis is struggling
32+
LogEngine.warn('Railway Redis connected, skipping health check due to performance issues');
3333

3434
// Connection success is logged by the 'ready' event handler in redis.ts
3535
} catch (err) {
36-
LogEngine.error(`Redis 8-alpine connection failed: ${err}`);
36+
LogEngine.error(`EMERGENCY: Railway Redis 8-alpine connection failed: ${err}`);
3737
throw err;
3838
}
3939
}
4040

4141
/**
42-
* Redis 8-alpine health check
42+
* Emergency Railway Redis health check - reduced functionality
4343
*/
4444
private async healthCheck(): Promise<void> {
4545
try {
46+
LogEngine.warn('EMERGENCY: Attempting Railway Redis health check with extended timeout...');
47+
4648
const pong = await this.executeWithRetry(
4749
() => this.client.ping(),
48-
'health check ping',
49-
1, // Single attempt for health check
50+
'emergency health check ping',
51+
1, // Single attempt only
5052
0, // No delay for health check
51-
2000 // 2 second timeout for ping
53+
8000 // Extended timeout for ping
5254
);
5355

5456
if (pong !== 'PONG') {
55-
throw new Error(`Redis 8-alpine health check failed: expected PONG, got ${pong}`);
57+
throw new Error(`Railway Redis health check failed: expected PONG, got ${pong}`);
5658
}
5759

58-
LogEngine.debug('Redis 8-alpine health check passed');
60+
LogEngine.warn('Railway Redis health check passed despite performance issues');
5961
} catch (error) {
60-
LogEngine.warn(`Redis 8-alpine health check failed: ${error}`);
61-
// Don't throw - connection might still work for operations
62+
LogEngine.error(`EMERGENCY: Railway Redis health check completely failed: ${error}`);
63+
LogEngine.error('SUGGESTION: Consider upgrading Railway Redis plan or switching to external Redis provider');
64+
// Don't throw - allow connection to proceed
6265
}
6366
}
6467

@@ -78,79 +81,87 @@ export class RedisService {
7881
completeTransformedData: event
7982
});
8083

81-
// Use Redis LIST for FIFO queue with Railway-optimized retry logic
84+
LogEngine.warn(`EMERGENCY: Attempting lPush to Railway Redis with extended timeout...`);
85+
86+
// Emergency lPush with maximum timeout for Railway Redis issues
8287
const result = await this.executeWithRetry(
8388
() => this.client.lPush(queueName, eventJson),
8489
`lPush to ${queueName}`,
85-
3 // max retries
90+
5, // More retries for critical operation
91+
2000, // Longer initial delay
92+
20000 // Emergency: 20 second timeout for lPush
8693
);
8794

88-
LogEngine.info(`✅ Event successfully queued: ${event.data?.eventId || 'unknown'} -> ${queueName} (${result} items in queue)`);
95+
LogEngine.info(`✅ EMERGENCY SUCCESS: Event queued after Railway Redis struggle: ${event.data?.eventId || 'unknown'} -> ${queueName} (${result} items in queue)`);
8996
return result;
9097
} catch (err) {
91-
LogEngine.error(`Error publishing event to queue: ${err}`);
98+
LogEngine.error(`EMERGENCY FAILURE: Railway Redis lPush completely failed: ${err}`);
9299
throw err;
93100
}
94101
}
95102

96103
/**
97-
* Redis 8-alpine & Railway-optimized retry logic for operations
104+
* Emergency Railway Redis 8-alpine retry logic with extended timeouts
98105
*/
99106
private async executeWithRetry<T>(
100107
operation: () => Promise<T>,
101108
operationName: string,
102109
maxRetries: number = 3,
103-
baseDelay: number = 500, // Faster initial retry for Redis 8
104-
operationTimeout: number = 5000 // Reduced to 5s for Redis 8-alpine
110+
baseDelay: number = 1000, // Back to 1s for Railway issues
111+
operationTimeout: number = 15000 // Emergency: 15s timeout for Railway Redis issues
105112
): Promise<T> {
106113
let lastError: Error = new Error('Unknown error');
107114

108115
for (let attempt = 1; attempt <= maxRetries; attempt++) {
109116
try {
110-
// Redis 8-alpine optimized timeout wrapper
117+
// Emergency timeout for Railway's struggling Redis
111118
const timeoutPromise = new Promise<never>((_, reject) => {
112119
setTimeout(() => {
113-
reject(new Error(`Redis operation ${operationName} timed out after ${operationTimeout}ms (Redis 8-alpine)`));
120+
reject(new Error(`Emergency: Redis operation ${operationName} timed out after ${operationTimeout}ms (Railway Redis 8-alpine struggling)`));
114121
}, operationTimeout);
115122
});
116123

124+
LogEngine.debug(`Attempting Redis operation ${operationName} (attempt ${attempt}/${maxRetries}) with ${operationTimeout}ms timeout`);
125+
117126
return await Promise.race([operation(), timeoutPromise]);
118127
} catch (error) {
119128
lastError = error as Error;
120129

121130
if (attempt === maxRetries) {
122-
LogEngine.error(`Redis 8 operation ${operationName} failed after ${maxRetries} attempts: ${lastError.message}`);
131+
LogEngine.error(`EMERGENCY: Railway Redis operation ${operationName} failed after ${maxRetries} attempts with ${operationTimeout}ms timeouts: ${lastError.message}`);
123132
break;
124133
}
125134

126-
// Redis 8-alpine specific error patterns
135+
// Railway Redis emergency error patterns
127136
const isRetryableError = (
128137
lastError.message.includes('ETIMEDOUT') ||
129138
lastError.message.includes('ECONNRESET') ||
130139
lastError.message.includes('ENOTFOUND') ||
131140
lastError.message.includes('Connection is closed') ||
132141
lastError.message.includes('timed out') ||
133142
lastError.message.includes('Redis 8-alpine') ||
134-
lastError.message.includes('LOADING') // Redis 8 loading state
143+
lastError.message.includes('LOADING') ||
144+
lastError.message.includes('struggling') // Emergency pattern
135145
);
136146

137147
if (!isRetryableError) {
138-
LogEngine.error(`Redis 8 operation ${operationName} failed with non-retryable error: ${lastError.message}`);
148+
LogEngine.error(`Railway Redis operation ${operationName} failed with non-retryable error: ${lastError.message}`);
139149
break;
140150
}
141151

142-
// Faster exponential backoff for Redis 8
143-
const delay = Math.min(baseDelay * Math.pow(1.5, attempt - 1), 2000); // Cap at 2s
144-
LogEngine.warn(`Redis 8 operation ${operationName} failed (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms: ${lastError.message}`);
152+
// Extended delays for Railway Redis issues
153+
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), 5000); // Cap at 5s
154+
LogEngine.warn(`RAILWAY EMERGENCY: Redis operation ${operationName} failed (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms: ${lastError.message}`);
145155

146156
await new Promise(resolve => setTimeout(resolve, delay));
147157

148-
// Try to reconnect if connection is closed
158+
// Emergency reconnection for Railway
149159
if (!this.isConnected()) {
150160
try {
161+
LogEngine.warn('Emergency reconnection attempt for Railway Redis...');
151162
await this.connect();
152163
} catch (reconnectError) {
153-
LogEngine.warn(`Redis 8 reconnection failed during retry: ${reconnectError}`);
164+
LogEngine.error(`Railway Redis emergency reconnection failed: ${reconnectError}`);
154165
}
155166
}
156167
}
@@ -160,37 +171,42 @@ export class RedisService {
160171
}
161172

162173
/**
163-
* Redis 8-alpine optimized duplicate detection
174+
* Emergency Railway Redis duplicate detection with extended timeouts
164175
* @param eventId - Unique event identifier
165176
* @returns Promise<boolean> - true if event exists
166177
*/
167178
async eventExists(eventId: string): Promise<boolean> {
168179
const key = `${redisEventConfig.keyPrefix}${eventId}`;
169180

170181
try {
171-
// Redis 8 optimization: Use GET instead of EXISTS for better performance
172-
// If key exists, it will return 'processed', if not, it returns null
182+
// Emergency GET with extended timeout for Railway Redis issues
173183
const result = await this.executeWithRetry(
174184
() => this.client.get(key),
175185
`get check for ${eventId}`,
176-
2, // Fewer retries for duplicate checks
177-
300, // Faster retry (300ms)
178-
3000 // Shorter timeout (3s) for GET operations
186+
3, // More retries for Railway issues
187+
1000, // Longer retry delay
188+
10000 // Emergency: 10s timeout for GET
179189
);
180190

181191
return result !== null;
182192
} catch (error) {
183-
// Fallback to EXISTS if GET fails
184-
LogEngine.warn(`GET operation failed for ${eventId}, falling back to EXISTS: ${error}`);
193+
// Emergency fallback to EXISTS with even more tolerance
194+
LogEngine.warn(`Railway Redis GET failed for ${eventId}, attempting emergency EXISTS fallback: ${error}`);
185195

186-
const exists = await this.executeWithRetry(
187-
() => this.client.exists(key),
188-
`exists fallback for ${eventId}`,
189-
1, // Single retry for fallback
190-
500,
191-
2000 // Even shorter timeout for fallback
192-
);
193-
return exists === 1;
196+
try {
197+
const exists = await this.executeWithRetry(
198+
() => this.client.exists(key),
199+
`emergency exists for ${eventId}`,
200+
2, // Reduced retries for fallback
201+
2000, // Longer delay
202+
8000 // Emergency EXISTS timeout
203+
);
204+
return exists === 1;
205+
} catch (fallbackError) {
206+
LogEngine.error(`Railway Redis emergency fallback failed for ${eventId}: ${fallbackError}`);
207+
// Return false to allow processing (better than blocking)
208+
return false;
209+
}
194210
}
195211
}
196212

0 commit comments

Comments
 (0)