@vlasky/zongji 0.7.0 → 0.8.0
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/CHANGELOG.md +22 -0
- package/README.md +71 -12
- package/index.d.ts +284 -7
- package/index.js +375 -19
- package/lib/binlog_event.js +889 -24
- package/lib/charset_map.js +86 -0
- package/lib/code_map.js +44 -1
- package/lib/common.js +376 -20
- package/lib/gtid_set.js +237 -0
- package/lib/mariadb_gtid.js +107 -0
- package/lib/packet/binlog.js +7 -1
- package/lib/rows_event.js +39 -7
- package/lib/sequence/binlog.js +179 -24
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to @vlasky/zongji since forking from nevill/zongji.
|
|
4
4
|
|
|
5
|
+
## [0.8.0] - 2026-07-06
|
|
6
|
+
|
|
7
|
+
- Export the `GtidSet` class (`import ZongJi, { GtidSet } from '@vlasky/zongji'`): parse a persisted set (e.g. a `zongji.gtidSet` checkpoint or `@@gtid_executed`), `add()` transactions, and test single-transaction membership with `contains(event.gtid)` - handy for snapshot drain barriers ("was this event already covered when I took my snapshot?") without maintaining a separate GTID parser. Fully tagged-GTID aware.
|
|
8
|
+
- Close the remaining resume-position gap for multi-table statements (multi-table `UPDATE`, foreign-key cascades). The server writes all of a statement's TableMap events before any row event, so a `filename`/`position` pair persisted while the first table's rows were being processed pointed past the later TableMaps; a consumer resuming there dropped the remaining tables' rows with no error. The resume position now freezes at a transaction's first TableMap and advances to the commit marker's end position, so a persisted position always replays whole transactions. Behaviour change: for row-logged transactions the position advances per transaction rather than per event, so resuming after a mid-transaction crash re-delivers that transaction's earlier row events (at-least-once, as already documented for the single-table case fixed in 0.7.1). Statement-logged content (`binlog_format=STATEMENT`/`MIXED`) still advances per event; its replay has no table-metadata dependency, so it never had the dropped-rows hazard.
|
|
9
|
+
- Tagged GTID support (MySQL 8.3+, `GTID_NEXT='AUTOMATIC:tag'`). Tagged transactions arrive as GTID_TAGGED_LOG_EVENT (code 42, a self-describing serialization format unrelated to the classic layout, previously `unknown`) and now decode as ordinary `gtid` events with `event.tag` set and `event.gtid` in `uuid:tag:gno` form. GTID sets accept and print the tagged grammar (`uuid:1-5:tag_a:1`), `zongji.gtidSet` tracks tagged transactions, `start({ gtidSet })` resumes from a tagged checkpoint (COM_BINLOG_DUMP_GTID's tagged payload encoding, understood by MySQL 8.3+ only), and `startAtEnd` seeds from a `gtid_executed` containing tags. This is also a compatibility fix: once a MySQL 8.3+ server has ever executed a tagged GTID it writes every Previous_gtids event in the tagged set encoding forever (surviving `RESET BINARY LOGS AND GTIDS`), which previously crashed parsing at the start of every dump, and any tag in `gtid_executed` broke `startAtEnd` seeding.
|
|
10
|
+
- New `rowsquery` event (MySQL ROWS_QUERY_LOG_EVENT, previously `unknown`): the SQL statement text behind the row events that follow, MySQL's analogue of MariaDB's `annotaterows`. The server sends it to every consumer while `binlog_rows_query_log_events=ON` (default `OFF`); no start option is involved, unlike MariaDB's opt-in `requestAnnotateRows`.
|
|
11
|
+
- `event.gtid` no longer leaks onto events that belong to no transaction: it is now cleared once a commit marker (Xid, XA Prepare, or a COMMIT/ROLLBACK/XA COMMIT/XA ROLLBACK query) has been delivered, so e.g. a rotate arriving between two transactions reports `gtid: undefined` instead of the previous transaction's GTID. The commit event itself still carries its transaction's GTID. A DDL statement has no commit marker of its own, so its GTID persists until the next transaction begins. New `zongji.lastGtid` getter exposes the same tracked value (the GTID of the transaction currently being delivered); it is only coherent read synchronously inside a `'binlog'` handler.
|
|
12
|
+
- Fix text CHAR and VARCHAR columns always decoding as utf8: they now decode via the column's own character set, as TEXT columns already did, so e.g. `latin1` and `greek` columns no longer produce mojibake. In the same pass, charsets whose stored byte order differs from the iconv-lite encoding of the same name are now mapped explicitly: MySQL's `latin1` is decoded as cp1252 (its real definition; `€`, `Ÿ` and friends in 0x80-0x9F previously came back as control characters from TEXT columns), and `ucs2`/`utf16`/`utf32` are decoded big-endian (previously byte-swapped garbage from TEXT columns). Behaviour change: consumers that stored the old garbled output will see correct strings after upgrading.
|
|
13
|
+
- MariaDB support (tested against MariaDB 11.8): ZongJi now detects the server flavour and follows MariaDB binlogs natively, announcing `@mariadb_slave_capability=4` so the server sends its real GTID events instead of rewriting them for legacy clients. MariaDB's GTID model (a `domain-server-sequence` watermark per replication domain) is fully supported: events carry `event.gtid` in MariaDB format, `zongji.gtidSet` exposes a MariaDB GTID position (seeded from `start({ gtidSet })`, from `@@gtid_current_pos` under `startAtEnd`, or from the GTID list event at the start of a binlog file), and `start({ gtidSet: '0-1-1234' })` resumes via `@slave_connect_state` with server-side skipping, including across failover. New event types: `mariadbgtid`, `mariadbgtidlist`, `binlogcheckpoint`, `startencryption`, plus `xaprepare` (also emitted by MySQL 5.7+, previously `unknown`). MariaDB data types decode to match mysql2 query results: `UUID`, `INET4` and `INET6` as canonical text, `VECTOR` as a raw Buffer, `JSON` as the LONGTEXT alias text it is, `COMPRESSED` columns transparently decompressed (with correct charsets, under all `binlog_row_metadata` settings), MariaDB 5.3-era "hires" temporals (`mysql56_temporal_format=OFF`) decoded correctly instead of desynchronising the row, and compressed binlog events (`log_bin_compress=ON`) decoded transparently as ordinary query/row events. `binlog_row_metadata=FULL` works on MariaDB 10.5+ with the same self-describing decode as MySQL; tables whose classic temporal columns could hide hires encodings automatically use `INFORMATION_SCHEMA` instead, and `UUID`/`INET*`/`VECTOR` values arrive as raw Buffers in FULL mode (the binlog cannot distinguish them from `BINARY`). An opt-in `requestAnnotateRows` start option asks the server for `annotaterows` events carrying the SQL statement text behind each row operation. The test suite runs against MariaDB 11.8 in CI alongside MySQL 5.7/8.0/8.4, and an offline MariaDB fixture pins the captured wire bytes of all of the above in the parser tests.
|
|
14
|
+
- Fix the resume position (`options.position`) going stale when the events carrying real positions are excluded by `includeEvents`: filtered events now advance it at the packet layer under the same safety rules as delivered ones (never past a TableMap, never a zero position). On MySQL the position merely lagged; on MariaDB, where events inside a transaction carry `end_log_pos=0`, common filter sets froze it entirely.
|
|
15
|
+
- Fix a filtered `rotate` event leaving an incoherent resume pair: the filename never updated while later events advanced the position into the new file. Rotates now update the `filename`/`position` pair before event filtering, whether delivered or not.
|
|
16
|
+
- GTID-based resume: `start({ gtidSet })` issues COM_BINLOG_DUMP_GTID, letting the server locate the correct binlog file and skip already-processed transactions itself; a persisted checkpoint therefore survives failover to another server in the same replication topology (requires `gtid_mode=ON`). `zongji.gtidSet` exposes the executed set for persisting: seeded exactly from the start set, from the server's `gtid_executed` under `startAtEnd`, or from the stream's Previous_gtids event when reading from the start of a binlog file, and extended only as observed transactions commit. Purged-GTID and GTID-mode errors surface through the `error` event. Heartbeat events (sent in place of server-side-skipped transactions and while idle) are now decoded instead of falling through to `unknown`, and Previous_gtids string formatting is canonical (single transactions print as `8`, not `8-8`)
|
|
17
|
+
- Support `binlog_row_metadata=FULL` (MySQL 8.0+): TableMap events now parse the optional metadata block (column names, signedness, character sets, enum/set value lists, primary key, column visibility), and when it is complete ZongJi decodes rows entirely from the binlog stream with no `INFORMATION_SCHEMA` queries and no connection pauses. Each TableMap event rebuilds the table's metadata, so `ALTER TABLE` can no longer leave stale column definitions behind. Enum/set values containing commas or quotes decode correctly in this mode (the `INFORMATION_SCHEMA` path cannot represent them). Under the default `binlog_row_metadata=MINIMAL`, integer signedness now comes from the binlog instead of being inferred from the `COLUMN_TYPE` string. TableMap events expose `columnNames`, `signedness`, `primaryKey` and `columnVisibility` where available, and column schemas gain `UNSIGNED`, `ENUM_VALUES` and `SET_VALUES`.
|
|
18
|
+
|
|
19
|
+
## [0.7.1] - 2026-07-04
|
|
20
|
+
|
|
21
|
+
- Fix `start()` calls made while a previous `start()` was still initialising silently discarding their filters. Since 0.7.0 filters are snapshotted and re-calling `start()` is the documented way to update them, but updates made between `start()` and the `ready` event were lost; consumers registering tables during boot (e.g. @vlasky/mysql-live-select) missed events for tables added in that window. Filters passed during initialisation now apply, exactly as when already running; stream options (filename/position/serverId) still come from the first call.
|
|
22
|
+
- Fix a resume-position gap that could silently drop row events. `options.position` was advanced past TableMap events on the cached-metadata path, so a consumer persisting `filename`/`position` for reconnect could resume between a TableMap and its row events; the resumed instance had no metadata for the table id and dropped those rows with no error. TableMap events no longer advance the resume position, closing the gap for single-table statements (the common case). A narrower window remains for multi-table statements (multi-table UPDATE, foreign-key cascades), where the server writes all TableMap events before any row events: emitting the first table's rows still advances the position past the later TableMaps. Rows already processed before a crash may be re-delivered after resume (at-least-once), which is recoverable where dropping is not.
|
|
23
|
+
- Fix rotate events corrupting the `filename`/`position` resume pair. A rotate's header position refers to the old binlog file (0 for the artificial rotate at the start of every dump), yet it was written into `options.position` alongside the new file's name; a consumer resuming from that pair after a real rotation could get "position > file size" or a mid-event read, and the artificial rotate silently reset the start position to 0. The rotate's payload position (the start of the new file) is now used, and the filename update is unconditional. Present in every zongji release since the original upstream project.
|
|
24
|
+
- Fix a corrupt GTID event's parse error being swallowed when `gtid` is excluded by `includeEvents`; the whole following transaction was then silently mislabelled as anonymous. The error now reaches the `error` event regardless of filtering.
|
|
25
|
+
- Schema drift between a binlog event being written and the metadata fetch (e.g. a column dropped in between) now emits a descriptive error naming the table and column counts, instead of throwing a bare TypeError from inside event parsing; the affected table's rows are skipped until its next TableMap event refreshes the metadata.
|
|
26
|
+
|
|
5
27
|
## [0.7.0] - 2026-07-04
|
|
6
28
|
|
|
7
29
|
### Breaking changes
|
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@vlasky/zongji)
|
|
4
4
|
[](https://github.com/vlasky/zongji/blob/master/LICENSE)
|
|
5
5
|
|
|
6
|
-
MySQL binlog-based change data capture (CDC) for Node.js, [originally created by Nevill Dutt](https://github.com/nevill/zongji).
|
|
6
|
+
MySQL and MariaDB binlog-based change data capture (CDC) for Node.js, [originally created by Nevill Dutt](https://github.com/nevill/zongji).
|
|
7
7
|
|
|
8
|
-
[@vlasky/zongji](https://github.com/vlasky/zongji) has been tested working with MySQL 5.7, 8.0 and 8.4.
|
|
8
|
+
[@vlasky/zongji](https://github.com/vlasky/zongji) has been tested working with MySQL 5.7, 8.0 and 8.4, and MariaDB 11.8.
|
|
9
9
|
|
|
10
10
|
It leverages [`mysql2`](https://github.com/sidorares/node-mysql2) for connections and authentication, while using zongji's binlog parsing and event pipeline.
|
|
11
11
|
|
|
@@ -72,8 +72,9 @@ const { default: ZongJi } = await import('@vlasky/zongji');
|
|
|
72
72
|
$ npm install @vlasky/zongji
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
* Enable
|
|
76
|
-
> Binlog checksum is enabled by default in all supported MySQL versions. Zongji can work with it, but it doesn't verify the checksum.
|
|
75
|
+
* Enable the binlog in `my.cnf`, restart the server after making the changes.
|
|
76
|
+
> Binlog checksum is enabled by default in all supported MySQL and MariaDB versions. Zongji can work with it, but it doesn't verify the checksum.
|
|
77
|
+
> On MariaDB, `binlog_format` defaults to `MIXED`, so setting it to `row` explicitly matters there in particular.
|
|
77
78
|
|
|
78
79
|
```
|
|
79
80
|
# Must be unique integer from 1-2^32
|
|
@@ -87,6 +88,12 @@ const { default: ZongJi } = await import('@vlasky/zongji');
|
|
|
87
88
|
binlog_do_db = employees # Optional, limit which databases to log
|
|
88
89
|
expire_logs_days = 10 # Optional, purge old logs
|
|
89
90
|
max_binlog_size = 100M # Optional, limit log size
|
|
91
|
+
|
|
92
|
+
# Recommended on MySQL 8.0+ / MariaDB 10.5+: makes binlog events carry
|
|
93
|
+
# complete column metadata (names, signedness, charsets, enum/set
|
|
94
|
+
# values), so ZongJi decodes rows without querying INFORMATION_SCHEMA
|
|
95
|
+
# (see below)
|
|
96
|
+
binlog_row_metadata = FULL
|
|
90
97
|
```
|
|
91
98
|
* Create an account with replication privileges, e.g. given privileges to account `zongji` (or any account that you use to read binary logs)
|
|
92
99
|
|
|
@@ -112,6 +119,17 @@ Option | Effect on row values
|
|
|
112
119
|
`decimalNumbers` | `DECIMAL` columns are returned as exact strings by default (e.g. `'-123.4500'`); set `decimalNumbers: true` to receive Numbers (may lose precision beyond 15 significant digits).
|
|
113
120
|
`jsonStrings` | `JSON` columns are returned as parsed JavaScript values by default; set `jsonStrings: true` to receive JSON strings.
|
|
114
121
|
|
|
122
|
+
### Table metadata source
|
|
123
|
+
|
|
124
|
+
To decode row events, ZongJi needs each table's column metadata. It obtains this in one of two ways:
|
|
125
|
+
|
|
126
|
+
* **From the binlog itself** (MySQL 8.0+ or MariaDB 10.5+ with `binlog_row_metadata=FULL`): every `TableMap` event carries column names, signedness, character sets and enum/set value lists. ZongJi decodes rows entirely from the stream: no `INFORMATION_SCHEMA` queries, no pausing the binlog connection, and no risk of the metadata being stale after an `ALTER TABLE` (each event describes the schema in force when it was written). Enum/set values containing commas or quotes also decode correctly only in this mode.
|
|
127
|
+
* **From `INFORMATION_SCHEMA`** (all other cases, including MySQL 5.7, the MySQL default `binlog_row_metadata=MINIMAL` and the MariaDB default `NO_LOG`): on the first event for each table, ZongJi briefly pauses the stream and fetches column metadata over its control connection. If the table is altered between the binlog write and the fetch, decoded rows may be misinterpreted until the next `TableMap` refresh.
|
|
128
|
+
|
|
129
|
+
Setting `binlog_row_metadata=FULL` on the server is therefore recommended. It is a dynamic global variable (`SET GLOBAL binlog_row_metadata=FULL`) with negligible binlog size overhead for typical schemas. Even under `MINIMAL`, MySQL 8.0+ binlogs carry exact integer signedness, which ZongJi uses in preference to parsing the column definition.
|
|
130
|
+
|
|
131
|
+
MariaDB caveats under `FULL`: the binlog cannot distinguish `UUID`, `INET4`, `INET6` and `VECTOR` columns from `BINARY`/`VARBINARY`, so in this mode their values arrive as raw `Buffer`s rather than the formatted values the `INFORMATION_SCHEMA` path produces; and tables containing classic temporal type codes (which may hide MariaDB 5.3-era "hires" columns the binlog cannot describe) automatically fall back to the `INFORMATION_SCHEMA` path.
|
|
132
|
+
|
|
115
133
|
Each instance includes the following methods:
|
|
116
134
|
|
|
117
135
|
Method Name | Arguments | Description
|
|
@@ -139,6 +157,8 @@ Option Name | Type | Description
|
|
|
139
157
|
`startAtEnd` | `boolean` | Pass `true` to only emit binlog events that occur after ZongJi's instantiation. Must be used in `start()` method for effect.<br>**Default:** `false`
|
|
140
158
|
`filename` | `string` | Begin reading events from this binlog file. If specified together with `position`, will take precedence over `startAtEnd`.
|
|
141
159
|
`position` | `integer` | Begin reading events from this position. Must be included with `filename`.
|
|
160
|
+
`gtidSet` | `string` | Resume from executed GTIDs (e.g. a persisted `zongji.gtidSet`): a MySQL GTID set (`'uuid:1-27'`; tagged transactions print as `'uuid:1-27:tag_a:1'` on MySQL 8.3+) or a MariaDB GTID position (`'0-1-1234'`), matching the server flavour. The server locates the correct binlog file itself and skips transactions already processed, so a checkpoint remains valid across failover to another server in the same replication topology. Requires `gtid_mode=ON` on MySQL; always available on MariaDB. Pass `''` to stream the entire available history. Takes precedence over `filename`/`position` and `startAtEnd`.
|
|
161
|
+
`requestAnnotateRows` | `boolean` | MariaDB only: ask the server to send `annotaterows` events (the SQL statement text preceding each row operation's events). Off by default; note the statement text may contain sensitive literals that the row images alone would not expose. Ignored on MySQL.<br>**Default:** `false`
|
|
142
162
|
`includeEvents` | `[string]` | Array of event names to include<br>**Example:** `['writerows', 'updaterows', 'deleterows']`
|
|
143
163
|
`excludeEvents` | `[string]` | Array of event names to exclude<br>**Example:** `['rotate', 'tablemap']`
|
|
144
164
|
`includeSchema` | `object` | Object describing which databases and tables to include (Only for row events). Use database names as the key and pass an array of table names or `true` (for the entire database).<br>**Example:** ```{ 'my_database': ['allow_table', 'another_table'], 'another_db': true }```
|
|
@@ -156,20 +176,57 @@ Event name | Description
|
|
|
156
176
|
`unknown` | Catch any other events
|
|
157
177
|
`query` | [Insert/Update/Delete Query](https://dev.mysql.com/doc/internals/en/query-event.html)
|
|
158
178
|
`intvar` | [Autoincrement and LAST_INSERT_ID](https://dev.mysql.com/doc/internals/en/intvar-event.html)
|
|
159
|
-
`rotate` | [New Binlog file](https://dev.mysql.com/doc/internals/en/rotate-event.html)
|
|
179
|
+
`rotate` | [New Binlog file](https://dev.mysql.com/doc/internals/en/rotate-event.html). The `filename` and `position` resume properties stay updated across rotations whether or not this event is included; include it only if you want to observe rotations yourself.
|
|
160
180
|
`format` | [Format Description](https://dev.mysql.com/doc/internals/en/format-description-event.html)
|
|
161
181
|
`xid` | [Transaction ID](https://dev.mysql.com/doc/internals/en/xid-event.html)
|
|
162
|
-
`gtid` | GTID event with `gtid`, `sid`, `gno` properties
|
|
163
|
-
`anonymousgtid` |
|
|
164
|
-
`previousgtids` | Previous GTIDs event with `gtidSet` and `sids`
|
|
182
|
+
`gtid` | MySQL GTID event with `gtid`, `sid`, `gno` properties. Tagged GTIDs (MySQL 8.3+, `GTID_NEXT='AUTOMATIC:tag'`) arrive as the same event with `tag` set and `gtid` in `uuid:tag:gno` form
|
|
183
|
+
`anonymousgtid` | MySQL anonymous GTID event (same shape as `gtid`)
|
|
184
|
+
`previousgtids` | MySQL Previous GTIDs event with `gtidSet` and `sids`
|
|
185
|
+
`mariadbgtid` | MariaDB GTID event with `gtid` (`'domain-server-sequence'`), `domainId`, `serverId`, `seqNo` and flags (`standalone`, `isDdl`, XA states). Replaces the `BEGIN` query event of a transaction.
|
|
186
|
+
`mariadbgtidlist` | MariaDB GTID list event (start of every binlog file): the last GTID per domain and server seen so far, MariaDB's analogue of `previousgtids`
|
|
187
|
+
`binlogcheckpoint` | MariaDB binlog checkpoint event naming the oldest binlog file still needed for crash recovery (informational)
|
|
188
|
+
`annotaterows` | MariaDB: the SQL `statement` that produced the row events that follow (the analogue of MySQL's rows-query event). Only sent when `start()` was given `requestAnnotateRows: true`; useful for audit trails and statement attribution in CDC records
|
|
189
|
+
`rowsquery` | MySQL: the SQL `statement` that produced the row events that follow (the analogue of MariaDB's `annotaterows`). Sent to every consumer while the server runs with `binlog_rows_query_log_events=ON` (default `OFF`); no `start()` option is involved. The same sensitivity note applies: statement text may contain literals
|
|
190
|
+
`startencryption` | MariaDB encrypted-binlog marker (informational: the server decrypts before sending, the stream itself is cleartext)
|
|
191
|
+
`xaprepare` | XA PREPARE event (MySQL 5.7+ and MariaDB) terminating an XA-prepared event group, with `xidFormatId`, `xidGtrid`, `xidBqual` and `onePhase`
|
|
192
|
+
`heartbeat` | Sent by the server instead of real events: while the connection idles, and in place of transactions skipped server-side during a GTID resume. Carries `binlogName`; `nextPosition` holds the advanced position.
|
|
165
193
|
`tablemap` | Before any row event (must be included for any other row events)
|
|
166
194
|
`writerows` | Rows inserted, row data array available as `rows` property on event object
|
|
167
195
|
`updaterows` | Rows changed, row data array available as `rows` property on event object
|
|
168
196
|
`deleterows` | Rows deleted, row data array available as `rows` property on event object
|
|
169
|
-
`transactionpayload` | Compressed transaction from MySQL 8.0.20+ servers with `binlog_transaction_compression=ON`. ZongJi cannot decode the row events inside it, so it emits an `error` (once per instance) naming the server setting responsible.
|
|
197
|
+
`transactionpayload` | Compressed transaction from MySQL 8.0.20+ servers with `binlog_transaction_compression=ON`. ZongJi cannot decode the row events inside it, so it emits an `error` (once per instance) naming the server setting responsible. (MariaDB's equivalent, `log_bin_compress=ON`, IS supported: its compressed events decode transparently as ordinary `query` and row events.)
|
|
170
198
|
`partialupdaterows` | Partial JSON update from MySQL 8.0+ servers with `binlog_row_value_options=PARTIAL_JSON`. ZongJi cannot decode the JSON diff format, so it emits an `error` (once per instance) naming the server setting responsible.
|
|
171
199
|
|
|
172
|
-
When the server runs with `gtid_mode=ON
|
|
200
|
+
When the server runs with `gtid_mode=ON` (MySQL), or always on MariaDB, every emitted event carries a `gtid` property (`'uuid:sequence'` on MySQL, `'domain-server-sequence'` on MariaDB) identifying the transaction it belongs to, tracked internally even if GTID events are excluded by `includeEvents`. It is `undefined` for anonymous transactions, for events seen before the stream's first GTID event, and for events that belong to no transaction (e.g. a rotate arriving between one transaction's commit and the next transaction's GTID event; the commit event itself still carries its transaction's `gtid`). One exception: a DDL statement has no commit marker of its own, so its `gtid` persists on such trailing events until the next transaction begins. Use it for checkpointing and deduplication. The same value is available as `zongji.lastGtid` while an event is being delivered; it is only coherent when read synchronously inside a `'binlog'` handler, so checkpointing code should prefer `event.gtid`.
|
|
201
|
+
|
|
202
|
+
### File and position resume
|
|
203
|
+
|
|
204
|
+
`zongji.options.filename` and `zongji.options.position` always hold a safe resume point for the connected server. The position advances only at transaction boundaries: it freezes when a transaction's first `TableMap` arrives and jumps to the commit marker's end position, so a persisted pair never points inside a transaction (where row events could be separated from the table metadata they need and silently dropped). Resuming from a pair persisted mid-transaction therefore replays that whole transaction: consumers should treat delivery as at-least-once and make their processing idempotent. Read the pair synchronously inside a `'binlog'` handler when checkpointing per event.
|
|
205
|
+
|
|
206
|
+
### GTID-based resume
|
|
207
|
+
|
|
208
|
+
`zongji.gtidSet` exposes the executed GTIDs as a string: a MySQL GTID set (e.g. `'3e11fa47-…:1-27'`) or a MariaDB GTID position (e.g. `'0-1-1234'`), depending on the connected server. It is the value ZongJi started from, extended as each observed transaction *commits*, so a persisted value never claims a transaction whose row events were still in flight. Persist it alongside (or instead of) `filename`/`position` and resume with:
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
zongji.start({ gtidSet: savedGtidSet, serverId: 5 });
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Unlike file+position checkpoints, a GTID checkpoint is valid on any server in the same replication topology, which makes resuming across a failover possible. The server performs the skipping itself, so already-processed transactions are not resent.
|
|
215
|
+
|
|
216
|
+
The `GtidSet` class behind it is exported for consumers that need to work with GTID sets themselves, e.g. testing whether an incoming `event.gtid` was already covered by a snapshot:
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
import { GtidSet } from '@vlasky/zongji';
|
|
220
|
+
|
|
221
|
+
const snapshot = GtidSet.parse(savedGtidSet); // or @@gtid_executed
|
|
222
|
+
if (!snapshot.contains(event.gtid)) {
|
|
223
|
+
applyChange(event);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`zongji.gtidSet` is available when `start()` was given a `gtidSet`, with `startAtEnd` (seeded from the server's `gtid_executed` on MySQL, `gtid_current_pos` on MariaDB), or when reading from the start of a binlog file (seeded from its Previous_gtids or MariaDB GTID list event). It is `undefined` when resuming from an arbitrary mid-file file+position checkpoint, where no exact value can be known. Tagged GTIDs (MySQL 8.3+) are fully supported: tagged transactions appear in the set as `uuid:…:tag:intervals` sections, and a checkpoint containing tags resumes correctly (its wire encoding is only understood by MySQL 8.3+ servers).
|
|
228
|
+
|
|
229
|
+
On MySQL, GTID resume requires `gtid_mode=ON` and a set is a per-source-UUID collection of intervals. On MariaDB, GTIDs are always on and a position is a single watermark per replication domain (`domain-server-sequence`): resuming replays each listed domain after its watermark, and domains not listed replay from the beginning. If the server has purged binlogs your checkpoint still needs, `start()` emits an error (code 1236) naming the problem; MariaDB additionally reports a checkpoint ahead of its binlog as error 1945/1955. A fresh snapshot is then required.
|
|
173
230
|
|
|
174
231
|
**Event Methods**
|
|
175
232
|
|
|
@@ -183,6 +240,7 @@ Name | Description
|
|
|
183
240
|
## Important Notes
|
|
184
241
|
|
|
185
242
|
* :star2: All MySQL column types are supported, with type casting similar to [mysql2](https://github.com/sidorares/node-mysql2).
|
|
243
|
+
* MariaDB-specific types and features are supported: `UUID`, `INET4` and `INET6` decode to their canonical text forms and `VECTOR` to a raw `Buffer` of little-endian float32s (matching mysql2 query results); MariaDB `JSON` is a `LONGTEXT` alias and yields strings; `COMPRESSED` columns are decompressed transparently; compressed binlog events (`log_bin_compress=ON`) decode as ordinary query/row events; and MariaDB 5.3-era "hires" temporal columns (tables written under `mysql56_temporal_format=OFF`) decode correctly.
|
|
186
244
|
* :speak_no_evil: 64-bit integers are decoded exactly using native BigInt (see #108). If an integer is within the safe range of JS numbers (-2^53, 2^53), a Number is returned, otherwise an exact String. This also applies to 64-bit integers inside JSON columns.
|
|
187
245
|
* :point_right: `TRUNCATE` statement does not cause corresponding `DeleteRows` event. Use unqualified `DELETE FROM` for same effect.
|
|
188
246
|
* When using fractional seconds with `DATETIME` and `TIMESTAMP` data types, only millisecond precision is available due to the limit of Javascript's `Date` object.
|
|
@@ -191,8 +249,9 @@ Name | Description
|
|
|
191
249
|
## Run Tests
|
|
192
250
|
|
|
193
251
|
* Install [Docker](https://www.docker.com/community-edition#download)
|
|
194
|
-
* Run `docker-compose up -d` to start MySQL containers
|
|
195
|
-
* Run `npm test` to execute the test suite
|
|
252
|
+
* Run `docker-compose up -d` to start the MySQL and MariaDB containers
|
|
253
|
+
* Run `npm test` to execute the test suite against MySQL 8.4 (port 3306)
|
|
254
|
+
* Run `TEST_MYSQL_PORT=3308 npm test` to execute it against MariaDB 11.8; MySQL-only tests skip automatically, and the MariaDB-specific suite in `test/mariadb.js` runs (it no-ops against MySQL)
|
|
196
255
|
|
|
197
256
|
## References
|
|
198
257
|
|
package/index.d.ts
CHANGED
|
@@ -11,8 +11,27 @@ export interface ZongJiOptions {
|
|
|
11
11
|
filename?: string;
|
|
12
12
|
/** Binlog position to start from */
|
|
13
13
|
position?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Executed GTIDs to resume from (e.g. a persisted zongji.gtidSet): a
|
|
16
|
+
* MySQL GTID set ('uuid:1-5,...', tagged intervals as
|
|
17
|
+
* 'uuid:1-5:tag_a:1,...' on MySQL 8.3+) or a MariaDB GTID position
|
|
18
|
+
* ('0-1-1234,...'), matching the server flavour. The server locates
|
|
19
|
+
* the right binlog file and skips transactions already processed
|
|
20
|
+
* (MySQL: COM_BINLOG_DUMP_GTID, requires gtid_mode=ON; MariaDB:
|
|
21
|
+
* @slave_connect_state, always available), so this works across
|
|
22
|
+
* failover to another server in the same topology. '' streams the
|
|
23
|
+
* server's entire available history. Takes precedence over
|
|
24
|
+
* filename/position. A set containing tags uses a wire encoding only
|
|
25
|
+
* MySQL 8.3+ understands; older servers reject it.
|
|
26
|
+
*/
|
|
27
|
+
gtidSet?: string;
|
|
14
28
|
/** If true, use non-blocking mode */
|
|
15
29
|
nonBlock?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* MariaDB only: request ANNOTATE_ROWS events (the SQL statement text
|
|
32
|
+
* preceding each row operation's TableMap events). Ignored on MySQL.
|
|
33
|
+
*/
|
|
34
|
+
requestAnnotateRows?: boolean;
|
|
16
35
|
/** List of event types to include (e.g., ['tablemap', 'writerows']) */
|
|
17
36
|
includeEvents?: string[];
|
|
18
37
|
/** List of event types to exclude */
|
|
@@ -23,13 +42,22 @@ export interface ZongJiOptions {
|
|
|
23
42
|
excludeSchema?: Record<string, string[] | true>;
|
|
24
43
|
}
|
|
25
44
|
|
|
26
|
-
// Column metadata from INFORMATION_SCHEMA
|
|
45
|
+
// Column metadata, either fetched from INFORMATION_SCHEMA or synthesised
|
|
46
|
+
// from the binlog's own TableMap metadata (binlog_row_metadata=FULL).
|
|
47
|
+
// In the synthesised form COLUMN_TYPE is approximate for character columns
|
|
48
|
+
// (no display width) and COLLATION_NAME/COLUMN_COMMENT are not available.
|
|
27
49
|
export interface ColumnSchema {
|
|
28
50
|
COLUMN_NAME: string;
|
|
29
51
|
COLLATION_NAME: string | null;
|
|
30
52
|
CHARACTER_SET_NAME: string | null;
|
|
31
53
|
COLUMN_COMMENT: string;
|
|
32
54
|
COLUMN_TYPE: string;
|
|
55
|
+
/** Exact signedness from binlog metadata (MySQL 8.0+); numeric columns only */
|
|
56
|
+
UNSIGNED?: boolean;
|
|
57
|
+
/** ENUM value list from binlog metadata (binlog_row_metadata=FULL) */
|
|
58
|
+
ENUM_VALUES?: string[];
|
|
59
|
+
/** SET value list from binlog metadata (binlog_row_metadata=FULL) */
|
|
60
|
+
SET_VALUES?: string[];
|
|
33
61
|
}
|
|
34
62
|
|
|
35
63
|
// Column information from TableMap event
|
|
@@ -95,9 +123,15 @@ export interface GtidEvent extends BinlogEvent {
|
|
|
95
123
|
flags: number;
|
|
96
124
|
/** Source UUID */
|
|
97
125
|
sid: string;
|
|
98
|
-
/** Group number */
|
|
99
|
-
gno: number;
|
|
100
|
-
/**
|
|
126
|
+
/** Group number (exact string beyond Number.MAX_SAFE_INTEGER) */
|
|
127
|
+
gno: number | string;
|
|
128
|
+
/**
|
|
129
|
+
* GTID tag (MySQL 8.3+, set via GTID_NEXT='AUTOMATIC:tag'); '' for
|
|
130
|
+
* untagged transactions. Only present on events that arrived as
|
|
131
|
+
* GTID_TAGGED_LOG_EVENT; classic GTID events leave it undefined.
|
|
132
|
+
*/
|
|
133
|
+
tag?: string;
|
|
134
|
+
/** Full GTID string (sid:gno, or sid:tag:gno when tagged) */
|
|
101
135
|
gtid: string;
|
|
102
136
|
}
|
|
103
137
|
|
|
@@ -109,8 +143,8 @@ export interface AnonymousGtidEvent extends BinlogEvent {
|
|
|
109
143
|
flags: number;
|
|
110
144
|
/** Source UUID */
|
|
111
145
|
sid: string;
|
|
112
|
-
/** Group number */
|
|
113
|
-
gno: number;
|
|
146
|
+
/** Group number (exact string beyond Number.MAX_SAFE_INTEGER) */
|
|
147
|
+
gno: number | string;
|
|
114
148
|
/** Full GTID string (sid:gno) */
|
|
115
149
|
gtid: string;
|
|
116
150
|
}
|
|
@@ -123,6 +157,12 @@ export interface GtidInterval {
|
|
|
123
157
|
|
|
124
158
|
export interface GtidSidEntry {
|
|
125
159
|
sid: string;
|
|
160
|
+
/**
|
|
161
|
+
* GTID tag ('' for the untagged entry). Only present when the server
|
|
162
|
+
* wrote the tagged set encoding (MySQL 8.3+ after any tagged GTID);
|
|
163
|
+
* each (sid, tag) pair is its own entry, repeating the sid.
|
|
164
|
+
*/
|
|
165
|
+
tag?: string;
|
|
126
166
|
intervals: GtidInterval[];
|
|
127
167
|
}
|
|
128
168
|
|
|
@@ -197,7 +237,45 @@ export interface TableMapEvent extends BinlogEvent {
|
|
|
197
237
|
columnsMetadata: Record<string, unknown>[];
|
|
198
238
|
/** Reference to the tableMap cache */
|
|
199
239
|
tableMap: Record<number, TableMapEntry>;
|
|
200
|
-
/**
|
|
240
|
+
/**
|
|
241
|
+
* Column names carried by the event itself (MySQL 8.0+ with
|
|
242
|
+
* binlog_row_metadata=FULL)
|
|
243
|
+
*/
|
|
244
|
+
columnNames?: string[];
|
|
245
|
+
/**
|
|
246
|
+
* Per-column signedness from binlog metadata (MySQL 8.0+, MINIMAL and
|
|
247
|
+
* FULL); true = unsigned. Undefined entries are non-numeric columns.
|
|
248
|
+
*/
|
|
249
|
+
signedness?: (boolean | undefined)[];
|
|
250
|
+
/**
|
|
251
|
+
* Primary key column indexes (0-based, all-columns numbering) from
|
|
252
|
+
* binlog_row_metadata=FULL
|
|
253
|
+
*/
|
|
254
|
+
primaryKey?: number[];
|
|
255
|
+
/**
|
|
256
|
+
* Per-column visibility from binlog_row_metadata=FULL (MySQL 8.0.23+);
|
|
257
|
+
* false = INVISIBLE column
|
|
258
|
+
*/
|
|
259
|
+
columnVisibility?: boolean[];
|
|
260
|
+
/**
|
|
261
|
+
* Per-column geometry subtype from binlog metadata (0 = geometry,
|
|
262
|
+
* 1 = point, ...); undefined entries are non-spatial columns
|
|
263
|
+
*/
|
|
264
|
+
geometryTypes?: (number | undefined)[];
|
|
265
|
+
/**
|
|
266
|
+
* True when the event carries binlog_row_metadata=FULL metadata, in
|
|
267
|
+
* which case zongji decodes rows without querying INFORMATION_SCHEMA
|
|
268
|
+
*/
|
|
269
|
+
hasSelfDescribingMetadata(): boolean;
|
|
270
|
+
/**
|
|
271
|
+
* True when the table contains classic temporal type codes, which on
|
|
272
|
+
* MariaDB may hide 5.3 "hires" columns that binlog metadata cannot
|
|
273
|
+
* reveal; such tables use the INFORMATION_SCHEMA path even under FULL
|
|
274
|
+
*/
|
|
275
|
+
hasAmbiguousTemporalColumns(): boolean;
|
|
276
|
+
/** Build ColumnSchema entries from the event's own metadata (FULL only) */
|
|
277
|
+
buildColumnSchemas(): ColumnSchema[];
|
|
278
|
+
/** Update column info after table metadata is available */
|
|
201
279
|
updateColumnInfo(): void;
|
|
202
280
|
}
|
|
203
281
|
|
|
@@ -262,6 +340,141 @@ export interface PartialUpdateRowsEvent extends BinlogEvent {
|
|
|
262
340
|
getEventName(): 'partialupdaterows';
|
|
263
341
|
}
|
|
264
342
|
|
|
343
|
+
// Heartbeat event - sent while the connection idles (when a heartbeat
|
|
344
|
+
// period is configured) and after transactions were skipped server-side
|
|
345
|
+
// during a GTID dump; nextPosition carries the advanced position
|
|
346
|
+
export interface HeartbeatEvent extends BinlogEvent {
|
|
347
|
+
getTypeName(): 'Heartbeat';
|
|
348
|
+
getEventName(): 'heartbeat';
|
|
349
|
+
/** Current binlog filename */
|
|
350
|
+
binlogName: string;
|
|
351
|
+
/**
|
|
352
|
+
* Heartbeat position beyond 4 GiB (MariaDB only: sent in a sub-header
|
|
353
|
+
* when the u32 nextPosition cannot represent it, which is then 0)
|
|
354
|
+
*/
|
|
355
|
+
position?: number | string;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// MariaDB GTID event (code 162) - replaces the BEGIN Query event of a
|
|
359
|
+
// transaction (standalone=false) or marks a standalone group such as DDL
|
|
360
|
+
export interface MariadbGtidEvent extends BinlogEvent {
|
|
361
|
+
getTypeName(): 'MariadbGtid';
|
|
362
|
+
getEventName(): 'mariadbgtid';
|
|
363
|
+
/** Sequence number within the domain (exact string beyond 2^53) */
|
|
364
|
+
seqNo: number | string;
|
|
365
|
+
/** Replication domain id */
|
|
366
|
+
domainId: number;
|
|
367
|
+
/** Originating server id (from the event header) */
|
|
368
|
+
serverId: number;
|
|
369
|
+
/** Raw flags2 byte */
|
|
370
|
+
flags2: number;
|
|
371
|
+
/** True for standalone groups (DDL, ...) with no terminating commit */
|
|
372
|
+
standalone: boolean;
|
|
373
|
+
/** True when the group can be safely rolled back */
|
|
374
|
+
transactional: boolean;
|
|
375
|
+
/** True when the group contains DDL */
|
|
376
|
+
isDdl: boolean;
|
|
377
|
+
/** XA PREPARE group; XID fields present */
|
|
378
|
+
preparedXa: boolean;
|
|
379
|
+
/** XA COMMIT/ROLLBACK group; XID fields present */
|
|
380
|
+
completedXa: boolean;
|
|
381
|
+
/** Group-commit id (same for all members of one group commit) */
|
|
382
|
+
commitId?: number | string;
|
|
383
|
+
/** XA format id, when preparedXa/completedXa */
|
|
384
|
+
xidFormatId?: number;
|
|
385
|
+
/** XA gtrid, when preparedXa/completedXa */
|
|
386
|
+
xidGtrid?: Buffer;
|
|
387
|
+
/** XA bqual, when preparedXa/completedXa */
|
|
388
|
+
xidBqual?: Buffer;
|
|
389
|
+
/** Raw flags_extra byte, when present */
|
|
390
|
+
flagsExtra?: number;
|
|
391
|
+
/** Number of extra engines, when FL_EXTRA_MULTI_ENGINE is set */
|
|
392
|
+
extraEngines?: number;
|
|
393
|
+
/** Start-alter sequence number, for COMMIT/ROLLBACK ALTER groups */
|
|
394
|
+
saSeqNo?: number | string;
|
|
395
|
+
/** Thread id of the originating connection (MariaDB 11.5+) */
|
|
396
|
+
threadId?: number;
|
|
397
|
+
/** Full GTID string (domain-server-sequence) */
|
|
398
|
+
gtid: string;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export interface MariadbGtidListEntry {
|
|
402
|
+
domainId: number;
|
|
403
|
+
serverId: number;
|
|
404
|
+
seqNo: number | string;
|
|
405
|
+
/** Full GTID string (domain-server-sequence) */
|
|
406
|
+
gtid: string;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// MariaDB GTID list event (code 163) - at the start of every binlog file,
|
|
410
|
+
// the last GTID per (domain, server) seen so far; MariaDB's analogue of
|
|
411
|
+
// MySQL's Previous_gtids
|
|
412
|
+
export interface MariadbGtidListEvent extends BinlogEvent {
|
|
413
|
+
getTypeName(): 'MariadbGtidList';
|
|
414
|
+
getEventName(): 'mariadbgtidlist';
|
|
415
|
+
count: number;
|
|
416
|
+
/** High 4 bits of the count word (FLAG_UNTIL_REACHED, FLAG_IGN_GTIDS) */
|
|
417
|
+
flags: number;
|
|
418
|
+
gtids: MariadbGtidListEntry[];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// MariaDB binlog checkpoint event (code 161) - XA-recovery marker naming
|
|
422
|
+
// the oldest binlog file that may still be needed for crash recovery
|
|
423
|
+
export interface BinlogCheckpointEvent extends BinlogEvent {
|
|
424
|
+
getTypeName(): 'BinlogCheckpoint';
|
|
425
|
+
getEventName(): 'binlogcheckpoint';
|
|
426
|
+
binlogName: string;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// MariaDB annotate rows event (code 160) - the SQL statement text for the
|
|
430
|
+
// row events that follow
|
|
431
|
+
export interface AnnotateRowsEvent extends BinlogEvent {
|
|
432
|
+
getTypeName(): 'AnnotateRows';
|
|
433
|
+
getEventName(): 'annotaterows';
|
|
434
|
+
/**
|
|
435
|
+
* The event carries the statement's original bytes with no charset
|
|
436
|
+
* metadata; decoded as utf8 (exact for utf8/utf8mb4 clients, the
|
|
437
|
+
* overwhelmingly common case).
|
|
438
|
+
*/
|
|
439
|
+
statement: string;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// MySQL rows query event (code 29) - the SQL statement text for the row
|
|
443
|
+
// events that follow, sent while the server runs with
|
|
444
|
+
// binlog_rows_query_log_events=ON
|
|
445
|
+
export interface RowsQueryEvent extends BinlogEvent {
|
|
446
|
+
getTypeName(): 'RowsQuery';
|
|
447
|
+
getEventName(): 'rowsquery';
|
|
448
|
+
/**
|
|
449
|
+
* The event carries the statement's original bytes with no charset
|
|
450
|
+
* metadata; decoded as utf8 (exact for utf8/utf8mb4 clients, the
|
|
451
|
+
* overwhelmingly common case).
|
|
452
|
+
*/
|
|
453
|
+
statement: string;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// MariaDB start encryption event (code 164) - informational; the dump
|
|
457
|
+
// thread decrypts server-side and the stream itself is cleartext
|
|
458
|
+
export interface StartEncryptionEvent extends BinlogEvent {
|
|
459
|
+
getTypeName(): 'StartEncryption';
|
|
460
|
+
getEventName(): 'startencryption';
|
|
461
|
+
scheme: number;
|
|
462
|
+
keyVersion: number;
|
|
463
|
+
nonce: Buffer;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// XA prepare event (code 38, MySQL 5.7+ and MariaDB) - terminates an
|
|
467
|
+
// XA-prepared event group
|
|
468
|
+
export interface XaPrepareEvent extends BinlogEvent {
|
|
469
|
+
getTypeName(): 'XaPrepare';
|
|
470
|
+
getEventName(): 'xaprepare';
|
|
471
|
+
/** XA COMMIT ... ONE PHASE (commits immediately) */
|
|
472
|
+
onePhase: boolean;
|
|
473
|
+
xidFormatId: number;
|
|
474
|
+
xidGtrid: Buffer;
|
|
475
|
+
xidBqual: Buffer;
|
|
476
|
+
}
|
|
477
|
+
|
|
265
478
|
// Unknown event type
|
|
266
479
|
export interface UnknownEvent extends BinlogEvent {
|
|
267
480
|
getTypeName(): 'Unknown';
|
|
@@ -284,6 +497,14 @@ export type AnyBinlogEvent =
|
|
|
284
497
|
| UpdateRowsEvent
|
|
285
498
|
| TransactionPayloadEvent
|
|
286
499
|
| PartialUpdateRowsEvent
|
|
500
|
+
| HeartbeatEvent
|
|
501
|
+
| MariadbGtidEvent
|
|
502
|
+
| MariadbGtidListEvent
|
|
503
|
+
| BinlogCheckpointEvent
|
|
504
|
+
| AnnotateRowsEvent
|
|
505
|
+
| RowsQueryEvent
|
|
506
|
+
| StartEncryptionEvent
|
|
507
|
+
| XaPrepareEvent
|
|
287
508
|
| UnknownEvent;
|
|
288
509
|
|
|
289
510
|
/** Union of all possible getTypeName() return values (e.g. 'Rotate', 'WriteRows') */
|
|
@@ -329,14 +550,42 @@ declare class ZongJi extends EventEmitter {
|
|
|
329
550
|
stopped: boolean;
|
|
330
551
|
/** Whether binlog checksum is enabled */
|
|
331
552
|
useChecksum: boolean;
|
|
553
|
+
/**
|
|
554
|
+
* Server version string as reported by SELECT VERSION(), e.g.
|
|
555
|
+
* '8.4.5' or '11.8.8-MariaDB-ubu2404-log'. Set during start()
|
|
556
|
+
* initialisation, before the 'ready' event.
|
|
557
|
+
*/
|
|
558
|
+
serverVersion: string | undefined;
|
|
559
|
+
/** True when the connected server is MariaDB (set during start()) */
|
|
560
|
+
isMariaDb: boolean;
|
|
332
561
|
/** Current binlog options */
|
|
333
562
|
options: {
|
|
334
563
|
serverId?: number;
|
|
335
564
|
filename?: string;
|
|
336
565
|
position?: number;
|
|
337
566
|
startAtEnd?: boolean;
|
|
567
|
+
gtidSet?: string;
|
|
338
568
|
nonBlock?: boolean;
|
|
569
|
+
requestAnnotateRows?: boolean;
|
|
339
570
|
};
|
|
571
|
+
/**
|
|
572
|
+
* The transactions this instance knows to be processed: the start()
|
|
573
|
+
* seed plus every transaction whose commit has been observed. A MySQL
|
|
574
|
+
* GTID set ('uuid:1-5,...') or a MariaDB GTID position ('0-1-1234,...')
|
|
575
|
+
* depending on the connected server. Persist it and pass to
|
|
576
|
+
* start({ gtidSet }) to resume, including on a different server.
|
|
577
|
+
* Undefined when no exact seed was available (a mid-file file+position
|
|
578
|
+
* start).
|
|
579
|
+
*/
|
|
580
|
+
readonly gtidSet: string | undefined;
|
|
581
|
+
/**
|
|
582
|
+
* The GTID of the transaction whose events are currently being
|
|
583
|
+
* delivered (equal to their event.gtid); undefined between
|
|
584
|
+
* transactions and for anonymous transactions. Only coherent when
|
|
585
|
+
* read synchronously inside a 'binlog' handler: checkpointing code
|
|
586
|
+
* should use event.gtid.
|
|
587
|
+
*/
|
|
588
|
+
readonly lastGtid: string | undefined;
|
|
340
589
|
/** Current filter settings */
|
|
341
590
|
filters: {
|
|
342
591
|
includeEvents?: string[];
|
|
@@ -394,3 +643,31 @@ declare class ZongJi extends EventEmitter {
|
|
|
394
643
|
}
|
|
395
644
|
|
|
396
645
|
export default ZongJi;
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* A MySQL GTID set (the model behind zongji.gtidSet): which transactions
|
|
649
|
+
* per source are already executed. String form matches @@gtid_executed,
|
|
650
|
+
* including tagged GTIDs (MySQL 8.3+): 'uuid:1-5:11,uuid2:1-27',
|
|
651
|
+
* 'uuid:1-5:tag_a:1'. Exported so consumers can parse persisted sets and
|
|
652
|
+
* test event.gtid membership without their own parser.
|
|
653
|
+
*/
|
|
654
|
+
export class GtidSet {
|
|
655
|
+
/**
|
|
656
|
+
* Parses a GTID set string ('' gives an empty set). Within a uuid
|
|
657
|
+
* block a non-numeric item is a tag naming the source of the interval
|
|
658
|
+
* items that follow it. Throws on malformed input.
|
|
659
|
+
*/
|
|
660
|
+
static parse(text: string): GtidSet;
|
|
661
|
+
/** Adds a single transaction ('uuid:gno' or 'uuid:tag:gno') */
|
|
662
|
+
add(gtid: string): void;
|
|
663
|
+
/**
|
|
664
|
+
* Whether a single transaction ('uuid:gno' or 'uuid:tag:gno', e.g. an
|
|
665
|
+
* event.gtid) is contained in the set. null/undefined (an anonymous
|
|
666
|
+
* transaction's event.gtid) is never contained; a malformed string
|
|
667
|
+
* throws.
|
|
668
|
+
*/
|
|
669
|
+
contains(gtid: string | null | undefined): boolean;
|
|
670
|
+
isEmpty(): boolean;
|
|
671
|
+
/** Canonical text form (sorted; tagged intervals after untagged) */
|
|
672
|
+
toString(): string;
|
|
673
|
+
}
|