@voltro/sql-mysql 0.1.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 +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +1062 -0
- package/dist/index.d.ts +373 -0
- package/dist/index.js +946 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { CdcConfig } from '@voltro/database';
|
|
2
|
+
import { ChangeEvent } from '@voltro/database';
|
|
3
|
+
import { ChangeStrategy } from '@voltro/database';
|
|
4
|
+
import { ConfigError } from 'effect';
|
|
5
|
+
import { ConnectionConfig } from '@voltro/database';
|
|
6
|
+
import { Context } from 'effect';
|
|
7
|
+
import { DataStore } from '@voltro/database';
|
|
8
|
+
import { DialectId } from '@voltro/database';
|
|
9
|
+
import { DialectReplicationAdapter } from '@voltro/database';
|
|
10
|
+
import { Effect } from 'effect';
|
|
11
|
+
import { EventEmitter } from 'node:events';
|
|
12
|
+
import { Layer } from 'effect';
|
|
13
|
+
import { ManagedRuntime } from 'effect';
|
|
14
|
+
import { MysqlClient } from '@effect/sql-mysql2';
|
|
15
|
+
import { Predicate } from '@voltro/database';
|
|
16
|
+
import { QueryDescriptor } from '@voltro/database';
|
|
17
|
+
import { QueryNamespace } from '@voltro/database';
|
|
18
|
+
import { RawSqlFragment } from '@voltro/database/sql';
|
|
19
|
+
import { RetryDecision } from '@voltro/database';
|
|
20
|
+
import { Row } from '@voltro/database';
|
|
21
|
+
import { SqlClient } from '@effect/sql';
|
|
22
|
+
import { SqlClient as SqlClient_2 } from '@effect/sql/SqlClient';
|
|
23
|
+
import { SqlDialect } from '@voltro/database';
|
|
24
|
+
import { SqlError } from '@effect/sql';
|
|
25
|
+
import { TableLike } from '@voltro/database';
|
|
26
|
+
import { TransactionConnection } from '@effect/sql/SqlClient';
|
|
27
|
+
|
|
28
|
+
export declare interface BinlogCdcHandle {
|
|
29
|
+
readonly stop: () => Promise<void>;
|
|
30
|
+
/** Last position the reader has advanced past (for a final checkpoint). */
|
|
31
|
+
readonly currentPosition: () => BinlogPosition | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export declare interface BinlogCdcOptions {
|
|
35
|
+
readonly connection: BinlogConnection;
|
|
36
|
+
/** Unique per reader. Duplicate server_id silently breaks binlog streams. */
|
|
37
|
+
readonly serverId: number;
|
|
38
|
+
readonly variant: 'mysql' | 'mariadb';
|
|
39
|
+
/** Unqualified table names to surface; undefined = every table in `database`. */
|
|
40
|
+
readonly includeTables?: ReadonlyArray<string>;
|
|
41
|
+
/** Resume point. Omit / null → start at the current end of the binlog. */
|
|
42
|
+
readonly startPosition?: BinlogPosition | null;
|
|
43
|
+
readonly onChange: (event: ChangeEvent) => void;
|
|
44
|
+
/** Called with the next position after each handled event (for checkpointing). */
|
|
45
|
+
readonly onPosition?: (pos: BinlogPosition) => void;
|
|
46
|
+
readonly onError?: (err: unknown) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Re-resolve the current binlog end. Used when the persisted position
|
|
49
|
+
* is no longer valid (binlog purged past it — err 1236 — or after a
|
|
50
|
+
* failover to a different primary). Provided by the store
|
|
51
|
+
* (SHOW MASTER STATUS).
|
|
52
|
+
*/
|
|
53
|
+
readonly resolveStartPosition?: () => Promise<BinlogPosition | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Called when the reader had to skip forward to the current binlog end
|
|
56
|
+
* (a gap: purge/failover). The caller should treat live state as
|
|
57
|
+
* possibly-stale and re-query (self-heal) — the dispatcher's
|
|
58
|
+
* per-subscribe re-query already does this on the next change.
|
|
59
|
+
*/
|
|
60
|
+
readonly onResync?: () => void;
|
|
61
|
+
/**
|
|
62
|
+
* Watchdog liveness cadence + stall threshold (ms). The reader tracks its
|
|
63
|
+
* last progress (any binlog event or a fresh attach); when nothing has
|
|
64
|
+
* arrived for `stallThresholdMs`, the watchdog probes the primary's current
|
|
65
|
+
* binlog end (`resolveStartPosition`) and — if the primary has advanced past
|
|
66
|
+
* the reader — force-reconnects. This is the SAFETY NET for a silently-dead
|
|
67
|
+
* stream: a server-side idle timeout / half-open socket can end the binlog
|
|
68
|
+
* connection WITHOUT mysql2 emitting an `error`, so `zongji` never fires and
|
|
69
|
+
* the plain error-driven reconnect never runs. The watchdog does not depend
|
|
70
|
+
* on any stream event. Defaults: probe every 15s, stall after 25s.
|
|
71
|
+
*/
|
|
72
|
+
readonly watchdogIntervalMs?: number;
|
|
73
|
+
readonly stallThresholdMs?: number;
|
|
74
|
+
/**
|
|
75
|
+
* TCP keepalive idle delay (ms) for the replication socket — the PROACTIVE
|
|
76
|
+
* defense against the stall the watchdog only recovers from. A binlog dump
|
|
77
|
+
* connection carries NO traffic during quiet periods (zongji requests no
|
|
78
|
+
* replication heartbeat), so an idle NAT / firewall / load-balancer silently
|
|
79
|
+
* reaps it and a half-open socket (e.g. the host slept) is never noticed.
|
|
80
|
+
* Enabling keepalive with a SHORT idle delay makes the OS emit a probe after
|
|
81
|
+
* this many ms of silence: the socket is never truly idle (intermediaries
|
|
82
|
+
* keep it), and a dead peer surfaces as a connection error in seconds rather
|
|
83
|
+
* than sitting frozen forever. mysql2 defaults keepalive ON but with a 0
|
|
84
|
+
* delay (→ the OS default idle of ~2h), which is useless here. Default 10s.
|
|
85
|
+
*/
|
|
86
|
+
readonly keepAliveInitialDelayMs?: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export declare interface BinlogConnection {
|
|
90
|
+
readonly host: string;
|
|
91
|
+
readonly port: number;
|
|
92
|
+
readonly username: string;
|
|
93
|
+
readonly password: string;
|
|
94
|
+
readonly database: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export declare interface BinlogPosition {
|
|
98
|
+
readonly filename: string;
|
|
99
|
+
readonly position: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export declare const CDC_OFFSETS_TABLE = "_voltro_cdc_offsets";
|
|
103
|
+
|
|
104
|
+
declare class CdcDeliveryGate {
|
|
105
|
+
private readonly variant;
|
|
106
|
+
private readonly ttlMs;
|
|
107
|
+
private readonly schedule;
|
|
108
|
+
/** fingerprint → count of occurrences seen but not yet paired. A value of N
|
|
109
|
+
* means N deliveries have happened and are each awaiting their duplicate. */
|
|
110
|
+
private readonly seen;
|
|
111
|
+
constructor(variant: 'mysql' | 'mariadb', ttlMs?: number, schedule?: DelayScheduler);
|
|
112
|
+
/** Fingerprint an event by (table, op, pk, decoded-row-image). Returns null
|
|
113
|
+
* when there's no usable primary key (nothing to pair on → always admit). */
|
|
114
|
+
private key;
|
|
115
|
+
/**
|
|
116
|
+
* Decide whether to deliver this candidate. Returns true for the FIRST
|
|
117
|
+
* occurrence of a fingerprint (deliver it) and false for the paired second
|
|
118
|
+
* occurrence (the same write arriving over the other path — drop it).
|
|
119
|
+
*/
|
|
120
|
+
admit(event: ChangeEvent): boolean;
|
|
121
|
+
private expireOne;
|
|
122
|
+
/** Distinct fingerprints currently awaiting their pair — tests / diagnostics. */
|
|
123
|
+
get pending(): number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parse a `ConnectionConfig` into the mysql-specific shape. The same
|
|
128
|
+
* resolver handles `mariadb://` URLs — the wire protocol is identical,
|
|
129
|
+
* and the dialect's `id` field is what discriminates downstream.
|
|
130
|
+
*
|
|
131
|
+
* Defaults: port 3306, user `app`, password `app`, database `app`.
|
|
132
|
+
*/
|
|
133
|
+
export declare const connectionFromConfig: (config: ConnectionConfig) => MysqlConnection;
|
|
134
|
+
|
|
135
|
+
/** Injectable scheduler so tests can drive the TTL deterministically without
|
|
136
|
+
* real timers. The default unref's the timer so it never keeps the process
|
|
137
|
+
* alive. */
|
|
138
|
+
declare type DelayScheduler = (fn: () => void, ms: number) => void;
|
|
139
|
+
|
|
140
|
+
export declare const makeMysqlSqlLayer: (options: MysqlConnection) => ReturnType<typeof MysqlClient.layerConfig>;
|
|
141
|
+
|
|
142
|
+
export declare const makeMysqlSqlLayerFromConfig: (config: ConnectionConfig) => ReturnType<typeof MysqlClient.layerConfig>;
|
|
143
|
+
|
|
144
|
+
export declare const makeMysqlStore: (options: MysqlStoreOptions) => Promise<MysqlStore>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* MariaDB 10.6+. Same driver, separate dialect id so logs / boot
|
|
148
|
+
* banners / docs can say "mariadb" when that's what's live. MariaDB
|
|
149
|
+
* quirks (SEQUENCE-vs-AUTO_INCREMENT, JSON_VALID strictness) get
|
|
150
|
+
* handled inside the store via `variant === 'mariadb'` guards as they
|
|
151
|
+
* surface.
|
|
152
|
+
*/
|
|
153
|
+
export declare const mariadbDialect: SqlDialect;
|
|
154
|
+
|
|
155
|
+
declare interface MysqlAdapterFriend {
|
|
156
|
+
readonly runEffect: <A, E>(effect: Effect.Effect<A, E, SqlClient_2>) => Promise<A>;
|
|
157
|
+
readonly variant: 'mysql' | 'mariadb';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export { MysqlClient }
|
|
161
|
+
|
|
162
|
+
export declare interface MysqlConnection {
|
|
163
|
+
readonly host: string;
|
|
164
|
+
readonly port: number;
|
|
165
|
+
readonly username: string;
|
|
166
|
+
readonly password: string;
|
|
167
|
+
readonly database: string;
|
|
168
|
+
readonly maxConnections?: number;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* MySQL 8+. MySQL has no `INSERT ... RETURNING` in any version
|
|
173
|
+
* (that's MariaDB-only in this package) — the store produces
|
|
174
|
+
* post-image rows via a write-then-SELECT fallback on every mutation.
|
|
175
|
+
*/
|
|
176
|
+
export declare const mysqlDialect: SqlDialect;
|
|
177
|
+
|
|
178
|
+
export declare const mysqlReplicationAdapter: () => DialectReplicationAdapter;
|
|
179
|
+
|
|
180
|
+
export declare const mysqlRetryFilter: (err: unknown) => RetryDecision;
|
|
181
|
+
|
|
182
|
+
export declare class MysqlStore implements DataStore {
|
|
183
|
+
private readonly sql;
|
|
184
|
+
private readonly runtime;
|
|
185
|
+
private readonly variant;
|
|
186
|
+
private readonly changeStrategy;
|
|
187
|
+
private readonly namespace;
|
|
188
|
+
private readonly emitter;
|
|
189
|
+
private inflightTxns;
|
|
190
|
+
private readonly log;
|
|
191
|
+
private cdcHandle;
|
|
192
|
+
private cdcCheckpointTimer;
|
|
193
|
+
private cdcPendingPosition;
|
|
194
|
+
private cdcReplicaId;
|
|
195
|
+
private cdcStreamName;
|
|
196
|
+
private readonly cdcGate;
|
|
197
|
+
constructor(sql: SqlClient.SqlClient, runtime: ManagedRuntime.ManagedRuntime<SqlClient.SqlClient | MysqlClient.MysqlClient, never>, variant: 'mysql' | 'mariadb', changeStrategy?: ChangeStrategy, namespace?: QueryNamespace, sharedEmitter?: EventEmitter, sharedGate?: CdcDeliveryGate);
|
|
198
|
+
/**
|
|
199
|
+
* Bind a per-request tenant namespace (a DATABASE on mysql/mariadb),
|
|
200
|
+
* returning a view that qualifies every table reference to
|
|
201
|
+
* `<database>.<table>`. Shares this store's pool + emitter + CDC. The
|
|
202
|
+
* database must already exist — see the migrator's namespace fan-out.
|
|
203
|
+
*/
|
|
204
|
+
withNamespace(namespace: string | null): DataStore;
|
|
205
|
+
/** Qualify a table to this view's namespace (no-op when unset). */
|
|
206
|
+
private nsT;
|
|
207
|
+
/* Excluded from this release type: dialectId */
|
|
208
|
+
/**
|
|
209
|
+
* Friend-handle the mysql/mariadb replication adapter reads via a
|
|
210
|
+
* structural check on `DataStore` (declared in
|
|
211
|
+
* `./replicationAdapter.ts`). The adapter needs raw SQL access against
|
|
212
|
+
* THIS store's managed runtime to issue `@@global.gtid_executed` /
|
|
213
|
+
* `@@global.gtid_current_pos` — without a friend handle it would need
|
|
214
|
+
* its own connection pool + config, which would double the deployment
|
|
215
|
+
* cost.
|
|
216
|
+
*
|
|
217
|
+
* The handle stays on the store class as a public-but-prefixed
|
|
218
|
+
* property so the type cast in `adapterFriend(store)` resolves
|
|
219
|
+
* without exporting the runtime to the wider API surface. The
|
|
220
|
+
* `variant` field flows through so the adapter picks the right
|
|
221
|
+
* dialect-specific SQL (mysql vs mariadb session var name).
|
|
222
|
+
*/
|
|
223
|
+
get __mysqlReplicationFriend(): MysqlAdapterFriend;
|
|
224
|
+
private executeQuery;
|
|
225
|
+
/**
|
|
226
|
+
* `RETURNING` clause support across the mysql family is NOT uniform:
|
|
227
|
+
*
|
|
228
|
+
* - MySQL (every version, including 8.x): NO `RETURNING` at all.
|
|
229
|
+
* - MariaDB 10.5+: `INSERT ... RETURNING *` ✓
|
|
230
|
+
* - MariaDB 10.0+: `DELETE ... RETURNING *` ✓
|
|
231
|
+
* - MariaDB (any version): `UPDATE ... RETURNING *` ✗ — explicitly
|
|
232
|
+
* not supported, despite the otherwise
|
|
233
|
+
* broad RETURNING coverage.
|
|
234
|
+
*
|
|
235
|
+
* Two per-operation flags keep the dispatch explicit. The fallback
|
|
236
|
+
* paths INSERT/UPDATE/DELETE-then-SELECT-by-known-id all rely on
|
|
237
|
+
* the framework's auto-inject middleware which generates ids
|
|
238
|
+
* client-side (TypeID / ULID / Snowflake), so we always know the
|
|
239
|
+
* row's primary key BEFORE issuing the mutation. Getters (not
|
|
240
|
+
* inline `=` initializers) avoid referring to `this.variant`
|
|
241
|
+
* before constructor assignment.
|
|
242
|
+
*/
|
|
243
|
+
private get supportsInsertReturning();
|
|
244
|
+
private get supportsDeleteReturning();
|
|
245
|
+
private get supportsUpdateReturning();
|
|
246
|
+
private executeInsert;
|
|
247
|
+
/**
|
|
248
|
+
* AUTO_INCREMENT id recovery for a single INSERT with no client-side
|
|
249
|
+
* id. `LAST_INSERT_ID()` is connection-scoped, so INSERT →
|
|
250
|
+
* LAST_INSERT_ID() → post-image SELECT must share one pinned
|
|
251
|
+
* connection. When the caller already holds a txn connection we run
|
|
252
|
+
* on it; otherwise we open a short transaction to pin the pool
|
|
253
|
+
* connection for the round-trip. Retries on a deadlock/lock-timeout.
|
|
254
|
+
*/
|
|
255
|
+
private insertRecoverAutoId;
|
|
256
|
+
/**
|
|
257
|
+
* Run `body` on ONE pinned connection. Inside a caller-held txn we run
|
|
258
|
+
* on that connection directly; otherwise we open a short transaction so
|
|
259
|
+
* the connection-scoped `LAST_INSERT_ID()` stays valid across the
|
|
260
|
+
* INSERT → read → post-SELECT round-trip. The transactional path retries
|
|
261
|
+
* on a deadlock / lock-wait timeout (mirrors `updateMany`/`deleteMany`).
|
|
262
|
+
*/
|
|
263
|
+
private runPinned;
|
|
264
|
+
private executeInsertMany;
|
|
265
|
+
/**
|
|
266
|
+
* AUTO_INCREMENT id recovery for a multi-row INSERT with no client-side
|
|
267
|
+
* ids. `LAST_INSERT_ID()` returns the FIRST generated id; the engine
|
|
268
|
+
* allocates the rest consecutively (guaranteed for a single multi-row
|
|
269
|
+
* `INSERT … VALUES` under the default `innodb_autoinc_lock_mode`), so
|
|
270
|
+
* the range is [first, first + n - 1]. Re-selects that range ordered by
|
|
271
|
+
* id (= input order) for the post-images. Same connection-pinning +
|
|
272
|
+
* retry as the single-row path.
|
|
273
|
+
*/
|
|
274
|
+
private insertManyRecoverAutoIds;
|
|
275
|
+
private executePatchJson;
|
|
276
|
+
private executeUpdate;
|
|
277
|
+
private executeDelete;
|
|
278
|
+
private routeEvent;
|
|
279
|
+
private executeUpsert;
|
|
280
|
+
/**
|
|
281
|
+
* MariaDB 10.5+ native upsert:
|
|
282
|
+
* `INSERT … ON DUPLICATE KEY UPDATE col = VALUES(col), … RETURNING *`.
|
|
283
|
+
* One round-trip. `RETURNING *` gives the final row (post-insert OR
|
|
284
|
+
* post-update). The conflict columns + id are excluded from the SET
|
|
285
|
+
* list (they identify the row and must not be silently rewritten),
|
|
286
|
+
* matching the postgres `ON CONFLICT … DO UPDATE` shape.
|
|
287
|
+
*/
|
|
288
|
+
private executeMariadbUpsert;
|
|
289
|
+
private executeInsertIgnore;
|
|
290
|
+
private findByConflict;
|
|
291
|
+
query(d: QueryDescriptor): Promise<readonly Readonly<Record<string, unknown>>[]>;
|
|
292
|
+
raw<T extends object = Row>(fragment: RawSqlFragment, _opts?: {
|
|
293
|
+
dependsOn?: ReadonlyArray<string>;
|
|
294
|
+
}): Promise<ReadonlyArray<T>>;
|
|
295
|
+
private runWithEager;
|
|
296
|
+
/* Excluded from this release type: getInternalRunWithEager */
|
|
297
|
+
insert(t: string, r: Row): Promise<Readonly<Record<string, unknown>>>;
|
|
298
|
+
insertMany(t: string, rows: ReadonlyArray<Row>): Promise<readonly Readonly<Record<string, unknown>>[]>;
|
|
299
|
+
patchJson(t: string, pk: string, path: string, value: unknown): Promise<Readonly<Record<string, unknown>> | null>;
|
|
300
|
+
upsert(t: string, r: Row, options: {
|
|
301
|
+
conflictColumns: ReadonlyArray<string>;
|
|
302
|
+
update?: ReadonlyArray<string> | ((existing: Row) => Readonly<Record<string, unknown>>);
|
|
303
|
+
}): Promise<Readonly<Record<string, unknown>>>;
|
|
304
|
+
insertIgnore(t: string, r: Row, options: {
|
|
305
|
+
conflictColumns: ReadonlyArray<string>;
|
|
306
|
+
}): Promise<Readonly<Record<string, unknown>>>;
|
|
307
|
+
update(t: string, pk: string, p: Readonly<Record<string, unknown>>): Promise<Readonly<Record<string, unknown>> | null>;
|
|
308
|
+
delete(t: string, pk: string): Promise<boolean>;
|
|
309
|
+
updateMany(table: string, patch: Readonly<Record<string, unknown>>, options: {
|
|
310
|
+
where: Predicate;
|
|
311
|
+
}): Promise<number>;
|
|
312
|
+
deleteMany(table: string, options: {
|
|
313
|
+
where: Predicate;
|
|
314
|
+
}): Promise<number>;
|
|
315
|
+
/* Excluded from this release type: emitChange */
|
|
316
|
+
/* Excluded from this release type: startCdcConsumer */
|
|
317
|
+
/* Excluded from this release type: assertBinlogConfig */
|
|
318
|
+
/* Excluded from this release type: resolveBinlogEnd */
|
|
319
|
+
/* Excluded from this release type: readCdcOffset */
|
|
320
|
+
/* Excluded from this release type: flushCdcOffset */
|
|
321
|
+
/* Excluded from this release type: getInternalExecuteQuery */
|
|
322
|
+
/* Excluded from this release type: getInternalExecuteInsert */
|
|
323
|
+
/* Excluded from this release type: getInternalExecuteInsertMany */
|
|
324
|
+
/* Excluded from this release type: getInternalExecutePatchJson */
|
|
325
|
+
/* Excluded from this release type: getInternalExecuteUpdate */
|
|
326
|
+
/* Excluded from this release type: getInternalExecuteDelete */
|
|
327
|
+
/* Excluded from this release type: getInternalExecuteUpsert */
|
|
328
|
+
/* Excluded from this release type: getInternalExecuteInsertIgnore */
|
|
329
|
+
transactional<T>(work: (tx: DataStore) => Promise<T>): Promise<T>;
|
|
330
|
+
onChange(listener: (event: ChangeEvent) => void): () => void;
|
|
331
|
+
/** Cross-instance reactivity seam — emit an externally-sourced event to
|
|
332
|
+
* local subscribers without re-persisting. The binlog CDC consumer and
|
|
333
|
+
* `@voltro/plugin-broadcast` route through here. See
|
|
334
|
+
* `DataStore.injectExternalChange`. */
|
|
335
|
+
/** 'fleet' under binlog CDC (every replica gets the full stream);
|
|
336
|
+
* 'local' under inline emission. See DataStore.changeScope. */
|
|
337
|
+
get changeScope(): 'local' | 'fleet';
|
|
338
|
+
injectExternalChange(event: ChangeEvent): void;
|
|
339
|
+
run<A, E>(effect: Effect.Effect<A, E, SqlClient.SqlClient>): Promise<A>;
|
|
340
|
+
close(gracePeriodMs?: number): Promise<void>;
|
|
341
|
+
/** Liveness probe — `SELECT 1`. Rejects if the pool can't answer. */
|
|
342
|
+
ping(): Promise<void>;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export declare interface MysqlStoreOptions {
|
|
346
|
+
readonly sqlLayer: Layer.Layer<SqlClient.SqlClient | MysqlClient.MysqlClient, ConfigError.ConfigError | SqlError.SqlError, never>;
|
|
347
|
+
readonly tracerLayer?: Layer.Layer<never, never, never>;
|
|
348
|
+
/** `'inline'` (default): local reactivity only — the writing instance sees
|
|
349
|
+
* its own deltas. `'cdc'`: ADDITIVE cross-instance fan-out — the writer
|
|
350
|
+
* still emits its own deltas inline, and a ROW-format binlog reader also
|
|
351
|
+
* surfaces every OTHER replica's writes on this replica's `onChange` (own
|
|
352
|
+
* echoes deduped). Supported on BOTH mysql-8 and mariadb (same binlog
|
|
353
|
+
* reader, per-variant detection). */
|
|
354
|
+
readonly changeStrategy?: ChangeStrategy;
|
|
355
|
+
/** Binlog CDC wiring — required when `changeStrategy: 'cdc'` (mysql or mariadb). */
|
|
356
|
+
readonly cdcConfig?: CdcConfig;
|
|
357
|
+
/** Variant id — `'mysql'` or `'mariadb'`. Surfaces in spans + logs;
|
|
358
|
+
* the wire protocol is identical so no behavioural branch needed. */
|
|
359
|
+
readonly variant?: 'mysql' | 'mariadb';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Start a binlog reader. Resolves once the reader is attached and
|
|
364
|
+
* streaming (so the caller can guarantee "reader attached before
|
|
365
|
+
* subscriptions"). The returned handle stops it.
|
|
366
|
+
*/
|
|
367
|
+
export declare const startBinlogCdc: (opts: BinlogCdcOptions) => Promise<BinlogCdcHandle>;
|
|
368
|
+
|
|
369
|
+
declare type TxnContext = Context.Tag.Service<typeof TransactionConnection>;
|
|
370
|
+
|
|
371
|
+
export declare const _voltroCdcOffsetsTable: TableLike;
|
|
372
|
+
|
|
373
|
+
export { }
|