@vlasky/zongji 0.7.1 → 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 +14 -0
- package/README.md +71 -12
- package/index.d.ts +284 -7
- package/index.js +350 -28
- package/lib/binlog_event.js +879 -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 +178 -26
- package/package.json +6 -3
package/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import mysql from 'mysql2';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
import initBinlogClass from './lib/sequence/binlog.js';
|
|
4
|
+
import { GtidSet } from './lib/gtid_set.js';
|
|
5
|
+
import { MariadbGtidPosition } from './lib/mariadb_gtid.js';
|
|
4
6
|
|
|
5
7
|
const ConnectionConfigMap = {
|
|
6
8
|
'Connection': obj => obj.config,
|
|
@@ -36,11 +38,18 @@ class ZongJi extends EventEmitter {
|
|
|
36
38
|
this.tableMap = {};
|
|
37
39
|
this._warnedUnsupported = new Set();
|
|
38
40
|
this._currentGtid = undefined;
|
|
41
|
+
this._executedGtids = null;
|
|
42
|
+
this._pendingGtid = undefined;
|
|
43
|
+
this._positionFrozen = false;
|
|
44
|
+
this._seedGtidsFromStream = false;
|
|
45
|
+
this._startFromGtids = false;
|
|
39
46
|
this.ready = false;
|
|
40
47
|
this.stopped = false;
|
|
41
48
|
this._starting = false;
|
|
42
49
|
this._startEpoch = 0;
|
|
43
50
|
this.useChecksum = false;
|
|
51
|
+
this.serverVersion = undefined;
|
|
52
|
+
this.isMariaDb = false;
|
|
44
53
|
|
|
45
54
|
this._dsn = dsn;
|
|
46
55
|
this.ctrlConnection = null;
|
|
@@ -192,6 +201,110 @@ class ZongJi extends EventEmitter {
|
|
|
192
201
|
});
|
|
193
202
|
}
|
|
194
203
|
|
|
204
|
+
// The transactions this instance knows to be processed: the start()
|
|
205
|
+
// seed plus every transaction whose commit has been observed. Persist
|
|
206
|
+
// it and pass to start({ gtidSet }) to resume, including on a different
|
|
207
|
+
// server in the same replication topology. A MySQL GTID set
|
|
208
|
+
// ('uuid:1-5,...') or a MariaDB GTID position ('0-1-1234,...')
|
|
209
|
+
// depending on the connected server. Undefined when no exact seed was
|
|
210
|
+
// available (see start()).
|
|
211
|
+
get gtidSet() {
|
|
212
|
+
return this._executedGtids ? this._executedGtids.toString() : undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// The GTID of the transaction whose events are currently being
|
|
216
|
+
// delivered (what event.gtid is for them); undefined between
|
|
217
|
+
// transactions. Only coherent when read synchronously inside a
|
|
218
|
+
// 'binlog' handler: checkpointing code should use event.gtid.
|
|
219
|
+
get lastGtid() {
|
|
220
|
+
return this._currentGtid;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Called from the packet layer (before event filtering) for
|
|
224
|
+
// GTID-relevant events. A transaction's GTID enters the executed set
|
|
225
|
+
// only once its commit marker has been seen (Xid, or a Query event
|
|
226
|
+
// other than BEGIN, e.g. DDL or COMMIT), so a persisted zongji.gtidSet
|
|
227
|
+
// never claims a transaction whose row events were still in flight.
|
|
228
|
+
// Returns true when the event definitively closes the current
|
|
229
|
+
// transaction, so the caller can stop attaching its GTID to
|
|
230
|
+
// subsequent events.
|
|
231
|
+
_trackGtidProgress(eventName, event) {
|
|
232
|
+
const fold = () => {
|
|
233
|
+
if (this._pendingGtid !== undefined && this._executedGtids) {
|
|
234
|
+
this._executedGtids.add(this._pendingGtid);
|
|
235
|
+
}
|
|
236
|
+
this._pendingGtid = undefined;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
switch (eventName) {
|
|
240
|
+
case 'gtid':
|
|
241
|
+
// A new transaction implies the previous one committed, so this
|
|
242
|
+
// also covers commit markers hidden by event filtering
|
|
243
|
+
fold();
|
|
244
|
+
this._pendingGtid = event.gtid;
|
|
245
|
+
break;
|
|
246
|
+
case 'anonymousgtid':
|
|
247
|
+
fold();
|
|
248
|
+
break;
|
|
249
|
+
case 'xid':
|
|
250
|
+
fold();
|
|
251
|
+
return true;
|
|
252
|
+
case 'xaprepare':
|
|
253
|
+
// Ends the group's event delivery on both flavours, but must NOT
|
|
254
|
+
// fold: the prepared transaction commits later under the XA
|
|
255
|
+
// COMMIT statement's own GTID, and the pending GTID folds when
|
|
256
|
+
// that (or any next) GTID event arrives
|
|
257
|
+
return true;
|
|
258
|
+
case 'query': {
|
|
259
|
+
// Fold only on definite commit markers. Anything else (BEGIN,
|
|
260
|
+
// XA START/END, DDL, SAVEPOINT, ...) must not fold: claiming a
|
|
261
|
+
// transaction early risks losing its remaining events on resume,
|
|
262
|
+
// whereas not folding merely delays the checkpoint until the
|
|
263
|
+
// next transaction's GTID arrives (at-least-once redelivery).
|
|
264
|
+
const query = event.query.trim().toUpperCase();
|
|
265
|
+
if (query === 'COMMIT' || query === 'ROLLBACK' ||
|
|
266
|
+
query.startsWith('XA COMMIT') ||
|
|
267
|
+
query.startsWith('XA ROLLBACK')) {
|
|
268
|
+
fold();
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'previousgtids':
|
|
274
|
+
// When dumping from the start of a binlog file, its Previous_gtids
|
|
275
|
+
// event is the exact "everything before this point" seed
|
|
276
|
+
if (this._seedGtidsFromStream && this._executedGtids === null) {
|
|
277
|
+
try {
|
|
278
|
+
this._executedGtids = GtidSet.parse(event.gtidSet);
|
|
279
|
+
} catch {
|
|
280
|
+
// Leave unseeded; gtidSet stays undefined
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
break;
|
|
284
|
+
case 'mariadbgtid':
|
|
285
|
+
// As with 'gtid': a new event group implies the previous one
|
|
286
|
+
// completed. This also closes standalone groups (DDL), which have
|
|
287
|
+
// no commit marker at all
|
|
288
|
+
fold();
|
|
289
|
+
this._pendingGtid = {
|
|
290
|
+
domainId: event.domainId,
|
|
291
|
+
serverId: event.serverId,
|
|
292
|
+
seqNo: event.seqNo,
|
|
293
|
+
};
|
|
294
|
+
break;
|
|
295
|
+
case 'mariadbgtidlist':
|
|
296
|
+
// At the start of a binlog file this is the exact "everything
|
|
297
|
+
// before this point" seed, MariaDB's Previous_gtids analogue.
|
|
298
|
+
// Fake mid-stream lists on GTID connects never seed: the position
|
|
299
|
+
// is already non-null then
|
|
300
|
+
if (this._seedGtidsFromStream && this._executedGtids === null) {
|
|
301
|
+
this._executedGtids = MariadbGtidPosition.fromGtidList(event.gtids);
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
|
|
195
308
|
_findBinlogEnd(next) {
|
|
196
309
|
this.ctrlConnection.query('SHOW BINARY LOGS', (err, rows) => {
|
|
197
310
|
if (err) {
|
|
@@ -264,14 +377,18 @@ class ZongJi extends EventEmitter {
|
|
|
264
377
|
filename,
|
|
265
378
|
position,
|
|
266
379
|
startAtEnd,
|
|
380
|
+
gtidSet,
|
|
267
381
|
nonBlock,
|
|
382
|
+
requestAnnotateRows,
|
|
268
383
|
} = {}) {
|
|
269
384
|
this.options = {
|
|
270
385
|
serverId,
|
|
271
386
|
filename,
|
|
272
387
|
position,
|
|
273
388
|
startAtEnd,
|
|
389
|
+
gtidSet,
|
|
274
390
|
nonBlock,
|
|
391
|
+
requestAnnotateRows,
|
|
275
392
|
};
|
|
276
393
|
}
|
|
277
394
|
|
|
@@ -361,8 +478,38 @@ class ZongJi extends EventEmitter {
|
|
|
361
478
|
const epoch = this._startEpoch;
|
|
362
479
|
|
|
363
480
|
// A resumed stream must not attribute early events to a GTID seen
|
|
364
|
-
// before the restart
|
|
481
|
+
// before the restart, nor inherit a position freeze from a
|
|
482
|
+
// transaction the previous stream was in the middle of
|
|
365
483
|
this._currentGtid = undefined;
|
|
484
|
+
this._pendingGtid = undefined;
|
|
485
|
+
this._positionFrozen = false;
|
|
486
|
+
|
|
487
|
+
// Executed-GTID-set tracking (drives the zongji.gtidSet checkpoint).
|
|
488
|
+
// Exact seeds: an explicit start set, the server's gtid_executed for
|
|
489
|
+
// startAtEnd (fetched below), or the stream's first Previous_gtids
|
|
490
|
+
// event when dumping from the start of a binlog file. An arbitrary
|
|
491
|
+
// mid-file file+position start has no exact seed, so gtidSet stays
|
|
492
|
+
// undefined there.
|
|
493
|
+
this._executedGtids = null;
|
|
494
|
+
this._seedGtidsFromStream = false;
|
|
495
|
+
this._startFromGtids = options.gtidSet != null;
|
|
496
|
+
if (this._startFromGtids) {
|
|
497
|
+
// The flavour is sniffed from the format (a MySQL set always
|
|
498
|
+
// contains ':', a MariaDB position never does; '' is valid for
|
|
499
|
+
// either) and verified against the server after detection
|
|
500
|
+
try {
|
|
501
|
+
this._executedGtids = String(options.gtidSet).includes(':')
|
|
502
|
+
? GtidSet.parse(options.gtidSet)
|
|
503
|
+
: MariadbGtidPosition.parse(options.gtidSet);
|
|
504
|
+
} catch (err) {
|
|
505
|
+
this._starting = false;
|
|
506
|
+
this.emit('error', err);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
} else if (!options.startAtEnd &&
|
|
510
|
+
(options.position === undefined || options.position <= 4)) {
|
|
511
|
+
this._seedGtidsFromStream = true;
|
|
512
|
+
}
|
|
366
513
|
|
|
367
514
|
this.stopped = false;
|
|
368
515
|
|
|
@@ -373,6 +520,28 @@ class ZongJi extends EventEmitter {
|
|
|
373
520
|
this._options(options);
|
|
374
521
|
this._filters(options);
|
|
375
522
|
|
|
523
|
+
// Server flavour steers the rest of the preamble: MariaDB has no
|
|
524
|
+
// gtid_mode/gtid_executed (its GTIDs are domain-server-sequence
|
|
525
|
+
// watermarks, always on) and needs its own dump-request dialect.
|
|
526
|
+
// SELECT VERSION() is used rather than the handshake packet because
|
|
527
|
+
// MariaDB prefixes the handshake version with "5.5.5-" for old-client
|
|
528
|
+
// compatibility, while VERSION() always reports e.g.
|
|
529
|
+
// "11.8.8-MariaDB-ubu2404-log".
|
|
530
|
+
const detectServer = (resolve, reject) => {
|
|
531
|
+
this.ctrlConnection.query('SELECT VERSION() AS version', (err, rows) => {
|
|
532
|
+
// Never overwrite state belonging to a newer start()
|
|
533
|
+
if (epoch !== this._startEpoch) {
|
|
534
|
+
return resolve();
|
|
535
|
+
}
|
|
536
|
+
if (err) {
|
|
537
|
+
return reject(err);
|
|
538
|
+
}
|
|
539
|
+
this.serverVersion = rows[0].version;
|
|
540
|
+
this.isMariaDb = /mariadb/i.test(this.serverVersion);
|
|
541
|
+
resolve();
|
|
542
|
+
});
|
|
543
|
+
};
|
|
544
|
+
|
|
376
545
|
const testChecksum = (resolve, reject) => {
|
|
377
546
|
this._isChecksumEnabled((err, checksumEnabled) => {
|
|
378
547
|
if (err) {
|
|
@@ -388,6 +557,10 @@ class ZongJi extends EventEmitter {
|
|
|
388
557
|
|
|
389
558
|
const findBinlogEnd = (resolve, reject) => {
|
|
390
559
|
this._findBinlogEnd((err, result) => {
|
|
560
|
+
// As above: never mutate options for a superseded start()
|
|
561
|
+
if (epoch !== this._startEpoch) {
|
|
562
|
+
return resolve();
|
|
563
|
+
}
|
|
391
564
|
if (err) {
|
|
392
565
|
return reject(err);
|
|
393
566
|
}
|
|
@@ -405,6 +578,30 @@ class ZongJi extends EventEmitter {
|
|
|
405
578
|
});
|
|
406
579
|
};
|
|
407
580
|
|
|
581
|
+
// For startAtEnd the server's own executed set is the exact seed for
|
|
582
|
+
// zongji.gtidSet ("everything up to now"); transactions racing between
|
|
583
|
+
// this query and the dump start are streamed and merge idempotently
|
|
584
|
+
const seedGtidsFromServer = (resolve, reject) => {
|
|
585
|
+
this.ctrlConnection.query(
|
|
586
|
+
'SELECT @@GLOBAL.gtid_executed AS gtidExecuted', (err, rows) => {
|
|
587
|
+
// A stale seed must not overwrite state belonging to a newer
|
|
588
|
+
// start() that superseded this one while the query was in flight
|
|
589
|
+
if (epoch !== this._startEpoch) {
|
|
590
|
+
return resolve();
|
|
591
|
+
}
|
|
592
|
+
if (err) {
|
|
593
|
+
return reject(err);
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
this._executedGtids =
|
|
597
|
+
GtidSet.parse(rows[0].gtidExecuted.replace(/\s/g, ''));
|
|
598
|
+
} catch (parseErr) {
|
|
599
|
+
return reject(parseErr);
|
|
600
|
+
}
|
|
601
|
+
resolve();
|
|
602
|
+
});
|
|
603
|
+
};
|
|
604
|
+
|
|
408
605
|
// Attach the current transaction's GTID (tracked at the packet layer,
|
|
409
606
|
// even when 'gtid' events are filtered out). Gtid/AnonymousGtid events
|
|
410
607
|
// keep their own parsed value.
|
|
@@ -428,6 +625,24 @@ class ZongJi extends EventEmitter {
|
|
|
428
625
|
|
|
429
626
|
switch (event.getTypeName()) {
|
|
430
627
|
case 'TableMap': {
|
|
628
|
+
// On MariaDB, classic temporal codes may hide 5.3 "hires"
|
|
629
|
+
// columns that no binlog metadata (FULL included) can reveal;
|
|
630
|
+
// such tables must take the INFORMATION_SCHEMA path below
|
|
631
|
+
if (event.hasSelfDescribingMetadata() &&
|
|
632
|
+
!(this.isMariaDb && event.hasAmbiguousTemporalColumns())) {
|
|
633
|
+
// MySQL 8.0+ with binlog_row_metadata=FULL: the event itself
|
|
634
|
+
// carries complete column metadata as of binlog write time, so
|
|
635
|
+
// no INFORMATION_SCHEMA round-trip (and no connection pause) is
|
|
636
|
+
// needed. Rebuilt on every TableMap event, so ALTER TABLE never
|
|
637
|
+
// leaves stale columns behind.
|
|
638
|
+
this.tableMap[event.tableId] = {
|
|
639
|
+
columnSchemas: event.buildColumnSchemas(),
|
|
640
|
+
parentSchema: event.schemaName,
|
|
641
|
+
tableName: event.tableName,
|
|
642
|
+
};
|
|
643
|
+
event.updateColumnInfo();
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
431
646
|
const tableMap = this.tableMap[event.tableId];
|
|
432
647
|
if (!tableMap || tableMap.tableName !== event.tableName || tableMap.columns.length !== event.columnCount) {
|
|
433
648
|
if (!this.connection) return;
|
|
@@ -453,42 +668,144 @@ class ZongJi extends EventEmitter {
|
|
|
453
668
|
}
|
|
454
669
|
break;
|
|
455
670
|
}
|
|
456
|
-
case 'Rotate':
|
|
457
|
-
// The payload position is the first event of the NEW file: the
|
|
458
|
-
// only value coherent with binlogName. The header nextPosition
|
|
459
|
-
// refers to the OLD file (0 for the artificial rotate at dump
|
|
460
|
-
// start), so rotates are excluded from the generic update below;
|
|
461
|
-
// persisting (new filename, old-file offset) would corrupt the
|
|
462
|
-
// resume point until the next non-rotate event repaired it.
|
|
463
|
-
this.options.filename = event.binlogName;
|
|
464
|
-
this.options.position = event.position;
|
|
465
|
-
break;
|
|
466
671
|
}
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
672
|
+
// Rotate events update the (filename, position) resume pair at the
|
|
673
|
+
// packet layer (lib/sequence/binlog.js), before event filtering, so
|
|
674
|
+
// the pair stays coherent even when 'rotate' is excluded by
|
|
675
|
+
// includeEvents. They remain excluded from the generic update below:
|
|
676
|
+
// the header nextPosition refers to the OLD file (0 for the
|
|
677
|
+
// artificial rotate at dump start), and persisting (new filename,
|
|
678
|
+
// old-file offset) would corrupt the resume point.
|
|
679
|
+
// Never advance the resume position into a transaction's
|
|
680
|
+
// TableMap-to-commit span: a consumer persisting options.position
|
|
681
|
+
// could otherwise resume between a TableMap and row events that
|
|
682
|
+
// depend on it, and with no cached table metadata those rows would
|
|
683
|
+
// be silently dropped (a multi-table statement writes all its
|
|
684
|
+
// TableMaps before any row event, so even a position after one
|
|
685
|
+
// table's rows is inside the danger zone). The packet layer
|
|
686
|
+
// freezes the position at the first TableMap and unfreezes it at
|
|
687
|
+
// the commit marker; on resume the whole transaction is replayed,
|
|
688
|
+
// so rows already seen may be re-emitted (at-least-once), which is
|
|
689
|
+
// recoverable where dropping is not.
|
|
690
|
+
// MariaDB writes end_log_pos=0 on every event inside a transaction
|
|
691
|
+
// (TableMap, row events); only commits and standalone events carry a
|
|
692
|
+
// real position. Adopting a zero would corrupt the resume point.
|
|
478
693
|
const typeName = event.getTypeName();
|
|
479
|
-
if (typeName !== 'TableMap' && typeName !== 'Rotate'
|
|
694
|
+
if (typeName !== 'TableMap' && typeName !== 'Rotate' &&
|
|
695
|
+
!this._positionFrozen && event.nextPosition > 0) {
|
|
480
696
|
this.options.position = event.nextPosition;
|
|
481
697
|
}
|
|
482
698
|
this.emit('binlog', attachGtid(event));
|
|
483
699
|
};
|
|
484
700
|
|
|
485
|
-
|
|
701
|
+
// Announce MariaDB slave capability 4 ("understands GTID events") on
|
|
702
|
+
// the binlog connection before the dump command is enqueued. Without
|
|
703
|
+
// it the server rewrites its own events for pre-GTID clients: GTID
|
|
704
|
+
// events become literal BEGIN Query events and standalone
|
|
705
|
+
// GTID/checkpoint events become dummy Query events, so no GTID
|
|
706
|
+
// information ever reaches the stream.
|
|
707
|
+
const setMariaDbCapability = (resolve, reject) => {
|
|
708
|
+
this.connection.query('SET @mariadb_slave_capability=4', (err) => {
|
|
709
|
+
if (epoch !== this._startEpoch) {
|
|
710
|
+
return resolve();
|
|
711
|
+
}
|
|
712
|
+
if (err) {
|
|
713
|
+
return reject(err);
|
|
714
|
+
}
|
|
715
|
+
resolve();
|
|
716
|
+
});
|
|
717
|
+
};
|
|
486
718
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
719
|
+
// MariaDB has no COM_BINLOG_DUMP_GTID: the resume position travels in
|
|
720
|
+
// the @slave_connect_state session variable and the dump command is a
|
|
721
|
+
// plain COM_BINLOG_DUMP whose filename/position the server then
|
|
722
|
+
// ignores. An empty position means "from the oldest binlog available"
|
|
723
|
+
const setMariaDbConnectState = (resolve, reject) => {
|
|
724
|
+
// Validated as digits/dashes/commas by the parser, so safe to inline
|
|
725
|
+
const position = this._executedGtids.toString();
|
|
726
|
+
this.connection.query(
|
|
727
|
+
`SET @slave_connect_state='${position}'`, (err) => {
|
|
728
|
+
if (epoch !== this._startEpoch) {
|
|
729
|
+
return resolve();
|
|
730
|
+
}
|
|
731
|
+
if (err) {
|
|
732
|
+
return reject(err);
|
|
733
|
+
}
|
|
734
|
+
resolve();
|
|
735
|
+
});
|
|
736
|
+
};
|
|
490
737
|
|
|
491
|
-
|
|
738
|
+
// MariaDB's analogue of the gtid_executed seed below: the last GTID
|
|
739
|
+
// per domain in the binlog or applied by a slave thread, whichever is
|
|
740
|
+
// most recent (matches what a real replica would send on connect)
|
|
741
|
+
const seedMariaDbGtidsFromServer = (resolve, reject) => {
|
|
742
|
+
this.ctrlConnection.query(
|
|
743
|
+
'SELECT @@GLOBAL.gtid_current_pos AS gtidPos', (err, rows) => {
|
|
744
|
+
if (epoch !== this._startEpoch) {
|
|
745
|
+
return resolve();
|
|
746
|
+
}
|
|
747
|
+
if (err) {
|
|
748
|
+
return reject(err);
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
this._executedGtids =
|
|
752
|
+
MariadbGtidPosition.parse(rows[0].gtidPos.trim());
|
|
753
|
+
} catch (parseErr) {
|
|
754
|
+
return reject(parseErr);
|
|
755
|
+
}
|
|
756
|
+
resolve();
|
|
757
|
+
});
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
// Detection must complete before the rest of the preamble because the
|
|
761
|
+
// seed query below is MySQL-only
|
|
762
|
+
new Promise(detectServer)
|
|
763
|
+
.then(() => {
|
|
764
|
+
if (this._startFromGtids) {
|
|
765
|
+
// The start set was parsed by format before the server flavour
|
|
766
|
+
// was known; an empty position is valid for either flavour,
|
|
767
|
+
// anything else must match the connected server
|
|
768
|
+
const isMariaDbPosition =
|
|
769
|
+
this._executedGtids instanceof MariadbGtidPosition;
|
|
770
|
+
if (isMariaDbPosition !== this.isMariaDb) {
|
|
771
|
+
if (this._executedGtids.isEmpty()) {
|
|
772
|
+
this._executedGtids = this.isMariaDb
|
|
773
|
+
? new MariadbGtidPosition()
|
|
774
|
+
: new GtidSet();
|
|
775
|
+
} else if (this.isMariaDb) {
|
|
776
|
+
throw new Error(
|
|
777
|
+
'gtidSet is a MySQL GTID set but the server is MariaDB; ' +
|
|
778
|
+
'pass a MariaDB GTID position instead ' +
|
|
779
|
+
'(domain-server-sequence, e.g. \'0-1-1234\')');
|
|
780
|
+
} else {
|
|
781
|
+
throw new Error(
|
|
782
|
+
'gtidSet is a MariaDB GTID position but the server is ' +
|
|
783
|
+
'MySQL; pass a MySQL GTID set instead ' +
|
|
784
|
+
'(e.g. \'uuid:1-5\')');
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
let promises = [new Promise(testChecksum)];
|
|
790
|
+
|
|
791
|
+
if (this.isMariaDb) {
|
|
792
|
+
promises.push(new Promise(setMariaDbCapability));
|
|
793
|
+
if (this._startFromGtids) {
|
|
794
|
+
promises.push(new Promise(setMariaDbConnectState));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (this.options.startAtEnd) {
|
|
799
|
+
promises.push(new Promise(findBinlogEnd));
|
|
800
|
+
if (this._executedGtids === null) {
|
|
801
|
+
promises.push(new Promise(this.isMariaDb
|
|
802
|
+
? seedMariaDbGtidsFromServer
|
|
803
|
+
: seedGtidsFromServer));
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return Promise.all(promises);
|
|
808
|
+
})
|
|
492
809
|
.then(() => {
|
|
493
810
|
// Abort if a newer start() superseded this one (after a stop())
|
|
494
811
|
// while promises were pending
|
|
@@ -667,3 +984,8 @@ class ZongJi extends EventEmitter {
|
|
|
667
984
|
}
|
|
668
985
|
|
|
669
986
|
export default ZongJi;
|
|
987
|
+
// The GTID set model behind zongji.gtidSet, exported so consumers can
|
|
988
|
+
// parse persisted sets and test membership (e.g. "was this event.gtid
|
|
989
|
+
// already covered by my snapshot?") without their own parser. Handles
|
|
990
|
+
// tagged GTIDs (MySQL 8.3+).
|
|
991
|
+
export { GtidSet };
|