@engjts/nexus 0.1.7 → 0.1.9
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/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
- package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
- package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
- package/dist/advanced/playground/playground.d.ts +19 -0
- package/dist/advanced/playground/playground.d.ts.map +1 -1
- package/dist/advanced/playground/playground.js +70 -0
- package/dist/advanced/playground/playground.js.map +1 -1
- package/dist/advanced/playground/types.d.ts +20 -0
- package/dist/advanced/playground/types.d.ts.map +1 -1
- package/dist/core/application.d.ts +14 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/application.js +173 -71
- package/dist/core/application.js.map +1 -1
- package/dist/core/context-pool.d.ts +2 -13
- package/dist/core/context-pool.d.ts.map +1 -1
- package/dist/core/context-pool.js +7 -45
- package/dist/core/context-pool.js.map +1 -1
- package/dist/core/context.d.ts +108 -5
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +449 -53
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/middleware.d.ts +6 -0
- package/dist/core/middleware.d.ts.map +1 -1
- package/dist/core/middleware.js +83 -84
- package/dist/core/middleware.js.map +1 -1
- package/dist/core/performance/fast-json.d.ts +149 -0
- package/dist/core/performance/fast-json.d.ts.map +1 -0
- package/dist/core/performance/fast-json.js +473 -0
- package/dist/core/performance/fast-json.js.map +1 -0
- package/dist/core/router/file-router.d.ts +20 -7
- package/dist/core/router/file-router.d.ts.map +1 -1
- package/dist/core/router/file-router.js +41 -13
- package/dist/core/router/file-router.js.map +1 -1
- package/dist/core/router/index.d.ts +6 -0
- package/dist/core/router/index.d.ts.map +1 -1
- package/dist/core/router/index.js +33 -6
- package/dist/core/router/index.js.map +1 -1
- package/dist/core/router/radix-tree.d.ts +4 -1
- package/dist/core/router/radix-tree.d.ts.map +1 -1
- package/dist/core/router/radix-tree.js +7 -3
- package/dist/core/router/radix-tree.js.map +1 -1
- package/dist/core/serializer.d.ts +251 -0
- package/dist/core/serializer.d.ts.map +1 -0
- package/dist/core/serializer.js +290 -0
- package/dist/core/serializer.js.map +1 -0
- package/dist/core/types.d.ts +39 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1849
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -170
- package/src/advanced/playground/types.ts +0 -20
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1335
- package/src/core/context-pool.ts +0 -127
- package/src/core/context.ts +0 -412
- package/src/core/index.ts +0 -80
- package/src/core/middleware.ts +0 -262
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -594
- package/src/core/router/index.ts +0 -227
- package/src/core/router/radix-tree.ts +0 -226
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -574
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -264
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- package/tsconfig.json +0 -39
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
# Job Queue System
|
|
2
|
-
|
|
3
|
-
Background job processing dengan support untuk retry, concurrency control, rate limiting, dan callbacks.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
1. [Introduction](#introduction)
|
|
8
|
-
2. [Core Concepts](#core-concepts)
|
|
9
|
-
3. [Basic Usage](#basic-usage)
|
|
10
|
-
4. [Configuration](#configuration)
|
|
11
|
-
5. [Job Handlers](#job-handlers)
|
|
12
|
-
6. [Job States](#job-states)
|
|
13
|
-
7. [Callbacks & Hooks](#callbacks--hooks)
|
|
14
|
-
8. [Advanced Features](#advanced-features)
|
|
15
|
-
9. [Event Listeners](#event-listeners)
|
|
16
|
-
10. [Best Practices](#best-practices)
|
|
17
|
-
11. [Examples](#examples)
|
|
18
|
-
|
|
19
|
-
## Introduction
|
|
20
|
-
|
|
21
|
-
Job Queue adalah sistem untuk menjalankan tugas-tugas berat/panjang di background tanpa memblokir request HTTP. Cocok untuk:
|
|
22
|
-
|
|
23
|
-
- **Email sending** - Kirim email asynchronous
|
|
24
|
-
- **File processing** - Upload, convert, compress files
|
|
25
|
-
- **API calls** - Fetch data dari external API dengan retry
|
|
26
|
-
- **Data import** - Import data dalam batch
|
|
27
|
-
- **Notifications** - Send push notifications, SMS, dsb
|
|
28
|
-
- **Scheduled tasks** - Jalankan task di waktu tertentu
|
|
29
|
-
|
|
30
|
-
## Core Concepts
|
|
31
|
-
|
|
32
|
-
### Job
|
|
33
|
-
|
|
34
|
-
Unit kerja yang akan diproses oleh queue. Setiap job memiliki:
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
interface Job<Data = any, Result = any> {
|
|
38
|
-
id: string; // Unique job ID
|
|
39
|
-
name: string; // Job type/name
|
|
40
|
-
data: Data; // Job payload
|
|
41
|
-
result?: Result; // Job result (after completion)
|
|
42
|
-
error?: { message: string }; // Error info (if failed)
|
|
43
|
-
state: JobState; // Current state
|
|
44
|
-
attemptsMade: number; // Retry count
|
|
45
|
-
maxAttempts: number; // Max retry attempts
|
|
46
|
-
priority: number; // Higher = process first
|
|
47
|
-
createdAt: number; // Creation timestamp
|
|
48
|
-
runAt: number; // When to run (for delayed jobs)
|
|
49
|
-
updatedAt: number; // Last update timestamp
|
|
50
|
-
metadata?: Record<string, any>; // Custom metadata
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### JobQueue
|
|
55
|
-
|
|
56
|
-
Container untuk job dan handler processing logic.
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
const queue = new JobQueue('email', {
|
|
60
|
-
concurrency: 5, // Max 5 jobs running simultaneously
|
|
61
|
-
retry: { ... }, // Retry configuration
|
|
62
|
-
limiter: { ... }, // Rate limiting
|
|
63
|
-
hooks: { ... } // Callback hooks
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Job States
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
waiting → active → completed
|
|
71
|
-
↓
|
|
72
|
-
failed/delayed → active → completed
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
- **waiting**: Job siap dijalankan
|
|
76
|
-
- **delayed**: Job dijadwalkan untuk nanti
|
|
77
|
-
- **active**: Job sedang diproses
|
|
78
|
-
- **completed**: Job berhasil
|
|
79
|
-
- **failed**: Job gagal permanent
|
|
80
|
-
- **paused**: Queue dalam mode pause
|
|
81
|
-
|
|
82
|
-
## Basic Usage
|
|
83
|
-
|
|
84
|
-
### 1. Create Queue
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
import { JobQueue } from './src/advanced/jobs/queue';
|
|
88
|
-
|
|
89
|
-
const emailQueue = new JobQueue('email', {
|
|
90
|
-
concurrency: 2
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### 2. Register Job Handler
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
emailQueue.process('send-email', async (job) => {
|
|
98
|
-
const { email, subject, message } = job.data;
|
|
99
|
-
|
|
100
|
-
// Do work here
|
|
101
|
-
const result = await sendEmail(email, subject, message);
|
|
102
|
-
|
|
103
|
-
// Return result
|
|
104
|
-
return result;
|
|
105
|
-
});
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### 3. Add Job to Queue
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
const job = await emailQueue.add('send-email', {
|
|
112
|
-
email: 'user@example.com',
|
|
113
|
-
subject: 'Welcome!',
|
|
114
|
-
message: 'Thanks for signing up'
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
console.log(job.id); // jobId untuk tracking
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### 4. Listen to Events
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
emailQueue.on('completed', (job, result) => {
|
|
124
|
-
console.log('Email sent:', result);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
emailQueue.on('failed', (job, error) => {
|
|
128
|
-
console.log('Email failed:', error.message);
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Configuration
|
|
133
|
-
|
|
134
|
-
### QueueOptions
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
interface QueueOptions<Data = any> {
|
|
138
|
-
concurrency?: number; // Default: 5
|
|
139
|
-
retry?: RetryOptions; // Retry config
|
|
140
|
-
limiter?: RateLimitOptions; // Rate limit config
|
|
141
|
-
hooks?: QueueHooks<Data>; // Callback hooks
|
|
142
|
-
store?: QueueStore<Data>; // Custom storage
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Retry Configuration
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
const queue = new JobQueue('tasks', {
|
|
150
|
-
retry: {
|
|
151
|
-
attempts: 3, // Retry 3 times max
|
|
152
|
-
backoff: 'exponential', // 'fixed' or 'exponential'
|
|
153
|
-
delay: 1000, // Initial delay: 1 second
|
|
154
|
-
maxDelay: 60000 // Max delay: 1 minute
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**Exponential backoff example:**
|
|
160
|
-
- Attempt 1 fails → wait 1s
|
|
161
|
-
- Attempt 2 fails → wait 2s
|
|
162
|
-
- Attempt 3 fails → wait 4s
|
|
163
|
-
- Attempt 4 = FAIL PERMANENT
|
|
164
|
-
|
|
165
|
-
### Rate Limiting
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
const queue = new JobQueue('api-calls', {
|
|
169
|
-
concurrency: 10, // 10 concurrent jobs
|
|
170
|
-
limiter: {
|
|
171
|
-
max: 5, // But max 5 jobs
|
|
172
|
-
duration: 60000 // Per minute (60 seconds)
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Result: 5 jobs per minute, even with 10 concurrency
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Custom Storage
|
|
180
|
-
|
|
181
|
-
Default menggunakan in-memory storage. Bisa custom:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
class PostgresQueueStore implements QueueStore {
|
|
185
|
-
async enqueue(job: Job) { ... }
|
|
186
|
-
async dequeue(): Promise<Job | undefined> { ... }
|
|
187
|
-
async update(job: Job) { ... }
|
|
188
|
-
async get(id: string) { ... }
|
|
189
|
-
async list(state?: JobState) { ... }
|
|
190
|
-
async stats() { ... }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const queue = new JobQueue('email', {
|
|
194
|
-
store: new PostgresQueueStore()
|
|
195
|
-
});
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Job Handlers
|
|
199
|
-
|
|
200
|
-
### Simple Handler
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
queue.process('simple-job', async (job) => {
|
|
204
|
-
console.log('Processing:', job.data);
|
|
205
|
-
return { success: true };
|
|
206
|
-
});
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Handler dengan Error Handling
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
queue.process('risky-job', async (job) => {
|
|
213
|
-
try {
|
|
214
|
-
const result = await riskyOperation(job.data);
|
|
215
|
-
return result;
|
|
216
|
-
} catch (error) {
|
|
217
|
-
throw new Error(`Failed: ${error.message}`);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Handler dengan Async Operations
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
queue.process('db-import', async (job) => {
|
|
226
|
-
const records = job.data.records;
|
|
227
|
-
|
|
228
|
-
// Batch insert
|
|
229
|
-
const inserted = await db.users.insert(records);
|
|
230
|
-
|
|
231
|
-
// Return count
|
|
232
|
-
return { inserted: inserted.length };
|
|
233
|
-
});
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Handler Accessing Job Meta
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
queue.process('task', async (job) => {
|
|
240
|
-
console.log('Job ID:', job.id);
|
|
241
|
-
console.log('Attempt:', job.attemptsMade);
|
|
242
|
-
console.log('Max retries:', job.maxAttempts);
|
|
243
|
-
console.log('Priority:', job.priority);
|
|
244
|
-
console.log('Metadata:', job.metadata);
|
|
245
|
-
|
|
246
|
-
return { done: true };
|
|
247
|
-
});
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## Job States
|
|
251
|
-
|
|
252
|
-
### State Transitions
|
|
253
|
-
|
|
254
|
-
```
|
|
255
|
-
1. Add job → waiting state
|
|
256
|
-
2. Dequeue & start → active state
|
|
257
|
-
3. Complete → completed state
|
|
258
|
-
OR fail → delayed state (if retry available) or failed state
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Check Job State
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
const job = await queue.add('task', data);
|
|
265
|
-
console.log(job.state); // 'waiting'
|
|
266
|
-
|
|
267
|
-
// Later...
|
|
268
|
-
const currentJob = await queue.store.get(job.id);
|
|
269
|
-
console.log(currentJob.state); // 'completed', 'failed', etc
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Callbacks & Hooks
|
|
273
|
-
|
|
274
|
-
### Three Types of Callbacks
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
const queue = new JobQueue('email', {
|
|
278
|
-
hooks: {
|
|
279
|
-
onComplete: async (job, result) => {
|
|
280
|
-
// Called when job succeeds
|
|
281
|
-
},
|
|
282
|
-
onFailed: async (job, error) => {
|
|
283
|
-
// Called when job fails permanently
|
|
284
|
-
},
|
|
285
|
-
onRetry: async (job, attempt, delay) => {
|
|
286
|
-
// Called before each retry
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Example: Email Callbacks
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
const queue = new JobQueue('email', {
|
|
296
|
-
hooks: {
|
|
297
|
-
onComplete: async (job, result) => {
|
|
298
|
-
// Update database: email_logs.status = 'sent'
|
|
299
|
-
await db.emailLogs.update(
|
|
300
|
-
{ jobId: job.id },
|
|
301
|
-
{ status: 'sent', result }
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
// Send webhook
|
|
305
|
-
await webhook.post('/events/email-sent', {
|
|
306
|
-
email: job.data.email,
|
|
307
|
-
timestamp: new Date()
|
|
308
|
-
});
|
|
309
|
-
},
|
|
310
|
-
|
|
311
|
-
onFailed: async (job, error) => {
|
|
312
|
-
// Update database: email_logs.status = 'failed'
|
|
313
|
-
await db.emailLogs.update(
|
|
314
|
-
{ jobId: job.id },
|
|
315
|
-
{
|
|
316
|
-
status: 'failed',
|
|
317
|
-
error: error.message,
|
|
318
|
-
failedAt: new Date()
|
|
319
|
-
}
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Alert admin
|
|
323
|
-
await slack.notify({
|
|
324
|
-
channel: '#alerts',
|
|
325
|
-
text: `Email delivery failed: ${job.data.email}`
|
|
326
|
-
});
|
|
327
|
-
},
|
|
328
|
-
|
|
329
|
-
onRetry: async (job, attempt, delay) => {
|
|
330
|
-
// Log retry attempt
|
|
331
|
-
await db.jobRetries.insert({
|
|
332
|
-
jobId: job.id,
|
|
333
|
-
attempt,
|
|
334
|
-
nextRetryIn: delay
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
## Advanced Features
|
|
342
|
-
|
|
343
|
-
### 1. Priority Jobs
|
|
344
|
-
|
|
345
|
-
Higher priority jobs execute first:
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
// Low priority
|
|
349
|
-
await queue.add('email', data, { priority: 0 });
|
|
350
|
-
|
|
351
|
-
// Medium priority
|
|
352
|
-
await queue.add('email', data, { priority: 5 });
|
|
353
|
-
|
|
354
|
-
// High priority (execute first)
|
|
355
|
-
await queue.add('email', data, { priority: 10 });
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
### 2. Delayed Jobs
|
|
359
|
-
|
|
360
|
-
Schedule job untuk nanti:
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
// Run 5 minutes from now
|
|
364
|
-
await queue.add('reminder', data, {
|
|
365
|
-
delay: 5 * 60 * 1000
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// Run 1 hour from now
|
|
369
|
-
await queue.add('report', data, {
|
|
370
|
-
delay: 60 * 60 * 1000
|
|
371
|
-
});
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
### 3. Bulk Add
|
|
375
|
-
|
|
376
|
-
Add multiple jobs at once:
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
const jobs = [
|
|
380
|
-
{ name: 'send-email', data: { email: 'user1@ex.com' } },
|
|
381
|
-
{ name: 'send-email', data: { email: 'user2@ex.com' } },
|
|
382
|
-
{ name: 'send-email', data: { email: 'user3@ex.com' } }
|
|
383
|
-
];
|
|
384
|
-
|
|
385
|
-
await queue.addBulk(jobs);
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### 4. Manual Retry
|
|
389
|
-
|
|
390
|
-
Retry failed job manually:
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
try {
|
|
394
|
-
await queue.retry(jobId);
|
|
395
|
-
} catch (error) {
|
|
396
|
-
console.log('Job not found');
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### 5. Pause & Resume
|
|
401
|
-
|
|
402
|
-
Control queue processing:
|
|
403
|
-
|
|
404
|
-
```typescript
|
|
405
|
-
queue.pause(); // Stop processing new jobs
|
|
406
|
-
queue.resume(); // Resume processing
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### 6. Queue Statistics
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
const stats = await queue.stats();
|
|
413
|
-
|
|
414
|
-
console.log(stats);
|
|
415
|
-
// {
|
|
416
|
-
// waiting: 5,
|
|
417
|
-
// active: 2,
|
|
418
|
-
// completed: 100,
|
|
419
|
-
// failed: 3,
|
|
420
|
-
// delayed: 10
|
|
421
|
-
// }
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
## Event Listeners
|
|
425
|
-
|
|
426
|
-
Queue extends EventEmitter, emit 5 events:
|
|
427
|
-
|
|
428
|
-
```typescript
|
|
429
|
-
// Job added to queue
|
|
430
|
-
queue.on('added', (job) => {
|
|
431
|
-
console.log('Job added:', job.id);
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// Job started processing
|
|
435
|
-
queue.on('active', (job) => {
|
|
436
|
-
console.log('Job processing:', job.id);
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
// Job completed successfully
|
|
440
|
-
queue.on('completed', (job, result) => {
|
|
441
|
-
console.log('Job done:', result);
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
// Job failed
|
|
445
|
-
queue.on('failed', (job, error) => {
|
|
446
|
-
console.log('Job error:', error.message);
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
// Job about to retry
|
|
450
|
-
queue.on('retrying', (job, attempt, delay) => {
|
|
451
|
-
console.log(`Retry ${attempt} in ${delay}ms`);
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
// Queue paused
|
|
455
|
-
queue.on('paused', () => {
|
|
456
|
-
console.log('Queue paused');
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// Queue resumed
|
|
460
|
-
queue.on('resumed', () => {
|
|
461
|
-
console.log('Queue resumed');
|
|
462
|
-
});
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
## Best Practices
|
|
466
|
-
|
|
467
|
-
### 1. Use Meaningful Job Names
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
// Good
|
|
471
|
-
queue.process('send-welcome-email', handler);
|
|
472
|
-
queue.process('import-users-csv', handler);
|
|
473
|
-
queue.process('generate-report', handler);
|
|
474
|
-
|
|
475
|
-
// Avoid
|
|
476
|
-
queue.process('job1', handler);
|
|
477
|
-
queue.process('task', handler);
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
### 2. Include Metadata for Tracking
|
|
481
|
-
|
|
482
|
-
```typescript
|
|
483
|
-
await queue.add('email', emailData, {
|
|
484
|
-
metadata: {
|
|
485
|
-
userId: user.id,
|
|
486
|
-
campaignId: campaign.id,
|
|
487
|
-
source: 'signup-flow'
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
// Later in handler
|
|
492
|
-
queue.process('email', async (job) => {
|
|
493
|
-
console.log('From campaign:', job.metadata?.campaignId);
|
|
494
|
-
});
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
### 3. Set Appropriate Concurrency
|
|
498
|
-
|
|
499
|
-
```typescript
|
|
500
|
-
// Email: 2-5 concurrent (don't overwhelm SMTP)
|
|
501
|
-
new JobQueue('email', { concurrency: 3 });
|
|
502
|
-
|
|
503
|
-
// API calls: 10-20 concurrent
|
|
504
|
-
new JobQueue('api', { concurrency: 15 });
|
|
505
|
-
|
|
506
|
-
// Database: 5-10 concurrent (DB connection limits)
|
|
507
|
-
new JobQueue('db', { concurrency: 8 });
|
|
508
|
-
|
|
509
|
-
// CPU intensive: 1-2 concurrent
|
|
510
|
-
new JobQueue('cpu-heavy', { concurrency: 1 });
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
### 4. Use Rate Limiting for External APIs
|
|
514
|
-
|
|
515
|
-
```typescript
|
|
516
|
-
new JobQueue('external-api', {
|
|
517
|
-
concurrency: 20,
|
|
518
|
-
limiter: {
|
|
519
|
-
max: 100, // 100 requests
|
|
520
|
-
duration: 60000 // per minute (API rate limit)
|
|
521
|
-
}
|
|
522
|
-
});
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
### 5. Implement Proper Error Handling
|
|
526
|
-
|
|
527
|
-
```typescript
|
|
528
|
-
queue.process('api-call', async (job) => {
|
|
529
|
-
const maxRetries = 3;
|
|
530
|
-
|
|
531
|
-
try {
|
|
532
|
-
// Your operation
|
|
533
|
-
const result = await fetchFromAPI(job.data.url);
|
|
534
|
-
return result;
|
|
535
|
-
} catch (error) {
|
|
536
|
-
// Log detailed error
|
|
537
|
-
console.error({
|
|
538
|
-
jobId: job.id,
|
|
539
|
-
attempt: job.attemptsMade,
|
|
540
|
-
maxAttempts: job.maxAttempts,
|
|
541
|
-
error: error.message,
|
|
542
|
-
stack: error.stack
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
throw error; // Let queue handle retry
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
### 6. Use Callbacks for Side Effects
|
|
551
|
-
|
|
552
|
-
```typescript
|
|
553
|
-
const queue = new JobQueue('notifications', {
|
|
554
|
-
hooks: {
|
|
555
|
-
onComplete: async (job, result) => {
|
|
556
|
-
// Update database
|
|
557
|
-
await db.notifications.update(job.id, {
|
|
558
|
-
status: 'sent',
|
|
559
|
-
sentAt: new Date(),
|
|
560
|
-
result
|
|
561
|
-
});
|
|
562
|
-
},
|
|
563
|
-
onFailed: async (job, error) => {
|
|
564
|
-
// Store failure reason
|
|
565
|
-
await db.notifications.update(job.id, {
|
|
566
|
-
status: 'failed',
|
|
567
|
-
error: error.message,
|
|
568
|
-
failedAt: new Date()
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
// Send alert
|
|
572
|
-
await alertService.notify({
|
|
573
|
-
type: 'notification-failed',
|
|
574
|
-
jobId: job.id,
|
|
575
|
-
error: error.message
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
### 7. Validate Job Data
|
|
583
|
-
|
|
584
|
-
```typescript
|
|
585
|
-
import { z } from 'zod';
|
|
586
|
-
|
|
587
|
-
const emailSchema = z.object({
|
|
588
|
-
email: z.string().email(),
|
|
589
|
-
subject: z.string().min(5),
|
|
590
|
-
message: z.string().min(10)
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
queue.process('send-email', async (job) => {
|
|
594
|
-
// Validate before processing
|
|
595
|
-
const validated = emailSchema.parse(job.data);
|
|
596
|
-
|
|
597
|
-
return await sendEmail(validated);
|
|
598
|
-
});
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
## Examples
|
|
602
|
-
|
|
603
|
-
### Example 1: Email Queue with Callbacks
|
|
604
|
-
|
|
605
|
-
```typescript
|
|
606
|
-
import { JobQueue } from './src/advanced/jobs/queue';
|
|
607
|
-
|
|
608
|
-
const emailQueue = new JobQueue('email', {
|
|
609
|
-
concurrency: 3,
|
|
610
|
-
retry: {
|
|
611
|
-
attempts: 3,
|
|
612
|
-
backoff: 'exponential',
|
|
613
|
-
delay: 1000
|
|
614
|
-
},
|
|
615
|
-
hooks: {
|
|
616
|
-
onComplete: async (job, result) => {
|
|
617
|
-
await db.emails.updateStatus(job.id, 'sent');
|
|
618
|
-
await analytics.track('email_sent', {
|
|
619
|
-
email: job.data.email
|
|
620
|
-
});
|
|
621
|
-
},
|
|
622
|
-
onFailed: async (job, error) => {
|
|
623
|
-
await db.emails.updateStatus(job.id, 'failed');
|
|
624
|
-
await alertService.notify({
|
|
625
|
-
message: `Email failed: ${job.data.email}`,
|
|
626
|
-
error: error.message
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
emailQueue.process('send-email', async (job) => {
|
|
633
|
-
const { email, subject, message } = job.data;
|
|
634
|
-
await mailService.send(email, subject, message);
|
|
635
|
-
return { sent: true };
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
// Usage in endpoint
|
|
639
|
-
app.post('/api/send-email', async (ctx) => {
|
|
640
|
-
const job = await emailQueue.add('send-email', {
|
|
641
|
-
email: ctx.body.email,
|
|
642
|
-
subject: ctx.body.subject,
|
|
643
|
-
message: ctx.body.message
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
return { jobId: job.id };
|
|
647
|
-
});
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
### Example 2: File Processing Queue
|
|
651
|
-
|
|
652
|
-
```typescript
|
|
653
|
-
const fileQueue = new JobQueue('files', {
|
|
654
|
-
concurrency: 2,
|
|
655
|
-
limiter: {
|
|
656
|
-
max: 10,
|
|
657
|
-
duration: 60000
|
|
658
|
-
}
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
fileQueue.process('process-upload', async (job) => {
|
|
662
|
-
const { fileId, fileName } = job.data;
|
|
663
|
-
|
|
664
|
-
// Download file
|
|
665
|
-
const file = await storage.download(fileId);
|
|
666
|
-
|
|
667
|
-
// Process
|
|
668
|
-
const processed = await imageService.optimize(file);
|
|
669
|
-
|
|
670
|
-
// Upload result
|
|
671
|
-
const resultId = await storage.upload(processed);
|
|
672
|
-
|
|
673
|
-
return { resultId, fileName };
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
// Add to queue from endpoint
|
|
677
|
-
app.post('/api/upload', async (ctx) => {
|
|
678
|
-
const file = ctx.files.file;
|
|
679
|
-
const fileId = await storage.save(file);
|
|
680
|
-
|
|
681
|
-
const job = await fileQueue.add('process-upload', {
|
|
682
|
-
fileId,
|
|
683
|
-
fileName: file.name
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
return {
|
|
687
|
-
fileId,
|
|
688
|
-
jobId: job.id,
|
|
689
|
-
status: 'processing'
|
|
690
|
-
};
|
|
691
|
-
});
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
### Example 3: Scheduled Tasks
|
|
695
|
-
|
|
696
|
-
```typescript
|
|
697
|
-
const schedulerQueue = new JobQueue('scheduler', {
|
|
698
|
-
concurrency: 5
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
schedulerQueue.process('send-daily-report', async (job) => {
|
|
702
|
-
const { userId } = job.data;
|
|
703
|
-
const report = await generateReport(userId);
|
|
704
|
-
await emailService.send(userId, 'Daily Report', report);
|
|
705
|
-
return { sent: true };
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
// Schedule every day at 8 AM
|
|
709
|
-
setInterval(async () => {
|
|
710
|
-
const now = new Date();
|
|
711
|
-
if (now.getHours() === 8 && now.getMinutes() === 0) {
|
|
712
|
-
const users = await db.users.findAll();
|
|
713
|
-
await schedulerQueue.addBulk(
|
|
714
|
-
users.map(u => ({
|
|
715
|
-
name: 'send-daily-report',
|
|
716
|
-
data: { userId: u.id }
|
|
717
|
-
}))
|
|
718
|
-
);
|
|
719
|
-
}
|
|
720
|
-
}, 60000); // Check every minute
|
|
721
|
-
```
|
|
722
|
-
|
|
723
|
-
### Example 4: Data Import
|
|
724
|
-
|
|
725
|
-
```typescript
|
|
726
|
-
const importQueue = new JobQueue('import', {
|
|
727
|
-
concurrency: 1,
|
|
728
|
-
retry: { attempts: 2 }
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
importQueue.process('import-csv', async (job) => {
|
|
732
|
-
const { fileId, userId } = job.data;
|
|
733
|
-
|
|
734
|
-
// Read CSV
|
|
735
|
-
const file = await storage.read(fileId);
|
|
736
|
-
const records = await parseCSV(file);
|
|
737
|
-
|
|
738
|
-
// Validate
|
|
739
|
-
const validated = records.map(r => validateRecord(r));
|
|
740
|
-
|
|
741
|
-
// Insert
|
|
742
|
-
const inserted = await db.users.insertMany(validated);
|
|
743
|
-
|
|
744
|
-
// Notify user
|
|
745
|
-
await emailService.send(userId, 'Import Complete',
|
|
746
|
-
`Successfully imported ${inserted.length} records`
|
|
747
|
-
);
|
|
748
|
-
|
|
749
|
-
return { imported: inserted.length };
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
importQueue.on('failed', async (job, error) => {
|
|
753
|
-
const { userId } = job.data;
|
|
754
|
-
await emailService.send(userId, 'Import Failed',
|
|
755
|
-
`Error: ${error.message}`
|
|
756
|
-
);
|
|
757
|
-
});
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
---
|
|
761
|
-
|
|
762
|
-
## Summary
|
|
763
|
-
|
|
764
|
-
Job Queue cocok untuk:
|
|
765
|
-
- ✅ Long-running operations (jangan block HTTP response)
|
|
766
|
-
- ✅ Retry logic dengan exponential backoff
|
|
767
|
-
- ✅ Rate limiting & concurrency control
|
|
768
|
-
- ✅ Callback hooks untuk success/failure
|
|
769
|
-
- ✅ Priority & delayed job scheduling
|
|
770
|
-
- ✅ Bulk operations
|
|
771
|
-
|
|
772
|
-
Use dengan bijak untuk improve application reliability & user experience!
|