@powersync/service-module-mysql 0.0.0-dev-20241015210820
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 +20 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/dev/.env.template +2 -0
- package/dev/README.md +9 -0
- package/dev/config/sync_rules.yaml +12 -0
- package/dev/docker/mysql/docker-compose.yaml +17 -0
- package/dev/docker/mysql/init-scripts/my.cnf +9 -0
- package/dev/docker/mysql/init-scripts/mysql.sql +38 -0
- package/dist/api/MySQLRouteAPIAdapter.d.ts +24 -0
- package/dist/api/MySQLRouteAPIAdapter.js +311 -0
- package/dist/api/MySQLRouteAPIAdapter.js.map +1 -0
- package/dist/common/ReplicatedGTID.d.ts +59 -0
- package/dist/common/ReplicatedGTID.js +110 -0
- package/dist/common/ReplicatedGTID.js.map +1 -0
- package/dist/common/check-source-configuration.d.ts +3 -0
- package/dist/common/check-source-configuration.js +46 -0
- package/dist/common/check-source-configuration.js.map +1 -0
- package/dist/common/common-index.d.ts +6 -0
- package/dist/common/common-index.js +7 -0
- package/dist/common/common-index.js.map +1 -0
- package/dist/common/get-replication-columns.d.ts +12 -0
- package/dist/common/get-replication-columns.js +103 -0
- package/dist/common/get-replication-columns.js.map +1 -0
- package/dist/common/get-tables-from-pattern.d.ts +7 -0
- package/dist/common/get-tables-from-pattern.js +28 -0
- package/dist/common/get-tables-from-pattern.js.map +1 -0
- package/dist/common/mysql-to-sqlite.d.ts +4 -0
- package/dist/common/mysql-to-sqlite.js +56 -0
- package/dist/common/mysql-to-sqlite.js.map +1 -0
- package/dist/common/read-executed-gtid.d.ts +6 -0
- package/dist/common/read-executed-gtid.js +40 -0
- package/dist/common/read-executed-gtid.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/module/MySQLModule.d.ts +13 -0
- package/dist/module/MySQLModule.js +46 -0
- package/dist/module/MySQLModule.js.map +1 -0
- package/dist/replication/BinLogReplicationJob.d.ts +14 -0
- package/dist/replication/BinLogReplicationJob.js +88 -0
- package/dist/replication/BinLogReplicationJob.js.map +1 -0
- package/dist/replication/BinLogReplicator.d.ts +13 -0
- package/dist/replication/BinLogReplicator.js +25 -0
- package/dist/replication/BinLogReplicator.js.map +1 -0
- package/dist/replication/BinLogStream.d.ts +43 -0
- package/dist/replication/BinLogStream.js +421 -0
- package/dist/replication/BinLogStream.js.map +1 -0
- package/dist/replication/MySQLConnectionManager.d.ts +43 -0
- package/dist/replication/MySQLConnectionManager.js +81 -0
- package/dist/replication/MySQLConnectionManager.js.map +1 -0
- package/dist/replication/MySQLConnectionManagerFactory.d.ts +10 -0
- package/dist/replication/MySQLConnectionManagerFactory.js +21 -0
- package/dist/replication/MySQLConnectionManagerFactory.js.map +1 -0
- package/dist/replication/MySQLErrorRateLimiter.d.ts +10 -0
- package/dist/replication/MySQLErrorRateLimiter.js +43 -0
- package/dist/replication/MySQLErrorRateLimiter.js.map +1 -0
- package/dist/replication/zongji/zongji-utils.d.ts +7 -0
- package/dist/replication/zongji/zongji-utils.js +19 -0
- package/dist/replication/zongji/zongji-utils.js.map +1 -0
- package/dist/types/types.d.ts +50 -0
- package/dist/types/types.js +61 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/mysql_utils.d.ts +14 -0
- package/dist/utils/mysql_utils.js +38 -0
- package/dist/utils/mysql_utils.js.map +1 -0
- package/package.json +51 -0
- package/src/api/MySQLRouteAPIAdapter.ts +357 -0
- package/src/common/ReplicatedGTID.ts +158 -0
- package/src/common/check-source-configuration.ts +59 -0
- package/src/common/common-index.ts +6 -0
- package/src/common/get-replication-columns.ts +124 -0
- package/src/common/get-tables-from-pattern.ts +44 -0
- package/src/common/mysql-to-sqlite.ts +59 -0
- package/src/common/read-executed-gtid.ts +43 -0
- package/src/index.ts +5 -0
- package/src/module/MySQLModule.ts +53 -0
- package/src/replication/BinLogReplicationJob.ts +97 -0
- package/src/replication/BinLogReplicator.ts +35 -0
- package/src/replication/BinLogStream.ts +547 -0
- package/src/replication/MySQLConnectionManager.ts +104 -0
- package/src/replication/MySQLConnectionManagerFactory.ts +28 -0
- package/src/replication/MySQLErrorRateLimiter.ts +44 -0
- package/src/replication/zongji/zongji-utils.ts +32 -0
- package/src/replication/zongji/zongji.d.ts +98 -0
- package/src/types/types.ts +102 -0
- package/src/utils/mysql_utils.ts +47 -0
- package/test/src/binlog_stream.test.ts +288 -0
- package/test/src/binlog_stream_utils.ts +152 -0
- package/test/src/env.ts +7 -0
- package/test/src/setup.ts +7 -0
- package/test/src/util.ts +62 -0
- package/test/tsconfig.json +28 -0
- package/tsconfig.json +26 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { container } from '@powersync/lib-services-framework';
|
|
2
|
+
import { replication } from '@powersync/service-core';
|
|
3
|
+
import { BinLogStream } from './BinLogStream.js';
|
|
4
|
+
export class BinLogReplicationJob extends replication.AbstractReplicationJob {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
super(options);
|
|
7
|
+
this.connectionFactory = options.connectionFactory;
|
|
8
|
+
}
|
|
9
|
+
get slot_name() {
|
|
10
|
+
return this.options.storage.slot_name;
|
|
11
|
+
}
|
|
12
|
+
async keepAlive() { }
|
|
13
|
+
async replicate() {
|
|
14
|
+
try {
|
|
15
|
+
await this.replicateLoop();
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
// Fatal exception
|
|
19
|
+
container.reporter.captureException(e, {
|
|
20
|
+
metadata: {
|
|
21
|
+
replication_slot: this.slot_name
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
this.logger.error(`Replication failed on ${this.slot_name}`, e);
|
|
25
|
+
// Slot removal type error logic goes here
|
|
26
|
+
// if (e) {
|
|
27
|
+
// // This stops replication on this slot, and creates a new slot
|
|
28
|
+
// await this.options.storage.factory.slotRemoved(this.slot_name);
|
|
29
|
+
// }
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
this.abortController.abort();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async replicateLoop() {
|
|
36
|
+
while (!this.isStopped) {
|
|
37
|
+
await this.replicateOnce();
|
|
38
|
+
if (!this.isStopped) {
|
|
39
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async replicateOnce() {
|
|
44
|
+
// New connections on every iteration (every error with retry),
|
|
45
|
+
// otherwise we risk repeating errors related to the connection,
|
|
46
|
+
// such as caused by cached PG schemas.
|
|
47
|
+
const connectionManager = this.connectionFactory.create({
|
|
48
|
+
// Pool connections are only used intermittently.
|
|
49
|
+
idleTimeout: 30000
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal });
|
|
53
|
+
if (this.isStopped) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const stream = new BinLogStream({
|
|
57
|
+
abortSignal: this.abortController.signal,
|
|
58
|
+
storage: this.options.storage,
|
|
59
|
+
connections: connectionManager
|
|
60
|
+
});
|
|
61
|
+
await stream.replicate();
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
this.logger.error(`Replication error`, e);
|
|
65
|
+
if (e.cause != null) {
|
|
66
|
+
this.logger.error(`cause`, e.cause);
|
|
67
|
+
}
|
|
68
|
+
// TODO not recoverable error
|
|
69
|
+
if (false) {
|
|
70
|
+
throw e;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Report the error if relevant, before retrying
|
|
74
|
+
container.reporter.captureException(e, {
|
|
75
|
+
metadata: {
|
|
76
|
+
replication_slot: this.slot_name
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// This sets the retry delay
|
|
80
|
+
this.rateLimiter?.reportError(e);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await connectionManager.end();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=BinLogReplicationJob.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinLogReplicationJob.js","sourceRoot":"","sources":["../../src/replication/BinLogReplicationJob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOjD,MAAM,OAAO,oBAAqB,SAAQ,WAAW,CAAC,sBAAsB;IAG1E,YAAY,OAAoC;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IACrD,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,SAAS,KAAI,CAAC;IAEpB,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,kBAAkB;YAClB,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,EAAE;gBACrC,QAAQ,EAAE;oBACR,gBAAgB,EAAE,IAAI,CAAC,SAAS;iBACjC;aACF,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;YAEhE,4CAA4C;YAC5C,aAAa;YACb,qEAAqE;YACrE,sEAAsE;YACtE,MAAM;QACR,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACvB,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;YAE3B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACpB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,+DAA+D;QAC/D,gEAAgE;QAChE,uCAAuC;QACvC,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;YACtD,iDAAiD;YACjD,WAAW,EAAE,KAAM;SACpB,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;gBAC9B,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM;gBACxC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;gBAC7B,WAAW,EAAE,iBAAiB;aAC/B,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;gBACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YACtC,CAAC;YACD,6BAA6B;YAC7B,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,CAAC;YACV,CAAC;iBAAM,CAAC;gBACN,gDAAgD;gBAChD,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,EAAE;oBACrC,QAAQ,EAAE;wBACR,gBAAgB,EAAE,IAAI,CAAC,SAAS;qBACjC;iBACF,CAAC,CAAC;gBACH,4BAA4B;gBAC5B,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,iBAAiB,CAAC,GAAG,EAAE,CAAC;QAChC,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { replication, storage } from '@powersync/service-core';
|
|
2
|
+
import { BinLogReplicationJob } from './BinLogReplicationJob.js';
|
|
3
|
+
import { MySQLConnectionManagerFactory } from './MySQLConnectionManagerFactory.js';
|
|
4
|
+
export interface BinLogReplicatorOptions extends replication.AbstractReplicatorOptions {
|
|
5
|
+
connectionFactory: MySQLConnectionManagerFactory;
|
|
6
|
+
}
|
|
7
|
+
export declare class BinLogReplicator extends replication.AbstractReplicator<BinLogReplicationJob> {
|
|
8
|
+
private readonly connectionFactory;
|
|
9
|
+
constructor(options: BinLogReplicatorOptions);
|
|
10
|
+
createJob(options: replication.CreateJobOptions): BinLogReplicationJob;
|
|
11
|
+
cleanUp(syncRulesStorage: storage.SyncRulesBucketStorage): Promise<void>;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { replication } from '@powersync/service-core';
|
|
2
|
+
import { BinLogReplicationJob } from './BinLogReplicationJob.js';
|
|
3
|
+
export class BinLogReplicator extends replication.AbstractReplicator {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
super(options);
|
|
6
|
+
this.connectionFactory = options.connectionFactory;
|
|
7
|
+
}
|
|
8
|
+
createJob(options) {
|
|
9
|
+
return new BinLogReplicationJob({
|
|
10
|
+
id: this.createJobId(options.storage.group_id),
|
|
11
|
+
storage: options.storage,
|
|
12
|
+
lock: options.lock,
|
|
13
|
+
connectionFactory: this.connectionFactory,
|
|
14
|
+
rateLimiter: this.rateLimiter
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async cleanUp(syncRulesStorage) {
|
|
18
|
+
// The MySQL module does not create anything which requires cleanup on the MySQL server.
|
|
19
|
+
}
|
|
20
|
+
async stop() {
|
|
21
|
+
await super.stop();
|
|
22
|
+
await this.connectionFactory.shutdown();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=BinLogReplicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinLogReplicator.js","sourceRoot":"","sources":["../../src/replication/BinLogReplicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAW,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAOjE,MAAM,OAAO,gBAAiB,SAAQ,WAAW,CAAC,kBAAwC;IAGxF,YAAY,OAAgC;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IACrD,CAAC;IAED,SAAS,CAAC,OAAqC;QAC7C,OAAO,IAAI,oBAAoB,CAAC;YAC9B,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC9C,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;YACzC,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,gBAAgD;QAC5D,wFAAwF;IAC1F,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as sync_rules from '@powersync/service-sync-rules';
|
|
2
|
+
import { storage } from '@powersync/service-core';
|
|
3
|
+
import { MySQLConnectionManager } from './MySQLConnectionManager.js';
|
|
4
|
+
export interface BinLogStreamOptions {
|
|
5
|
+
connections: MySQLConnectionManager;
|
|
6
|
+
storage: storage.SyncRulesBucketStorage;
|
|
7
|
+
abortSignal: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export type Data = Record<string, any>;
|
|
10
|
+
export declare class BinLogStream {
|
|
11
|
+
protected options: BinLogStreamOptions;
|
|
12
|
+
private readonly syncRules;
|
|
13
|
+
private readonly groupId;
|
|
14
|
+
private readonly storage;
|
|
15
|
+
private readonly connections;
|
|
16
|
+
private abortSignal;
|
|
17
|
+
private tableCache;
|
|
18
|
+
constructor(options: BinLogStreamOptions);
|
|
19
|
+
get connectionTag(): string;
|
|
20
|
+
get connectionId(): number;
|
|
21
|
+
get stopped(): boolean;
|
|
22
|
+
get defaultSchema(): string;
|
|
23
|
+
handleRelation(batch: storage.BucketStorageBatch, entity: storage.SourceEntityDescriptor, snapshot: boolean): Promise<storage.SourceTable>;
|
|
24
|
+
getQualifiedTableNames(batch: storage.BucketStorageBatch, tablePattern: sync_rules.TablePattern): Promise<storage.SourceTable[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Checks if the initial sync has been completed yet.
|
|
27
|
+
*/
|
|
28
|
+
protected checkInitialReplicated(): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Does the initial replication of the database tables.
|
|
31
|
+
*
|
|
32
|
+
* If (partial) replication was done before on this slot, this clears the state
|
|
33
|
+
* and starts again from scratch.
|
|
34
|
+
*/
|
|
35
|
+
startInitialReplication(): Promise<void>;
|
|
36
|
+
private snapshotTable;
|
|
37
|
+
replicate(): Promise<void>;
|
|
38
|
+
initReplication(): Promise<void>;
|
|
39
|
+
private getTable;
|
|
40
|
+
streamChanges(): Promise<void>;
|
|
41
|
+
private writeChanges;
|
|
42
|
+
private writeChange;
|
|
43
|
+
}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import async from 'async';
|
|
3
|
+
import { framework, getUuidReplicaIdentityBson, Metrics, storage } from '@powersync/service-core';
|
|
4
|
+
import * as common from '../common/common-index.js';
|
|
5
|
+
import * as zongji_utils from './zongji/zongji-utils.js';
|
|
6
|
+
import { ReplicatedGTID } from '../common/common-index.js';
|
|
7
|
+
/**
|
|
8
|
+
* MySQL does not have same relation structure. Just returning unique key as string.
|
|
9
|
+
* @param source
|
|
10
|
+
*/
|
|
11
|
+
function getMysqlRelId(source) {
|
|
12
|
+
return `${source.schema}.${source.name}`;
|
|
13
|
+
}
|
|
14
|
+
export class BinLogStream {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.tableCache = new Map();
|
|
18
|
+
this.storage = options.storage;
|
|
19
|
+
this.connections = options.connections;
|
|
20
|
+
this.syncRules = options.storage.getParsedSyncRules({ defaultSchema: this.defaultSchema });
|
|
21
|
+
this.groupId = options.storage.group_id;
|
|
22
|
+
this.abortSignal = options.abortSignal;
|
|
23
|
+
}
|
|
24
|
+
get connectionTag() {
|
|
25
|
+
return this.connections.connectionTag;
|
|
26
|
+
}
|
|
27
|
+
get connectionId() {
|
|
28
|
+
// Default to 1 if not set
|
|
29
|
+
return this.connections.connectionId ? Number.parseInt(this.connections.connectionId) : 1;
|
|
30
|
+
}
|
|
31
|
+
get stopped() {
|
|
32
|
+
return this.abortSignal.aborted;
|
|
33
|
+
}
|
|
34
|
+
get defaultSchema() {
|
|
35
|
+
return this.connections.databaseName;
|
|
36
|
+
}
|
|
37
|
+
async handleRelation(batch, entity, snapshot) {
|
|
38
|
+
const result = await this.storage.resolveTable({
|
|
39
|
+
group_id: this.groupId,
|
|
40
|
+
connection_id: this.connectionId,
|
|
41
|
+
connection_tag: this.connectionTag,
|
|
42
|
+
entity_descriptor: entity,
|
|
43
|
+
sync_rules: this.syncRules
|
|
44
|
+
});
|
|
45
|
+
this.tableCache.set(entity.objectId, result.table);
|
|
46
|
+
// Drop conflicting tables. This includes for example renamed tables.
|
|
47
|
+
await batch.drop(result.dropTables);
|
|
48
|
+
// Snapshot if:
|
|
49
|
+
// 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere)
|
|
50
|
+
// 2. Snapshot is not already done, AND:
|
|
51
|
+
// 3. The table is used in sync rules.
|
|
52
|
+
const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny;
|
|
53
|
+
if (shouldSnapshot) {
|
|
54
|
+
// Truncate this table, in case a previous snapshot was interrupted.
|
|
55
|
+
await batch.truncate([result.table]);
|
|
56
|
+
let gtid;
|
|
57
|
+
// Start the snapshot inside a transaction.
|
|
58
|
+
// We use a dedicated connection for this.
|
|
59
|
+
const connection = await this.connections.getStreamingConnection();
|
|
60
|
+
const promiseConnection = connection.promise();
|
|
61
|
+
try {
|
|
62
|
+
await promiseConnection.query('BEGIN');
|
|
63
|
+
try {
|
|
64
|
+
gtid = await common.readExecutedGtid(promiseConnection);
|
|
65
|
+
await this.snapshotTable(connection.connection, batch, result.table);
|
|
66
|
+
await promiseConnection.query('COMMIT');
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
await promiseConnection.query('ROLLBACK');
|
|
70
|
+
throw e;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
connection.release();
|
|
75
|
+
}
|
|
76
|
+
const [table] = await batch.markSnapshotDone([result.table], gtid.comparable);
|
|
77
|
+
return table;
|
|
78
|
+
}
|
|
79
|
+
return result.table;
|
|
80
|
+
}
|
|
81
|
+
async getQualifiedTableNames(batch, tablePattern) {
|
|
82
|
+
if (tablePattern.connectionTag != this.connectionTag) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
let tableRows;
|
|
86
|
+
const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined;
|
|
87
|
+
if (tablePattern.isWildcard) {
|
|
88
|
+
const result = await this.connections.query(`SELECT TABLE_NAME
|
|
89
|
+
FROM information_schema.tables
|
|
90
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?;
|
|
91
|
+
`, [tablePattern.schema, tablePattern.tablePattern]);
|
|
92
|
+
tableRows = result[0];
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const result = await this.connections.query(`SELECT TABLE_NAME
|
|
96
|
+
FROM information_schema.tables
|
|
97
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?;
|
|
98
|
+
`, [tablePattern.schema, tablePattern.tablePattern]);
|
|
99
|
+
tableRows = result[0];
|
|
100
|
+
}
|
|
101
|
+
let tables = [];
|
|
102
|
+
for (let row of tableRows) {
|
|
103
|
+
const name = row['TABLE_NAME'];
|
|
104
|
+
if (prefix && !name.startsWith(prefix)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const result = await this.connections.query(`SELECT 1
|
|
108
|
+
FROM information_schema.tables
|
|
109
|
+
WHERE table_schema = ? AND table_name = ?
|
|
110
|
+
AND table_type = 'BASE TABLE';`, [tablePattern.schema, tablePattern.name]);
|
|
111
|
+
if (result[0].length == 0) {
|
|
112
|
+
logger.info(`Skipping ${tablePattern.schema}.${name} - no table exists/is not a base table`);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const connection = await this.connections.getConnection();
|
|
116
|
+
const replicationColumns = await common.getReplicationIdentityColumns({
|
|
117
|
+
connection: connection,
|
|
118
|
+
schema: tablePattern.schema,
|
|
119
|
+
table_name: tablePattern.name
|
|
120
|
+
});
|
|
121
|
+
connection.release();
|
|
122
|
+
const table = await this.handleRelation(batch, {
|
|
123
|
+
name,
|
|
124
|
+
schema: tablePattern.schema,
|
|
125
|
+
objectId: getMysqlRelId(tablePattern),
|
|
126
|
+
replicationColumns: replicationColumns.columns
|
|
127
|
+
}, false);
|
|
128
|
+
tables.push(table);
|
|
129
|
+
}
|
|
130
|
+
return tables;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Checks if the initial sync has been completed yet.
|
|
134
|
+
*/
|
|
135
|
+
async checkInitialReplicated() {
|
|
136
|
+
const status = await this.storage.getStatus();
|
|
137
|
+
if (status.snapshot_done && status.checkpoint_lsn) {
|
|
138
|
+
logger.info(`Initial replication already done. MySQL appears healthy`);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Does the initial replication of the database tables.
|
|
145
|
+
*
|
|
146
|
+
* If (partial) replication was done before on this slot, this clears the state
|
|
147
|
+
* and starts again from scratch.
|
|
148
|
+
*/
|
|
149
|
+
async startInitialReplication() {
|
|
150
|
+
await this.storage.clear();
|
|
151
|
+
// Replication will be performed in a single transaction on this connection
|
|
152
|
+
const connection = await this.connections.getStreamingConnection();
|
|
153
|
+
const promiseConnection = connection.promise();
|
|
154
|
+
const headGTID = await common.readExecutedGtid(promiseConnection);
|
|
155
|
+
logger.info(`Using snapshot checkpoint GTID:: '${headGTID}'`);
|
|
156
|
+
try {
|
|
157
|
+
logger.info(`Starting initial replication`);
|
|
158
|
+
await promiseConnection.query('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY');
|
|
159
|
+
await promiseConnection.query('START TRANSACTION');
|
|
160
|
+
const sourceTables = this.syncRules.getSourceTables();
|
|
161
|
+
await this.storage.startBatch({ zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, async (batch) => {
|
|
162
|
+
for (let tablePattern of sourceTables) {
|
|
163
|
+
const tables = await this.getQualifiedTableNames(batch, tablePattern);
|
|
164
|
+
for (let table of tables) {
|
|
165
|
+
await this.snapshotTable(connection, batch, table);
|
|
166
|
+
await batch.markSnapshotDone([table], headGTID.comparable);
|
|
167
|
+
await framework.container.probes.touch();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
await batch.commit(headGTID.comparable);
|
|
171
|
+
});
|
|
172
|
+
logger.info(`Initial replication done`);
|
|
173
|
+
await promiseConnection.query('COMMIT');
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
await promiseConnection.query('ROLLBACK');
|
|
177
|
+
throw e;
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
connection.release();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async snapshotTable(connection, batch, table) {
|
|
184
|
+
logger.info(`Replicating ${table.qualifiedName}`);
|
|
185
|
+
// TODO count rows and log progress at certain batch sizes
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
// MAX_EXECUTION_TIME(0) hint disables execution timeout for this query
|
|
188
|
+
connection
|
|
189
|
+
.query(`SELECT /*+ MAX_EXECUTION_TIME(0) */ * FROM ${table.schema}.${table.table}`)
|
|
190
|
+
.stream()
|
|
191
|
+
.on('error', (err) => {
|
|
192
|
+
reject(err);
|
|
193
|
+
})
|
|
194
|
+
.on('data', async (row) => {
|
|
195
|
+
connection.pause();
|
|
196
|
+
const record = common.toSQLiteRow(row);
|
|
197
|
+
await batch.save({
|
|
198
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
199
|
+
sourceTable: table,
|
|
200
|
+
before: undefined,
|
|
201
|
+
beforeReplicaId: undefined,
|
|
202
|
+
after: record,
|
|
203
|
+
afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns)
|
|
204
|
+
});
|
|
205
|
+
connection.resume();
|
|
206
|
+
// TODO: These metrics can probably be reported in batches
|
|
207
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
208
|
+
})
|
|
209
|
+
.on('end', async function () {
|
|
210
|
+
await batch.flush();
|
|
211
|
+
resolve();
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async replicate() {
|
|
216
|
+
try {
|
|
217
|
+
// If anything errors here, the entire replication process is halted, and
|
|
218
|
+
// all connections automatically closed, including this one.
|
|
219
|
+
await this.initReplication();
|
|
220
|
+
await this.streamChanges();
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
await this.storage.reportError(e);
|
|
224
|
+
throw e;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async initReplication() {
|
|
228
|
+
const connection = await this.connections.getConnection();
|
|
229
|
+
await common.checkSourceConfiguration(connection);
|
|
230
|
+
connection.release();
|
|
231
|
+
const initialReplicationCompleted = await this.checkInitialReplicated();
|
|
232
|
+
if (!initialReplicationCompleted) {
|
|
233
|
+
await this.startInitialReplication();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
getTable(tableId) {
|
|
237
|
+
const table = this.tableCache.get(tableId);
|
|
238
|
+
if (table == null) {
|
|
239
|
+
// We should always receive a replication message before the relation is used.
|
|
240
|
+
// If we can't find it, it's a bug.
|
|
241
|
+
throw new Error(`Missing relation cache for ${tableId}`);
|
|
242
|
+
}
|
|
243
|
+
return table;
|
|
244
|
+
}
|
|
245
|
+
async streamChanges() {
|
|
246
|
+
// Auto-activate as soon as initial replication is done
|
|
247
|
+
await this.storage.autoActivate();
|
|
248
|
+
const connection = await this.connections.getConnection();
|
|
249
|
+
const { checkpoint_lsn } = await this.storage.getStatus();
|
|
250
|
+
logger.info(`Last known LSN from storage: ${checkpoint_lsn}`);
|
|
251
|
+
const fromGTID = checkpoint_lsn
|
|
252
|
+
? common.ReplicatedGTID.fromSerialized(checkpoint_lsn)
|
|
253
|
+
: await common.readExecutedGtid(connection);
|
|
254
|
+
const binLogPositionState = fromGTID.position;
|
|
255
|
+
connection.release();
|
|
256
|
+
await this.storage.startBatch({ zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, async (batch) => {
|
|
257
|
+
const zongji = this.connections.createBinlogListener();
|
|
258
|
+
let currentGTID = null;
|
|
259
|
+
const queue = async.queue(async (evt) => {
|
|
260
|
+
// State machine
|
|
261
|
+
switch (true) {
|
|
262
|
+
case zongji_utils.eventIsGTIDLog(evt):
|
|
263
|
+
currentGTID = common.ReplicatedGTID.fromBinLogEvent({
|
|
264
|
+
raw_gtid: {
|
|
265
|
+
server_id: evt.serverId,
|
|
266
|
+
transaction_range: evt.transactionRange
|
|
267
|
+
},
|
|
268
|
+
position: {
|
|
269
|
+
filename: binLogPositionState.filename,
|
|
270
|
+
offset: evt.nextPosition
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
break;
|
|
274
|
+
case zongji_utils.eventIsRotation(evt):
|
|
275
|
+
// Update the position
|
|
276
|
+
binLogPositionState.filename = evt.binlogName;
|
|
277
|
+
binLogPositionState.offset = evt.position;
|
|
278
|
+
break;
|
|
279
|
+
case zongji_utils.eventIsWriteMutation(evt):
|
|
280
|
+
// TODO, can multiple tables be present?
|
|
281
|
+
const writeTableInfo = evt.tableMap[evt.tableId];
|
|
282
|
+
await this.writeChanges(batch, {
|
|
283
|
+
type: storage.SaveOperationTag.INSERT,
|
|
284
|
+
data: evt.rows,
|
|
285
|
+
database: writeTableInfo.parentSchema,
|
|
286
|
+
table: writeTableInfo.tableName,
|
|
287
|
+
sourceTable: this.getTable(getMysqlRelId({
|
|
288
|
+
schema: writeTableInfo.parentSchema,
|
|
289
|
+
name: writeTableInfo.tableName
|
|
290
|
+
}))
|
|
291
|
+
});
|
|
292
|
+
break;
|
|
293
|
+
case zongji_utils.eventIsUpdateMutation(evt):
|
|
294
|
+
const updateTableInfo = evt.tableMap[evt.tableId];
|
|
295
|
+
await this.writeChanges(batch, {
|
|
296
|
+
type: storage.SaveOperationTag.UPDATE,
|
|
297
|
+
data: evt.rows.map((row) => row.after),
|
|
298
|
+
previous_data: evt.rows.map((row) => row.before),
|
|
299
|
+
database: updateTableInfo.parentSchema,
|
|
300
|
+
table: updateTableInfo.tableName,
|
|
301
|
+
sourceTable: this.getTable(getMysqlRelId({
|
|
302
|
+
schema: updateTableInfo.parentSchema,
|
|
303
|
+
name: updateTableInfo.tableName
|
|
304
|
+
}))
|
|
305
|
+
});
|
|
306
|
+
break;
|
|
307
|
+
case zongji_utils.eventIsDeleteMutation(evt):
|
|
308
|
+
// TODO, can multiple tables be present?
|
|
309
|
+
const deleteTableInfo = evt.tableMap[evt.tableId];
|
|
310
|
+
await this.writeChanges(batch, {
|
|
311
|
+
type: storage.SaveOperationTag.DELETE,
|
|
312
|
+
data: evt.rows,
|
|
313
|
+
database: deleteTableInfo.parentSchema,
|
|
314
|
+
table: deleteTableInfo.tableName,
|
|
315
|
+
// TODO cleanup
|
|
316
|
+
sourceTable: this.getTable(getMysqlRelId({
|
|
317
|
+
schema: deleteTableInfo.parentSchema,
|
|
318
|
+
name: deleteTableInfo.tableName
|
|
319
|
+
}))
|
|
320
|
+
});
|
|
321
|
+
break;
|
|
322
|
+
case zongji_utils.eventIsXid(evt):
|
|
323
|
+
Metrics.getInstance().transactions_replicated_total.add(1);
|
|
324
|
+
// Need to commit with a replicated GTID with updated next position
|
|
325
|
+
await batch.commit(new common.ReplicatedGTID({
|
|
326
|
+
raw_gtid: currentGTID.raw,
|
|
327
|
+
position: {
|
|
328
|
+
filename: binLogPositionState.filename,
|
|
329
|
+
offset: evt.nextPosition
|
|
330
|
+
}
|
|
331
|
+
}).comparable);
|
|
332
|
+
currentGTID = null;
|
|
333
|
+
// chunks_replicated_total.add(1);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
}, 1);
|
|
337
|
+
zongji.on('binlog', (evt) => {
|
|
338
|
+
logger.info(`Pushing Binlog event ${evt.getEventName()}`);
|
|
339
|
+
queue.push(evt);
|
|
340
|
+
});
|
|
341
|
+
logger.info(`Starting replication from ${binLogPositionState.filename}:${binLogPositionState.offset}`);
|
|
342
|
+
zongji.start({
|
|
343
|
+
includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'],
|
|
344
|
+
excludeEvents: [],
|
|
345
|
+
filename: binLogPositionState.filename,
|
|
346
|
+
position: binLogPositionState.offset
|
|
347
|
+
});
|
|
348
|
+
// Forever young
|
|
349
|
+
await new Promise((resolve, reject) => {
|
|
350
|
+
queue.error((error) => {
|
|
351
|
+
zongji.stop();
|
|
352
|
+
queue.kill();
|
|
353
|
+
reject(error);
|
|
354
|
+
});
|
|
355
|
+
this.abortSignal.addEventListener('abort', async () => {
|
|
356
|
+
zongji.stop();
|
|
357
|
+
queue.kill();
|
|
358
|
+
if (!queue.length) {
|
|
359
|
+
await queue.drain();
|
|
360
|
+
}
|
|
361
|
+
resolve();
|
|
362
|
+
}, { once: true });
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
async writeChanges(batch, msg) {
|
|
367
|
+
for (const [index, row] of msg.data.entries()) {
|
|
368
|
+
await this.writeChange(batch, {
|
|
369
|
+
...msg,
|
|
370
|
+
data: row,
|
|
371
|
+
previous_data: msg.previous_data?.[index]
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
async writeChange(batch, payload) {
|
|
377
|
+
switch (payload.type) {
|
|
378
|
+
case storage.SaveOperationTag.INSERT:
|
|
379
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
380
|
+
const record = common.toSQLiteRow(payload.data);
|
|
381
|
+
return await batch.save({
|
|
382
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
383
|
+
sourceTable: payload.sourceTable,
|
|
384
|
+
before: undefined,
|
|
385
|
+
beforeReplicaId: undefined,
|
|
386
|
+
after: record,
|
|
387
|
+
afterReplicaId: getUuidReplicaIdentityBson(record, payload.sourceTable.replicaIdColumns)
|
|
388
|
+
});
|
|
389
|
+
case storage.SaveOperationTag.UPDATE:
|
|
390
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
391
|
+
// "before" may be null if the replica id columns are unchanged
|
|
392
|
+
// It's fine to treat that the same as an insert.
|
|
393
|
+
const beforeUpdated = payload.previous_data ? common.toSQLiteRow(payload.previous_data) : undefined;
|
|
394
|
+
const after = common.toSQLiteRow(payload.data);
|
|
395
|
+
return await batch.save({
|
|
396
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
397
|
+
sourceTable: payload.sourceTable,
|
|
398
|
+
before: beforeUpdated,
|
|
399
|
+
beforeReplicaId: beforeUpdated
|
|
400
|
+
? getUuidReplicaIdentityBson(beforeUpdated, payload.sourceTable.replicaIdColumns)
|
|
401
|
+
: undefined,
|
|
402
|
+
after: common.toSQLiteRow(payload.data),
|
|
403
|
+
afterReplicaId: getUuidReplicaIdentityBson(after, payload.sourceTable.replicaIdColumns)
|
|
404
|
+
});
|
|
405
|
+
case storage.SaveOperationTag.DELETE:
|
|
406
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
407
|
+
const beforeDeleted = common.toSQLiteRow(payload.data);
|
|
408
|
+
return await batch.save({
|
|
409
|
+
tag: storage.SaveOperationTag.DELETE,
|
|
410
|
+
sourceTable: payload.sourceTable,
|
|
411
|
+
before: beforeDeleted,
|
|
412
|
+
beforeReplicaId: getUuidReplicaIdentityBson(beforeDeleted, payload.sourceTable.replicaIdColumns),
|
|
413
|
+
after: undefined,
|
|
414
|
+
afterReplicaId: undefined
|
|
415
|
+
});
|
|
416
|
+
default:
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
//# sourceMappingURL=BinLogStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinLogStream.js","sourceRoot":"","sources":["../../src/replication/BinLogStream.ts"],"names":[],"mappings":"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"}
|