Skip to content

Commit 3fa8dfc

Browse files
authored
Merge pull request #10 from noxify/store-result
Add job result storage functionality
2 parents 4c64d96 + b1b203d commit 3fa8dfc

File tree

32 files changed

+1339
-137
lines changed

32 files changed

+1339
-137
lines changed

.changeset/clean-brooms-cut.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
"@vorsteh-queue/core": minor
3+
"@vorsteh-queue/adapter-drizzle": minor
4+
"@vorsteh-queue/adapter-prisma": minor
5+
---
6+
7+
Add job result storage functionality
8+
9+
Job handlers can now return values that are automatically stored in the database and made available through events and job records.
10+
11+
**New Features:**
12+
13+
- Added optional `result` field to job records for storing handler return values
14+
- Enhanced `BaseJob` interface with generic result type parameter
15+
- Updated all adapters (Drizzle, Prisma, Memory) to support result storage
16+
- Results are accessible in `job:completed` events and job queries
17+
18+
**Database Changes:**
19+
20+
- Added `result` JSONB column to queue_jobs table in both Drizzle and Prisma schemas
21+
- Column is optional and backward compatible with existing jobs
22+
23+
**API Changes:**
24+
25+
- `JobHandler<TPayload, TResult>` now supports result type parameter
26+
- `updateJobStatus()` method accepts optional result parameter
27+
- Job completion events include result data
28+
29+
**Examples:**
30+
31+
```typescript
32+
// Handler with typed result
33+
queue.register<EmailPayload, EmailResult>("send-email", async (job) => {
34+
await sendEmail(job.payload)
35+
return { messageId: "msg_123", sent: true } // Stored in job.result
36+
})
37+
38+
// Access results in events
39+
queue.on("job:completed", (job) => {
40+
console.log("Result:", job.result) // { messageId: "msg_123", sent: true }
41+
})
42+
```
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
"@vorsteh-queue/core": minor
3+
"@vorsteh-queue/adapter-drizzle": minor
4+
"@vorsteh-queue/adapter-prisma": minor
5+
---
6+
7+
Add configurable job timeouts and rename `processingInterval` to `pollInterval`.
8+
9+
**New Features:**
10+
11+
- **Configurable job timeouts**: Set `timeout: number` for specific timeout or `timeout: false` to disable timeout completely
12+
- **Job-specific timeout override**: Individual jobs can override queue default timeout settings
13+
- **Timeout field storage**: Job timeout configuration is now stored in job records for proper handling
14+
15+
**Breaking Changes:**
16+
17+
- **Renamed `processingInterval` to `pollInterval`**: More accurately describes the interval between queue polling cycles
18+
- **Database schema changes**: Added `timeout` column to queue_jobs table (JSONB field, nullable)
19+
20+
**Migration Guide:**
21+
22+
```typescript
23+
// Before
24+
const queue = new Queue(adapter, {
25+
name: "my-queue",
26+
processingInterval: 1000, // OLD
27+
})
28+
29+
// After
30+
const queue = new Queue(adapter, {
31+
name: "my-queue",
32+
pollInterval: 1000, // NEW
33+
})
34+
35+
// New timeout features
36+
await queue.add("long-job", payload, { timeout: false }) // Disable timeout
37+
await queue.add("quick-job", payload, { timeout: 5000 }) // 5 second timeout
38+
```
39+
40+
**Database Migration Required:**
41+
42+
- **Drizzle users**: Run migrations to add `timeout` column
43+
- **Prisma users**: Run `prisma db push` or `prisma migrate` to apply schema changes
44+
45+
**Examples:**
46+
47+
```typescript
48+
// Disable timeout for long-running data processing
49+
await queue.add("process-large-dataset", data, { timeout: false })
50+
51+
// Set specific timeout for API calls
52+
await queue.add("external-api-call", params, { timeout: 30000 })
53+
54+
// Use default timeout (30 seconds) - no change needed
55+
await queue.add("normal-job", payload)
56+
```
57+
58+
This release improves queue configuration clarity and provides flexible timeout control for different job types.

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ jobs:
100100
run: pnpm test:core
101101

102102
- name: Test Drizzle PostgreSQL (PGlite)
103-
run: pnpm test:drizzle-postgres
103+
run: pnpm test:drizzle
104104

105105
- name: Test Prisma (Testcontainers - Postgres)
106106
run: |
107107
pnpm -F adapter-prisma prisma:generate
108-
pnpm test:prisma-postgres
108+
pnpm test:prisma
109109
env:
110110
# Ensure Docker is available for Testcontainers
111111
TESTCONTAINERS_RYUK_DISABLED: true

.github/workflows/docs.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Deploy production docs
2+
3+
on:
4+
pull_request:
5+
types:
6+
- closed
7+
branches:
8+
- "changeset-release/main"
9+
10+
jobs:
11+
deploy-production:
12+
runs-on: ubuntu-latest
13+
if: github.event.pull_request.merged == true
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Setup
19+
uses: ./.github/setup
20+
21+
- name: Install Vercel CLI
22+
run: npm install --global vercel@latest
23+
- name: Deploy to Vercel
24+
run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
25+
env:
26+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
27+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}

README.md

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@
2525
│ ├── adapter-drizzle/ # Drizzle ORM adapter (PostgreSQL)
2626
│ └── adapter-prisma/ # Prisma ORM adapter (PostgreSQL)
2727
├── examples/ # Standalone usage examples
28-
│ ├── drizzle-pg/ # Drizzle + node-postgres
29-
│ ├── drizzle-postgres/ # Drizzle + postgres.js
30-
│ ├── progress-tracking/ # Real-time progress updates
31-
│ ├── event-system/ # Comprehensive event monitoring
32-
│ └── pm2-workers/ # Production deployment with PM2
3328
└── tooling/ # Shared development tools
3429
```
3530

@@ -84,21 +79,22 @@ const queue = new Queue(new PostgresPrismaQueueAdapter(prisma), { name: "my-queu
8479
// Register job handlers
8580
queue.register<EmailPayload, EmailResult>("send-email", async (job) => {
8681
console.log(`Sending email to ${job.payload.to}`)
87-
82+
8883
// Send email logic here
8984
await sendEmail(job.payload)
90-
91-
return {
92-
messageId: "msg_123",
93-
sent: true
85+
86+
// Return result - will be stored in job.result field
87+
return {
88+
messageId: "msg_123",
89+
sent: true,
9490
}
9591
})
9692

9793
// Add jobs
98-
await queue.add("send-email", {
99-
100-
subject: "Welcome!",
101-
body: "Welcome to our service!"
94+
await queue.add("send-email", {
95+
96+
subject: "Welcome!",
97+
body: "Welcome to our service!",
10298
})
10399
await queue.add(
104100
"send-email",
@@ -115,14 +111,7 @@ queue.start()
115111

116112
## Examples
117113

118-
Check out the [examples directory](./examples/) for complete, runnable examples:
119-
120-
- **[drizzle-pg](./examples/drizzle-pg/)** - Basic example using Drizzle ORM with node-postgres (pg)
121-
- **[drizzle-pglite](./examples/drizzle-pglite/)** - Zero-setup example using Drizzle ORM with PGlite (embedded PostgreSQL)
122-
- **[drizzle-postgres](./examples/drizzle-postgres/)** - Advanced example using Drizzle ORM with postgres.js and recurring jobs
123-
- **[event-system](./examples/event-system/)** - Comprehensive event monitoring and statistics using Drizzle ORM with postgres.js
124-
- **[pm2-workers](./examples/pm2-workers/)** - Manage multiple Vorsteh Queues with PM2 using Drizzle ORM with postgres.js
125-
- **[progress-tracking](./examples/progress-tracking/)** - Real-time job progress tracking using Drizzle ORM with postgres.js
114+
Check out the [examples directory](./examples/) for complete, runnable examples.
126115

127116
> **Note**: All examples demonstrate the UTC-first timezone approach and automatic job cleanup features.
128117
@@ -196,6 +185,49 @@ await queue.add("business-task", payload, {
196185
4. **Simple and predictable** - no runtime timezone complexity
197186
5. **Server timezone independent** - works consistently across environments
198187

188+
## Job Results
189+
190+
Job handlers can return results that are automatically stored and made available:
191+
192+
```typescript
193+
interface ProcessResult {
194+
processed: number
195+
errors: string[]
196+
duration: number
197+
}
198+
199+
queue.register<{ items: string[] }, ProcessResult>("process-data", async (job) => {
200+
const startTime = Date.now()
201+
const errors: string[] = []
202+
let processed = 0
203+
204+
for (const item of job.payload.items) {
205+
try {
206+
await processItem(item)
207+
processed++
208+
} catch (error) {
209+
errors.push(`Failed to process ${item}: ${error.message}`)
210+
}
211+
}
212+
213+
// Return result - automatically stored in job.result field
214+
return {
215+
processed,
216+
errors,
217+
duration: Date.now() - startTime,
218+
}
219+
})
220+
221+
// Access results in events
222+
queue.on("job:completed", (job) => {
223+
const result = job.result as ProcessResult
224+
console.log(`Processed ${result.processed} items in ${result.duration}ms`)
225+
if (result.errors.length > 0) {
226+
console.warn(`Errors: ${result.errors.join(", ")}`)
227+
}
228+
})
229+
```
230+
199231
## Progress Tracking
200232

201233
```typescript
@@ -235,6 +267,7 @@ queue.on("job:processing", (job) => {
235267

236268
queue.on("job:completed", (job) => {
237269
console.log(`🎉 Job ${job.name} completed successfully`)
270+
console.log(`📊 Result:`, job.result) // Access job result
238271
})
239272

240273
queue.on("job:failed", (job) => {

examples/README.md

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,87 @@ Standalone examples demonstrating different configurations of vorsteh-queue.
55
## Available Examples
66

77
### [drizzle-pg](./drizzle-pg/)
8+
89
Basic usage with Drizzle ORM and node-postgres driver.
10+
911
- Simple job processing
1012
- Priority handling
1113
- Delayed jobs
1214

15+
### [drizzle-pglite](./drizzle-pglite/)
16+
17+
Zero-setup example using Drizzle ORM with PGlite (embedded PostgreSQL).
18+
19+
- No external database required
20+
- Multiple job types with results
21+
- Progress tracking
22+
- Event system
23+
1324
### [drizzle-postgres](./drizzle-postgres/)
25+
1426
Advanced usage with Drizzle ORM and postgres.js driver.
27+
1528
- Multiple job types
1629
- Recurring jobs
1730
- Event handling
1831
- Queue monitoring
1932

33+
### [event-system](./event-system/)
34+
35+
Comprehensive event monitoring and statistics.
36+
37+
- Job lifecycle events
38+
- Real-time statistics
39+
- Performance monitoring
40+
- Error tracking
41+
42+
### [pm2-workers](./pm2-workers/)
43+
44+
Production deployment with PM2 process manager.
45+
46+
- Multiple worker processes
47+
- Process management
48+
- Scaling configuration
49+
- Production monitoring
50+
51+
### [prisma-client](./prisma-client/)
52+
53+
Prisma ORM with PostgreSQL using driver adapters.
54+
55+
- Type-safe database operations
56+
- Modern Prisma client w/o rust engine
57+
- Job management
58+
59+
### [prisma-client-js](./prisma-client-js/)
60+
61+
Prisma ORM with traditional prisma-client-js provider.
62+
63+
- Legacy Prisma setup
64+
- PostgreSQL integration
65+
- Job management
66+
67+
### [progress-tracking](./progress-tracking/)
68+
69+
Real-time job progress tracking demonstration.
70+
71+
- Progress updates
72+
- Long-running jobs
73+
- Progress monitoring
74+
- Status reporting
75+
76+
### [result-storage](./result-storage/)
77+
78+
Job result storage and retrieval with progress tracking.
79+
80+
- Job return values
81+
- Result persistence
82+
- Progress tracking
83+
- Error handling
84+
2085
## Quick Start
2186

2287
1. Choose an example directory
2388
2. Follow the README instructions in that directory
2489
3. Ensure PostgreSQL is running locally or update the DATABASE_URL
2590

26-
Each example is self-contained and can be used as a starting point for your own implementation.
91+
Each example is self-contained and can be used as a starting point for your own implementation.

examples/prisma-client-js/prisma/schema.prisma

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ model QueueJob {
2424
completedAt DateTime? @map("completed_at") @db.Timestamptz(6)
2525
failedAt DateTime? @map("failed_at") @db.Timestamptz(6)
2626
error Json? @db.JsonB
27+
result Json? @db.JsonB
2728
progress Int? @default(0)
29+
timeout Json? @db.JsonB
2830
cron String? @db.VarChar(255)
2931
repeatEvery Int? @map("repeat_every")
3032
repeatLimit Int? @map("repeat_limit")

examples/prisma-client/prisma/schema.prisma

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ model QueueJob {
3131
completedAt DateTime? @map("completed_at") @db.Timestamptz(6)
3232
failedAt DateTime? @map("failed_at") @db.Timestamptz(6)
3333
error Json? @db.JsonB
34+
result Json? @db.JsonB
3435
progress Int? @default(0)
36+
timeout Json? @db.JsonB
3537
cron String? @db.VarChar(255)
3638
repeatEvery Int? @map("repeat_every")
3739
repeatLimit Int? @map("repeat_limit")

0 commit comments

Comments
 (0)