@eventferry/mysql 3.3.0 → 3.3.2

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.
Files changed (2) hide show
  1. package/CHANGELOG.md +195 -0
  2. package/package.json +9 -5
package/CHANGELOG.md ADDED
@@ -0,0 +1,195 @@
1
+ # @eventferry/mysql
2
+
3
+ ## 3.3.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [715523f]
8
+ - Updated dependencies [fb0549d]
9
+ - @eventferry/core@3.4.0
10
+
11
+ ## 3.3.1
12
+
13
+ ### Patch Changes
14
+
15
+ - 3c33f71: **chore: ship `CHANGELOG.md` inside the npm tarball**
16
+
17
+ Previously, each package's `files` allowlist contained only `"dist"` (and `"sql"` for `@eventferry/postgres`), so the auto-generated `CHANGELOG.md` was never published. Users browsing the package on npmjs.com or unpacking the tarball couldn't see release notes — they had to navigate to the GitHub repo.
18
+
19
+ This release adds `"CHANGELOG.md"` to the `files` array of every publishable package. Starting with this version, the per-version release notes are accessible:
20
+
21
+ - Directly in `node_modules/@eventferry/<pkg>/CHANGELOG.md` after `npm install`
22
+ - In the file listing on npmjs.com (under the "Code" / "Files" tab, depending on the npm UI)
23
+ - Inside the tarball downloaded from `https://registry.npmjs.org/...`
24
+
25
+ No code or API surface changes.
26
+
27
+ - Updated dependencies [3c33f71]
28
+ - @eventferry/core@3.3.1
29
+
30
+ ## 3.3.0
31
+
32
+ ### Minor Changes
33
+
34
+ - cdc20cf: **feat: DLQ enrichment + backpressure runtime + quota multiplier — Tier 1 of the reliability gap closed**
35
+
36
+ ### DLQ enrichment
37
+
38
+ Records routed to the dead-letter queue now carry the full context an operator needs to triage:
39
+
40
+ | Header | Set by | Note |
41
+ | --------------------------- | --------- | ------------------------------------------------------------------------------------------------ |
42
+ | `original-topic` | relay | already existed |
43
+ | `dlq-reason` | publisher | already existed (`error.message`) |
44
+ | `dlq-failed-at` | publisher | already existed (ISO timestamp) |
45
+ | `dlq-error-class` | publisher | **new** — `error.name` / constructor name |
46
+ | `dlq-attempts` | relay | **new** — string-encoded `attempts` count |
47
+ | `dlq-original-aggregate-id` | relay | **new** — for joining with business state |
48
+ | `dlq-original-message-id` | relay | **new** — for dedup / idempotency lookups |
49
+ | `dlq-error-stack` | relay | **new** — opt-in via `DlqConfig.includeStackTraces`, truncated to `maxStackBytes` (default 4 KB) |
50
+
51
+ ```ts
52
+ new Relay({
53
+ store,
54
+ publisher,
55
+ dlq: { topic: "orders.dlq", includeStackTraces: true, maxStackBytes: 4096 },
56
+ });
57
+ ```
58
+
59
+ ### Backpressure runtime behavior
60
+
61
+ When the driver classifies a failure as `errorKind: "backpressure"` (client-side producer queue full), the relay no longer treats it like a regular retriable failure. Instead:
62
+
63
+ - The record is re-queued via the new `OutboxStore.requeue(id, retryAt)` method,
64
+ - `attempts` is **not incremented** — the buffer being full is a "slow down" signal, not the record's fault,
65
+ - The retry is scheduled `RetryConfig.backpressureDelayMs` ms ahead (default 1000 ms).
66
+
67
+ Stores that don't implement `requeue` fall back to `markFailed` (with attempts++); both `@eventferry/postgres` and `@eventferry/mysql` ship a real implementation.
68
+
69
+ ### Quota multiplier
70
+
71
+ When the driver classifies a failure as `errorKind: "quota"` (broker `THROTTLING_QUOTA_EXCEEDED`), the scheduled retry delay is multiplied by `RetryConfig.quotaMultiplier` (default 5) so the producer gives the broker breathing room. Quota failures DO count as attempts — after the budget is exhausted the record routes to DLQ + `dead`.
72
+
73
+ ### New / changed types
74
+
75
+ - `RetryConfig` gains `backpressureDelayMs?` and `quotaMultiplier?`.
76
+ - `DlqConfig` gains `includeStackTraces?` and `maxStackBytes?`.
77
+ - `OutboxStore.requeue?(recordId, retryAt)` is a new **optional** method. Stores without it fall through to `markFailed`.
78
+
79
+ ### Backward compatibility
80
+
81
+ Pure-additive everywhere. Default behavior matches the prior release:
82
+
83
+ - A `RetryConfig` without `backpressureDelayMs` uses 1000 ms (sensible default).
84
+ - A `DlqConfig` without `includeStackTraces` keeps DLQ messages small (default off).
85
+ - An `OutboxStore` without `requeue` falls back to `markFailed` — same as before, just with a documented quirk.
86
+
87
+ This closes the last three Tier 1 items in `docs/kafka-gap-analysis/reliability.md`. Phase A reliability surface is now ~100% complete.
88
+
89
+ ### Patch Changes
90
+
91
+ - Updated dependencies [cdc20cf]
92
+ - @eventferry/core@3.3.0
93
+
94
+ ## 3.2.1
95
+
96
+ ### Patch Changes
97
+
98
+ - 9beb3e2: **chore: migrate to independent versioning (Astro pattern)**
99
+
100
+ Fixes the major-version inflation that produced four consecutive surprise majors (`1.0.4 → 2.0.0`, `2.0.0 → 3.0.0`, `3.0.0 → 4.0.0 corrected to 3.1.0`, `3.1.0 → 4.0.0 corrected to 3.2.0`) from changesets whose frontmatter only asked for `minor`.
101
+
102
+ **Root cause** (cited in [changesets/changesets#1759](https://github.com/changesets/changesets/issues/1759) and [docs/decisions.md](https://github.com/changesets/changesets/blob/main/docs/decisions.md)): the adapters listed `@eventferry/core` as a `peerDependency` with `workspace:*`. Changesets' documented rule is that an internal bump of a peer forces a major bump on the dependent — and the `fixed: [["@eventferry/*"]]` group reconciler then propagated that major across every package in the group.
103
+
104
+ **Fix** (exactly the [Astro config](https://github.com/withastro/astro/blob/main/.changeset/config.json)):
105
+
106
+ 1. `.changeset/config.json` — drop `fixed`, set `linked: []`, enable
107
+ `___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH.onlyUpdatePeerDependentsWhenOutOfRange: true`.
108
+ 2. Move `@eventferry/core` from `peerDependencies` to `dependencies` in
109
+ `@eventferry/postgres`, `@eventferry/mysql`, `@eventferry/kafka`, and
110
+ `@eventferry/schema-registry`. External user-facing peers (`pg`,
111
+ `mysql2`, `kafkajs`, `@confluentinc/kafka-javascript`,
112
+ `@kafkajs/confluent-schema-registry`) stay unchanged.
113
+
114
+ **Effect on releases.** Packages now evolve at independent semver tempos: a `core: minor` changeset produces `core@3.3.0` alongside `postgres@3.2.1` (patch, from "Updated dependencies"). No more major surprises. No more manual force-push corrections.
115
+
116
+ **Effect on consumers.** Pure-additive at the install boundary: `npm i @eventferry/kafka` now resolves `@eventferry/core` automatically (it's a regular dep). Previously consumers had to install it themselves as a peer; the typical flow already did this. No source-code changes required.
117
+
118
+ - Updated dependencies [9beb3e2]
119
+ - @eventferry/core@3.2.1
120
+
121
+ ## 3.2.0
122
+
123
+ ### Patch Changes
124
+
125
+ - @eventferry/core@3.2.0
126
+
127
+ ## 3.1.0
128
+
129
+ ### Patch Changes
130
+
131
+ - Updated dependencies [da39b08]
132
+ - @eventferry/core@3.1.0
133
+
134
+ ## 3.0.0
135
+
136
+ ### Patch Changes
137
+
138
+ - Updated dependencies [f0c7483]
139
+ - @eventferry/core@3.0.0
140
+
141
+ ## 2.0.0
142
+
143
+ ### Minor Changes
144
+
145
+ - 0085bb1: **feat: MySQL & MariaDB support — `@eventferry/mysql`**
146
+
147
+ eventferry now ships with first-class MySQL support, in lockstep parity with the Postgres adapter. If you write to MySQL and publish to Kafka/Redpanda, you can stop hand-rolling the dual-write fix.
148
+
149
+ **What you get**
150
+
151
+ - **`MysqlStore`** — the same `OutboxStore` contract as `@eventferry/postgres`, ported to MySQL **8.0.1+** and MariaDB **10.6+**. Lock-free claim via `SELECT … FOR UPDATE SKIP LOCKED`, **strict per-aggregate ordering** under concurrent relays (same NOT-EXISTS head guard as Postgres), and a **crash-recovery reaper** so a relay crash between claim and ack never orphans rows.
152
+ - **`createMigrationSql`** — idempotent DDL for the outbox table (InnoDB + utf8mb4 + `DATETIME(3)` + native `JSON` columns). One call, you're set.
153
+ - **`MysqlBinlogRelay`** — a CDC streaming relay that tails the MySQL **binary log** (row-based) via the optional `@vlasky/zongji` peer dep. This is the MySQL analogue of `PostgresStreamingRelay` over WAL, and the same mechanism Debezium uses — but as a Node.js library, not a JVM cluster. Drops latency from "one poll interval" to a few milliseconds and lets you go after high-throughput workloads.
154
+ - **`purgeDone`** — batched retention of published rows, same shape as the Postgres adapter (`DELETE … ORDER BY id LIMIT`).
155
+
156
+ **Quick start**
157
+
158
+ ```bash
159
+ npm i @eventferry/mysql @eventferry/core mysql2
160
+ ```
161
+
162
+ ```ts
163
+ import mysql from "mysql2/promise";
164
+ import { MysqlStore, createMigrationSql } from "@eventferry/mysql";
165
+
166
+ const pool = mysql.createPool({ host, user, password, database });
167
+ await pool.query(createMigrationSql("outbox"));
168
+
169
+ const store = new MysqlStore({ pool });
170
+
171
+ // Inside your business transaction:
172
+ await store.enqueue(conn, {
173
+ topic: "orders.created",
174
+ aggregateType: "order",
175
+ aggregateId: order.id,
176
+ payload: { orderId: order.id, total: order.total },
177
+ });
178
+ ```
179
+
180
+ Hand the store to the core `Relay` and you have at-least-once event publishing with retries, DLQ routing, and strict per-aggregate ordering. For CDC mode (binlog), see the binlog section in [`@eventferry/mysql` README](./packages/mysql/README.md).
181
+
182
+ **Caveats (be honest)**
183
+
184
+ - **No native low-latency waker** — MySQL has no `LISTEN/NOTIFY` analogue. Either use the binlog relay, or tune the poll interval down (e.g. 100ms).
185
+ - **`@vlasky/zongji` is an optional peer** — only required if you actually use `MysqlBinlogRelay`. You'll get a clear runtime error if you forget it.
186
+ - **Older MySQL versions are not supported** — `FOR UPDATE SKIP LOCKED` is hard-required; pre-8.0.1 / pre-MariaDB-10.6 would serialize on every claim.
187
+ - **Binlog server config** is the user's responsibility — `binlog_format=ROW`, `binlog_row_image=FULL`, and a user with `REPLICATION SLAVE` + `REPLICATION CLIENT` grants. README has the full snippet.
188
+
189
+ **Roadmap status**
190
+
191
+ This closes the **MySQL / MariaDB** row on [ROADMAP.md](./ROADMAP.md) (Phase 1). Next up: SQL Server, then MongoDB.
192
+
193
+ ### Patch Changes
194
+
195
+ - @eventferry/core@2.0.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventferry/mysql",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
4
4
  "description": "MySQL / MariaDB store for @eventferry (polling + SKIP LOCKED claim)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -19,8 +19,12 @@
19
19
  }
20
20
  },
21
21
  "files": [
22
- "dist"
22
+ "dist",
23
+ "CHANGELOG.md"
23
24
  ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
24
28
  "keywords": [
25
29
  "outbox",
26
30
  "outbox-pattern",
@@ -47,7 +51,7 @@
47
51
  "node": ">=18"
48
52
  },
49
53
  "dependencies": {
50
- "@eventferry/core": "3.3.0"
54
+ "@eventferry/core": "3.4.0"
51
55
  },
52
56
  "peerDependencies": {
53
57
  "mysql2": "^3.0.0",
@@ -62,8 +66,8 @@
62
66
  "mysql2": "^3.11.5",
63
67
  "tsup": "^8.3.5",
64
68
  "typescript": "^5.7.2",
65
- "vitest": "^2.1.8",
66
- "@eventferry/core": "3.3.0"
69
+ "vitest": "^3.2.6",
70
+ "@eventferry/core": "3.4.0"
67
71
  },
68
72
  "scripts": {
69
73
  "build": "tsup",