@krizaka/outbox 0.1.4 → 0.1.5
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/README.md +3 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @krizaka/outbox
|
|
2
2
|
|
|
3
|
-
The transactional outbox of every Krizaka product. An event is **written in the caller's transaction**, so it exists exactly when the work committed, and is **delivered after the commit, at least once**, by workers that lease a batch. No broker: PostgreSQL and `FOR UPDATE SKIP LOCKED` share the work between instances. It replaces
|
|
3
|
+
The transactional outbox of every Krizaka product. An event is **written in the caller's transaction**, so it exists exactly when the work committed, and is **delivered after the commit, at least once**, by workers that lease a batch. No broker: PostgreSQL and `FOR UPDATE SKIP LOCKED` share the work between instances. It replaces Folioviv's `mail_outbox` (whose `flushOutbox` nobody called) and the analytics outboxes the products carried; it is the TypeScript twin of `krizaka-messaging`'s `krizaka_outbox` (the envelope is `id`, `type`, `occurredAt`, `payload`).
|
|
4
4
|
|
|
5
|
-
> **The guarantee is
|
|
5
|
+
> **The guarantee is _at least once_. Consumers must be idempotent on the message id.** A claim is exclusive while its lease is valid. A worker that delivers and then crashes before `complete` lets the lease expire, and the message is delivered **again**. The outbox never promises « exactly once ».
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { emitMigration, transaction } from "@krizaka/db";
|
|
@@ -32,7 +32,7 @@ await transaction(connect, (tx) => consumeOnce(tx, "credits", message.id, clock.
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
- **`append(tx, { topic, type, payload, id?, availableAt? })`** takes the transaction's `Query` first. A given `id` makes the append idempotent (the same fact twice is one message). `type` is a routing key, `evt.<module>.<fact>.v1` like Java's.
|
|
35
|
-
- **`claim(query, { owner, topic? })`** is `UPDATE … WHERE id IN (SELECT … FOR UPDATE SKIP LOCKED)` and sets the lease. **`complete`** and **`fail(error)`** act only for the lease's owner (a worker that lost its lease cannot overwrite the new holder). `fail` backs off — `availableAt = now + backoffSeconds × 2^(attempts−1)`, capped — and after `maxAttempts` the message is **dead** (kept, with its last error; `retryDead` gives it another life). A message whose
|
|
35
|
+
- **`claim(query, { owner, topic? })`** is `UPDATE … WHERE id IN (SELECT … FOR UPDATE SKIP LOCKED)` and sets the lease. **`complete`** and **`fail(error)`** act only for the lease's owner (a worker that lost its lease cannot overwrite the new holder). `fail` backs off — `availableAt = now + backoffSeconds × 2^(attempts−1)`, capped — and after `maxAttempts` the message is **dead** (kept, with its last error; `retryDead` gives it another life). A message whose _last_ allowed attempt's lease expired with no answer is dead too, not delivered a ninth time.
|
|
36
36
|
- **`runWorker`** loops `drainOnce` (claim a batch, deliver in order, record each outcome), sleeps through the `sleep` you pass when a round found nothing, and ends when the signal is aborted. Run `drainOnce` from a scheduler instead if you prefer cron to a loop.
|
|
37
37
|
- **Settings** (`@krizaka/outbox/settings`, needs `@krizaka/settings` and `zod`): `outbox.batchSize` (50, 1–500), `outbox.leaseSeconds` (60, 10–3600: longer than your slowest delivery), `outbox.maxAttempts` (8, 1–50), `outbox.backoffSeconds` (30, 1–3600), `outbox.backoffMaxSeconds` (3600), `outbox.retentionDays` (7). `purge` removes delivered and dead messages older than the retention: call it from the product's job.
|
|
38
38
|
- **Tables** (`outboxSchema`, Postgres schema `outbox`): `message` (id, topic, event_type, payload, created_at, available_at, attempts, leased_until, lease_owner, delivered_at, dead_at, last_error) and `processed` (consumer, message_id), which `consumeOnce` uses: the marker and the effect commit in the same transaction, so a crash leaves neither and a redelivery finds the marker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/outbox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "The transactional outbox of every Krizaka product: an event written in the caller's transaction, delivered after the commit at least once by leased workers, with doubling retries, dead letters and an idempotent-consumer helper. No broker, no database client, no wall clock.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"krizaka",
|
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
"typescript": "^5.9.3",
|
|
56
56
|
"vitest": "^4.1.11",
|
|
57
57
|
"zod": "^4.6.7",
|
|
58
|
-
"@krizaka/config": "^0.
|
|
58
|
+
"@krizaka/config": "^0.21.0"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@krizaka/
|
|
62
|
-
"@krizaka/
|
|
63
|
-
"@krizaka/
|
|
61
|
+
"@krizaka/settings": "^0.7.0",
|
|
62
|
+
"@krizaka/time": "^0.2.0",
|
|
63
|
+
"@krizaka/db": "^0.3.1"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"vitest": ">=4",
|