Skip to content

Commit f437efb

Browse files
authored
feat: dynamic retrieval of database connection configuration (#110)
test fix: Node.js >= 20 will return localhost ipv6 address
1 parent 7cdf72a commit f437efb

File tree

7 files changed

+127
-5
lines changed

7 files changed

+127
-5
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
"devDependencies": {
2323
"@eggjs/tsconfig": "^1.3.2",
2424
"@types/mocha": "^10.0.1",
25-
"@types/node": "^18.14.6",
25+
"@types/node": "^20.2.5",
2626
"egg-bin": "^6.1.2",
2727
"eslint": "^8.29.0",
2828
"eslint-config-egg": "^12.1.0",
2929
"git-contributor": "^2.0.0",
3030
"mm": "^3.3.0",
31-
"typescript": "^4.9.5"
31+
"typescript": "^5.1.3"
3232
},
3333
"homepage": "https://github.com/ali-sdk/ali-rds",
3434
"repository": {

src/PoolConfig.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import MySQLPoolConfig from 'mysql/lib/PoolConfig';
2+
import type { PoolConfig, ConnectionConfig } from 'mysql';
3+
import type { GetConnectionConfig } from './types';
4+
5+
export class RDSPoolConfig extends MySQLPoolConfig {
6+
#getConnectionConfig: GetConnectionConfig;
7+
8+
constructor(options: PoolConfig, getConnectionConfig: GetConnectionConfig) {
9+
super(options);
10+
this.#getConnectionConfig = getConnectionConfig;
11+
}
12+
13+
newConnectionConfig(): ConnectionConfig {
14+
return {
15+
...super.newConnectionConfig(),
16+
...this.#getConnectionConfig(),
17+
};
18+
}
19+
}

src/client.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { PoolConnectionPromisify, RDSClientOptions, TransactionContext, Tra
66
import { Operator } from './operator';
77
import { RDSConnection } from './connection';
88
import { RDSTransaction } from './transaction';
9+
import { RDSPoolConfig } from './PoolConfig';
910
import literals from './literals';
1011

1112
interface PoolPromisify extends Omit<Pool, 'query'> {
@@ -35,7 +36,18 @@ export class RDSClient extends Operator {
3536
constructor(options: RDSClientOptions) {
3637
super();
3738
const { connectionStorage, connectionStorageKey, ...mysqlOptions } = options;
38-
this.#pool = mysql.createPool(mysqlOptions) as unknown as PoolPromisify;
39+
// get connection options from getConnectionConfig method every time
40+
if (mysqlOptions.getConnectionConfig) {
41+
// eslint-disable-next-line @typescript-eslint/no-var-requires
42+
const Pool = require('mysql/lib/Pool');
43+
this.#pool = new Pool({
44+
config: new RDSPoolConfig(mysqlOptions, mysqlOptions.getConnectionConfig),
45+
});
46+
// override _needsChangeUser to return false
47+
(this.#pool as any)._needsChangeUser = () => false;
48+
} else {
49+
this.#pool = mysql.createPool(mysqlOptions) as unknown as PoolPromisify;
50+
}
3951
[
4052
'query',
4153
'getConnection',

src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { AsyncLocalStorage } from 'async_hooks';
2-
import type { PoolConnection, PoolConfig } from 'mysql';
2+
import type { PoolConnection, PoolConfig, ConnectionConfig } from 'mysql';
33
import { RDSTransaction } from './transaction';
44

5+
export type GetConnectionConfig = () => ConnectionConfig;
6+
57
export interface RDSClientOptions extends PoolConfig {
68
connectionStorageKey?: string;
79
connectionStorage?: AsyncLocalStorage<Record<PropertyKey, RDSTransaction>>;
10+
getConnectionConfig?: GetConnectionConfig
811
}
912

1013
export interface PoolConnectionPromisify extends Omit<PoolConnection, 'query'> {

test/PoolConfig.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { strict as assert } from 'node:assert';
2+
import fs from 'node:fs/promises';
3+
import path from 'node:path';
4+
import mm from 'mm';
5+
import config from './config';
6+
import { RDSClient } from '../src/client';
7+
8+
describe('test/PoolConfig.test.ts', () => {
9+
const prefix = 'prefix-PoolConfig' + process.version + '-';
10+
const table = 'ali-sdk-test-user';
11+
let db: RDSClient;
12+
let index = 0;
13+
let newConnectionCount = 0;
14+
before(async () => {
15+
db = new RDSClient({
16+
// test getConnectionConfig
17+
connectionLimit: 2,
18+
getConnectionConfig() {
19+
console.log('get connection config index: %d', ++index);
20+
return config;
21+
},
22+
});
23+
db.pool.on('acquire', conn => {
24+
console.log('acquire connection %o', conn.threadId);
25+
});
26+
db.pool.on('connection', conn => {
27+
newConnectionCount++;
28+
console.log('new connection %o', conn.threadId);
29+
});
30+
db.pool.on('enqueue', () => {
31+
console.log('Waiting for available connection slot');
32+
});
33+
db.pool.on('release', conn => {
34+
console.log('release connection %o', conn.threadId);
35+
});
36+
37+
try {
38+
const sql = await fs.readFile(path.join(__dirname, 'rds_init.sql'), 'utf-8');
39+
await db.query(sql);
40+
} catch (err) {
41+
console.log('init table error: %s', err);
42+
}
43+
await db.query('delete from ?? where name like ?', [ table, prefix + '%' ]);
44+
});
45+
46+
after(async () => {
47+
return await db.end();
48+
});
49+
50+
afterEach(() => {
51+
mm.restore();
52+
});
53+
54+
describe('new RDSClient(options.getConnectionConfig)', () => {
55+
it('should get connection config from newConnectionConfig()', async () => {
56+
assert.equal(db.pool.config.connectionConfig.database, undefined);
57+
assert.equal(index, 1);
58+
assert.equal((db.pool.config as any).newConnectionConfig().database, 'test');
59+
assert.equal(index, 2);
60+
});
61+
62+
it('should connect rds success', async () => {
63+
const rows = await db.query('show tables');
64+
// console.log(rows);
65+
assert(rows);
66+
assert(Array.isArray(rows));
67+
assert.equal(index, 2);
68+
const results = await Promise.all([
69+
db.query('show tables'),
70+
db.query('show tables'),
71+
db.query('show tables'),
72+
]);
73+
assert.equal(results.length, 3);
74+
assert(results[0]);
75+
assert(Array.isArray(results[0]));
76+
assert(results[1]);
77+
assert(Array.isArray(results[1]));
78+
assert(results[2]);
79+
assert(Array.isArray(results[2]));
80+
assert.equal(index, 3);
81+
assert.equal(newConnectionCount, 2);
82+
});
83+
});
84+
});

test/client.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ describe('test/client.test.ts', () => {
172172
it('should beginTransaction error', async () => {
173173
const failDB = new RDSClient({
174174
port: 12312,
175+
host: '127.0.0.1',
175176
});
176177
await assert.rejects(async () => {
177178
await failDB.beginTransaction();
@@ -339,6 +340,7 @@ describe('test/client.test.ts', () => {
339340
it('should beginTransactionScope() error', async () => {
340341
const failDB = new RDSClient({
341342
port: 12312,
343+
host: '127.0.0.1',
342344
});
343345
await assert.rejects(async () => {
344346
await failDB.beginTransactionScope(async () => {
@@ -1321,6 +1323,7 @@ describe('test/client.test.ts', () => {
13211323
it('should throw error when mysql connect fail', async () => {
13221324
const db = new RDSClient({
13231325
port: 33061,
1326+
host: '127.0.0.1',
13241327
});
13251328
try {
13261329
await db.getConnection();

test/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export default {
2-
host: 'localhost',
2+
// host: 'localhost',
3+
host: '127.0.0.1',
34
port: 3306,
45
user: 'root',
56
password: '',

0 commit comments

Comments
 (0)