@getvision/server 0.4.2-107ad21-develop → 0.4.3-d4c761e-develop
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.
- package/CHANGELOG.md +7 -0
- package/README.md +80 -0
- package/package.json +1 -1
- package/src/event-bus.ts +57 -21
- package/src/vision-app.ts +20 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -205,6 +205,17 @@ const app = new Vision({
|
|
|
205
205
|
},
|
|
206
206
|
pubsub: {
|
|
207
207
|
devMode: true, // In-memory BullMQ for local dev
|
|
208
|
+
redis: {
|
|
209
|
+
host: 'localhost',
|
|
210
|
+
port: 6379,
|
|
211
|
+
password: 'your-password',
|
|
212
|
+
// Connection settings to prevent timeouts
|
|
213
|
+
keepAlive: 30000, // Keep connection alive (30s)
|
|
214
|
+
maxRetriesPerRequest: 20, // Retry failed commands
|
|
215
|
+
enableReadyCheck: true, // Check Redis is ready
|
|
216
|
+
connectTimeout: 10000, // Connection timeout (10s)
|
|
217
|
+
enableOfflineQueue: true // Queue commands when offline
|
|
218
|
+
},
|
|
208
219
|
// BullMQ options
|
|
209
220
|
queue: {
|
|
210
221
|
defaultJobOptions: {
|
|
@@ -362,6 +373,75 @@ app.service('users')
|
|
|
362
373
|
})
|
|
363
374
|
```
|
|
364
375
|
|
|
376
|
+
## Redis Connection Configuration
|
|
377
|
+
|
|
378
|
+
### Preventing Connection Drops
|
|
379
|
+
|
|
380
|
+
Redis connections can close due to timeouts or network issues, causing workers to fail with "Connection is closed" errors. Vision Server includes sensible defaults to help prevent this:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
const app = new Vision({
|
|
384
|
+
service: { name: 'My API' },
|
|
385
|
+
pubsub: {
|
|
386
|
+
devMode: false, // Use Redis in production
|
|
387
|
+
redis: {
|
|
388
|
+
host: process.env.REDIS_HOST,
|
|
389
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
390
|
+
password: process.env.REDIS_PASSWORD,
|
|
391
|
+
// Connection settings (these are the defaults)
|
|
392
|
+
keepAlive: 30000, // Keep connection alive (30s)
|
|
393
|
+
maxRetriesPerRequest: 20, // Retry failed commands
|
|
394
|
+
enableReadyCheck: true, // Check Redis is ready before commands
|
|
395
|
+
connectTimeout: 10000, // Connection timeout (10s)
|
|
396
|
+
enableOfflineQueue: true // Queue commands when offline
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**What this helps with:**
|
|
403
|
+
- ✅ **keepAlive (30s)**: Helps prevent idle connection timeouts
|
|
404
|
+
- ✅ **Automatic reconnection**: Retries up to 10 times with exponential backoff
|
|
405
|
+
- ✅ **Connection settings**: Each Queue/Worker/QueueEvents uses these connection settings
|
|
406
|
+
- ✅ **Offline queue**: Commands are queued when Redis is temporarily unavailable
|
|
407
|
+
|
|
408
|
+
### Environment Variables
|
|
409
|
+
|
|
410
|
+
You can also configure Redis via environment variables:
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
# Option 1: Redis URL (recommended)
|
|
414
|
+
REDIS_URL=redis://:password@hostname:6379
|
|
415
|
+
|
|
416
|
+
# Option 2: Individual variables
|
|
417
|
+
REDIS_HOST=hostname
|
|
418
|
+
REDIS_PORT=6379
|
|
419
|
+
REDIS_PASSWORD=password
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Troubleshooting
|
|
423
|
+
|
|
424
|
+
**"Connection is closed" errors:**
|
|
425
|
+
- Vision Server now includes automatic reconnection with exponential backoff
|
|
426
|
+
- Check logs for `🔄 Redis reconnecting...` messages
|
|
427
|
+
- If reconnection fails after 10 attempts, check your Redis server health
|
|
428
|
+
|
|
429
|
+
**"Could not renew lock for job" errors:**
|
|
430
|
+
- This happens when workers lose connection during job processing
|
|
431
|
+
- The new keepAlive setting (30s) prevents this
|
|
432
|
+
- Increase `keepAlive` if you have very long-running jobs
|
|
433
|
+
|
|
434
|
+
**Custom retry strategy:**
|
|
435
|
+
```typescript
|
|
436
|
+
pubsub: {
|
|
437
|
+
redis: {
|
|
438
|
+
// ... other settings
|
|
439
|
+
keepAlive: 60000, // 60s for long-running jobs
|
|
440
|
+
maxRetriesPerRequest: 30 // More retries for unstable networks
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
365
445
|
## Hono Compatibility
|
|
366
446
|
|
|
367
447
|
Vision extends Hono, so all Hono features work:
|
package/package.json
CHANGED
package/src/event-bus.ts
CHANGED
|
@@ -10,6 +10,26 @@ export interface EventBusConfig {
|
|
|
10
10
|
host?: string
|
|
11
11
|
port?: number
|
|
12
12
|
password?: string
|
|
13
|
+
/**
|
|
14
|
+
* Enable keepalive to prevent connection timeouts (default: true in production)
|
|
15
|
+
*/
|
|
16
|
+
keepAlive?: number
|
|
17
|
+
/**
|
|
18
|
+
* Max retry attempts for failed commands (default: 20)
|
|
19
|
+
*/
|
|
20
|
+
maxRetriesPerRequest?: number
|
|
21
|
+
/**
|
|
22
|
+
* Enable ready check before executing commands (default: true)
|
|
23
|
+
*/
|
|
24
|
+
enableReadyCheck?: boolean
|
|
25
|
+
/**
|
|
26
|
+
* Connection timeout in ms (default: 10000)
|
|
27
|
+
*/
|
|
28
|
+
connectTimeout?: number
|
|
29
|
+
/**
|
|
30
|
+
* Enable offline queue (default: true)
|
|
31
|
+
*/
|
|
32
|
+
enableOfflineQueue?: boolean
|
|
13
33
|
}
|
|
14
34
|
queue?: Omit<QueueOptions, 'connection'>
|
|
15
35
|
worker?: Omit<WorkerOptions, 'connection'>
|
|
@@ -86,6 +106,38 @@ export class EventBus {
|
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Get Redis connection configuration
|
|
111
|
+
* Includes keepalive, retry strategy, and connection pooling
|
|
112
|
+
*/
|
|
113
|
+
private getRedisConnection() {
|
|
114
|
+
const baseConfig = this.config.redis || {
|
|
115
|
+
host: 'localhost',
|
|
116
|
+
port: 6379,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
host: baseConfig.host,
|
|
121
|
+
port: baseConfig.port,
|
|
122
|
+
password: baseConfig.password,
|
|
123
|
+
keepAlive: baseConfig.keepAlive ?? 30000, // 30 seconds keepalive
|
|
124
|
+
maxRetriesPerRequest: baseConfig.maxRetriesPerRequest ?? 20,
|
|
125
|
+
enableReadyCheck: baseConfig.enableReadyCheck ?? true,
|
|
126
|
+
connectTimeout: baseConfig.connectTimeout ?? 10000,
|
|
127
|
+
enableOfflineQueue: baseConfig.enableOfflineQueue ?? true,
|
|
128
|
+
// Retry strategy for automatic reconnection
|
|
129
|
+
retryStrategy: (times: number) => {
|
|
130
|
+
if (times > 10) {
|
|
131
|
+
console.error('❌ Redis connection failed after 10 retries')
|
|
132
|
+
return null // Stop retrying
|
|
133
|
+
}
|
|
134
|
+
const delay = Math.min(times * 200, 3000)
|
|
135
|
+
console.log(`🔄 Redis reconnecting... attempt ${times}, delay ${delay}ms`)
|
|
136
|
+
return delay
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
89
141
|
/**
|
|
90
142
|
* Get or create a queue for an event
|
|
91
143
|
*/
|
|
@@ -97,13 +149,9 @@ export class EventBus {
|
|
|
97
149
|
|
|
98
150
|
let queue = this.queues.get(eventName)
|
|
99
151
|
if (!queue) {
|
|
100
|
-
const connection = this.config.redis || {
|
|
101
|
-
host: 'localhost',
|
|
102
|
-
port: 6379,
|
|
103
|
-
}
|
|
104
152
|
queue = new Queue(eventName, {
|
|
105
153
|
...(this.config.queue || {}),
|
|
106
|
-
connection,
|
|
154
|
+
connection: this.getRedisConnection(),
|
|
107
155
|
})
|
|
108
156
|
this.queues.set(eventName, queue)
|
|
109
157
|
}
|
|
@@ -206,10 +254,6 @@ export class EventBus {
|
|
|
206
254
|
this.devModeHandlers.set(eventName, handlers)
|
|
207
255
|
} else {
|
|
208
256
|
// Production mode - create BullMQ worker
|
|
209
|
-
const connection = this.config.redis || {
|
|
210
|
-
host: 'localhost',
|
|
211
|
-
port: 6379,
|
|
212
|
-
}
|
|
213
257
|
const workerKey = `${eventName}-handler`
|
|
214
258
|
|
|
215
259
|
// Close existing worker if it exists
|
|
@@ -231,7 +275,7 @@ export class EventBus {
|
|
|
231
275
|
},
|
|
232
276
|
{
|
|
233
277
|
...(this.config.worker || {}),
|
|
234
|
-
connection,
|
|
278
|
+
connection: this.getRedisConnection(),
|
|
235
279
|
concurrency:
|
|
236
280
|
options?.concurrency ??
|
|
237
281
|
this.config.workerConcurrency ??
|
|
@@ -244,13 +288,9 @@ export class EventBus {
|
|
|
244
288
|
|
|
245
289
|
// Listen to queue events
|
|
246
290
|
if (!this.queueEvents.has(eventName)) {
|
|
247
|
-
const connection = this.config.redis || {
|
|
248
|
-
host: 'localhost',
|
|
249
|
-
port: 6379,
|
|
250
|
-
}
|
|
251
291
|
const queueEvents = new QueueEvents(eventName, {
|
|
252
292
|
...(this.config.queueEvents || {}),
|
|
253
|
-
connection,
|
|
293
|
+
connection: this.getRedisConnection(),
|
|
254
294
|
})
|
|
255
295
|
|
|
256
296
|
queueEvents.on('completed', ({ jobId }) => {
|
|
@@ -284,10 +324,6 @@ export class EventBus {
|
|
|
284
324
|
this.devModeHandlers.set(cronName, handlers)
|
|
285
325
|
} else {
|
|
286
326
|
// Production mode - create BullMQ worker for cron jobs
|
|
287
|
-
const connection = this.config.redis || {
|
|
288
|
-
host: 'localhost',
|
|
289
|
-
port: 6379,
|
|
290
|
-
}
|
|
291
327
|
const cronWorkerKey = `${cronName}-handler`
|
|
292
328
|
|
|
293
329
|
// Close existing cron worker if it exists
|
|
@@ -315,7 +351,7 @@ export class EventBus {
|
|
|
315
351
|
},
|
|
316
352
|
{
|
|
317
353
|
...(this.config.worker || {}),
|
|
318
|
-
connection,
|
|
354
|
+
connection: this.getRedisConnection(),
|
|
319
355
|
concurrency: this.config.worker?.concurrency ?? 1,
|
|
320
356
|
}
|
|
321
357
|
)
|
|
@@ -326,7 +362,7 @@ export class EventBus {
|
|
|
326
362
|
if (!this.queueEvents.has(cronName)) {
|
|
327
363
|
const queueEvents = new QueueEvents(cronName, {
|
|
328
364
|
...(this.config.queueEvents || {}),
|
|
329
|
-
connection,
|
|
365
|
+
connection: this.getRedisConnection(),
|
|
330
366
|
})
|
|
331
367
|
|
|
332
368
|
queueEvents.on('completed', ({ jobId }) => {
|
package/src/vision-app.ts
CHANGED
|
@@ -124,6 +124,26 @@ export interface VisionConfig {
|
|
|
124
124
|
host?: string
|
|
125
125
|
port?: number
|
|
126
126
|
password?: string
|
|
127
|
+
/**
|
|
128
|
+
* Enable keepalive to prevent connection timeouts (default: 30000ms)
|
|
129
|
+
*/
|
|
130
|
+
keepAlive?: number
|
|
131
|
+
/**
|
|
132
|
+
* Max retry attempts for failed commands (default: 20)
|
|
133
|
+
*/
|
|
134
|
+
maxRetriesPerRequest?: number
|
|
135
|
+
/**
|
|
136
|
+
* Enable ready check before executing commands (default: true)
|
|
137
|
+
*/
|
|
138
|
+
enableReadyCheck?: boolean
|
|
139
|
+
/**
|
|
140
|
+
* Connection timeout in ms (default: 10000)
|
|
141
|
+
*/
|
|
142
|
+
connectTimeout?: number
|
|
143
|
+
/**
|
|
144
|
+
* Enable offline queue (default: true)
|
|
145
|
+
*/
|
|
146
|
+
enableOfflineQueue?: boolean
|
|
127
147
|
}
|
|
128
148
|
devMode?: boolean // Use in-memory event bus (no Redis required)
|
|
129
149
|
eventBus?: EventBus // Share EventBus instance across apps (for sub-apps)
|