@powersync/service-module-mysql 0.9.10 → 0.9.11
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 +12 -0
- package/dist/replication/BinLogReplicationJob.d.ts +0 -1
- package/dist/replication/BinLogReplicationJob.js +16 -38
- package/dist/replication/BinLogReplicationJob.js.map +1 -1
- package/dist/replication/MySQLConnectionManager.d.ts +5 -1
- package/dist/replication/MySQLConnectionManager.js +21 -14
- package/dist/replication/MySQLConnectionManager.js.map +1 -1
- package/dist/replication/MySQLConnectionManagerFactory.js +8 -4
- package/dist/replication/MySQLConnectionManagerFactory.js.map +1 -1
- package/package.json +5 -5
- package/src/replication/BinLogReplicationJob.ts +16 -38
- package/src/replication/MySQLConnectionManager.ts +25 -14
- package/src/replication/MySQLConnectionManagerFactory.ts +9 -4
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @powersync/service-module-mysql
|
|
2
2
|
|
|
3
|
+
## 0.9.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d889219: Fix memory leaks when retrying replication after errors.
|
|
8
|
+
- Updated dependencies [b364581]
|
|
9
|
+
- Updated dependencies [d889219]
|
|
10
|
+
- Updated dependencies [0ace0d3]
|
|
11
|
+
- Updated dependencies [7eb7957]
|
|
12
|
+
- Updated dependencies [b364581]
|
|
13
|
+
- @powersync/service-core@1.16.2
|
|
14
|
+
|
|
3
15
|
## 0.9.10
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -10,7 +10,6 @@ export declare class BinLogReplicationJob extends replication.AbstractReplicatio
|
|
|
10
10
|
get slot_name(): string;
|
|
11
11
|
keepAlive(): Promise<void>;
|
|
12
12
|
replicate(): Promise<void>;
|
|
13
|
-
replicateLoop(): Promise<void>;
|
|
14
13
|
replicateOnce(): Promise<void>;
|
|
15
14
|
getReplicationLagMillis(): Promise<number | undefined>;
|
|
16
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { container, logger as defaultLogger } from '@powersync/lib-services-framework';
|
|
2
2
|
import { POWERSYNC_VERSION, replication } from '@powersync/service-core';
|
|
3
|
-
import {
|
|
3
|
+
import { BinLogStream } from './BinLogStream.js';
|
|
4
4
|
export class BinLogReplicationJob extends replication.AbstractReplicationJob {
|
|
5
5
|
connectionFactory;
|
|
6
6
|
lastStream = null;
|
|
@@ -17,29 +17,29 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob {
|
|
|
17
17
|
}
|
|
18
18
|
async replicate() {
|
|
19
19
|
try {
|
|
20
|
-
await this.
|
|
20
|
+
await this.replicateOnce();
|
|
21
21
|
}
|
|
22
22
|
catch (e) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
if (!this.abortController.signal.aborted) {
|
|
24
|
+
this.logger.error(`Replication error`, e);
|
|
25
|
+
if (e.cause != null) {
|
|
26
|
+
this.logger.error(`cause`, e.cause);
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Report the error if relevant, before retrying
|
|
29
|
+
container.reporter.captureException(e, {
|
|
30
|
+
metadata: {
|
|
31
|
+
replication_slot: this.slot_name
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
// This sets the retry delay
|
|
35
|
+
this.rateLimiter.reportError(e);
|
|
36
|
+
}
|
|
37
|
+
// No need to rethrow - the error is already logged, and retry behavior is the same on error
|
|
30
38
|
}
|
|
31
39
|
finally {
|
|
32
40
|
this.abortController.abort();
|
|
33
41
|
}
|
|
34
42
|
}
|
|
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
43
|
async replicateOnce() {
|
|
44
44
|
// New connections on every iteration (every error with retry),
|
|
45
45
|
// otherwise we risk repeating errors related to the connection,
|
|
@@ -72,28 +72,6 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob {
|
|
|
72
72
|
this.lastStream = stream;
|
|
73
73
|
await stream.replicate();
|
|
74
74
|
}
|
|
75
|
-
catch (e) {
|
|
76
|
-
if (this.abortController.signal.aborted) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
this.logger.error(`Replication error`, e);
|
|
80
|
-
if (e.cause != null) {
|
|
81
|
-
this.logger.error(`cause`, e.cause);
|
|
82
|
-
}
|
|
83
|
-
if (e instanceof BinlogConfigurationError) {
|
|
84
|
-
throw e;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
// Report the error if relevant, before retrying
|
|
88
|
-
container.reporter.captureException(e, {
|
|
89
|
-
metadata: {
|
|
90
|
-
replication_slot: this.slot_name
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
// This sets the retry delay
|
|
94
|
-
this.rateLimiter?.reportError(e);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
75
|
finally {
|
|
98
76
|
await connectionManager.end();
|
|
99
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BinLogReplicationJob.js","sourceRoot":"","sources":["../../src/replication/BinLogReplicationJob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,
|
|
1
|
+
{"version":3,"file":"BinLogReplicationJob.js","sourceRoot":"","sources":["../../src/replication/BinLogReplicationJob.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { NormalizedMySQLConnectionConfig } from '../types/types.js';
|
|
2
2
|
import mysqlPromise from 'mysql2/promise';
|
|
3
3
|
import mysql, { FieldPacket, RowDataPacket } from 'mysql2';
|
|
4
|
+
import { BaseObserver } from '@powersync/lib-services-framework';
|
|
4
5
|
import { ZongJi } from '@powersync/mysql-zongji';
|
|
5
|
-
export
|
|
6
|
+
export interface MySQLConnectionManagerListener {
|
|
7
|
+
onEnded(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare class MySQLConnectionManager extends BaseObserver<MySQLConnectionManagerListener> {
|
|
6
10
|
options: NormalizedMySQLConnectionConfig;
|
|
7
11
|
poolOptions: mysqlPromise.PoolOptions;
|
|
8
12
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as mysql_utils from '../utils/mysql-utils.js';
|
|
2
|
-
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import { BaseObserver, logger } from '@powersync/lib-services-framework';
|
|
3
3
|
import { ZongJi } from '@powersync/mysql-zongji';
|
|
4
|
-
export class MySQLConnectionManager {
|
|
4
|
+
export class MySQLConnectionManager extends BaseObserver {
|
|
5
5
|
options;
|
|
6
6
|
poolOptions;
|
|
7
7
|
/**
|
|
@@ -15,6 +15,7 @@ export class MySQLConnectionManager {
|
|
|
15
15
|
binlogListeners = [];
|
|
16
16
|
isClosed = false;
|
|
17
17
|
constructor(options, poolOptions) {
|
|
18
|
+
super();
|
|
18
19
|
this.options = options;
|
|
19
20
|
this.poolOptions = poolOptions;
|
|
20
21
|
// The pool is lazy - no connections are opened until a query is performed.
|
|
@@ -84,18 +85,24 @@ export class MySQLConnectionManager {
|
|
|
84
85
|
return this.promisePool.getConnection();
|
|
85
86
|
}
|
|
86
87
|
async end() {
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
if (this.isClosed) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
for (const listener of this.binlogListeners) {
|
|
92
|
+
listener.stop();
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
await this.promisePool.end();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// We don't particularly care if any errors are thrown when shutting down the pool
|
|
99
|
+
logger.warn('Error shutting down MySQL connection pool', error);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
this.isClosed = true;
|
|
103
|
+
this.iterateListeners((listener) => {
|
|
104
|
+
listener.onEnded?.();
|
|
105
|
+
});
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MySQLConnectionManager.js","sourceRoot":"","sources":["../../src/replication/MySQLConnectionManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"MySQLConnectionManager.js","sourceRoot":"","sources":["../../src/replication/MySQLConnectionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { logger } from '@powersync/lib-services-framework';
|
|
2
2
|
import { MySQLConnectionManager } from './MySQLConnectionManager.js';
|
|
3
3
|
export class MySQLConnectionManagerFactory {
|
|
4
|
-
connectionManagers;
|
|
4
|
+
connectionManagers = new Set();
|
|
5
5
|
connectionConfig;
|
|
6
6
|
constructor(connectionConfig) {
|
|
7
7
|
this.connectionConfig = connectionConfig;
|
|
8
|
-
this.connectionManagers = [];
|
|
9
8
|
}
|
|
10
9
|
create(poolOptions) {
|
|
11
10
|
const manager = new MySQLConnectionManager(this.connectionConfig, poolOptions);
|
|
12
|
-
this.connectionManagers.
|
|
11
|
+
this.connectionManagers.add(manager);
|
|
12
|
+
manager.registerListener({
|
|
13
|
+
onEnded: () => {
|
|
14
|
+
this.connectionManagers.delete(manager);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
13
17
|
return manager;
|
|
14
18
|
}
|
|
15
19
|
async shutdown() {
|
|
16
20
|
logger.info('Shutting down MySQL connection Managers...');
|
|
17
|
-
for (const manager of this.connectionManagers) {
|
|
21
|
+
for (const manager of [...this.connectionManagers]) {
|
|
18
22
|
await manager.end();
|
|
19
23
|
}
|
|
20
24
|
logger.info('MySQL connection Managers shutdown completed.');
|
|
@@ -1 +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;IACvB,kBAAkB,
|
|
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;IACvB,kBAAkB,GAAG,IAAI,GAAG,EAA0B,CAAC;IACxD,gBAAgB,CAA2B;IAE3D,YAAY,gBAA0C;QACpD,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3C,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,GAAG,CAAC,OAAO,CAAC,CAAC;QAErC,OAAO,CAAC,gBAAgB,CAAC;YACvB,OAAO,EAAE,GAAG,EAAE;gBACZ,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1C,CAAC;SACF,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAC1D,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACnD,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC;QACtB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mysql",
|
|
3
3
|
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
4
|
"types": "dist/index.d.ts",
|
|
5
|
-
"version": "0.9.
|
|
5
|
+
"version": "0.9.11",
|
|
6
6
|
"license": "FSL-1.1-ALv2",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"uri-js": "^4.4.1",
|
|
32
32
|
"uuid": "^11.1.0",
|
|
33
33
|
"@powersync/lib-services-framework": "0.7.9",
|
|
34
|
-
"@powersync/service-core": "1.16.
|
|
34
|
+
"@powersync/service-core": "1.16.2",
|
|
35
35
|
"@powersync/service-sync-rules": "0.29.6",
|
|
36
36
|
"@powersync/service-types": "0.13.1",
|
|
37
37
|
"@powersync/service-jsonbig": "0.17.12"
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/async": "^3.2.24",
|
|
41
41
|
"@types/semver": "^7.5.4",
|
|
42
|
-
"@powersync/service-core-tests": "0.12.
|
|
43
|
-
"@powersync/service-module-mongodb-storage": "0.12.
|
|
44
|
-
"@powersync/service-module-postgres-storage": "0.10.
|
|
42
|
+
"@powersync/service-core-tests": "0.12.11",
|
|
43
|
+
"@powersync/service-module-mongodb-storage": "0.12.11",
|
|
44
|
+
"@powersync/service-module-postgres-storage": "0.10.11"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -b",
|
|
@@ -27,30 +27,29 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob {
|
|
|
27
27
|
|
|
28
28
|
async replicate() {
|
|
29
29
|
try {
|
|
30
|
-
await this.
|
|
30
|
+
await this.replicateOnce();
|
|
31
31
|
} catch (e) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
if (!this.abortController.signal.aborted) {
|
|
33
|
+
this.logger.error(`Replication error`, e);
|
|
34
|
+
if (e.cause != null) {
|
|
35
|
+
this.logger.error(`cause`, e.cause);
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Report the error if relevant, before retrying
|
|
38
|
+
container.reporter.captureException(e, {
|
|
39
|
+
metadata: {
|
|
40
|
+
replication_slot: this.slot_name
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// This sets the retry delay
|
|
44
|
+
this.rateLimiter.reportError(e);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// No need to rethrow - the error is already logged, and retry behavior is the same on error
|
|
39
48
|
} finally {
|
|
40
49
|
this.abortController.abort();
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
async replicateLoop() {
|
|
45
|
-
while (!this.isStopped) {
|
|
46
|
-
await this.replicateOnce();
|
|
47
|
-
|
|
48
|
-
if (!this.isStopped) {
|
|
49
|
-
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
53
|
async replicateOnce() {
|
|
55
54
|
// New connections on every iteration (every error with retry),
|
|
56
55
|
// otherwise we risk repeating errors related to the connection,
|
|
@@ -84,27 +83,6 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob {
|
|
|
84
83
|
});
|
|
85
84
|
this.lastStream = stream;
|
|
86
85
|
await stream.replicate();
|
|
87
|
-
} catch (e) {
|
|
88
|
-
if (this.abortController.signal.aborted) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
this.logger.error(`Replication error`, e);
|
|
92
|
-
if (e.cause != null) {
|
|
93
|
-
this.logger.error(`cause`, e.cause);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (e instanceof BinlogConfigurationError) {
|
|
97
|
-
throw e;
|
|
98
|
-
} else {
|
|
99
|
-
// Report the error if relevant, before retrying
|
|
100
|
-
container.reporter.captureException(e, {
|
|
101
|
-
metadata: {
|
|
102
|
-
replication_slot: this.slot_name
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
// This sets the retry delay
|
|
106
|
-
this.rateLimiter?.reportError(e);
|
|
107
|
-
}
|
|
108
86
|
} finally {
|
|
109
87
|
await connectionManager.end();
|
|
110
88
|
}
|
|
@@ -2,10 +2,14 @@ import { NormalizedMySQLConnectionConfig } from '../types/types.js';
|
|
|
2
2
|
import mysqlPromise from 'mysql2/promise';
|
|
3
3
|
import mysql, { FieldPacket, RowDataPacket } from 'mysql2';
|
|
4
4
|
import * as mysql_utils from '../utils/mysql-utils.js';
|
|
5
|
-
import { logger } from '@powersync/lib-services-framework';
|
|
5
|
+
import { BaseObserver, logger } from '@powersync/lib-services-framework';
|
|
6
6
|
import { ZongJi } from '@powersync/mysql-zongji';
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export interface MySQLConnectionManagerListener {
|
|
9
|
+
onEnded(): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class MySQLConnectionManager extends BaseObserver<MySQLConnectionManagerListener> {
|
|
9
13
|
/**
|
|
10
14
|
* Pool that can create streamable connections
|
|
11
15
|
*/
|
|
@@ -23,6 +27,7 @@ export class MySQLConnectionManager {
|
|
|
23
27
|
public options: NormalizedMySQLConnectionConfig,
|
|
24
28
|
public poolOptions: mysqlPromise.PoolOptions
|
|
25
29
|
) {
|
|
30
|
+
super();
|
|
26
31
|
// The pool is lazy - no connections are opened until a query is performed.
|
|
27
32
|
this.pool = mysql_utils.createPool(options, poolOptions);
|
|
28
33
|
this.promisePool = this.pool.promise();
|
|
@@ -98,18 +103,24 @@ export class MySQLConnectionManager {
|
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
async end(): Promise<void> {
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
if (this.isClosed) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const listener of this.binlogListeners) {
|
|
111
|
+
listener.stop();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
await this.promisePool.end();
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// We don't particularly care if any errors are thrown when shutting down the pool
|
|
118
|
+
logger.warn('Error shutting down MySQL connection pool', error);
|
|
119
|
+
} finally {
|
|
120
|
+
this.isClosed = true;
|
|
121
|
+
this.iterateListeners((listener) => {
|
|
122
|
+
listener.onEnded?.();
|
|
123
|
+
});
|
|
113
124
|
}
|
|
114
125
|
}
|
|
115
126
|
}
|
|
@@ -4,23 +4,28 @@ import { MySQLConnectionManager } from './MySQLConnectionManager.js';
|
|
|
4
4
|
import { ResolvedConnectionConfig } from '../types/types.js';
|
|
5
5
|
|
|
6
6
|
export class MySQLConnectionManagerFactory {
|
|
7
|
-
private readonly connectionManagers
|
|
7
|
+
private readonly connectionManagers = new Set<MySQLConnectionManager>();
|
|
8
8
|
public readonly connectionConfig: ResolvedConnectionConfig;
|
|
9
9
|
|
|
10
10
|
constructor(connectionConfig: ResolvedConnectionConfig) {
|
|
11
11
|
this.connectionConfig = connectionConfig;
|
|
12
|
-
this.connectionManagers = [];
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
create(poolOptions: mysql.PoolOptions) {
|
|
16
15
|
const manager = new MySQLConnectionManager(this.connectionConfig, poolOptions);
|
|
17
|
-
this.connectionManagers.
|
|
16
|
+
this.connectionManagers.add(manager);
|
|
17
|
+
|
|
18
|
+
manager.registerListener({
|
|
19
|
+
onEnded: () => {
|
|
20
|
+
this.connectionManagers.delete(manager);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
18
23
|
return manager;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
async shutdown() {
|
|
22
27
|
logger.info('Shutting down MySQL connection Managers...');
|
|
23
|
-
for (const manager of this.connectionManagers) {
|
|
28
|
+
for (const manager of [...this.connectionManagers]) {
|
|
24
29
|
await manager.end();
|
|
25
30
|
}
|
|
26
31
|
logger.info('MySQL connection Managers shutdown completed.');
|