diff --git a/common/lib/database_dialect/database_dialect.ts b/common/lib/database_dialect/database_dialect.ts index f4bfb212..a631f490 100644 --- a/common/lib/database_dialect/database_dialect.ts +++ b/common/lib/database_dialect/database_dialect.ts @@ -18,8 +18,6 @@ import { HostListProvider } from "../host_list_provider/host_list_provider"; import { HostListProviderService } from "../host_list_provider_service"; import { ClientWrapper } from "../client_wrapper"; import { FailoverRestriction } from "../plugins/failover/failover_restriction"; -import { AwsPoolClient } from "../aws_pool_client"; -import { AwsPoolConfig } from "../aws_pool_config"; import { ErrorHandler } from "../error_handler"; export enum DatabaseType { diff --git a/common/lib/driver_dialect/driver_dialect.ts b/common/lib/driver_dialect/driver_dialect.ts index b52e5260..c824dece 100644 --- a/common/lib/driver_dialect/driver_dialect.ts +++ b/common/lib/driver_dialect/driver_dialect.ts @@ -27,4 +27,6 @@ export interface DriverDialect { preparePoolClientProperties(props: Map, poolConfig: AwsPoolConfig | undefined): any; getAwsPoolClient(props: any): AwsPoolClient; + + setKeepAliveProperties(props: Map, keepAliveProps: any): void; } diff --git a/common/lib/wrapper_property.ts b/common/lib/wrapper_property.ts index f3000d36..bc3c4999 100644 --- a/common/lib/wrapper_property.ts +++ b/common/lib/wrapper_property.ts @@ -335,6 +335,12 @@ export class WrapperProperties { 600_000 // 10 min ); + static readonly KEEPALIVE_PROPERTIES = new WrapperProperty>( + "wrapperKeepAliveProperties", + "Map containing any keepAlive properties that the target driver accepts in the client configuration.", + null + ); + static removeWrapperProperties(props: Map): any { const persistingProperties = [ WrapperProperties.USER.name, diff --git a/docs/using-the-nodejs-wrapper/UsingTheNodejsWrapper.md b/docs/using-the-nodejs-wrapper/UsingTheNodejsWrapper.md index 604f5164..1f83ebb1 100644 --- a/docs/using-the-nodejs-wrapper/UsingTheNodejsWrapper.md +++ b/docs/using-the-nodejs-wrapper/UsingTheNodejsWrapper.md @@ -40,17 +40,18 @@ To enable logging when using the AWS Advanced NodeJS Wrapper, use the `LOG_LEVEL These parameters are applicable to any instance of the AWS Advanced NodeJS Wrapper. -| Parameter | Value | Required | Description | Default Value | -| ------------------------------ | --------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `host` | `string` | No | Database host. | `null` | -| `database` | `string` | No | Database name. | `null` | -| `user` | `string` | No | Database username. | `null` | -| `password` | `string` | No | Database password. | `null` | -| `transferSessionStateOnSwitch` | `boolean` | No | Enables transferring the session state to a new connection. | `true` | -| `resetSessionStateOnClose` | `boolean` | No | Enables resetting the session state before closing connection. | `true` | -| `enableGreenHostReplacement` | `boolean` | No | Enables replacing a green node host name with the original host name when the green host DNS doesn't exist anymore after a blue/green switchover. Refer to [Overview of Amazon RDS Blue/Green Deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html) for more details about green and blue nodes. | `false` | -| `clusterInstanceHostPattern` | `string` | If connecting using an IP address or custom domain URL: Yes

Otherwise: No | This parameter is not required unless connecting to an AWS RDS cluster via an IP address or custom domain URL. In those cases, this parameter specifies the cluster instance DNS pattern that will be used to build a complete instance endpoint. A "?" character in this pattern should be used as a placeholder for the DB instance identifiers of the instances in the cluster. See [here](#host-pattern) for more information.

Example: `?.my-domain.com`, `any-subdomain.?.my-domain.com`

Use case Example: If your cluster instance endpoints follow this pattern:`instanceIdentifier1.customHost`, `instanceIdentifier2.customHost`, etc. and you want your initial connection to be to `customHost:1234`, then your client configuration should look like this: `{ host: "customHost", port: 1234, database: "test", clusterInstanceHostPattern: "?.customHost" }` | If the provided host is not an IP address or custom domain, the NodeJS Wrapper will automatically acquire the cluster instance host pattern from the customer-provided host. | -| `mysqlQueryTimeout` | `number` | No | Query timeout in milliseconds. This is only applicable when using the AwsMySQLClient. To set query timeout for the AwsPGClient, please use the built-in `query_timeout` parameter. See the `node-postgres` [documentation](https://node-postgres.com/apis/client) for more details.. | 20000 | +| Parameter | Value | Required | Description | Default Value | +| ------------------------------ | ------------------ | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `host` | `string` | No | Database host. | `null` | +| `database` | `string` | No | Database name. | `null` | +| `user` | `string` | No | Database username. | `null` | +| `password` | `string` | No | Database password. | `null` | +| `transferSessionStateOnSwitch` | `boolean` | No | Enables transferring the session state to a new connection. | `true` | +| `resetSessionStateOnClose` | `boolean` | No | Enables resetting the session state before closing connection. | `true` | +| `enableGreenHostReplacement` | `boolean` | No | Enables replacing a green node host name with the original host name when the green host DNS doesn't exist anymore after a blue/green switchover. Refer to [Overview of Amazon RDS Blue/Green Deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html) for more details about green and blue nodes. | `false` | +| `clusterInstanceHostPattern` | `string` | If connecting using an IP address or custom domain URL: Yes

Otherwise: No | This parameter is not required unless connecting to an AWS RDS cluster via an IP address or custom domain URL. In those cases, this parameter specifies the cluster instance DNS pattern that will be used to build a complete instance endpoint. A "?" character in this pattern should be used as a placeholder for the DB instance identifiers of the instances in the cluster. See [here](#host-pattern) for more information.

Example: `?.my-domain.com`, `any-subdomain.?.my-domain.com`

Use case Example: If your cluster instance endpoints follow this pattern:`instanceIdentifier1.customHost`, `instanceIdentifier2.customHost`, etc. and you want your initial connection to be to `customHost:1234`, then your client configuration should look like this: `{ host: "customHost", port: 1234, database: "test", clusterInstanceHostPattern: "?.customHost" }` | If the provided host is not an IP address or custom domain, the NodeJS Wrapper will automatically acquire the cluster instance host pattern from the customer-provided host. | +| `mysqlQueryTimeout` | `number` | No | Query timeout in milliseconds. This is only applicable when using the AwsMySQLClient. To set query timeout for the AwsPGClient, please use the built-in `query_timeout` parameter. See the `node-postgres` [documentation](https://node-postgres.com/apis/client) for more details.. | 20000 | +| `wrapperKeepAliveProperties` | `Map` | No | If the underlying target driver has keepAlive properties available, properties within this map will be applied to the underlying target driver's client configuration. For example, the node-postgres driver's `keepAlive` and `keepAliveInitialDelayMillis` properties can be configured by setting this property in the client configuration: `{ wrapperKeepAliveProperties: new Map([["keepAlive", true], ["keepAliveInitialDelayMillis", 1234]]) }`.

Currently supported drivers: node-postgres | `null` | ## Host Pattern diff --git a/mysql/lib/client.ts b/mysql/lib/client.ts index 2955c17e..1c47e02b 100644 --- a/mysql/lib/client.ts +++ b/mysql/lib/client.ts @@ -31,7 +31,6 @@ import { ClientUtils } from "../../common/lib/utils/client_utils"; import { RdsMultiAZMySQLDatabaseDialect } from "./dialect/rds_multi_az_mysql_database_dialect"; import { TelemetryTraceLevel } from "../../common/lib/utils/telemetry/telemetry_trace_level"; import { MySQL2DriverDialect } from "./dialect/mysql2_driver_dialect"; -import { PluginManager } from "../../common/lib"; export class AwsMySQLClient extends AwsClient { private static readonly knownDialectsByCode: Map = new Map([ diff --git a/mysql/lib/dialect/mysql2_driver_dialect.ts b/mysql/lib/dialect/mysql2_driver_dialect.ts index 4f1dc3eb..c82225e1 100644 --- a/mysql/lib/dialect/mysql2_driver_dialect.ts +++ b/mysql/lib/dialect/mysql2_driver_dialect.ts @@ -23,6 +23,7 @@ import { AwsPoolClient } from "../../../common/lib/aws_pool_client"; import { AwsMysqlPoolClient } from "../mysql_pool_client"; import { MySQLClientWrapper } from "../../../common/lib/mysql_client_wrapper"; import { HostInfo } from "../../../common/lib/host_info"; +import { UnsupportedMethodError } from "../../../common/lib/utils/errors"; export class MySQL2DriverDialect implements DriverDialect { protected dialectName: string = this.constructor.name; @@ -32,7 +33,10 @@ export class MySQL2DriverDialect implements DriverDialect { } async connect(hostInfo: HostInfo, props: Map): Promise { - const targetClient = await createConnection(WrapperProperties.removeWrapperProperties(props)); + const driverProperties = WrapperProperties.removeWrapperProperties(props); + // MySQL2 does not support keep alive, explicitly check and throw an exception if this value is set. + this.setKeepAliveProperties(driverProperties, props.get(WrapperProperties.KEEPALIVE_PROPERTIES.name)); + const targetClient = await createConnection(driverProperties); return Promise.resolve(new MySQLClientWrapper(targetClient, hostInfo, props)); } @@ -52,4 +56,10 @@ export class MySQL2DriverDialect implements DriverDialect { getAwsPoolClient(props: PoolOptions): AwsPoolClient { return new AwsMysqlPoolClient(props); } + + setKeepAliveProperties(props: Map, keepAliveProps: any) { + if (keepAliveProps) { + throw new UnsupportedMethodError("Keep alive configuration is not supported for MySQL2."); + } + } } diff --git a/pg/lib/dialect/node_postgres_driver_dialect.ts b/pg/lib/dialect/node_postgres_driver_dialect.ts index a790f4d5..d604b45b 100644 --- a/pg/lib/dialect/node_postgres_driver_dialect.ts +++ b/pg/lib/dialect/node_postgres_driver_dialect.ts @@ -28,13 +28,17 @@ import { HostInfo } from "../../../common/lib/host_info"; export class NodePostgresDriverDialect implements DriverDialect { protected dialectName: string = this.constructor.name; + private static keepAlivePropertyName = "keepAlive"; + private static keepAliveInitialDelayMillisPropertyName = "keepAliveInitialDelayMillis"; getDialectName(): string { return this.dialectName; } async connect(hostInfo: HostInfo, props: Map): Promise { - const targetClient = new pkgPg.Client(WrapperProperties.removeWrapperProperties(props)); + const driverProperties = WrapperProperties.removeWrapperProperties(props); + this.setKeepAliveProperties(driverProperties, props.get(WrapperProperties.KEEPALIVE_PROPERTIES.name)); + const targetClient = new pkgPg.Client(driverProperties); await targetClient.connect(); return Promise.resolve(new PgClientWrapper(targetClient, hostInfo, props)); } @@ -55,4 +59,20 @@ export class NodePostgresDriverDialect implements DriverDialect { getAwsPoolClient(props: pkgPg.PoolConfig): AwsPoolClient { return new AwsPgPoolClient(props); } + + setKeepAliveProperties(props: Map, keepAliveProps: any) { + if (!keepAliveProps) { + return; + } + + const keepAlive = keepAliveProps.get(NodePostgresDriverDialect.keepAlivePropertyName); + const keepAliveInitialDelayMillis = keepAliveProps.get(NodePostgresDriverDialect.keepAliveInitialDelayMillisPropertyName); + + if (keepAlive) { + props.set(NodePostgresDriverDialect.keepAlivePropertyName, keepAlive); + } + if (keepAliveInitialDelayMillis) { + props.set(NodePostgresDriverDialect.keepAliveInitialDelayMillisPropertyName, keepAliveInitialDelayMillis); + } + } }