@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.
Files changed (96) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +67 -0
  3. package/README.md +3 -0
  4. package/dev/.env.template +2 -0
  5. package/dev/README.md +9 -0
  6. package/dev/config/sync_rules.yaml +12 -0
  7. package/dev/docker/mysql/docker-compose.yaml +17 -0
  8. package/dev/docker/mysql/init-scripts/my.cnf +9 -0
  9. package/dev/docker/mysql/init-scripts/mysql.sql +38 -0
  10. package/dist/api/MySQLRouteAPIAdapter.d.ts +24 -0
  11. package/dist/api/MySQLRouteAPIAdapter.js +311 -0
  12. package/dist/api/MySQLRouteAPIAdapter.js.map +1 -0
  13. package/dist/common/ReplicatedGTID.d.ts +59 -0
  14. package/dist/common/ReplicatedGTID.js +110 -0
  15. package/dist/common/ReplicatedGTID.js.map +1 -0
  16. package/dist/common/check-source-configuration.d.ts +3 -0
  17. package/dist/common/check-source-configuration.js +46 -0
  18. package/dist/common/check-source-configuration.js.map +1 -0
  19. package/dist/common/common-index.d.ts +6 -0
  20. package/dist/common/common-index.js +7 -0
  21. package/dist/common/common-index.js.map +1 -0
  22. package/dist/common/get-replication-columns.d.ts +12 -0
  23. package/dist/common/get-replication-columns.js +103 -0
  24. package/dist/common/get-replication-columns.js.map +1 -0
  25. package/dist/common/get-tables-from-pattern.d.ts +7 -0
  26. package/dist/common/get-tables-from-pattern.js +28 -0
  27. package/dist/common/get-tables-from-pattern.js.map +1 -0
  28. package/dist/common/mysql-to-sqlite.d.ts +4 -0
  29. package/dist/common/mysql-to-sqlite.js +56 -0
  30. package/dist/common/mysql-to-sqlite.js.map +1 -0
  31. package/dist/common/read-executed-gtid.d.ts +6 -0
  32. package/dist/common/read-executed-gtid.js +40 -0
  33. package/dist/common/read-executed-gtid.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.js +4 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/module/MySQLModule.d.ts +13 -0
  38. package/dist/module/MySQLModule.js +46 -0
  39. package/dist/module/MySQLModule.js.map +1 -0
  40. package/dist/replication/BinLogReplicationJob.d.ts +14 -0
  41. package/dist/replication/BinLogReplicationJob.js +88 -0
  42. package/dist/replication/BinLogReplicationJob.js.map +1 -0
  43. package/dist/replication/BinLogReplicator.d.ts +13 -0
  44. package/dist/replication/BinLogReplicator.js +25 -0
  45. package/dist/replication/BinLogReplicator.js.map +1 -0
  46. package/dist/replication/BinLogStream.d.ts +43 -0
  47. package/dist/replication/BinLogStream.js +421 -0
  48. package/dist/replication/BinLogStream.js.map +1 -0
  49. package/dist/replication/MySQLConnectionManager.d.ts +43 -0
  50. package/dist/replication/MySQLConnectionManager.js +81 -0
  51. package/dist/replication/MySQLConnectionManager.js.map +1 -0
  52. package/dist/replication/MySQLConnectionManagerFactory.d.ts +10 -0
  53. package/dist/replication/MySQLConnectionManagerFactory.js +21 -0
  54. package/dist/replication/MySQLConnectionManagerFactory.js.map +1 -0
  55. package/dist/replication/MySQLErrorRateLimiter.d.ts +10 -0
  56. package/dist/replication/MySQLErrorRateLimiter.js +43 -0
  57. package/dist/replication/MySQLErrorRateLimiter.js.map +1 -0
  58. package/dist/replication/zongji/zongji-utils.d.ts +7 -0
  59. package/dist/replication/zongji/zongji-utils.js +19 -0
  60. package/dist/replication/zongji/zongji-utils.js.map +1 -0
  61. package/dist/types/types.d.ts +50 -0
  62. package/dist/types/types.js +61 -0
  63. package/dist/types/types.js.map +1 -0
  64. package/dist/utils/mysql_utils.d.ts +14 -0
  65. package/dist/utils/mysql_utils.js +38 -0
  66. package/dist/utils/mysql_utils.js.map +1 -0
  67. package/package.json +51 -0
  68. package/src/api/MySQLRouteAPIAdapter.ts +357 -0
  69. package/src/common/ReplicatedGTID.ts +158 -0
  70. package/src/common/check-source-configuration.ts +59 -0
  71. package/src/common/common-index.ts +6 -0
  72. package/src/common/get-replication-columns.ts +124 -0
  73. package/src/common/get-tables-from-pattern.ts +44 -0
  74. package/src/common/mysql-to-sqlite.ts +59 -0
  75. package/src/common/read-executed-gtid.ts +43 -0
  76. package/src/index.ts +5 -0
  77. package/src/module/MySQLModule.ts +53 -0
  78. package/src/replication/BinLogReplicationJob.ts +97 -0
  79. package/src/replication/BinLogReplicator.ts +35 -0
  80. package/src/replication/BinLogStream.ts +547 -0
  81. package/src/replication/MySQLConnectionManager.ts +104 -0
  82. package/src/replication/MySQLConnectionManagerFactory.ts +28 -0
  83. package/src/replication/MySQLErrorRateLimiter.ts +44 -0
  84. package/src/replication/zongji/zongji-utils.ts +32 -0
  85. package/src/replication/zongji/zongji.d.ts +98 -0
  86. package/src/types/types.ts +102 -0
  87. package/src/utils/mysql_utils.ts +47 -0
  88. package/test/src/binlog_stream.test.ts +288 -0
  89. package/test/src/binlog_stream_utils.ts +152 -0
  90. package/test/src/env.ts +7 -0
  91. package/test/src/setup.ts +7 -0
  92. package/test/src/util.ts +62 -0
  93. package/test/tsconfig.json +28 -0
  94. package/tsconfig.json +26 -0
  95. package/tsconfig.tsbuildinfo +1 -0
  96. 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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"}