@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,43 @@
|
|
|
1
|
+
import { NormalizedMySQLConnectionConfig } from '../types/types.js';
|
|
2
|
+
import mysqlPromise from 'mysql2/promise';
|
|
3
|
+
import mysql from 'mysql2';
|
|
4
|
+
import ZongJi from '@powersync/mysql-zongji';
|
|
5
|
+
export declare class MySQLConnectionManager {
|
|
6
|
+
options: NormalizedMySQLConnectionConfig;
|
|
7
|
+
poolOptions: mysqlPromise.PoolOptions;
|
|
8
|
+
/**
|
|
9
|
+
* Pool that can create streamable connections
|
|
10
|
+
*/
|
|
11
|
+
private readonly pool;
|
|
12
|
+
/**
|
|
13
|
+
* Pool that can create promise-based connections
|
|
14
|
+
*/
|
|
15
|
+
private readonly promisePool;
|
|
16
|
+
private binlogListeners;
|
|
17
|
+
constructor(options: NormalizedMySQLConnectionConfig, poolOptions: mysqlPromise.PoolOptions);
|
|
18
|
+
get connectionTag(): string;
|
|
19
|
+
get connectionId(): string;
|
|
20
|
+
get databaseName(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new replication listener
|
|
23
|
+
*/
|
|
24
|
+
createBinlogListener(): ZongJi;
|
|
25
|
+
/**
|
|
26
|
+
* Run a query using a connection from the pool
|
|
27
|
+
* A promise with the result is returned
|
|
28
|
+
* @param query
|
|
29
|
+
* @param params
|
|
30
|
+
*/
|
|
31
|
+
query(query: string, params?: any[]): Promise<[mysqlPromise.RowDataPacket[], mysqlPromise.FieldPacket[]]>;
|
|
32
|
+
/**
|
|
33
|
+
* Get a streamable connection from this manager's pool
|
|
34
|
+
* The connection should be released when it is no longer needed
|
|
35
|
+
*/
|
|
36
|
+
getStreamingConnection(): Promise<mysql.PoolConnection>;
|
|
37
|
+
/**
|
|
38
|
+
* Get a promise connection from this manager's pool
|
|
39
|
+
* The connection should be released when it is no longer needed
|
|
40
|
+
*/
|
|
41
|
+
getConnection(): Promise<mysqlPromise.PoolConnection>;
|
|
42
|
+
end(): Promise<void>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as mysql_utils from '../utils/mysql_utils.js';
|
|
2
|
+
import ZongJi from '@powersync/mysql-zongji';
|
|
3
|
+
export class MySQLConnectionManager {
|
|
4
|
+
constructor(options, poolOptions) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
this.poolOptions = poolOptions;
|
|
7
|
+
this.binlogListeners = [];
|
|
8
|
+
// The pool is lazy - no connections are opened until a query is performed.
|
|
9
|
+
this.pool = mysql_utils.createPool(options, poolOptions);
|
|
10
|
+
this.promisePool = this.pool.promise();
|
|
11
|
+
}
|
|
12
|
+
get connectionTag() {
|
|
13
|
+
return this.options.tag;
|
|
14
|
+
}
|
|
15
|
+
get connectionId() {
|
|
16
|
+
return this.options.id;
|
|
17
|
+
}
|
|
18
|
+
get databaseName() {
|
|
19
|
+
return this.options.database;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a new replication listener
|
|
23
|
+
*/
|
|
24
|
+
createBinlogListener() {
|
|
25
|
+
const listener = new ZongJi({
|
|
26
|
+
host: this.options.hostname,
|
|
27
|
+
user: this.options.username,
|
|
28
|
+
password: this.options.password
|
|
29
|
+
});
|
|
30
|
+
this.binlogListeners.push(listener);
|
|
31
|
+
return listener;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Run a query using a connection from the pool
|
|
35
|
+
* A promise with the result is returned
|
|
36
|
+
* @param query
|
|
37
|
+
* @param params
|
|
38
|
+
*/
|
|
39
|
+
async query(query, params) {
|
|
40
|
+
return this.promisePool.query(query, params);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get a streamable connection from this manager's pool
|
|
44
|
+
* The connection should be released when it is no longer needed
|
|
45
|
+
*/
|
|
46
|
+
async getStreamingConnection() {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
this.pool.getConnection((err, connection) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
reject(err);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
resolve(connection);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get a promise connection from this manager's pool
|
|
60
|
+
* The connection should be released when it is no longer needed
|
|
61
|
+
*/
|
|
62
|
+
async getConnection() {
|
|
63
|
+
return this.promisePool.getConnection();
|
|
64
|
+
}
|
|
65
|
+
async end() {
|
|
66
|
+
for (const listener of this.binlogListeners) {
|
|
67
|
+
listener.stop();
|
|
68
|
+
}
|
|
69
|
+
await new Promise((resolve, reject) => {
|
|
70
|
+
this.pool.end((err) => {
|
|
71
|
+
if (err) {
|
|
72
|
+
reject(err);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
resolve();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=MySQLConnectionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MySQLConnectionManager.js","sourceRoot":"","sources":["../../src/replication/MySQLConnectionManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AACvD,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAE7C,MAAM,OAAO,sBAAsB;IAYjC,YACS,OAAwC,EACxC,WAAqC;QADrC,YAAO,GAAP,OAAO,CAAiC;QACxC,gBAAW,GAAX,WAAW,CAA0B;QAJtC,oBAAe,GAAa,EAAE,CAAC;QAMrC,2EAA2E;QAC3E,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACzD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IACzC,CAAC;IAED,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;IAC1B,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,oBAAoB;QAClB,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC;YAC1B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;YAC3B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;YAC3B,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEpC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,KAAa,EAAE,MAAc;QACvC,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAkB,KAAK,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,sBAAsB;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;gBAC1C,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,UAAU,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,GAAG;QACP,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5C,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACpB,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACN,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import mysql from 'mysql2/promise';
|
|
2
|
+
import { MySQLConnectionManager } from './MySQLConnectionManager.js';
|
|
3
|
+
import { ResolvedConnectionConfig } from '../types/types.js';
|
|
4
|
+
export declare class MySQLConnectionManagerFactory {
|
|
5
|
+
private readonly connectionManagers;
|
|
6
|
+
private readonly connectionConfig;
|
|
7
|
+
constructor(connectionConfig: ResolvedConnectionConfig);
|
|
8
|
+
create(poolOptions: mysql.PoolOptions): MySQLConnectionManager;
|
|
9
|
+
shutdown(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import { MySQLConnectionManager } from './MySQLConnectionManager.js';
|
|
3
|
+
export class MySQLConnectionManagerFactory {
|
|
4
|
+
constructor(connectionConfig) {
|
|
5
|
+
this.connectionConfig = connectionConfig;
|
|
6
|
+
this.connectionManagers = [];
|
|
7
|
+
}
|
|
8
|
+
create(poolOptions) {
|
|
9
|
+
const manager = new MySQLConnectionManager(this.connectionConfig, poolOptions);
|
|
10
|
+
this.connectionManagers.push(manager);
|
|
11
|
+
return manager;
|
|
12
|
+
}
|
|
13
|
+
async shutdown() {
|
|
14
|
+
logger.info('Shutting down MySQL connection Managers...');
|
|
15
|
+
for (const manager of this.connectionManagers) {
|
|
16
|
+
await manager.end();
|
|
17
|
+
}
|
|
18
|
+
logger.info('MySQL connection Managers shutdown completed.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=MySQLConnectionManagerFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MySQLConnectionManagerFactory.js","sourceRoot":"","sources":["../../src/replication/MySQLConnectionManagerFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,MAAM,OAAO,6BAA6B;IAIxC,YAAY,gBAA0C;QACpD,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,WAA8B;QACnC,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;QAC/E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAC1D,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC9C,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC;QACtB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ErrorRateLimiter } from '@powersync/service-core';
|
|
2
|
+
export declare class MySQLErrorRateLimiter implements ErrorRateLimiter {
|
|
3
|
+
nextAllowed: number;
|
|
4
|
+
waitUntilAllowed(options?: {
|
|
5
|
+
signal?: AbortSignal | undefined;
|
|
6
|
+
} | undefined): Promise<void>;
|
|
7
|
+
mayPing(): boolean;
|
|
8
|
+
reportError(e: any): void;
|
|
9
|
+
private setDelay;
|
|
10
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { setTimeout } from 'timers/promises';
|
|
2
|
+
export class MySQLErrorRateLimiter {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.nextAllowed = Date.now();
|
|
5
|
+
}
|
|
6
|
+
async waitUntilAllowed(options) {
|
|
7
|
+
const delay = Math.max(0, this.nextAllowed - Date.now());
|
|
8
|
+
// Minimum delay between connections, even without errors
|
|
9
|
+
this.setDelay(500);
|
|
10
|
+
await setTimeout(delay, undefined, { signal: options?.signal });
|
|
11
|
+
}
|
|
12
|
+
mayPing() {
|
|
13
|
+
return Date.now() >= this.nextAllowed;
|
|
14
|
+
}
|
|
15
|
+
reportError(e) {
|
|
16
|
+
const message = e.message ?? '';
|
|
17
|
+
if (message.includes('password authentication failed')) {
|
|
18
|
+
// Wait 15 minutes, to avoid triggering Supabase's fail2ban
|
|
19
|
+
this.setDelay(900000);
|
|
20
|
+
}
|
|
21
|
+
else if (message.includes('ENOTFOUND')) {
|
|
22
|
+
// DNS lookup issue - incorrect URI or deleted instance
|
|
23
|
+
this.setDelay(120000);
|
|
24
|
+
}
|
|
25
|
+
else if (message.includes('ECONNREFUSED')) {
|
|
26
|
+
// Could be fail2ban or similar
|
|
27
|
+
this.setDelay(120000);
|
|
28
|
+
}
|
|
29
|
+
else if (message.includes('Unable to do postgres query on ended pool') ||
|
|
30
|
+
message.includes('Postgres unexpectedly closed connection')) {
|
|
31
|
+
// Connection timed out - ignore / immediately retry
|
|
32
|
+
// We don't explicitly set the delay to 0, since there could have been another error that
|
|
33
|
+
// we need to respect.
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.setDelay(30000);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
setDelay(delay) {
|
|
40
|
+
this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=MySQLErrorRateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MySQLErrorRateLimiter.js","sourceRoot":"","sources":["../../src/replication/MySQLErrorRateLimiter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,OAAO,qBAAqB;IAAlC;QACE,gBAAW,GAAW,IAAI,CAAC,GAAG,EAAE,CAAC;IAuCnC,CAAC;IArCC,KAAK,CAAC,gBAAgB,CAAC,OAA0D;QAC/E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACzD,yDAAyD;QACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACnB,MAAM,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,WAAW,CAAC;IACxC,CAAC;IAED,WAAW,CAAC,CAAM;QAChB,MAAM,OAAO,GAAI,CAAC,CAAC,OAAkB,IAAI,EAAE,CAAC;QAC5C,IAAI,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,CAAC;YACvD,2DAA2D;YAC3D,IAAI,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACzC,uDAAuD;YACvD,IAAI,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC5C,+BAA+B;YAC/B,IAAI,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC;QACzB,CAAC;aAAM,IACL,OAAO,CAAC,QAAQ,CAAC,2CAA2C,CAAC;YAC7D,OAAO,CAAC,QAAQ,CAAC,yCAAyC,CAAC,EAC3D,CAAC;YACD,oDAAoD;YACpD,yFAAyF;YACzF,sBAAsB;QACxB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,CAAC,KAAM,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,KAAa;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BinLogEvent, BinLogGTIDLogEvent, BinLogMutationEvent, BinLogRotationEvent, BinLogUpdateEvent, BinLogXidEvent } from '@powersync/mysql-zongji';
|
|
2
|
+
export declare function eventIsGTIDLog(event: BinLogEvent): event is BinLogGTIDLogEvent;
|
|
3
|
+
export declare function eventIsXid(event: BinLogEvent): event is BinLogXidEvent;
|
|
4
|
+
export declare function eventIsRotation(event: BinLogEvent): event is BinLogRotationEvent;
|
|
5
|
+
export declare function eventIsWriteMutation(event: BinLogEvent): event is BinLogMutationEvent;
|
|
6
|
+
export declare function eventIsDeleteMutation(event: BinLogEvent): event is BinLogMutationEvent;
|
|
7
|
+
export declare function eventIsUpdateMutation(event: BinLogEvent): event is BinLogUpdateEvent;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function eventIsGTIDLog(event) {
|
|
2
|
+
return event.getEventName() == 'gtidlog';
|
|
3
|
+
}
|
|
4
|
+
export function eventIsXid(event) {
|
|
5
|
+
return event.getEventName() == 'xid';
|
|
6
|
+
}
|
|
7
|
+
export function eventIsRotation(event) {
|
|
8
|
+
return event.getEventName() == 'rotate';
|
|
9
|
+
}
|
|
10
|
+
export function eventIsWriteMutation(event) {
|
|
11
|
+
return event.getEventName() == 'writerows';
|
|
12
|
+
}
|
|
13
|
+
export function eventIsDeleteMutation(event) {
|
|
14
|
+
return event.getEventName() == 'deleterows';
|
|
15
|
+
}
|
|
16
|
+
export function eventIsUpdateMutation(event) {
|
|
17
|
+
return event.getEventName() == 'updaterows';
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=zongji-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zongji-utils.js","sourceRoot":"","sources":["../../../src/replication/zongji/zongji-utils.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,OAAO,KAAK,CAAC,YAAY,EAAE,IAAI,SAAS,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,OAAO,KAAK,CAAC,YAAY,EAAE,IAAI,KAAK,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,OAAO,KAAK,CAAC,YAAY,EAAE,IAAI,QAAQ,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAkB;IACrD,OAAO,KAAK,CAAC,YAAY,EAAE,IAAI,WAAW,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAkB;IACtD,OAAO,KAAK,CAAC,YAAY,EAAE,IAAI,YAAY,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAkB;IACtD,OAAO,KAAK,CAAC,YAAY,EAAE,IAAI,YAAY,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as t from 'ts-codec';
|
|
2
|
+
export declare const MYSQL_CONNECTION_TYPE: "mysql";
|
|
3
|
+
export interface NormalizedMySQLConnectionConfig {
|
|
4
|
+
id: string;
|
|
5
|
+
tag: string;
|
|
6
|
+
hostname: string;
|
|
7
|
+
port: number;
|
|
8
|
+
database: string;
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
cacert?: string;
|
|
12
|
+
client_certificate?: string;
|
|
13
|
+
client_private_key?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const MySQLConnectionConfig: t.Intersection<t.Codec<{
|
|
16
|
+
type: string;
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
tag?: string | undefined;
|
|
19
|
+
debug_api?: boolean | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
type: string;
|
|
22
|
+
id?: string | undefined;
|
|
23
|
+
tag?: string | undefined;
|
|
24
|
+
debug_api?: boolean | undefined;
|
|
25
|
+
}, string, t.CodecProps>, t.ObjectCodec<{
|
|
26
|
+
type: t.LiteralCodec<"mysql">;
|
|
27
|
+
uri: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
28
|
+
hostname: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
29
|
+
port: t.OptionalCodec<t.Codec<number, string | number, string, t.CodecProps>>;
|
|
30
|
+
username: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
31
|
+
password: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
32
|
+
database: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
33
|
+
cacert: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
34
|
+
client_certificate: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
35
|
+
client_private_key: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
36
|
+
}>>;
|
|
37
|
+
/**
|
|
38
|
+
* Config input specified when starting services
|
|
39
|
+
*/
|
|
40
|
+
export type MySQLConnectionConfig = t.Decoded<typeof MySQLConnectionConfig>;
|
|
41
|
+
/**
|
|
42
|
+
* Resolved version of {@link MySQLConnectionConfig}
|
|
43
|
+
*/
|
|
44
|
+
export type ResolvedConnectionConfig = MySQLConnectionConfig & NormalizedMySQLConnectionConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Validate and normalize connection options.
|
|
47
|
+
*
|
|
48
|
+
* Returns destructured options.
|
|
49
|
+
*/
|
|
50
|
+
export declare function normalizeConnectionConfig(options: MySQLConnectionConfig): NormalizedMySQLConnectionConfig;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as service_types from '@powersync/service-types';
|
|
2
|
+
import * as t from 'ts-codec';
|
|
3
|
+
import * as urijs from 'uri-js';
|
|
4
|
+
export const MYSQL_CONNECTION_TYPE = 'mysql';
|
|
5
|
+
export const MySQLConnectionConfig = service_types.configFile.DataSourceConfig.and(t.object({
|
|
6
|
+
type: t.literal(MYSQL_CONNECTION_TYPE),
|
|
7
|
+
uri: t.string.optional(),
|
|
8
|
+
hostname: t.string.optional(),
|
|
9
|
+
port: service_types.configFile.portCodec.optional(),
|
|
10
|
+
username: t.string.optional(),
|
|
11
|
+
password: t.string.optional(),
|
|
12
|
+
database: t.string.optional(),
|
|
13
|
+
cacert: t.string.optional(),
|
|
14
|
+
client_certificate: t.string.optional(),
|
|
15
|
+
client_private_key: t.string.optional()
|
|
16
|
+
}));
|
|
17
|
+
/**
|
|
18
|
+
* Validate and normalize connection options.
|
|
19
|
+
*
|
|
20
|
+
* Returns destructured options.
|
|
21
|
+
*/
|
|
22
|
+
export function normalizeConnectionConfig(options) {
|
|
23
|
+
let uri;
|
|
24
|
+
if (options.uri) {
|
|
25
|
+
uri = urijs.parse(options.uri);
|
|
26
|
+
if (uri.scheme != 'mysql') {
|
|
27
|
+
throw new Error(`Invalid URI - protocol must be mysql, got ${uri.scheme}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
uri = urijs.parse('mysql:///');
|
|
32
|
+
}
|
|
33
|
+
const hostname = options.hostname ?? uri.host ?? '';
|
|
34
|
+
const port = Number(options.port ?? uri.port ?? 3306);
|
|
35
|
+
const database = options.database ?? uri.path?.substring(1) ?? '';
|
|
36
|
+
const [uri_username, uri_password] = (uri.userinfo ?? '').split(':');
|
|
37
|
+
const username = options.username ?? uri_username ?? '';
|
|
38
|
+
const password = options.password ?? uri_password ?? '';
|
|
39
|
+
if (hostname == '') {
|
|
40
|
+
throw new Error(`hostname required`);
|
|
41
|
+
}
|
|
42
|
+
if (username == '') {
|
|
43
|
+
throw new Error(`username required`);
|
|
44
|
+
}
|
|
45
|
+
if (password == '') {
|
|
46
|
+
throw new Error(`password required`);
|
|
47
|
+
}
|
|
48
|
+
if (database == '') {
|
|
49
|
+
throw new Error(`database required`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
id: options.id ?? 'default',
|
|
53
|
+
tag: options.tag ?? 'default',
|
|
54
|
+
hostname,
|
|
55
|
+
port,
|
|
56
|
+
database,
|
|
57
|
+
username,
|
|
58
|
+
password
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import mysql from 'mysql2';
|
|
2
|
+
import mysqlPromise from 'mysql2/promise';
|
|
3
|
+
import * as types from '../types/types.js';
|
|
4
|
+
export type RetriedQueryOptions = {
|
|
5
|
+
connection: mysqlPromise.Connection;
|
|
6
|
+
query: string;
|
|
7
|
+
params?: any[];
|
|
8
|
+
retries?: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Retry a simple query - up to 2 attempts total.
|
|
12
|
+
*/
|
|
13
|
+
export declare function retriedQuery(options: RetriedQueryOptions): Promise<[mysql.RowDataPacket[], mysql.FieldPacket[]]>;
|
|
14
|
+
export declare function createPool(config: types.NormalizedMySQLConnectionConfig, options?: mysql.PoolOptions): mysql.Pool;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import mysql from 'mysql2';
|
|
3
|
+
/**
|
|
4
|
+
* Retry a simple query - up to 2 attempts total.
|
|
5
|
+
*/
|
|
6
|
+
export async function retriedQuery(options) {
|
|
7
|
+
const { connection, query, params = [], retries = 2 } = options;
|
|
8
|
+
for (let tries = retries;; tries--) {
|
|
9
|
+
try {
|
|
10
|
+
logger.debug(`Executing query: ${query}`);
|
|
11
|
+
return connection.query(query, params);
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
if (tries == 1) {
|
|
15
|
+
throw e;
|
|
16
|
+
}
|
|
17
|
+
logger.warn('Query error, retrying', e);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function createPool(config, options) {
|
|
22
|
+
const sslOptions = {
|
|
23
|
+
ca: config.cacert,
|
|
24
|
+
key: config.client_private_key,
|
|
25
|
+
cert: config.client_certificate
|
|
26
|
+
};
|
|
27
|
+
const hasSSLOptions = Object.values(sslOptions).some((v) => !!v);
|
|
28
|
+
// TODO confirm if default options are fine for Powersync use case
|
|
29
|
+
return mysql.createPool({
|
|
30
|
+
host: config.hostname,
|
|
31
|
+
user: config.username,
|
|
32
|
+
password: config.password,
|
|
33
|
+
database: config.database,
|
|
34
|
+
ssl: hasSSLOptions ? sslOptions : undefined,
|
|
35
|
+
...(options || {})
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=mysql_utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_utils.js","sourceRoot":"","sources":["../../src/utils/mysql_utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,MAAM,QAAQ,CAAC;AAW3B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA4B;IAC7D,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,EAAE,OAAO,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAChE,KAAK,IAAI,KAAK,GAAG,OAAO,GAAI,KAAK,EAAE,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAC;YAC1C,OAAO,UAAU,CAAC,KAAK,CAA+B,KAAK,EAAE,MAAM,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,CAAC;YACV,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAA6C,EAAE,OAA2B;IACnG,MAAM,UAAU,GAAG;QACjB,EAAE,EAAE,MAAM,CAAC,MAAM;QACjB,GAAG,EAAE,MAAM,CAAC,kBAAkB;QAC9B,IAAI,EAAE,MAAM,CAAC,kBAAkB;KAChC,CAAC;IACF,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,kEAAkE;IAClE,OAAO,KAAK,CAAC,UAAU,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,QAAQ;QACrB,IAAI,EAAE,MAAM,CAAC,QAAQ;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAC3C,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;KACnB,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@powersync/service-module-mysql",
|
|
3
|
+
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
|
+
"types": "dist/index.d.ts",
|
|
5
|
+
"version": "0.0.0-dev-20241015210820",
|
|
6
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./types": {
|
|
19
|
+
"import": "./dist/types/types.js",
|
|
20
|
+
"require": "./dist/types/types.js",
|
|
21
|
+
"default": "./dist/types/types.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@powersync/mysql-zongji": "0.0.0-dev-20241008105633",
|
|
26
|
+
"semver": "^7.5.4",
|
|
27
|
+
"async": "^3.2.4",
|
|
28
|
+
"mysql2": "^3.11.0",
|
|
29
|
+
"ts-codec": "^1.2.2",
|
|
30
|
+
"uri-js": "^4.4.1",
|
|
31
|
+
"uuid": "^9.0.1",
|
|
32
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20241015210820",
|
|
33
|
+
"@powersync/service-core": "0.0.0-dev-20241015210820",
|
|
34
|
+
"@powersync/service-sync-rules": "0.0.0-dev-20241015210820",
|
|
35
|
+
"@powersync/service-types": "0.0.0-dev-20241015210820"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/semver": "^7.5.4",
|
|
39
|
+
"@types/async": "^3.2.24",
|
|
40
|
+
"@types/uuid": "^9.0.8",
|
|
41
|
+
"typescript": "^5.5.4",
|
|
42
|
+
"vite-tsconfig-paths": "^4.3.2",
|
|
43
|
+
"vitest": "^0.34.6"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -b",
|
|
47
|
+
"build:tests": "tsc -b test/tsconfig.json",
|
|
48
|
+
"clean": "rm -rf ./lib && tsc -b --clean",
|
|
49
|
+
"test": "vitest"
|
|
50
|
+
}
|
|
51
|
+
}
|