@elsikora/nestjs-typeorm-aws-connector 1.4.0 → 1.4.1-dev.1
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/README.md +17 -14
- package/dist/cjs/modules/typeorm-aws-connector/rotator/rotator.service.d.ts +22 -4
- package/dist/cjs/modules/typeorm-aws-connector/rotator/rotator.service.js +174 -64
- package/dist/cjs/modules/typeorm-aws-connector/rotator/rotator.service.js.map +1 -1
- package/dist/esm/modules/typeorm-aws-connector/rotator/rotator.service.d.ts +22 -4
- package/dist/esm/modules/typeorm-aws-connector/rotator/rotator.service.js +174 -64
- package/dist/esm/modules/typeorm-aws-connector/rotator/rotator.service.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
## 💡 Highlights
|
|
17
17
|
|
|
18
18
|
- 🔐 Zero-credential codebase — resolves all database config from AWS SSM Parameter Store & Secrets Manager at runtime
|
|
19
|
-
- ♻️ Built-in automatic credential rotation with health checks,
|
|
19
|
+
- ♻️ Built-in automatic credential rotation with health checks, staged driver promotion, and graceful old-pool drainage
|
|
20
20
|
- 🏗️ Split-source architecture — infra teams own host/port/secrets, app teams own pool/timeout/sync settings independently
|
|
21
21
|
- 📦 Dual ESM/CJS module output with full TypeScript declarations and broad NestJS compatibility (v8–v11)
|
|
22
22
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
## 📖 Description
|
|
38
38
|
**NestJS-TypeORM-AWS-Connector** is a production-grade NestJS module that eliminates the pain of managing database configuration in AWS-hosted applications. Instead of hardcoding credentials or juggling environment variables, this connector resolves your entire TypeORM `DataSourceOptions` from **AWS Systems Manager Parameter Store** and **AWS Secrets Manager** — with built-in credential rotation.
|
|
39
39
|
|
|
40
|
-
In modern cloud-native architectures, database credentials are rotated regularly for security compliance. Traditional approaches break connections during rotation, causing downtime. This connector
|
|
40
|
+
In modern cloud-native architectures, database credentials are rotated regularly for security compliance. Traditional approaches break connections during rotation, causing downtime. This connector avoids destructive mid-run `DataSource` teardown by validating a replacement connection first, promoting the new driver for future query runners, and retiring the previous pool only after existing query runners drain.
|
|
41
41
|
|
|
42
42
|
### Real-World Use Cases
|
|
43
43
|
|
|
@@ -65,8 +65,8 @@ The connector uses a **split-source model**: infrastructure-owned parameters (ho
|
|
|
65
65
|
## 🚀 Features
|
|
66
66
|
- ✨ ****AWS Parameter Store Integration** — Resolves database host, port, name, type, and tuning parameters from structured SSM paths with hierarchical namespace support**
|
|
67
67
|
- ✨ ****AWS Secrets Manager Integration** — Fetches username/password credentials from Secrets Manager with proper error handling for missing or malformed secrets**
|
|
68
|
-
- ✨ ****Automatic Credential Rotation** — Configurable interval-based rotation that
|
|
69
|
-
- ✨ ****Emergency Recovery** — After 3 consecutive rotation failures, automatically
|
|
68
|
+
- ✨ ****Automatic Credential Rotation** — Configurable interval-based rotation that validates a replacement connection before promoting it for future query runners**
|
|
69
|
+
- ✨ ****Emergency Recovery** — After 3 consecutive rotation failures, automatically retries recovery without destructively tearing down the live `DataSource`**
|
|
70
70
|
- ✨ ****Connection Health Validation** — Validates both existing and new connections with `SELECT 1` queries before and after rotation**
|
|
71
71
|
- ✨ ****Split-Source Configuration Model** — Infrastructure-owned settings (host, port, secret-id) use canonical AWS namespaces; app-owned settings use your module namespace**
|
|
72
72
|
- ✨ ****Raw Value Overrides** — Bypass SSM entirely for any field by providing direct values — perfect for local development with `host: '127.0.0.1'`**
|
|
@@ -124,10 +124,10 @@ sequenceDiagram
|
|
|
124
124
|
Rotator->>Connector: getTypeOrmOptions()
|
|
125
125
|
Connector->>SM: Fetch fresh credentials
|
|
126
126
|
SM-->>Connector: New username + password
|
|
127
|
-
Rotator->>TypeORM:
|
|
128
|
-
Rotator->>TypeORM: Initialize new DataSource
|
|
127
|
+
Rotator->>TypeORM: Initialize replacement DataSource
|
|
129
128
|
Rotator->>TypeORM: Verify with SELECT 1
|
|
130
|
-
Rotator->>TypeORM:
|
|
129
|
+
Rotator->>TypeORM: Promote replacement driver for future query runners
|
|
130
|
+
Rotator->>TypeORM: Retire previous pool after active query runners drain
|
|
131
131
|
end
|
|
132
132
|
```
|
|
133
133
|
|
|
@@ -337,10 +337,13 @@ TypeOrmAwsConnectorModule.register({
|
|
|
337
337
|
The rotation service will:
|
|
338
338
|
1. Validate current connection health
|
|
339
339
|
2. Fetch fresh credentials from AWS Secrets Manager
|
|
340
|
-
3. Create a
|
|
341
|
-
4. Verify the
|
|
342
|
-
5.
|
|
343
|
-
6.
|
|
340
|
+
3. Create and initialize a replacement `DataSource` with the updated credentials
|
|
341
|
+
4. Verify the replacement connection with a `SELECT 1` query
|
|
342
|
+
5. Promote the replacement driver for future query runners without destroying the live `DataSource`
|
|
343
|
+
6. Dispose the previous pool only after query runners created before the promotion are released
|
|
344
|
+
7. Attempt emergency recovery after 3 consecutive failures
|
|
345
|
+
|
|
346
|
+
Rotation is intended for long-lived services. For one-shot jobs, migrations, or explicit CLI tasks, keep `rotation.isEnabled` disabled so the connector does not register a background interval.
|
|
344
347
|
|
|
345
348
|
---
|
|
346
349
|
|
|
@@ -402,7 +405,7 @@ Required fields (`host`, `port`, `databaseName`, `type`, `secretId`) throw expli
|
|
|
402
405
|
|---|---|
|
|
403
406
|
| Dual ESM/CJS module output | ✅ Done |
|
|
404
407
|
| AWS Secrets Manager credential resolution | ✅ Done |
|
|
405
|
-
| Automatic credential rotation with
|
|
408
|
+
| Automatic credential rotation with staged driver promotion | ✅ Done |
|
|
406
409
|
| Emergency recovery after consecutive rotation failures | ✅ Done |
|
|
407
410
|
| Split-source configuration model (infra vs app namespaces) | ✅ Done |
|
|
408
411
|
| Raw value overrides for local development | ✅ Done |
|
|
@@ -429,7 +432,7 @@ A: No! Use raw value overrides to bypass AWS entirely during local development.
|
|
|
429
432
|
A: Currently PostgreSQL (`EDatabaseType.POSTGRES`) and MySQL (`EDatabaseType.MYSQL`). The `type` field is resolved from SSM or set directly.
|
|
430
433
|
|
|
431
434
|
**Q: What happens if AWS Secrets Manager is unreachable during rotation?**
|
|
432
|
-
A: The rotation service catches the error, increments a failure counter, and logs it. After 3 consecutive failures, it attempts an emergency recovery with a
|
|
435
|
+
A: The rotation service catches the error, increments a failure counter, and logs it. After 3 consecutive failures, it attempts an emergency recovery with a fresh replacement connection. Failed replacements are discarded, and the currently promoted pool stays in place until a later rotation succeeds.
|
|
433
436
|
|
|
434
437
|
**Q: Can I use this without `@elsikora/nestjs-aws-parameter-store-config`?**
|
|
435
438
|
A: No. The connector depends on `ParameterStoreConfigService.get()` for SSM lookups. You must register `ParameterStoreConfigModule` first. However, if you provide raw values for all fields, SSM lookups won't actually be invoked.
|
|
@@ -441,7 +444,7 @@ A: Infrastructure-owned fields like `host`, `port`, and `secretId` default to ca
|
|
|
441
444
|
A: Yes. The peer dependency accepts `@nestjs/common ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0`.
|
|
442
445
|
|
|
443
446
|
**Q: How do I disable credential rotation?**
|
|
444
|
-
A: Don't set the `rotation` config, or explicitly set `rotation.isEnabled: false`. The `RotatorService` will skip interval registration entirely.
|
|
447
|
+
A: Don't set the `rotation` config, or explicitly set `rotation.isEnabled: false`. The `RotatorService` will skip interval registration entirely. This is the recommended mode for one-shot jobs, migrations, and other short-lived processes.
|
|
445
448
|
|
|
446
449
|
**Q: What's the default rotation interval?**
|
|
447
450
|
A: 1 hour (3,600,000 ms). Override it via `rotation.intervalMs` or the SSM path `typeorm/rotation/interval-ms`.
|
|
@@ -1,23 +1,41 @@
|
|
|
1
|
-
import { OnModuleInit } from "@nestjs/common";
|
|
1
|
+
import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
|
2
2
|
import { SchedulerRegistry } from "@nestjs/schedule";
|
|
3
|
-
import { DataSource } from "typeorm";
|
|
3
|
+
import { DataSource, DataSourceOptions } from "typeorm";
|
|
4
4
|
import { TypeOrmAwsConnectorService } from '../typeorm-aws-connector.service';
|
|
5
|
-
export declare class RotatorService implements OnModuleInit {
|
|
5
|
+
export declare class RotatorService implements OnModuleDestroy, OnModuleInit {
|
|
6
6
|
private readonly dataSource;
|
|
7
7
|
private readonly schedulerRegistry;
|
|
8
8
|
private readonly connectorService;
|
|
9
9
|
private readonly rotationIntervalName;
|
|
10
|
+
private readonly activeQueryRunnerCountsByGeneration;
|
|
10
11
|
private consecutiveFailures;
|
|
12
|
+
private currentDriverGeneration;
|
|
13
|
+
private isDataSourceTrackingInstalled;
|
|
11
14
|
private isRotationInProgress;
|
|
12
15
|
private readonly LOGGER;
|
|
13
16
|
private readonly MAX_CONSECUTIVE_FAILURES;
|
|
17
|
+
private retiredDriverDisposal;
|
|
18
|
+
private retiredDrivers;
|
|
14
19
|
constructor(dataSource: DataSource | undefined, schedulerRegistry: SchedulerRegistry, connectorService: TypeOrmAwsConnectorService, rotationIntervalName: string);
|
|
20
|
+
onModuleDestroy(): Promise<void>;
|
|
15
21
|
onModuleInit(): void;
|
|
16
22
|
rotateDatabaseConnection(): Promise<void>;
|
|
23
|
+
protected createReplacementDataSource(options: DataSourceOptions): DataSource;
|
|
24
|
+
private asMutableDriver;
|
|
17
25
|
private attemptEmergencyRecovery;
|
|
26
|
+
private decrementActiveQueryRunnerCount;
|
|
27
|
+
private destroyReplacementDataSource;
|
|
28
|
+
private disposeRetiredDrivers;
|
|
29
|
+
private getExtraOptions;
|
|
18
30
|
private getRequiredDataSource;
|
|
19
|
-
private
|
|
31
|
+
private incrementActiveQueryRunnerCount;
|
|
32
|
+
private installDataSourceTracking;
|
|
33
|
+
private mergeDataSourceOptions;
|
|
34
|
+
private promoteReplacementDataSource;
|
|
20
35
|
private safeRotateDatabaseConnection;
|
|
36
|
+
private scheduleRetiredDriverDisposal;
|
|
37
|
+
private trackQueryRunnerRelease;
|
|
21
38
|
private validateConnectionHealth;
|
|
39
|
+
private validateRotationDataSourceOptions;
|
|
22
40
|
private verifyNewConnection;
|
|
23
41
|
}
|
|
@@ -12,10 +12,15 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
12
12
|
schedulerRegistry;
|
|
13
13
|
connectorService;
|
|
14
14
|
rotationIntervalName;
|
|
15
|
+
activeQueryRunnerCountsByGeneration = new Map();
|
|
15
16
|
consecutiveFailures = 0;
|
|
17
|
+
currentDriverGeneration = 0;
|
|
18
|
+
isDataSourceTrackingInstalled = false;
|
|
16
19
|
isRotationInProgress = false;
|
|
17
20
|
LOGGER;
|
|
18
21
|
MAX_CONSECUTIVE_FAILURES = 3;
|
|
22
|
+
retiredDriverDisposal = Promise.resolve();
|
|
23
|
+
retiredDrivers = [];
|
|
19
24
|
constructor(dataSource, schedulerRegistry, connectorService, rotationIntervalName) {
|
|
20
25
|
this.dataSource = dataSource;
|
|
21
26
|
this.schedulerRegistry = schedulerRegistry;
|
|
@@ -23,12 +28,16 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
23
28
|
this.rotationIntervalName = rotationIntervalName;
|
|
24
29
|
this.LOGGER = new common.Logger(`${RotatorService_1.name}:${this.rotationIntervalName}`);
|
|
25
30
|
}
|
|
31
|
+
async onModuleDestroy() {
|
|
32
|
+
await this.scheduleRetiredDriverDisposal(true);
|
|
33
|
+
}
|
|
26
34
|
onModuleInit() {
|
|
27
35
|
const rotationConfig = this.connectorService.getRotationConfig();
|
|
28
36
|
if (!rotationConfig.isEnabled) {
|
|
29
37
|
return;
|
|
30
38
|
}
|
|
31
|
-
this.getRequiredDataSource();
|
|
39
|
+
const dataSource = this.getRequiredDataSource();
|
|
40
|
+
this.installDataSourceTracking(dataSource);
|
|
32
41
|
const interval = setInterval(() => {
|
|
33
42
|
void this.safeRotateDatabaseConnection();
|
|
34
43
|
}, rotationConfig.intervalMs);
|
|
@@ -38,71 +47,39 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
38
47
|
async rotateDatabaseConnection() {
|
|
39
48
|
this.LOGGER.log("Launching DB credentials rotation interval...");
|
|
40
49
|
const dataSource = this.getRequiredDataSource();
|
|
50
|
+
this.installDataSourceTracking(dataSource);
|
|
51
|
+
if (!dataSource.isInitialized) {
|
|
52
|
+
this.LOGGER.warn("Skipping DB credentials rotation because the live DataSource is not initialized yet");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
41
55
|
// First verify the current connection is actually healthy
|
|
42
56
|
await this.validateConnectionHealth(dataSource);
|
|
43
|
-
// Backup existing configuration
|
|
44
|
-
const currentOptions = dataSource.options;
|
|
45
|
-
const subscribers = [...dataSource.subscribers];
|
|
46
57
|
// Get fresh credentials from AWS
|
|
47
58
|
const freshOptions = await this.connectorService.getTypeOrmOptions();
|
|
48
|
-
const mergedOptions =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// @ts-ignore
|
|
61
|
-
// eslint-disable-next-line @elsikora/typescript/no-unsafe-assignment
|
|
62
|
-
username: freshOptions.username,
|
|
63
|
-
};
|
|
64
|
-
// Destroy old connection only if initialized
|
|
65
|
-
if (dataSource.isInitialized) {
|
|
66
|
-
await dataSource.destroy();
|
|
67
|
-
}
|
|
68
|
-
// Create and initialize a new data source with fresh credentials
|
|
69
|
-
const newDataSource = new typeorm.DataSource(mergedOptions);
|
|
70
|
-
await newDataSource.initialize();
|
|
71
|
-
// Verify the new connection works by performing a simple query
|
|
72
|
-
await this.verifyNewConnection(newDataSource);
|
|
73
|
-
// Re-add subscribers
|
|
74
|
-
for (const subscriber of subscribers) {
|
|
75
|
-
newDataSource.subscribers.push(subscriber);
|
|
76
|
-
}
|
|
77
|
-
// Update the existing data source with new connection
|
|
78
|
-
dataSource.setOptions(mergedOptions);
|
|
79
|
-
this.replaceDataSourceDriver(dataSource, newDataSource);
|
|
59
|
+
const mergedOptions = this.mergeDataSourceOptions(dataSource.options, freshOptions);
|
|
60
|
+
const nextDataSource = this.createReplacementDataSource(mergedOptions);
|
|
61
|
+
try {
|
|
62
|
+
await nextDataSource.initialize();
|
|
63
|
+
await this.verifyNewConnection(nextDataSource);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
await this.destroyReplacementDataSource(nextDataSource);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
this.promoteReplacementDataSource(dataSource, nextDataSource, mergedOptions);
|
|
70
|
+
void this.scheduleRetiredDriverDisposal(false);
|
|
80
71
|
this.LOGGER.log("Rotation completed successfully!");
|
|
81
72
|
}
|
|
73
|
+
createReplacementDataSource(options) {
|
|
74
|
+
return new typeorm.DataSource(options);
|
|
75
|
+
}
|
|
76
|
+
asMutableDriver(driver) {
|
|
77
|
+
return driver;
|
|
78
|
+
}
|
|
82
79
|
async attemptEmergencyRecovery() {
|
|
83
80
|
try {
|
|
84
81
|
this.LOGGER.log("Attempting emergency database connection recovery...");
|
|
85
|
-
|
|
86
|
-
// Get fresh credentials and connection options from AWS
|
|
87
|
-
const freshOptions = await this.connectorService.getTypeOrmOptions();
|
|
88
|
-
// If the data source is still initialized, destroy it first
|
|
89
|
-
if (dataSource.isInitialized) {
|
|
90
|
-
await dataSource.destroy();
|
|
91
|
-
}
|
|
92
|
-
// Create a completely new data source with fresh options
|
|
93
|
-
const recoveryDataSource = new typeorm.DataSource({
|
|
94
|
-
...freshOptions,
|
|
95
|
-
});
|
|
96
|
-
// Initialize the recovery data source
|
|
97
|
-
await recoveryDataSource.initialize();
|
|
98
|
-
// Transfer subscribers
|
|
99
|
-
const subscribers = [...dataSource.subscribers];
|
|
100
|
-
for (const subscriber of subscribers) {
|
|
101
|
-
recoveryDataSource.subscribers.push(subscriber);
|
|
102
|
-
}
|
|
103
|
-
// Replace the driver and connection objects
|
|
104
|
-
dataSource.setOptions(recoveryDataSource.options);
|
|
105
|
-
this.replaceDataSourceDriver(dataSource, recoveryDataSource);
|
|
82
|
+
await this.rotateDatabaseConnection();
|
|
106
83
|
// Reset failure counter after successful recovery
|
|
107
84
|
this.consecutiveFailures = 0;
|
|
108
85
|
this.LOGGER.log("Emergency recovery successful!");
|
|
@@ -111,15 +88,104 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
111
88
|
this.LOGGER.error(`Emergency recovery failed: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
|
|
112
89
|
}
|
|
113
90
|
}
|
|
91
|
+
decrementActiveQueryRunnerCount(generation) {
|
|
92
|
+
const nextCount = (this.activeQueryRunnerCountsByGeneration.get(generation) ?? 0) - 1;
|
|
93
|
+
if (nextCount <= 0) {
|
|
94
|
+
this.activeQueryRunnerCountsByGeneration.delete(generation);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.activeQueryRunnerCountsByGeneration.set(generation, nextCount);
|
|
98
|
+
}
|
|
99
|
+
async destroyReplacementDataSource(dataSource) {
|
|
100
|
+
if (!dataSource.isInitialized) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await dataSource.destroy();
|
|
104
|
+
}
|
|
105
|
+
async disposeRetiredDrivers(force) {
|
|
106
|
+
const pendingRetiredDrivers = [];
|
|
107
|
+
for (const retiredDriver of this.retiredDrivers) {
|
|
108
|
+
const activeQueryRunnerCount = this.activeQueryRunnerCountsByGeneration.get(retiredDriver.generation) ?? 0;
|
|
109
|
+
if (!force && activeQueryRunnerCount > 0) {
|
|
110
|
+
pendingRetiredDrivers.push(retiredDriver);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
await retiredDriver.driver.disconnect();
|
|
115
|
+
this.LOGGER.debug(`Disposed retired DB driver from generation ${String(retiredDriver.generation)}`);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
this.LOGGER.error(`Failed to dispose retired DB driver from generation ${String(retiredDriver.generation)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
119
|
+
if (!force) {
|
|
120
|
+
pendingRetiredDrivers.push(retiredDriver);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.retiredDrivers = pendingRetiredDrivers;
|
|
125
|
+
}
|
|
126
|
+
getExtraOptions(options) {
|
|
127
|
+
const extraCandidate = options.extra;
|
|
128
|
+
if (typeof extraCandidate !== "object" || extraCandidate === null || Array.isArray(extraCandidate)) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
...extraCandidate,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
114
135
|
getRequiredDataSource() {
|
|
115
136
|
if (!this.dataSource) {
|
|
116
137
|
throw new Error("Database rotation is enabled but TypeORM DataSource provider is not available.");
|
|
117
138
|
}
|
|
118
139
|
return this.dataSource;
|
|
119
140
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
141
|
+
incrementActiveQueryRunnerCount(generation) {
|
|
142
|
+
this.activeQueryRunnerCountsByGeneration.set(generation, (this.activeQueryRunnerCountsByGeneration.get(generation) ?? 0) + 1);
|
|
143
|
+
}
|
|
144
|
+
installDataSourceTracking(dataSource) {
|
|
145
|
+
if (this.isDataSourceTrackingInstalled) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const originalCreateQueryRunner = dataSource.createQueryRunner.bind(dataSource);
|
|
149
|
+
dataSource.createQueryRunner = (mode) => {
|
|
150
|
+
const generation = this.currentDriverGeneration;
|
|
151
|
+
this.incrementActiveQueryRunnerCount(generation);
|
|
152
|
+
try {
|
|
153
|
+
const queryRunner = originalCreateQueryRunner(mode);
|
|
154
|
+
this.trackQueryRunnerRelease(generation, queryRunner);
|
|
155
|
+
return queryRunner;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
this.decrementActiveQueryRunnerCount(generation);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
this.isDataSourceTrackingInstalled = true;
|
|
163
|
+
}
|
|
164
|
+
mergeDataSourceOptions(currentOptions, freshOptions) {
|
|
165
|
+
this.validateRotationDataSourceOptions(currentOptions);
|
|
166
|
+
this.validateRotationDataSourceOptions(freshOptions);
|
|
167
|
+
return {
|
|
168
|
+
...currentOptions,
|
|
169
|
+
...freshOptions,
|
|
170
|
+
extra: {
|
|
171
|
+
...this.getExtraOptions(currentOptions),
|
|
172
|
+
...this.getExtraOptions(freshOptions),
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
promoteReplacementDataSource(dataSource, nextDataSource, mergedOptions) {
|
|
177
|
+
const mutableCurrentDriver = this.asMutableDriver(dataSource.driver);
|
|
178
|
+
const mutableNextDriver = this.asMutableDriver(nextDataSource.driver);
|
|
179
|
+
const retiredGeneration = this.currentDriverGeneration;
|
|
180
|
+
this.currentDriverGeneration += 1;
|
|
181
|
+
mutableNextDriver.connection = dataSource;
|
|
182
|
+
dataSource.driver = mutableNextDriver;
|
|
183
|
+
dataSource.setOptions(mergedOptions);
|
|
184
|
+
mutableNextDriver.options = dataSource.options;
|
|
185
|
+
this.retiredDrivers.push({
|
|
186
|
+
driver: mutableCurrentDriver,
|
|
187
|
+
generation: retiredGeneration,
|
|
188
|
+
});
|
|
123
189
|
}
|
|
124
190
|
async safeRotateDatabaseConnection() {
|
|
125
191
|
if (this.isRotationInProgress) {
|
|
@@ -143,6 +209,36 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
143
209
|
this.isRotationInProgress = false;
|
|
144
210
|
}
|
|
145
211
|
}
|
|
212
|
+
scheduleRetiredDriverDisposal(force) {
|
|
213
|
+
const nextDisposal = this.retiredDriverDisposal
|
|
214
|
+
.catch((error) => {
|
|
215
|
+
this.LOGGER.error(`Retired driver disposal chain failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
216
|
+
})
|
|
217
|
+
.then(async () => {
|
|
218
|
+
await this.disposeRetiredDrivers(force);
|
|
219
|
+
});
|
|
220
|
+
this.retiredDriverDisposal = nextDisposal;
|
|
221
|
+
return nextDisposal;
|
|
222
|
+
}
|
|
223
|
+
trackQueryRunnerRelease(generation, queryRunner) {
|
|
224
|
+
const originalRelease = queryRunner.release.bind(queryRunner);
|
|
225
|
+
let isTrackedReleaseHandled = false;
|
|
226
|
+
queryRunner.release = async () => {
|
|
227
|
+
let shouldHandleTrackedRelease = false;
|
|
228
|
+
try {
|
|
229
|
+
await originalRelease();
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
shouldHandleTrackedRelease = !isTrackedReleaseHandled;
|
|
233
|
+
isTrackedReleaseHandled = true;
|
|
234
|
+
}
|
|
235
|
+
if (!shouldHandleTrackedRelease) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
this.decrementActiveQueryRunnerCount(generation);
|
|
239
|
+
await this.scheduleRetiredDriverDisposal(false);
|
|
240
|
+
};
|
|
241
|
+
}
|
|
146
242
|
async validateConnectionHealth(dataSource) {
|
|
147
243
|
if (!dataSource.isInitialized) {
|
|
148
244
|
this.LOGGER.warn("Current data source is not initialized, no health check needed");
|
|
@@ -151,8 +247,12 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
151
247
|
try {
|
|
152
248
|
// Test the current connection with a simple query
|
|
153
249
|
const queryRunner = dataSource.createQueryRunner();
|
|
154
|
-
|
|
155
|
-
|
|
250
|
+
try {
|
|
251
|
+
await queryRunner.query("SELECT 1");
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
await queryRunner.release();
|
|
255
|
+
}
|
|
156
256
|
this.LOGGER.debug("Current connection is healthy");
|
|
157
257
|
}
|
|
158
258
|
catch (error) {
|
|
@@ -160,11 +260,21 @@ exports.RotatorService = RotatorService_1 = class RotatorService {
|
|
|
160
260
|
// We don't throw here, we'll continue with rotation to fix the issue
|
|
161
261
|
}
|
|
162
262
|
}
|
|
263
|
+
validateRotationDataSourceOptions(options) {
|
|
264
|
+
if (options.type === "mysql" || options.type === "postgres") {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
throw new Error(`Database rotation is supported only for "mysql" and "postgres" data sources. Received: "${options.type}".`);
|
|
268
|
+
}
|
|
163
269
|
async verifyNewConnection(dataSource) {
|
|
164
270
|
try {
|
|
165
271
|
const queryRunner = dataSource.createQueryRunner();
|
|
166
|
-
|
|
167
|
-
|
|
272
|
+
try {
|
|
273
|
+
await queryRunner.query("SELECT 1");
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
await queryRunner.release();
|
|
277
|
+
}
|
|
168
278
|
this.LOGGER.debug("New connection verified successfully");
|
|
169
279
|
}
|
|
170
280
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotator.service.js","sources":["../../../../../../src/modules/typeorm-aws-connector/rotator/rotator.service.ts"],"sourcesContent":[null],"names":["RotatorService","Logger","DataSource","__decorate","Injectable","SchedulerRegistry","TypeOrmAwsConnectorService"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"rotator.service.js","sources":["../../../../../../src/modules/typeorm-aws-connector/rotator/rotator.service.ts"],"sourcesContent":[null],"names":["RotatorService","Logger","DataSource","__decorate","Injectable","SchedulerRegistry","TypeOrmAwsConnectorService"],"mappings":"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"}
|
|
@@ -1,23 +1,41 @@
|
|
|
1
|
-
import { OnModuleInit } from "@nestjs/common";
|
|
1
|
+
import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
|
2
2
|
import { SchedulerRegistry } from "@nestjs/schedule";
|
|
3
|
-
import { DataSource } from "typeorm";
|
|
3
|
+
import { DataSource, DataSourceOptions } from "typeorm";
|
|
4
4
|
import { TypeOrmAwsConnectorService } from '../typeorm-aws-connector.service';
|
|
5
|
-
export declare class RotatorService implements OnModuleInit {
|
|
5
|
+
export declare class RotatorService implements OnModuleDestroy, OnModuleInit {
|
|
6
6
|
private readonly dataSource;
|
|
7
7
|
private readonly schedulerRegistry;
|
|
8
8
|
private readonly connectorService;
|
|
9
9
|
private readonly rotationIntervalName;
|
|
10
|
+
private readonly activeQueryRunnerCountsByGeneration;
|
|
10
11
|
private consecutiveFailures;
|
|
12
|
+
private currentDriverGeneration;
|
|
13
|
+
private isDataSourceTrackingInstalled;
|
|
11
14
|
private isRotationInProgress;
|
|
12
15
|
private readonly LOGGER;
|
|
13
16
|
private readonly MAX_CONSECUTIVE_FAILURES;
|
|
17
|
+
private retiredDriverDisposal;
|
|
18
|
+
private retiredDrivers;
|
|
14
19
|
constructor(dataSource: DataSource | undefined, schedulerRegistry: SchedulerRegistry, connectorService: TypeOrmAwsConnectorService, rotationIntervalName: string);
|
|
20
|
+
onModuleDestroy(): Promise<void>;
|
|
15
21
|
onModuleInit(): void;
|
|
16
22
|
rotateDatabaseConnection(): Promise<void>;
|
|
23
|
+
protected createReplacementDataSource(options: DataSourceOptions): DataSource;
|
|
24
|
+
private asMutableDriver;
|
|
17
25
|
private attemptEmergencyRecovery;
|
|
26
|
+
private decrementActiveQueryRunnerCount;
|
|
27
|
+
private destroyReplacementDataSource;
|
|
28
|
+
private disposeRetiredDrivers;
|
|
29
|
+
private getExtraOptions;
|
|
18
30
|
private getRequiredDataSource;
|
|
19
|
-
private
|
|
31
|
+
private incrementActiveQueryRunnerCount;
|
|
32
|
+
private installDataSourceTracking;
|
|
33
|
+
private mergeDataSourceOptions;
|
|
34
|
+
private promoteReplacementDataSource;
|
|
20
35
|
private safeRotateDatabaseConnection;
|
|
36
|
+
private scheduleRetiredDriverDisposal;
|
|
37
|
+
private trackQueryRunnerRelease;
|
|
21
38
|
private validateConnectionHealth;
|
|
39
|
+
private validateRotationDataSourceOptions;
|
|
22
40
|
private verifyNewConnection;
|
|
23
41
|
}
|
|
@@ -10,10 +10,15 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
10
10
|
schedulerRegistry;
|
|
11
11
|
connectorService;
|
|
12
12
|
rotationIntervalName;
|
|
13
|
+
activeQueryRunnerCountsByGeneration = new Map();
|
|
13
14
|
consecutiveFailures = 0;
|
|
15
|
+
currentDriverGeneration = 0;
|
|
16
|
+
isDataSourceTrackingInstalled = false;
|
|
14
17
|
isRotationInProgress = false;
|
|
15
18
|
LOGGER;
|
|
16
19
|
MAX_CONSECUTIVE_FAILURES = 3;
|
|
20
|
+
retiredDriverDisposal = Promise.resolve();
|
|
21
|
+
retiredDrivers = [];
|
|
17
22
|
constructor(dataSource, schedulerRegistry, connectorService, rotationIntervalName) {
|
|
18
23
|
this.dataSource = dataSource;
|
|
19
24
|
this.schedulerRegistry = schedulerRegistry;
|
|
@@ -21,12 +26,16 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
21
26
|
this.rotationIntervalName = rotationIntervalName;
|
|
22
27
|
this.LOGGER = new Logger(`${RotatorService_1.name}:${this.rotationIntervalName}`);
|
|
23
28
|
}
|
|
29
|
+
async onModuleDestroy() {
|
|
30
|
+
await this.scheduleRetiredDriverDisposal(true);
|
|
31
|
+
}
|
|
24
32
|
onModuleInit() {
|
|
25
33
|
const rotationConfig = this.connectorService.getRotationConfig();
|
|
26
34
|
if (!rotationConfig.isEnabled) {
|
|
27
35
|
return;
|
|
28
36
|
}
|
|
29
|
-
this.getRequiredDataSource();
|
|
37
|
+
const dataSource = this.getRequiredDataSource();
|
|
38
|
+
this.installDataSourceTracking(dataSource);
|
|
30
39
|
const interval = setInterval(() => {
|
|
31
40
|
void this.safeRotateDatabaseConnection();
|
|
32
41
|
}, rotationConfig.intervalMs);
|
|
@@ -36,71 +45,39 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
36
45
|
async rotateDatabaseConnection() {
|
|
37
46
|
this.LOGGER.log("Launching DB credentials rotation interval...");
|
|
38
47
|
const dataSource = this.getRequiredDataSource();
|
|
48
|
+
this.installDataSourceTracking(dataSource);
|
|
49
|
+
if (!dataSource.isInitialized) {
|
|
50
|
+
this.LOGGER.warn("Skipping DB credentials rotation because the live DataSource is not initialized yet");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
39
53
|
// First verify the current connection is actually healthy
|
|
40
54
|
await this.validateConnectionHealth(dataSource);
|
|
41
|
-
// Backup existing configuration
|
|
42
|
-
const currentOptions = dataSource.options;
|
|
43
|
-
const subscribers = [...dataSource.subscribers];
|
|
44
55
|
// Get fresh credentials from AWS
|
|
45
56
|
const freshOptions = await this.connectorService.getTypeOrmOptions();
|
|
46
|
-
const mergedOptions =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// @ts-ignore
|
|
59
|
-
// eslint-disable-next-line @elsikora/typescript/no-unsafe-assignment
|
|
60
|
-
username: freshOptions.username,
|
|
61
|
-
};
|
|
62
|
-
// Destroy old connection only if initialized
|
|
63
|
-
if (dataSource.isInitialized) {
|
|
64
|
-
await dataSource.destroy();
|
|
65
|
-
}
|
|
66
|
-
// Create and initialize a new data source with fresh credentials
|
|
67
|
-
const newDataSource = new DataSource(mergedOptions);
|
|
68
|
-
await newDataSource.initialize();
|
|
69
|
-
// Verify the new connection works by performing a simple query
|
|
70
|
-
await this.verifyNewConnection(newDataSource);
|
|
71
|
-
// Re-add subscribers
|
|
72
|
-
for (const subscriber of subscribers) {
|
|
73
|
-
newDataSource.subscribers.push(subscriber);
|
|
74
|
-
}
|
|
75
|
-
// Update the existing data source with new connection
|
|
76
|
-
dataSource.setOptions(mergedOptions);
|
|
77
|
-
this.replaceDataSourceDriver(dataSource, newDataSource);
|
|
57
|
+
const mergedOptions = this.mergeDataSourceOptions(dataSource.options, freshOptions);
|
|
58
|
+
const nextDataSource = this.createReplacementDataSource(mergedOptions);
|
|
59
|
+
try {
|
|
60
|
+
await nextDataSource.initialize();
|
|
61
|
+
await this.verifyNewConnection(nextDataSource);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
await this.destroyReplacementDataSource(nextDataSource);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
this.promoteReplacementDataSource(dataSource, nextDataSource, mergedOptions);
|
|
68
|
+
void this.scheduleRetiredDriverDisposal(false);
|
|
78
69
|
this.LOGGER.log("Rotation completed successfully!");
|
|
79
70
|
}
|
|
71
|
+
createReplacementDataSource(options) {
|
|
72
|
+
return new DataSource(options);
|
|
73
|
+
}
|
|
74
|
+
asMutableDriver(driver) {
|
|
75
|
+
return driver;
|
|
76
|
+
}
|
|
80
77
|
async attemptEmergencyRecovery() {
|
|
81
78
|
try {
|
|
82
79
|
this.LOGGER.log("Attempting emergency database connection recovery...");
|
|
83
|
-
|
|
84
|
-
// Get fresh credentials and connection options from AWS
|
|
85
|
-
const freshOptions = await this.connectorService.getTypeOrmOptions();
|
|
86
|
-
// If the data source is still initialized, destroy it first
|
|
87
|
-
if (dataSource.isInitialized) {
|
|
88
|
-
await dataSource.destroy();
|
|
89
|
-
}
|
|
90
|
-
// Create a completely new data source with fresh options
|
|
91
|
-
const recoveryDataSource = new DataSource({
|
|
92
|
-
...freshOptions,
|
|
93
|
-
});
|
|
94
|
-
// Initialize the recovery data source
|
|
95
|
-
await recoveryDataSource.initialize();
|
|
96
|
-
// Transfer subscribers
|
|
97
|
-
const subscribers = [...dataSource.subscribers];
|
|
98
|
-
for (const subscriber of subscribers) {
|
|
99
|
-
recoveryDataSource.subscribers.push(subscriber);
|
|
100
|
-
}
|
|
101
|
-
// Replace the driver and connection objects
|
|
102
|
-
dataSource.setOptions(recoveryDataSource.options);
|
|
103
|
-
this.replaceDataSourceDriver(dataSource, recoveryDataSource);
|
|
80
|
+
await this.rotateDatabaseConnection();
|
|
104
81
|
// Reset failure counter after successful recovery
|
|
105
82
|
this.consecutiveFailures = 0;
|
|
106
83
|
this.LOGGER.log("Emergency recovery successful!");
|
|
@@ -109,15 +86,104 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
109
86
|
this.LOGGER.error(`Emergency recovery failed: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
|
|
110
87
|
}
|
|
111
88
|
}
|
|
89
|
+
decrementActiveQueryRunnerCount(generation) {
|
|
90
|
+
const nextCount = (this.activeQueryRunnerCountsByGeneration.get(generation) ?? 0) - 1;
|
|
91
|
+
if (nextCount <= 0) {
|
|
92
|
+
this.activeQueryRunnerCountsByGeneration.delete(generation);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.activeQueryRunnerCountsByGeneration.set(generation, nextCount);
|
|
96
|
+
}
|
|
97
|
+
async destroyReplacementDataSource(dataSource) {
|
|
98
|
+
if (!dataSource.isInitialized) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
await dataSource.destroy();
|
|
102
|
+
}
|
|
103
|
+
async disposeRetiredDrivers(force) {
|
|
104
|
+
const pendingRetiredDrivers = [];
|
|
105
|
+
for (const retiredDriver of this.retiredDrivers) {
|
|
106
|
+
const activeQueryRunnerCount = this.activeQueryRunnerCountsByGeneration.get(retiredDriver.generation) ?? 0;
|
|
107
|
+
if (!force && activeQueryRunnerCount > 0) {
|
|
108
|
+
pendingRetiredDrivers.push(retiredDriver);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await retiredDriver.driver.disconnect();
|
|
113
|
+
this.LOGGER.debug(`Disposed retired DB driver from generation ${String(retiredDriver.generation)}`);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
this.LOGGER.error(`Failed to dispose retired DB driver from generation ${String(retiredDriver.generation)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
117
|
+
if (!force) {
|
|
118
|
+
pendingRetiredDrivers.push(retiredDriver);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
this.retiredDrivers = pendingRetiredDrivers;
|
|
123
|
+
}
|
|
124
|
+
getExtraOptions(options) {
|
|
125
|
+
const extraCandidate = options.extra;
|
|
126
|
+
if (typeof extraCandidate !== "object" || extraCandidate === null || Array.isArray(extraCandidate)) {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
...extraCandidate,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
112
133
|
getRequiredDataSource() {
|
|
113
134
|
if (!this.dataSource) {
|
|
114
135
|
throw new Error("Database rotation is enabled but TypeORM DataSource provider is not available.");
|
|
115
136
|
}
|
|
116
137
|
return this.dataSource;
|
|
117
138
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
139
|
+
incrementActiveQueryRunnerCount(generation) {
|
|
140
|
+
this.activeQueryRunnerCountsByGeneration.set(generation, (this.activeQueryRunnerCountsByGeneration.get(generation) ?? 0) + 1);
|
|
141
|
+
}
|
|
142
|
+
installDataSourceTracking(dataSource) {
|
|
143
|
+
if (this.isDataSourceTrackingInstalled) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const originalCreateQueryRunner = dataSource.createQueryRunner.bind(dataSource);
|
|
147
|
+
dataSource.createQueryRunner = (mode) => {
|
|
148
|
+
const generation = this.currentDriverGeneration;
|
|
149
|
+
this.incrementActiveQueryRunnerCount(generation);
|
|
150
|
+
try {
|
|
151
|
+
const queryRunner = originalCreateQueryRunner(mode);
|
|
152
|
+
this.trackQueryRunnerRelease(generation, queryRunner);
|
|
153
|
+
return queryRunner;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
this.decrementActiveQueryRunnerCount(generation);
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
this.isDataSourceTrackingInstalled = true;
|
|
161
|
+
}
|
|
162
|
+
mergeDataSourceOptions(currentOptions, freshOptions) {
|
|
163
|
+
this.validateRotationDataSourceOptions(currentOptions);
|
|
164
|
+
this.validateRotationDataSourceOptions(freshOptions);
|
|
165
|
+
return {
|
|
166
|
+
...currentOptions,
|
|
167
|
+
...freshOptions,
|
|
168
|
+
extra: {
|
|
169
|
+
...this.getExtraOptions(currentOptions),
|
|
170
|
+
...this.getExtraOptions(freshOptions),
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
promoteReplacementDataSource(dataSource, nextDataSource, mergedOptions) {
|
|
175
|
+
const mutableCurrentDriver = this.asMutableDriver(dataSource.driver);
|
|
176
|
+
const mutableNextDriver = this.asMutableDriver(nextDataSource.driver);
|
|
177
|
+
const retiredGeneration = this.currentDriverGeneration;
|
|
178
|
+
this.currentDriverGeneration += 1;
|
|
179
|
+
mutableNextDriver.connection = dataSource;
|
|
180
|
+
dataSource.driver = mutableNextDriver;
|
|
181
|
+
dataSource.setOptions(mergedOptions);
|
|
182
|
+
mutableNextDriver.options = dataSource.options;
|
|
183
|
+
this.retiredDrivers.push({
|
|
184
|
+
driver: mutableCurrentDriver,
|
|
185
|
+
generation: retiredGeneration,
|
|
186
|
+
});
|
|
121
187
|
}
|
|
122
188
|
async safeRotateDatabaseConnection() {
|
|
123
189
|
if (this.isRotationInProgress) {
|
|
@@ -141,6 +207,36 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
141
207
|
this.isRotationInProgress = false;
|
|
142
208
|
}
|
|
143
209
|
}
|
|
210
|
+
scheduleRetiredDriverDisposal(force) {
|
|
211
|
+
const nextDisposal = this.retiredDriverDisposal
|
|
212
|
+
.catch((error) => {
|
|
213
|
+
this.LOGGER.error(`Retired driver disposal chain failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
214
|
+
})
|
|
215
|
+
.then(async () => {
|
|
216
|
+
await this.disposeRetiredDrivers(force);
|
|
217
|
+
});
|
|
218
|
+
this.retiredDriverDisposal = nextDisposal;
|
|
219
|
+
return nextDisposal;
|
|
220
|
+
}
|
|
221
|
+
trackQueryRunnerRelease(generation, queryRunner) {
|
|
222
|
+
const originalRelease = queryRunner.release.bind(queryRunner);
|
|
223
|
+
let isTrackedReleaseHandled = false;
|
|
224
|
+
queryRunner.release = async () => {
|
|
225
|
+
let shouldHandleTrackedRelease = false;
|
|
226
|
+
try {
|
|
227
|
+
await originalRelease();
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
shouldHandleTrackedRelease = !isTrackedReleaseHandled;
|
|
231
|
+
isTrackedReleaseHandled = true;
|
|
232
|
+
}
|
|
233
|
+
if (!shouldHandleTrackedRelease) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
this.decrementActiveQueryRunnerCount(generation);
|
|
237
|
+
await this.scheduleRetiredDriverDisposal(false);
|
|
238
|
+
};
|
|
239
|
+
}
|
|
144
240
|
async validateConnectionHealth(dataSource) {
|
|
145
241
|
if (!dataSource.isInitialized) {
|
|
146
242
|
this.LOGGER.warn("Current data source is not initialized, no health check needed");
|
|
@@ -149,8 +245,12 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
149
245
|
try {
|
|
150
246
|
// Test the current connection with a simple query
|
|
151
247
|
const queryRunner = dataSource.createQueryRunner();
|
|
152
|
-
|
|
153
|
-
|
|
248
|
+
try {
|
|
249
|
+
await queryRunner.query("SELECT 1");
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
await queryRunner.release();
|
|
253
|
+
}
|
|
154
254
|
this.LOGGER.debug("Current connection is healthy");
|
|
155
255
|
}
|
|
156
256
|
catch (error) {
|
|
@@ -158,11 +258,21 @@ let RotatorService = RotatorService_1 = class RotatorService {
|
|
|
158
258
|
// We don't throw here, we'll continue with rotation to fix the issue
|
|
159
259
|
}
|
|
160
260
|
}
|
|
261
|
+
validateRotationDataSourceOptions(options) {
|
|
262
|
+
if (options.type === "mysql" || options.type === "postgres") {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
throw new Error(`Database rotation is supported only for "mysql" and "postgres" data sources. Received: "${options.type}".`);
|
|
266
|
+
}
|
|
161
267
|
async verifyNewConnection(dataSource) {
|
|
162
268
|
try {
|
|
163
269
|
const queryRunner = dataSource.createQueryRunner();
|
|
164
|
-
|
|
165
|
-
|
|
270
|
+
try {
|
|
271
|
+
await queryRunner.query("SELECT 1");
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
await queryRunner.release();
|
|
275
|
+
}
|
|
166
276
|
this.LOGGER.debug("New connection verified successfully");
|
|
167
277
|
}
|
|
168
278
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotator.service.js","sources":["../../../../../../src/modules/typeorm-aws-connector/rotator/rotator.service.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"rotator.service.js","sources":["../../../../../../src/modules/typeorm-aws-connector/rotator/rotator.service.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elsikora/nestjs-typeorm-aws-connector",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1-dev.1",
|
|
4
4
|
"description": "NestJS application configuration with AWS Parameter Store and TypeORM",
|
|
5
5
|
"homepage": "https://github.com/ElsiKora/NestJS-AWS-TypeORM-Connector#readme",
|
|
6
6
|
"bugs": {
|