You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(docs): document worker queue limits and subsystem events
- Add maxQueueDepth and maxQueueWaitMs worker-pool options
- Add worker, session, and csrf lifecycle event reference entries
- Update taskTimeoutMs default to 5000 and note fault events
- Update type definitions for the new options and events
returnctx.send.json({ error: 'Worker not enabled' }, { status: 503 })
79
+
returnctx.send.json(
80
+
{
81
+
error: 'Worker not enabled'
82
+
},
83
+
{
84
+
status: 503
85
+
}
86
+
)
80
87
}
81
-
const result =awaitworker.run<{ done:boolean; value:number }>({ iterations: 50_000 })
82
-
returnctx.send.json({ value: result?.value })
88
+
const result =awaitworker.run<{ done:boolean; value:number }>({
89
+
iterations: 50_000
90
+
})
91
+
returnctx.send.json({
92
+
value: result?.value
93
+
})
83
94
}
84
95
```
85
96
@@ -105,7 +116,7 @@ worker: {
105
116
106
117
### `taskTimeoutMs`
107
118
108
-
Per-task timeout in milliseconds. Default is **30000**. A task that runs longer rejects with a timeout errorand the worker is respawned.
119
+
Per-task timeout in milliseconds. Default is **5000**. A task that runs longer rejects with a timeout error, the slot is reclaimed, and the worker is respawned. The reclaim surfaces as a [`worker:timeout`](/middleware/observability/events#workers) event followed by [`worker:respawn`](/middleware/observability/events#workers).
109
120
110
121
```typescript
111
122
worker: {
@@ -114,6 +125,31 @@ worker: {
114
125
}
115
126
```
116
127
128
+
### `maxQueueDepth`
129
+
130
+
Maximum accepted-but-unsettled tasks the pool holds before turning new work away. Default is the worker count times **8**, so a pool of 4 holds up to 32. Once the ceiling is hit a new dispatch is refused immediately rather than queued, which keeps a flood of work from piling up without bound:
131
+
132
+
```typescript
133
+
worker: {
134
+
scriptURL: workerScriptUrl,
135
+
poolSize: 4,
136
+
maxQueueDepth: 64
137
+
}
138
+
```
139
+
140
+
### `maxQueueWaitMs`
141
+
142
+
Maximum projected wait, measured as the chosen slot's pending count times `taskTimeoutMs`, before a dispatch is refused. Default is **2000**. A task that would otherwise sit behind a long backlog is turned away fast instead of waiting:
143
+
144
+
```typescript
145
+
worker: {
146
+
scriptURL: workerScriptUrl,
147
+
maxQueueWaitMs: 5_000
148
+
}
149
+
```
150
+
151
+
A refused dispatch rejects right away and surfaces as a [`worker:rejected`](/middleware/observability/events#workers) event, with `reason` saying whether `maxQueueDepth` or `maxQueueWaitMs` tripped it.
152
+
117
153
## Complete Example (Inline Worker)
118
154
119
155
Using an inline worker script with `Blob` and `createObjectURL`:
@@ -127,13 +163,21 @@ self.onmessage = (e) => {
127
163
const n = Math.max(0, Number(data.iterations) || 50000)
-**No pool:** A router created without `worker` leaves `ctx.getState('worker' as never)` undefined. Return 503 or a clear message when the route requires a worker.
153
197
-**Worker error:** When the worker calls `postMessage({ error: true, message: '...' })`, `worker.run()` rejects with an `Error` carrying that message. Without a message, the error reads `Worker returned an error with no message`.
154
-
-**Worker crash:** When the worker throws or crashes, `run()` rejects with `Worker task failed before responding`.
155
-
-**Task timeout:** When a task runs past `taskTimeoutMs` (default 30000), `run()` rejects with `Worker task exceeded <ms>ms timeout`.
198
+
-**Worker crash:** When the worker throws or crashes, `run()` rejects with `Worker task failed before responding`, and the slot recovers on its own.
199
+
-**Task timeout:** When a task runs past `taskTimeoutMs` (default 5000), `run()` rejects with `Worker task exceeded <ms>ms timeout`.
200
+
-**Refused under load:** When the pool is at `maxQueueDepth` or the projected wait passes `maxQueueWaitMs`, `run()` rejects with a queue-full or slot-busy error before the task ever starts.
156
201
157
-
Catch a rejected task and forward it to the [centralized error handler](/error-handling/object-details):
202
+
Every one of these faults also streams through the observability bus as a [worker event](/middleware/observability/events#workers), so a stall, crash, recovery, or refusal is visible without touching the request path. Catch a rejected task and forward it to the [centralized error handler](/error-handling/object-details):
Copy file name to clipboardExpand all lines: docs/id/core-concepts/worker-pool.md
+55-10Lines changed: 55 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -67,7 +67,7 @@ self.postMessage({
67
67
68
68
### 3. Pakai di Rute
69
69
70
-
Handle worker tinggal di framework state, jadi `ctx.getState` menjangkaunya dengan tipe `WorkerRunHandle`. Router yang dibuat tanpa `worker` membiarkan handle undefined, yang merupakan momen untuk mengembalikan 503:
70
+
Handle worker tinggal di framework state, jadi `ctx.getState` menjangkaunya dengan tipe `WorkerRunHandle`. Router yang dibuat tanpa `worker` membiarkan handle undefined, dan di situlah saatnya mengembalikan 503:
71
71
72
72
```typescript twoslash
73
73
// routes/heavy.ts
@@ -76,10 +76,21 @@ import type { Context, WorkerRunHandle } from '@neabyte/deserve'
returnctx.send.json({ error: 'Worker not enabled' }, { status: 503 })
79
+
returnctx.send.json(
80
+
{
81
+
error: 'Worker not enabled'
82
+
},
83
+
{
84
+
status: 503
85
+
}
86
+
)
80
87
}
81
-
const result =awaitworker.run<{ done:boolean; value:number }>({ iterations: 50_000 })
82
-
returnctx.send.json({ value: result?.value })
88
+
const result =awaitworker.run<{ done:boolean; value:number }>({
89
+
iterations: 50_000
90
+
})
91
+
returnctx.send.json({
92
+
value: result?.value
93
+
})
83
94
}
84
95
```
85
96
@@ -105,7 +116,7 @@ worker: {
105
116
106
117
### `taskTimeoutMs`
107
118
108
-
Timeout per tugas dalam milidetik. Default adalah **30000**. Tugas yang berjalan lebih lama ditolak dengan error timeoutdan worker dilahirkan ulang.
119
+
Timeout per tugas dalam milidetik. Default adalah **5000**. Tugas yang berjalan lebih lama ditolak dengan error timeout, slot direklaim, dan worker dijalankan ulang. Reklaim ini muncul sebagai event [`worker:timeout`](/id/middleware/observability/events#worker) lalu [`worker:respawn`](/id/middleware/observability/events#worker).
109
120
110
121
```typescript
111
122
worker: {
@@ -114,6 +125,31 @@ worker: {
114
125
}
115
126
```
116
127
128
+
### `maxQueueDepth`
129
+
130
+
Maksimum tugas diterima-tapi-belum-selesai yang ditahan pool sebelum menolak pekerjaan baru. Default adalah jumlah worker dikali **8**, jadi pool 4 menahan hingga 32. Begitu batas tercapai, dispatch baru ditolak langsung alih-alih diantrekan, sehingga banjir pekerjaan tidak menumpuk tanpa batas:
131
+
132
+
```typescript
133
+
worker: {
134
+
scriptURL: workerScriptUrl,
135
+
poolSize: 4,
136
+
maxQueueDepth: 64
137
+
}
138
+
```
139
+
140
+
### `maxQueueWaitMs`
141
+
142
+
Maksimum proyeksi tunggu, diukur sebagai jumlah tugas pending pada slot terpilih dikali `taskTimeoutMs`, sebelum dispatch ditolak. Default adalah **2000**. Tugas yang seharusnya menunggu di belakang antrean panjang ditolak cepat alih-alih menunggu:
143
+
144
+
```typescript
145
+
worker: {
146
+
scriptURL: workerScriptUrl,
147
+
maxQueueWaitMs: 5_000
148
+
}
149
+
```
150
+
151
+
Dispatch yang ditolak langsung gagal dan muncul sebagai event [`worker:rejected`](/id/middleware/observability/events#worker), dengan `reason` menyebut apakah `maxQueueDepth` atau `maxQueueWaitMs` yang memicunya.
152
+
117
153
## Contoh Lengkap (Worker Inline)
118
154
119
155
Memakai script worker inline dengan `Blob` dan `createObjectURL`:
@@ -127,13 +163,21 @@ self.onmessage = (e) => {
127
163
const n = Math.max(0, Number(data.iterations) || 50000)
-**Tanpa pool:** Router yang dibuat tanpa `worker` membiarkan `ctx.getState('worker' as never)` undefined. Kembalikan 503 atau pesan jelas ketika rute butuh worker.
153
197
-**Error worker:** Ketika worker memanggil `postMessage({ error: true, message: '...' })`, `worker.run()` ditolak dengan `Error` yang membawa pesan itu. Tanpa pesan, error berbunyi `Worker returned an error with no message`.
154
-
-**Crash worker:** Ketika worker melempar atau crash, `run()` ditolak dengan `Worker task failed before responding`.
155
-
-**Timeout tugas:** Ketika tugas berjalan melewati `taskTimeoutMs` (default 30000), `run()` ditolak dengan `Worker task exceeded <ms>ms timeout`.
198
+
-**Crash worker:** Ketika worker melempar atau crash, `run()` ditolak dengan `Worker task failed before responding`, dan slot pulih dengan sendirinya.
199
+
-**Timeout tugas:** Ketika tugas berjalan melewati `taskTimeoutMs` (default 5000), `run()` ditolak dengan `Worker task exceeded <ms>ms timeout`.
200
+
-**Ditolak di bawah beban:** Ketika pool mencapai `maxQueueDepth` atau proyeksi tunggu melewati `maxQueueWaitMs`, `run()` ditolak dengan error antrean-penuh atau slot-sibuk sebelum tugas sempat mulai.
156
201
157
-
Tangkap tugas yang ditolak dan teruskan ke [error handler terpusat](/id/error-handling/object-details):
202
+
Setiap kesalahan ini juga mengalir lewat bus observability sebagai [event worker](/id/middleware/observability/events#worker), jadi stall, crash, pemulihan, atau penolakan terlihat tanpa menyentuh jalur request. Tangkap tugas yang ditolak dan teruskan ke [error handler terpusat](/id/error-handling/object-details):
Ketika request diblokir, middleware mengembalikan pesan `RequestblockedbyCSRFprotection` dengan **status code 403**. Untuk membentuk response itu, daftarkan satu handler dengan [`router.catch()`](/id/error-handling/object-details), atau andalkan [perilaku default](/id/error-handling/default-behavior).
104
+
105
+
Aturan `origin` atau `secFetchSite` kustom yang melempar gagal pemeriksaannya sendiri dan jatuh aman ke penolakan, dan kesalahannya muncul sebagai event [`csrf:rule-error`](/id/middleware/observability/events#middleware) yang menyebut aturan mana yang rusak alih-alih tetap tersembunyi.
Listener yang sama menangkap kesalahan dari worker pool dan middleware bawaan. Task yang timeout, worker yang crash, dispatch yang ditolak di bawah beban, cookie session yang gagal didekode, dan aturan CSRF yang melempar masing-masing tiba sebagai event-nya sendiri. Saring berdasarkan kind yang terdaftar di [Referensi Event](/id/middleware/observability/events#worker) untuk merutekannya ke tempat log:
61
+
62
+
```typescript twoslash
63
+
import { Router } from'@neabyte/deserve'
64
+
65
+
const router =newRouter({
66
+
routesDir: './routes'
67
+
})
68
+
// ---cut---
69
+
router.on((event) => {
70
+
// Bereaksi pada kesalahan worker dan middleware
71
+
if (event.kind==='worker:crash'||event.kind==='session:invalid') {
72
+
console.error(event.kind, event.metadata)
73
+
}
74
+
})
75
+
```
76
+
54
77
## Memasangkan Dengan Penanganan Error
55
78
56
-
Dua hook menutup tugas berbeda:
79
+
Dua hook menangani tugas berbeda:
57
80
58
81
-[`router.catch()`](/id/error-handling/object-details) membentuk response yang diterima klien.
59
82
-`router.on()` mencatat apa yang terjadi untuk log dan metrik.
@@ -65,11 +88,20 @@ Pakai `catch` untuk mengontrol balasan, dan `on` untuk mengamatinya. Pengaturan
Copy file name to clipboardExpand all lines: docs/id/middleware/observability/events.md
+22-2Lines changed: 22 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ description: "Referensi semua event siklus hidup dan error yang dipancarkan rout
6
6
7
7
Setiap event dari [`router.on()`](/id/middleware/observability/overview) membawa diskriminan `kind` dan objek `metadata`. Halaman ini mendaftar setiap jenis dan field yang disediakannya.
8
8
9
-

9
+

10
10
11
11
## Server
12
12
@@ -15,7 +15,7 @@ Setiap event dari [`router.on()`](/id/middleware/observability/overview) membawa
15
15
|`server:listening`|`port`, `hostname`|
16
16
|`server:shutdown`| tidak ada |
17
17
18
-
`server:listening` menyala saat server mengikat port. `server:shutdown` menyala setelah server selesai dikuras.
18
+
`server:listening` menyala saat server mengikat port. `server:shutdown` menyala setelah server selesai menuntaskan request berjalan.
19
19
20
20
## Rute
21
21
@@ -41,6 +41,26 @@ Event reload datang dari hot reload saat berkas berubah di disk.
41
41
42
42
Event view datang dari [mesin rendering DVE](/id/rendering/).
`worker:timeout` menyala saat sebuah task melewati tenggatnya, `worker:crash` saat worker mati di tengah task, dan `worker:respawn` saat slot yang dibebaskan diganti. `worker:rejected` menyala saat sebuah dispatch ditolak di bawah beban, dengan `reason` menyebut apakah kedalaman antrean atau proyeksi tunggu yang memicu batas. Ini datang dari [worker pool](/id/core-concepts/worker-pool).
`session:invalid` menyala saat cookie bertanda tangan gagal didekode, dengan `reason` menyebut apakah nilainya dirusak, sudah lewat `maxAge`, atau malformed, sementara request lanjut tanpa session terpasang. Ini datang dari [middleware session](/id/middleware/session). `csrf:rule-error` menyala saat aturan CSRF kustom melempar, menyebut aturan mana yang rusak sementara pemeriksaan tetap jatuh aman ke penolakan. Ini datang dari [middleware CSRF](/id/middleware/csrf).
0 commit comments