diff --git a/dist/config/rds.d.ts b/dist/config/rds.d.ts index 1f950a82..ae60c260 100644 --- a/dist/config/rds.d.ts +++ b/dist/config/rds.d.ts @@ -10,6 +10,7 @@ declare const _default: { connectionLimit: number; waitForConnections: boolean; queueLimit: number; + usePool: boolean; }; }; }; diff --git a/dist/config/rds.js b/dist/config/rds.js index 50865d41..fdcfb90f 100644 --- a/dist/config/rds.js +++ b/dist/config/rds.js @@ -18,6 +18,10 @@ export default { queueLimit: Number( process.env.LESGO_AWS_RDS_AURORA_MYSQL_PROXY_QUEUE_LIMIT || '0' ), + usePool: + process.env.LESGO_AWS_RDS_AURORA_MYSQL_USE_POOL === 'false' + ? false + : true, }, }, }, diff --git a/dist/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.d.ts b/dist/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.d.ts new file mode 100644 index 00000000..c672e26b --- /dev/null +++ b/dist/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.d.ts @@ -0,0 +1,2 @@ +declare const disconnectMySQLClient: () => Promise; +export default disconnectMySQLClient; diff --git a/dist/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.js b/dist/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.js new file mode 100644 index 00000000..795c08f3 --- /dev/null +++ b/dist/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.js @@ -0,0 +1,59 @@ +var __awaiter = + (this && this.__awaiter) || + function (thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function (resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator['throw'](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done + ? resolve(result.value) + : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; +import { logger } from '../../utils'; +import { singleton } from './getMySQLProxyClient'; +const FILE = 'lesgo.services.RDSAuroraMySQLProxyService.disconnectMySQLClient'; +const disconnectMySQLClient = () => + __awaiter(void 0, void 0, void 0, function* () { + const singletonConns = Object.keys(singleton); + if (singletonConns.length === 0) { + logger.debug(`${FILE}::NO_CONNECTIONS_TO_DISCONNECT`); + return; + } + logger.debug(`${FILE}::PREPARING_TO_DISCONNECT`, { + singletonConns, + }); + singletonConns.forEach(singletonConn => + __awaiter(void 0, void 0, void 0, function* () { + try { + yield singleton[singletonConn].end(); + delete singleton[singletonConn]; + logger.debug(`${FILE}::COMPLETED`, { singletonConn }); + } catch (err) { + logger.error(`${FILE}::ERROR`, { singletonConn, err }); + } + }) + ); + }); +export default disconnectMySQLClient; diff --git a/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.d.ts b/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.d.ts index 04fb6a33..e5a8b8ad 100644 --- a/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.d.ts +++ b/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.d.ts @@ -1,8 +1,8 @@ -import { Pool, ConnectionOptions } from 'mysql2/promise'; +import { Pool, Connection, ConnectionOptions } from 'mysql2/promise'; import { RDSAuroraMySQLProxyClientOptions } from '../../types/aws'; export interface Singleton { - [key: string]: Pool; + [key: string]: Pool | Connection; } export declare const singleton: Singleton; -declare const getMySQLProxyClient: (connOptions?: ConnectionOptions, clientOpts?: RDSAuroraMySQLProxyClientOptions) => Promise; +declare const getMySQLProxyClient: (connOptions?: ConnectionOptions, clientOpts?: RDSAuroraMySQLProxyClientOptions) => Promise; export default getMySQLProxyClient; diff --git a/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.js b/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.js index e94eacfc..65dcff0d 100644 --- a/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.js +++ b/dist/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.js @@ -31,7 +31,7 @@ var __awaiter = step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; -import { createPool } from 'mysql2/promise'; +import { createPool, createConnection } from 'mysql2/promise'; import { logger, isEmpty, validateFields } from '../../utils'; import { rds as rdsConfig } from '../../config'; import { getSecretValue } from '../../utils/secretsmanager'; @@ -39,11 +39,13 @@ const FILE = 'lesgo.services.RDSAuroraMySQLProxyService.getMySQLProxyClient'; export const singleton = {}; const getMySQLProxyClient = (connOptions, clientOpts) => __awaiter(void 0, void 0, void 0, function* () { + var _a; const options = validateFields(clientOpts || {}, [ { key: 'region', type: 'string', required: false }, { key: 'singletonConn', type: 'string', required: false }, { key: 'dbCredentialsSecretId', type: 'string', required: false }, { key: 'databaseName', type: 'string', required: false }, + { key: 'usePool', type: 'boolean', required: false }, ]); const region = options.region || rdsConfig.aurora.mysql.region; const singletonConn = options.singletonConn || 'default'; @@ -52,6 +54,10 @@ const getMySQLProxyClient = (connOptions, clientOpts) => rdsConfig.aurora.mysql.proxy.dbCredentialsSecretId; const databaseName = options.databaseName || rdsConfig.aurora.mysql.databaseName; + const usePool = + (_a = options.usePool) !== null && _a !== void 0 + ? _a + : rdsConfig.aurora.mysql.proxy.usePool; if (!isEmpty(singleton[singletonConn])) { logger.debug(`${FILE}::REUSE_RDS_CONNECTION`); return singleton[singletonConn]; @@ -64,25 +70,37 @@ const getMySQLProxyClient = (connOptions, clientOpts) => singletonConn, } ); + const poolOptions = { + connectionLimit: rdsConfig.aurora.mysql.proxy.connectionLimit || 10, + waitForConnections: + rdsConfig.aurora.mysql.proxy.waitForConnections || true, + queueLimit: rdsConfig.aurora.mysql.proxy.queueLimit || 0, + }; const connOpts = Object.assign( - { - host: rdsConfig.aurora.mysql.proxy.host || dbCredentials.host, - database: databaseName, - port: rdsConfig.aurora.mysql.proxy.port || dbCredentials.port || 3306, - connectionLimit: rdsConfig.aurora.mysql.proxy.connectionLimit || 10, - waitForConnections: - rdsConfig.aurora.mysql.proxy.waitForConnections || true, - queueLimit: rdsConfig.aurora.mysql.proxy.queueLimit || 0, - }, + Object.assign( + { + host: rdsConfig.aurora.mysql.proxy.host || dbCredentials.host, + database: databaseName, + port: rdsConfig.aurora.mysql.proxy.port || dbCredentials.port || 3306, + }, + usePool ? poolOptions : {} + ), connOptions ); logger.debug(`${FILE}::CONN_OPTS`, { connOpts }); - const dbPool = createPool( - Object.assign( - { user: dbCredentials.username, password: dbCredentials.password }, - connOpts - ) - ); + const dbPool = usePool + ? createPool( + Object.assign( + { user: dbCredentials.username, password: dbCredentials.password }, + connOpts + ) + ) + : yield createConnection( + Object.assign( + { user: dbCredentials.username, password: dbCredentials.password }, + connOpts + ) + ); singleton[singletonConn] = dbPool; logger.debug(`${FILE}::NEW_RDS_CONNECTION`); return dbPool; diff --git a/dist/services/RDSAuroraMySQLProxyService/index.d.ts b/dist/services/RDSAuroraMySQLProxyService/index.d.ts index 0e7ffd99..dc778e5d 100644 --- a/dist/services/RDSAuroraMySQLProxyService/index.d.ts +++ b/dist/services/RDSAuroraMySQLProxyService/index.d.ts @@ -1,3 +1,4 @@ export { default as disconnectMySQLProxyClient } from './disconnectMySQLProxyClient'; +export { default as disconnectMySQLClient } from './disconnectMySQLClient'; export { default as getMySQLProxyClient } from './getMySQLProxyClient'; export { default as query } from './query'; diff --git a/dist/services/RDSAuroraMySQLProxyService/index.js b/dist/services/RDSAuroraMySQLProxyService/index.js index 0e7ffd99..dc778e5d 100644 --- a/dist/services/RDSAuroraMySQLProxyService/index.js +++ b/dist/services/RDSAuroraMySQLProxyService/index.js @@ -1,3 +1,4 @@ export { default as disconnectMySQLProxyClient } from './disconnectMySQLProxyClient'; +export { default as disconnectMySQLClient } from './disconnectMySQLClient'; export { default as getMySQLProxyClient } from './getMySQLProxyClient'; export { default as query } from './query'; diff --git a/dist/types/aws.d.ts b/dist/types/aws.d.ts index 7be9146e..d6601a4a 100644 --- a/dist/types/aws.d.ts +++ b/dist/types/aws.d.ts @@ -5,4 +5,5 @@ export interface ClientOptions { export interface RDSAuroraMySQLProxyClientOptions extends ClientOptions { dbCredentialsSecretId?: string; databaseName?: string; + usePool?: boolean; } diff --git a/dist/utils/db/mysql/proxy/disconnectMySQLDb.d.ts b/dist/utils/db/mysql/proxy/disconnectMySQLDb.d.ts new file mode 100644 index 00000000..45373384 --- /dev/null +++ b/dist/utils/db/mysql/proxy/disconnectMySQLDb.d.ts @@ -0,0 +1,5 @@ +/** + * @deprecated Disconnect db is no longer to be used due to the use of ConnectionPool + */ +declare const disconnectMySQLDb: () => Promise; +export default disconnectMySQLDb; diff --git a/dist/utils/db/mysql/proxy/disconnectMySQLDb.js b/dist/utils/db/mysql/proxy/disconnectMySQLDb.js new file mode 100644 index 00000000..d1f50523 --- /dev/null +++ b/dist/utils/db/mysql/proxy/disconnectMySQLDb.js @@ -0,0 +1,8 @@ +import { disconnectMySQLClient } from '../../../../services/RDSAuroraMySQLProxyService'; +/** + * @deprecated Disconnect db is no longer to be used due to the use of ConnectionPool + */ +const disconnectMySQLDb = () => { + return disconnectMySQLClient(); +}; +export default disconnectMySQLDb; diff --git a/dist/utils/db/mysql/proxy/getClient.d.ts b/dist/utils/db/mysql/proxy/getClient.d.ts index 22e462c6..41aadc08 100644 --- a/dist/utils/db/mysql/proxy/getClient.d.ts +++ b/dist/utils/db/mysql/proxy/getClient.d.ts @@ -1,4 +1,4 @@ import { ConnectionOptions } from 'mysql2/promise'; import { RDSAuroraMySQLProxyClientOptions } from '../../../../types/aws'; -declare const getClient: (connOptions?: ConnectionOptions, clientOpts?: RDSAuroraMySQLProxyClientOptions) => Promise; +declare const getClient: (connOptions?: ConnectionOptions, clientOpts?: RDSAuroraMySQLProxyClientOptions) => Promise; export default getClient; diff --git a/dist/utils/db/mysql/proxy/index.d.ts b/dist/utils/db/mysql/proxy/index.d.ts index 84712654..18af3562 100644 --- a/dist/utils/db/mysql/proxy/index.d.ts +++ b/dist/utils/db/mysql/proxy/index.d.ts @@ -1,3 +1,4 @@ export { default as disconnectDb } from './disconnectDb'; +export { default as disconnectMySQLDb } from './disconnectMySQLDb'; export { default as getClient } from './getClient'; export { default as query } from './query'; diff --git a/dist/utils/db/mysql/proxy/index.js b/dist/utils/db/mysql/proxy/index.js index 84712654..18af3562 100644 --- a/dist/utils/db/mysql/proxy/index.js +++ b/dist/utils/db/mysql/proxy/index.js @@ -1,3 +1,4 @@ export { default as disconnectDb } from './disconnectDb'; +export { default as disconnectMySQLDb } from './disconnectMySQLDb'; export { default as getClient } from './getClient'; export { default as query } from './query'; diff --git a/src/config/rds.ts b/src/config/rds.ts index 5f751404..dd9722c7 100644 --- a/src/config/rds.ts +++ b/src/config/rds.ts @@ -19,6 +19,10 @@ export default { queueLimit: Number( process.env.LESGO_AWS_RDS_AURORA_MYSQL_PROXY_QUEUE_LIMIT || '0' ), + usePool: + process.env.LESGO_AWS_RDS_AURORA_MYSQL_USE_POOL === 'false' + ? false + : true, }, }, }, diff --git a/src/services/RDSAuroraMySQLProxyService/__tests__/disconnectMySQLClient.test.ts b/src/services/RDSAuroraMySQLProxyService/__tests__/disconnectMySQLClient.test.ts new file mode 100644 index 00000000..9329819f --- /dev/null +++ b/src/services/RDSAuroraMySQLProxyService/__tests__/disconnectMySQLClient.test.ts @@ -0,0 +1,44 @@ +import { ConnectionOptions } from 'mysql2/promise'; +import getMySQLProxyClient, { singleton } from '../getMySQLProxyClient'; +import { disconnectMySQLClient } from '../../RDSAuroraMySQLProxyService'; +import { query } from '../../RDSAuroraMySQLProxyService'; +import { getSecretValue } from '../../../utils/secretsmanager'; + +jest.mock('../../../utils/secretsmanager'); + +describe('query', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('clear all singleton', async () => { + const connOptions: ConnectionOptions = { + host: 'localhost', + user: 'root', + password: 'password', + database: 'test', + }; + const clientOpts = { + region: 'us-west-2', + endpoint: 'http://localhost:8000', + }; + (getSecretValue as jest.Mock).mockResolvedValue(connOptions); + + await Promise.all([ + getMySQLProxyClient(connOptions, clientOpts), + getMySQLProxyClient(connOptions, { + ...clientOpts, + singletonConn: 'test1', + }), + ]); + + expect(Object.keys(singleton).length).toBe(2); + + const spy = jest.spyOn(Object.values(singleton)[0], 'end'); + + await disconnectMySQLClient(); + + expect(Object.keys(singleton).length).toBe(0); + expect(spy).toHaveBeenCalled(); + }); +}); diff --git a/src/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.ts b/src/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.ts new file mode 100644 index 00000000..2862d973 --- /dev/null +++ b/src/services/RDSAuroraMySQLProxyService/disconnectMySQLClient.ts @@ -0,0 +1,26 @@ +import { logger } from '../../utils'; +import { singleton } from './getMySQLProxyClient'; + +const FILE = 'lesgo.services.RDSAuroraMySQLProxyService.disconnectMySQLClient'; + +const disconnectMySQLClient = async () => { + const singletonConns = Object.keys(singleton); + if (singletonConns.length === 0) { + logger.debug(`${FILE}::NO_CONNECTIONS_TO_DISCONNECT`); + return; + } + logger.debug(`${FILE}::PREPARING_TO_DISCONNECT`, { + singletonConns, + }); + singletonConns.forEach(async singletonConn => { + try { + await singleton[singletonConn].end(); + delete singleton[singletonConn]; + logger.debug(`${FILE}::COMPLETED`, { singletonConn }); + } catch (err) { + logger.error(`${FILE}::ERROR`, { singletonConn, err }); + } + }); +}; + +export default disconnectMySQLClient; diff --git a/src/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.ts b/src/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.ts index 811aed8b..d49fa4f5 100644 --- a/src/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.ts +++ b/src/services/RDSAuroraMySQLProxyService/getMySQLProxyClient.ts @@ -1,4 +1,10 @@ -import { Pool, ConnectionOptions, createPool } from 'mysql2/promise'; +import { + Pool, + Connection, + ConnectionOptions, + createPool, + createConnection, +} from 'mysql2/promise'; import { logger, isEmpty, validateFields } from '../../utils'; import { rds as rdsConfig } from '../../config'; import { getSecretValue } from '../../utils/secretsmanager'; @@ -7,7 +13,7 @@ import { RDSAuroraMySQLProxyClientOptions } from '../../types/aws'; const FILE = 'lesgo.services.RDSAuroraMySQLProxyService.getMySQLProxyClient'; export interface Singleton { - [key: string]: Pool; + [key: string]: Pool | Connection; } export const singleton: Singleton = {}; @@ -21,6 +27,7 @@ const getMySQLProxyClient = async ( { key: 'singletonConn', type: 'string', required: false }, { key: 'dbCredentialsSecretId', type: 'string', required: false }, { key: 'databaseName', type: 'string', required: false }, + { key: 'usePool', type: 'boolean', required: false }, ]); const region = options.region || rdsConfig.aurora.mysql.region; @@ -29,6 +36,7 @@ const getMySQLProxyClient = async ( rdsConfig.aurora.mysql.proxy.dbCredentialsSecretId) as string; const databaseName = options.databaseName || rdsConfig.aurora.mysql.databaseName; + const usePool = options.usePool ?? rdsConfig.aurora.mysql.proxy.usePool; if (!isEmpty(singleton[singletonConn])) { logger.debug(`${FILE}::REUSE_RDS_CONNECTION`); @@ -40,22 +48,32 @@ const getMySQLProxyClient = async ( singletonConn, }); + const poolOptions = { + connectionLimit: rdsConfig.aurora.mysql.proxy.connectionLimit || 10, + waitForConnections: rdsConfig.aurora.mysql.proxy.waitForConnections || true, + queueLimit: rdsConfig.aurora.mysql.proxy.queueLimit || 0, + }; + const connOpts = { host: rdsConfig.aurora.mysql.proxy.host || dbCredentials.host, database: databaseName, port: rdsConfig.aurora.mysql.proxy.port || dbCredentials.port || 3306, - connectionLimit: rdsConfig.aurora.mysql.proxy.connectionLimit || 10, - waitForConnections: rdsConfig.aurora.mysql.proxy.waitForConnections || true, - queueLimit: rdsConfig.aurora.mysql.proxy.queueLimit || 0, + ...(usePool ? poolOptions : {}), ...connOptions, }; logger.debug(`${FILE}::CONN_OPTS`, { connOpts }); - const dbPool = createPool({ - user: dbCredentials.username, - password: dbCredentials.password, - ...connOpts, - }); + const dbPool = usePool + ? createPool({ + user: dbCredentials.username, + password: dbCredentials.password, + ...connOpts, + }) + : await createConnection({ + user: dbCredentials.username, + password: dbCredentials.password, + ...connOpts, + }); singleton[singletonConn] = dbPool; logger.debug(`${FILE}::NEW_RDS_CONNECTION`); diff --git a/src/services/RDSAuroraMySQLProxyService/index.ts b/src/services/RDSAuroraMySQLProxyService/index.ts index 0e7ffd99..dc778e5d 100644 --- a/src/services/RDSAuroraMySQLProxyService/index.ts +++ b/src/services/RDSAuroraMySQLProxyService/index.ts @@ -1,3 +1,4 @@ export { default as disconnectMySQLProxyClient } from './disconnectMySQLProxyClient'; +export { default as disconnectMySQLClient } from './disconnectMySQLClient'; export { default as getMySQLProxyClient } from './getMySQLProxyClient'; export { default as query } from './query'; diff --git a/src/types/aws.ts b/src/types/aws.ts index 27f17b23..1ae2c198 100644 --- a/src/types/aws.ts +++ b/src/types/aws.ts @@ -6,4 +6,5 @@ export interface ClientOptions { export interface RDSAuroraMySQLProxyClientOptions extends ClientOptions { dbCredentialsSecretId?: string; databaseName?: string; + usePool?: boolean; } diff --git a/src/utils/db/mysql/proxy/disconnectMySQLDb.ts b/src/utils/db/mysql/proxy/disconnectMySQLDb.ts new file mode 100644 index 00000000..f31307d7 --- /dev/null +++ b/src/utils/db/mysql/proxy/disconnectMySQLDb.ts @@ -0,0 +1,10 @@ +import { disconnectMySQLClient } from '../../../../services/RDSAuroraMySQLProxyService'; + +/** + * @deprecated Disconnect db is no longer to be used due to the use of ConnectionPool + */ +const disconnectMySQLDb = () => { + return disconnectMySQLClient(); +}; + +export default disconnectMySQLDb; diff --git a/src/utils/db/mysql/proxy/index.ts b/src/utils/db/mysql/proxy/index.ts index 84712654..18af3562 100644 --- a/src/utils/db/mysql/proxy/index.ts +++ b/src/utils/db/mysql/proxy/index.ts @@ -1,3 +1,4 @@ export { default as disconnectDb } from './disconnectDb'; +export { default as disconnectMySQLDb } from './disconnectMySQLDb'; export { default as getClient } from './getClient'; export { default as query } from './query';