create-chaaskit 0.1.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.
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add-infra.d.ts +6 -0
- package/dist/commands/add-infra.d.ts.map +1 -0
- package/dist/commands/add-infra.js +160 -0
- package/dist/commands/add-infra.js.map +1 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +63 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/db-sync.d.ts +13 -0
- package/dist/commands/db-sync.d.ts.map +1 -0
- package/dist/commands/db-sync.js +108 -0
- package/dist/commands/db-sync.js.map +1 -0
- package/dist/commands/dev.d.ts +7 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +61 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +214 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/.env.example +24 -0
- package/dist/templates/README.md +81 -0
- package/dist/templates/app/components/AcceptInviteClient.tsx +10 -0
- package/dist/templates/app/components/AdminDashboardClient.tsx +10 -0
- package/dist/templates/app/components/AdminTeamClient.tsx +10 -0
- package/dist/templates/app/components/AdminTeamsClient.tsx +10 -0
- package/dist/templates/app/components/AdminUsersClient.tsx +10 -0
- package/dist/templates/app/components/ApiKeysClient.tsx +10 -0
- package/dist/templates/app/components/AutomationsClient.tsx +10 -0
- package/dist/templates/app/components/ChatClient.tsx +13 -0
- package/dist/templates/app/components/ClientOnly.tsx +6 -0
- package/dist/templates/app/components/DocumentsClient.tsx +10 -0
- package/dist/templates/app/components/OAuthConsentClient.tsx +10 -0
- package/dist/templates/app/components/PricingClient.tsx +10 -0
- package/dist/templates/app/components/TeamSettingsClient.tsx +10 -0
- package/dist/templates/app/components/VerifyEmailClient.tsx +10 -0
- package/dist/templates/app/entry.client.tsx +12 -0
- package/dist/templates/app/entry.server.tsx +67 -0
- package/dist/templates/app/root.tsx +91 -0
- package/dist/templates/app/routes/_index.tsx +82 -0
- package/dist/templates/app/routes/admin._index.tsx +57 -0
- package/dist/templates/app/routes/admin.teams.$teamId.tsx +57 -0
- package/dist/templates/app/routes/admin.teams._index.tsx +57 -0
- package/dist/templates/app/routes/admin.users.tsx +57 -0
- package/dist/templates/app/routes/api-keys.tsx +57 -0
- package/dist/templates/app/routes/automations.tsx +57 -0
- package/dist/templates/app/routes/chat._index.tsx +11 -0
- package/dist/templates/app/routes/chat.admin._index.tsx +10 -0
- package/dist/templates/app/routes/chat.admin.teams.$teamId.tsx +10 -0
- package/dist/templates/app/routes/chat.admin.teams._index.tsx +10 -0
- package/dist/templates/app/routes/chat.admin.users.tsx +10 -0
- package/dist/templates/app/routes/chat.api-keys.tsx +10 -0
- package/dist/templates/app/routes/chat.automations.tsx +10 -0
- package/dist/templates/app/routes/chat.documents.tsx +10 -0
- package/dist/templates/app/routes/chat.team.$teamId.settings.tsx +10 -0
- package/dist/templates/app/routes/chat.thread.$threadId.tsx +11 -0
- package/dist/templates/app/routes/chat.tsx +39 -0
- package/dist/templates/app/routes/documents.tsx +57 -0
- package/dist/templates/app/routes/invite.$token.tsx +10 -0
- package/dist/templates/app/routes/login.tsx +334 -0
- package/dist/templates/app/routes/oauth.consent.tsx +10 -0
- package/dist/templates/app/routes/pricing.tsx +10 -0
- package/dist/templates/app/routes/privacy.tsx +197 -0
- package/dist/templates/app/routes/register.tsx +398 -0
- package/dist/templates/app/routes/shared.$shareId.tsx +226 -0
- package/dist/templates/app/routes/team.$teamId.settings.tsx +57 -0
- package/dist/templates/app/routes/terms.tsx +173 -0
- package/dist/templates/app/routes/thread.$threadId.tsx +102 -0
- package/dist/templates/app/routes/verify-email.tsx +10 -0
- package/dist/templates/app/routes.ts +47 -0
- package/dist/templates/config/app.config.ts +216 -0
- package/dist/templates/docs/admin.md +257 -0
- package/dist/templates/docs/api-keys.md +403 -0
- package/dist/templates/docs/authentication.md +247 -0
- package/dist/templates/docs/configuration.md +1212 -0
- package/dist/templates/docs/custom-pages.md +466 -0
- package/dist/templates/docs/deployment.md +362 -0
- package/dist/templates/docs/development.md +411 -0
- package/dist/templates/docs/documents.md +293 -0
- package/dist/templates/docs/extensions.md +639 -0
- package/dist/templates/docs/index.md +139 -0
- package/dist/templates/docs/installation.md +286 -0
- package/dist/templates/docs/mcp.md +952 -0
- package/dist/templates/docs/native-tools.md +688 -0
- package/dist/templates/docs/queue.md +514 -0
- package/dist/templates/docs/scheduled-prompts.md +279 -0
- package/dist/templates/docs/settings.md +415 -0
- package/dist/templates/docs/slack.md +318 -0
- package/dist/templates/docs/styling.md +288 -0
- package/dist/templates/extensions/agents/.gitkeep +0 -0
- package/dist/templates/extensions/pages/.gitkeep +0 -0
- package/dist/templates/extensions/payment-plans/.gitkeep +0 -0
- package/dist/templates/index.html +16 -0
- package/dist/templates/infra-aws/.github/workflows/deploy.yml +95 -0
- package/dist/templates/infra-aws/README.md +207 -0
- package/dist/templates/infra-aws/bin/cdk.ts +18 -0
- package/dist/templates/infra-aws/cdk.json +43 -0
- package/dist/templates/infra-aws/config/deployment.ts +156 -0
- package/dist/templates/infra-aws/lib/chaaskit-stack.ts +419 -0
- package/dist/templates/infra-aws/package.json +27 -0
- package/dist/templates/infra-aws/scripts/build-app.sh +63 -0
- package/dist/templates/infra-aws/tsconfig.json +25 -0
- package/dist/templates/package.json +46 -0
- package/dist/templates/prisma/schema/base.prisma +584 -0
- package/dist/templates/prisma/schema/custom.prisma +24 -0
- package/dist/templates/prisma/schema.prisma +271 -0
- package/dist/templates/public/favicon.svg +4 -0
- package/dist/templates/public/logo.svg +4 -0
- package/dist/templates/react-router.config.ts +11 -0
- package/dist/templates/server.js +52 -0
- package/dist/templates/src/main.tsx +8 -0
- package/dist/templates/tsconfig.json +26 -0
- package/dist/templates/vite.config.ts +26 -0
- package/package.json +46 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# Job Queue
|
|
2
|
+
|
|
3
|
+
The job queue system provides background job processing with support for multiple backends, scheduled jobs, recurring jobs, and flexible worker deployment.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The queue system consists of:
|
|
8
|
+
|
|
9
|
+
- **Queue Providers**: Pluggable backends (memory for dev, SQS for production)
|
|
10
|
+
- **Workers**: Process jobs with configurable concurrency
|
|
11
|
+
- **Scheduler**: Database-backed scheduling for delayed and recurring jobs
|
|
12
|
+
- **Handler Registry**: Register functions to process specific job types
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
### 1. Enable the Queue
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// config/app.config.ts
|
|
20
|
+
export const config: AppConfig = {
|
|
21
|
+
// ... other config
|
|
22
|
+
queue: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
providerConfig: { type: 'memory' },
|
|
25
|
+
worker: { mode: 'in-process', concurrency: 5 },
|
|
26
|
+
scheduler: { enabled: true },
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. Register Job Handlers
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// extensions/jobs/email.ts
|
|
35
|
+
import { registerJobHandler } from '@chaaskit/server';
|
|
36
|
+
|
|
37
|
+
registerJobHandler('email:send', async (job, ctx) => {
|
|
38
|
+
ctx.log('Sending email to', job.payload.to);
|
|
39
|
+
await sendEmail(job.payload);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
registerJobHandler('report:generate', async (job, ctx) => {
|
|
43
|
+
ctx.log('Generating report', job.payload.reportId);
|
|
44
|
+
ctx.progress(50); // Report progress
|
|
45
|
+
const report = await generateReport(job.payload);
|
|
46
|
+
ctx.progress(100);
|
|
47
|
+
return report;
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. Enqueue Jobs
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { getQueueProvider } from '@chaaskit/server';
|
|
55
|
+
|
|
56
|
+
const queue = getQueueProvider();
|
|
57
|
+
|
|
58
|
+
// Immediate job
|
|
59
|
+
await queue.enqueue('email:send', {
|
|
60
|
+
to: 'user@example.com',
|
|
61
|
+
subject: 'Welcome!'
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Delayed job (up to 15 min for SQS)
|
|
65
|
+
await queue.enqueue('reminder:send', { userId: '123' }, {
|
|
66
|
+
delay: 60000 // 1 minute
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Job with retry configuration
|
|
70
|
+
await queue.enqueue('webhook:send', { url: '...' }, {
|
|
71
|
+
maxRetries: 5,
|
|
72
|
+
timeout: 30000,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. Push Schema Changes
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pnpm db:push
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
### Full Config Options
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
queue: {
|
|
88
|
+
enabled: boolean;
|
|
89
|
+
|
|
90
|
+
// Provider configuration (discriminated union)
|
|
91
|
+
providerConfig:
|
|
92
|
+
| { type: 'memory'; maxHistorySize?: number }
|
|
93
|
+
| {
|
|
94
|
+
type: 'sqs';
|
|
95
|
+
region: string;
|
|
96
|
+
queueUrl: string;
|
|
97
|
+
deadLetterQueueUrl?: string;
|
|
98
|
+
visibilityTimeout?: number; // Default: 30s
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Worker configuration
|
|
102
|
+
worker?: {
|
|
103
|
+
mode: 'in-process' | 'standalone'; // Default: 'in-process'
|
|
104
|
+
concurrency?: number; // Default: 5
|
|
105
|
+
pollInterval?: number; // Default: 1000ms
|
|
106
|
+
shutdownTimeout?: number; // Default: 30000ms
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Scheduler configuration
|
|
110
|
+
scheduler?: {
|
|
111
|
+
enabled: boolean; // Default: false
|
|
112
|
+
pollInterval?: number; // Default: 60000ms (1 min)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Development Config
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
queue: {
|
|
121
|
+
enabled: true,
|
|
122
|
+
providerConfig: { type: 'memory', maxHistorySize: 1000 },
|
|
123
|
+
worker: { mode: 'in-process', concurrency: 2 },
|
|
124
|
+
scheduler: { enabled: true },
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Production Config (SQS)
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
queue: {
|
|
132
|
+
enabled: true,
|
|
133
|
+
providerConfig: {
|
|
134
|
+
type: 'sqs',
|
|
135
|
+
region: 'us-east-1',
|
|
136
|
+
queueUrl: process.env.SQS_QUEUE_URL!,
|
|
137
|
+
deadLetterQueueUrl: process.env.SQS_DLQ_URL,
|
|
138
|
+
visibilityTimeout: 60,
|
|
139
|
+
},
|
|
140
|
+
worker: { mode: 'in-process', concurrency: 10 },
|
|
141
|
+
scheduler: { enabled: true, pollInterval: 30000 },
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Queue Providers
|
|
146
|
+
|
|
147
|
+
### Memory Provider
|
|
148
|
+
|
|
149
|
+
In-memory queue for development and testing. Uses EventEmitter for instant job notification (no polling).
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
providerConfig: {
|
|
153
|
+
type: 'memory',
|
|
154
|
+
maxHistorySize: 1000 // Keep last N completed jobs
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Features:**
|
|
159
|
+
- Instant job processing (EventEmitter-based)
|
|
160
|
+
- Job history for debugging
|
|
161
|
+
- Deduplication support
|
|
162
|
+
- Priority queues
|
|
163
|
+
|
|
164
|
+
### SQS Provider
|
|
165
|
+
|
|
166
|
+
AWS SQS for production deployments. Requires `@aws-sdk/client-sqs`:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pnpm add @aws-sdk/client-sqs
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
providerConfig: {
|
|
174
|
+
type: 'sqs',
|
|
175
|
+
region: 'us-east-1',
|
|
176
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
177
|
+
deadLetterQueueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-dlq',
|
|
178
|
+
visibilityTimeout: 30,
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Environment Variables:**
|
|
183
|
+
```bash
|
|
184
|
+
AWS_ACCESS_KEY_ID=your-key
|
|
185
|
+
AWS_SECRET_ACCESS_KEY=your-secret
|
|
186
|
+
SQS_QUEUE_URL=https://sqs...
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Features:**
|
|
190
|
+
- Long polling (efficient, no wasted requests)
|
|
191
|
+
- Visibility timeout for at-least-once delivery
|
|
192
|
+
- Dead letter queue support
|
|
193
|
+
- FIFO queue support with deduplication
|
|
194
|
+
|
|
195
|
+
**Limitations:**
|
|
196
|
+
- Max delay: 15 minutes (use scheduler for longer delays)
|
|
197
|
+
- Max message size: 256 KB
|
|
198
|
+
|
|
199
|
+
## Job Handlers
|
|
200
|
+
|
|
201
|
+
### Registering Handlers
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { registerJobHandler } from '@chaaskit/server';
|
|
205
|
+
|
|
206
|
+
registerJobHandler<PayloadType, ResultType>(
|
|
207
|
+
'job:type',
|
|
208
|
+
async (job, ctx) => {
|
|
209
|
+
// job.id - Unique job ID
|
|
210
|
+
// job.type - Job type string
|
|
211
|
+
// job.payload - Your typed payload
|
|
212
|
+
// job.attempts - Current attempt number
|
|
213
|
+
// job.options - Job options (maxRetries, timeout, etc.)
|
|
214
|
+
|
|
215
|
+
// ctx.jobId - Same as job.id
|
|
216
|
+
// ctx.attempt - Current attempt (1-based)
|
|
217
|
+
// ctx.log() - Job-specific logging
|
|
218
|
+
// ctx.progress() - Report progress (0-100)
|
|
219
|
+
// ctx.signal - AbortSignal for cancellation
|
|
220
|
+
|
|
221
|
+
return result;
|
|
222
|
+
},
|
|
223
|
+
'Optional description for debugging'
|
|
224
|
+
);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Error Handling & Retries
|
|
228
|
+
|
|
229
|
+
Jobs automatically retry on failure:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
registerJobHandler('flaky:job', async (job, ctx) => {
|
|
233
|
+
if (Math.random() < 0.5) {
|
|
234
|
+
throw new Error('Random failure');
|
|
235
|
+
// Job will be retried up to maxRetries times
|
|
236
|
+
}
|
|
237
|
+
return 'success';
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Configure retries per job
|
|
241
|
+
await queue.enqueue('flaky:job', {}, {
|
|
242
|
+
maxRetries: 5, // Default: 3
|
|
243
|
+
timeout: 60000, // Job timeout in ms
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Timeout Handling
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
registerJobHandler('long:task', async (job, ctx) => {
|
|
251
|
+
// Check if job was cancelled/timed out
|
|
252
|
+
if (ctx.signal.aborted) {
|
|
253
|
+
throw new Error('Job cancelled');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// For long operations, periodically check the signal
|
|
257
|
+
for (const item of items) {
|
|
258
|
+
if (ctx.signal.aborted) break;
|
|
259
|
+
await processItem(item);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Scheduler
|
|
265
|
+
|
|
266
|
+
The scheduler enables delayed execution and recurring jobs using database storage.
|
|
267
|
+
|
|
268
|
+
### One-Time Scheduled Jobs
|
|
269
|
+
|
|
270
|
+
For delays longer than provider limits (e.g., > 15 min for SQS):
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { getScheduler } from '@chaaskit/server';
|
|
274
|
+
|
|
275
|
+
const scheduler = getScheduler();
|
|
276
|
+
|
|
277
|
+
// Schedule for a specific time
|
|
278
|
+
await scheduler.schedule(
|
|
279
|
+
'report:send',
|
|
280
|
+
{ reportId: '123', recipients: ['user@example.com'] },
|
|
281
|
+
new Date('2024-12-25T09:00:00Z')
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Cancel a scheduled job
|
|
285
|
+
await scheduler.cancelScheduledJob(jobId);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Recurring Jobs
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// Cron expression (daily at 9am)
|
|
292
|
+
await scheduler.registerRecurring({
|
|
293
|
+
name: 'daily-digest',
|
|
294
|
+
type: 'email:digest',
|
|
295
|
+
payload: { digestType: 'daily' },
|
|
296
|
+
schedule: '0 9 * * *',
|
|
297
|
+
timezone: 'America/New_York',
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Interval syntax
|
|
301
|
+
await scheduler.registerRecurring({
|
|
302
|
+
name: 'cache-cleanup',
|
|
303
|
+
type: 'maintenance:cleanup',
|
|
304
|
+
payload: {},
|
|
305
|
+
schedule: 'every 1 hour',
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Manage recurring jobs
|
|
309
|
+
await scheduler.disableRecurring('daily-digest');
|
|
310
|
+
await scheduler.enableRecurring('daily-digest');
|
|
311
|
+
await scheduler.deleteRecurring('daily-digest');
|
|
312
|
+
|
|
313
|
+
// List all recurring jobs
|
|
314
|
+
const jobs = await scheduler.listRecurring();
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Supported Schedule Formats:**
|
|
318
|
+
- Cron: `"0 9 * * *"` (daily at 9am), `"*/15 * * * *"` (every 15 min)
|
|
319
|
+
- Intervals: `"every 1 hour"`, `"every 30 minutes"`, `"every 1 day"`
|
|
320
|
+
|
|
321
|
+
## Worker Modes
|
|
322
|
+
|
|
323
|
+
### In-Process Mode (Default)
|
|
324
|
+
|
|
325
|
+
Worker runs within the main server process:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
worker: {
|
|
329
|
+
mode: 'in-process',
|
|
330
|
+
concurrency: 5
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Pros:** Simple setup, no extra processes
|
|
335
|
+
**Cons:** Shares resources with web server
|
|
336
|
+
|
|
337
|
+
### Standalone Mode
|
|
338
|
+
|
|
339
|
+
Run workers as separate processes for better scaling:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// config/app.config.ts
|
|
343
|
+
worker: {
|
|
344
|
+
mode: 'standalone' // Server won't start workers
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Then run workers separately:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# Using the CLI
|
|
352
|
+
pnpm queue-worker
|
|
353
|
+
|
|
354
|
+
# Or with environment overrides
|
|
355
|
+
QUEUE_CONCURRENCY=10 pnpm queue-worker
|
|
356
|
+
SCHEDULER_ENABLED=true pnpm queue-worker
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Environment Variables for Standalone Workers:**
|
|
360
|
+
```bash
|
|
361
|
+
QUEUE_CONCURRENCY=5 # Number of concurrent jobs
|
|
362
|
+
QUEUE_POLL_INTERVAL=1000 # Poll interval (ms)
|
|
363
|
+
QUEUE_SHUTDOWN_TIMEOUT=30000 # Graceful shutdown timeout (ms)
|
|
364
|
+
SCHEDULER_ENABLED=false # Enable scheduler in this worker
|
|
365
|
+
SCHEDULER_POLL_INTERVAL=60000 # Scheduler poll interval (ms)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## API Reference
|
|
369
|
+
|
|
370
|
+
### Queue Provider
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
interface QueueProvider {
|
|
374
|
+
readonly name: string;
|
|
375
|
+
|
|
376
|
+
// Enqueue a job
|
|
377
|
+
enqueue<T>(type: string, payload: T, options?: EnqueueOptions): Promise<Job<T>>;
|
|
378
|
+
|
|
379
|
+
// Receive jobs (long polling)
|
|
380
|
+
receive(maxMessages?: number, waitTimeSeconds?: number): Promise<ReceivedJob[]>;
|
|
381
|
+
|
|
382
|
+
// Acknowledge successful completion
|
|
383
|
+
acknowledge(receiptHandle: string): Promise<void>;
|
|
384
|
+
|
|
385
|
+
// Mark job as failed (will retry if attempts remain)
|
|
386
|
+
fail(receiptHandle: string, error: Error): Promise<void>;
|
|
387
|
+
|
|
388
|
+
// Get job by ID
|
|
389
|
+
getJob(jobId: string): Promise<Job | null>;
|
|
390
|
+
|
|
391
|
+
// Get queue statistics
|
|
392
|
+
getStats(): Promise<QueueStats>;
|
|
393
|
+
|
|
394
|
+
// Close provider
|
|
395
|
+
close(): Promise<void>;
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Enqueue Options
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
interface EnqueueOptions {
|
|
403
|
+
delay?: number; // Delay in ms
|
|
404
|
+
scheduledFor?: Date; // Specific execution time
|
|
405
|
+
maxRetries?: number; // Default: 3
|
|
406
|
+
timeout?: number; // Job timeout in ms (default: 30000)
|
|
407
|
+
deduplicationKey?: string; // Prevent duplicate jobs
|
|
408
|
+
priority?: number; // Lower = higher priority (default: 0)
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Job Context
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
interface JobContext {
|
|
416
|
+
jobId: string;
|
|
417
|
+
attempt: number; // 1-based attempt number
|
|
418
|
+
log: (msg: string, ...args) => void; // Job-specific logging
|
|
419
|
+
progress: (percent: number) => void; // Report progress 0-100
|
|
420
|
+
signal: AbortSignal; // Check for cancellation
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Database Models
|
|
425
|
+
|
|
426
|
+
The scheduler uses two database tables:
|
|
427
|
+
|
|
428
|
+
```prisma
|
|
429
|
+
model ScheduledJob {
|
|
430
|
+
id String @id
|
|
431
|
+
type String
|
|
432
|
+
payload String @db.Text
|
|
433
|
+
options String @db.Text
|
|
434
|
+
scheduledFor DateTime
|
|
435
|
+
status String // scheduled | enqueued | cancelled
|
|
436
|
+
error String?
|
|
437
|
+
createdAt DateTime
|
|
438
|
+
updatedAt DateTime
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
model RecurringJob {
|
|
442
|
+
id String @id
|
|
443
|
+
name String @unique
|
|
444
|
+
type String
|
|
445
|
+
payload String @db.Text
|
|
446
|
+
options String @db.Text
|
|
447
|
+
schedule String // Cron or interval
|
|
448
|
+
timezone String
|
|
449
|
+
enabled Boolean
|
|
450
|
+
nextRunAt DateTime?
|
|
451
|
+
lastRunAt DateTime?
|
|
452
|
+
lastError String?
|
|
453
|
+
createdAt DateTime
|
|
454
|
+
updatedAt DateTime
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Best Practices
|
|
459
|
+
|
|
460
|
+
### Job Design
|
|
461
|
+
|
|
462
|
+
1. **Keep jobs idempotent** - Jobs may run more than once
|
|
463
|
+
2. **Store minimal data** - Put IDs in payload, not full objects
|
|
464
|
+
3. **Handle timeouts gracefully** - Check `ctx.signal.aborted`
|
|
465
|
+
4. **Use meaningful job types** - `email:welcome`, `report:daily`, `webhook:send`
|
|
466
|
+
|
|
467
|
+
### Production Setup
|
|
468
|
+
|
|
469
|
+
1. **Use SQS with DLQ** - Failed jobs go to dead letter queue for inspection
|
|
470
|
+
2. **Run standalone workers** - Scale workers independently from web servers
|
|
471
|
+
3. **Monitor queue depth** - Alert when jobs back up
|
|
472
|
+
4. **Set appropriate timeouts** - Match job execution time
|
|
473
|
+
|
|
474
|
+
### Error Handling
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
registerJobHandler('critical:job', async (job, ctx) => {
|
|
478
|
+
try {
|
|
479
|
+
await riskyOperation();
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// Log with context
|
|
482
|
+
ctx.log('Operation failed', { error: error.message, attempt: ctx.attempt });
|
|
483
|
+
|
|
484
|
+
// Re-throw to trigger retry
|
|
485
|
+
throw error;
|
|
486
|
+
|
|
487
|
+
// Or handle gracefully and don't retry
|
|
488
|
+
// return { status: 'failed', reason: error.message };
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Troubleshooting
|
|
494
|
+
|
|
495
|
+
### Jobs Not Processing
|
|
496
|
+
|
|
497
|
+
1. Check queue is enabled: `queue.enabled: true`
|
|
498
|
+
2. Check worker mode matches your setup
|
|
499
|
+
3. Verify handler is registered for job type
|
|
500
|
+
4. Check server logs for errors
|
|
501
|
+
|
|
502
|
+
### SQS Connection Issues
|
|
503
|
+
|
|
504
|
+
1. Verify AWS credentials are set
|
|
505
|
+
2. Check queue URL is correct
|
|
506
|
+
3. Ensure IAM permissions include SQS actions
|
|
507
|
+
4. Test with AWS CLI: `aws sqs get-queue-attributes --queue-url ...`
|
|
508
|
+
|
|
509
|
+
### Scheduler Not Running
|
|
510
|
+
|
|
511
|
+
1. Enable scheduler: `scheduler.enabled: true`
|
|
512
|
+
2. Run `pnpm db:push` to create tables
|
|
513
|
+
3. Check database connectivity
|
|
514
|
+
4. Verify recurring job schedules are valid
|