claude-flow-novice 2.16.0 → 2.16.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/.claude/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
- package/.claude/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
- package/.claude/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
- package/.claude/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
- package/.claude/commands/CFN_LOOP_FRONTEND.md +1 -1
- package/.claude/commands/cfn-loop-cli.md +124 -46
- package/.claude/commands/cfn-loop-frontend.md +1 -1
- package/.claude/commands/cfn-loop-task.md +2 -2
- package/.claude/commands/deprecated/cfn-loop.md +2 -2
- package/.claude/hooks/cfn-invoke-post-edit.sh +31 -5
- package/.claude/hooks/cfn-post-edit.config.json +9 -2
- package/.claude/root-claude-distribute/CFN-CLAUDE.md +1 -1
- package/.claude/skills/cfn-backlog-management/SKILL.md +1 -1
- package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +1 -1
- package/claude-assets/agents/cfn-dev-team/analysts/root-cause-analyst.md +2 -2
- package/claude-assets/agents/cfn-dev-team/architecture/base-template-generator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +2 -2
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +2 -2
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +2 -2
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/documentation/pseudocode.md +1 -1
- package/claude-assets/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +1 -1
- package/claude-assets/agents/cfn-dev-team/product-owners/cto-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/product-owners/power-user-persona.md +1 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +11 -0
- package/claude-assets/agents/cfn-dev-team/utility/analyst.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/claude-code-expert.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +1 -1
- package/claude-assets/agents/custom/cfn-docker-expert.md +1 -0
- package/claude-assets/agents/custom/cfn-loops-cli-expert.md +326 -17
- package/claude-assets/agents/custom/cfn-redis-operations.md +529 -529
- package/claude-assets/agents/custom/cfn-system-expert.md +1 -1
- package/claude-assets/agents/custom/trigger-dev-expert.md +369 -0
- package/claude-assets/agents/docker-team/micro-sprint-planner.md +747 -747
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
- package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
- package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
- package/claude-assets/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
- package/claude-assets/commands/CFN_LOOP_FRONTEND.md +1 -1
- package/claude-assets/commands/cfn-loop-cli.md +124 -46
- package/claude-assets/commands/cfn-loop-frontend.md +1 -1
- package/claude-assets/commands/cfn-loop-task.md +2 -2
- package/claude-assets/commands/deprecated/cfn-loop.md +2 -2
- package/claude-assets/hooks/GIT-HOOKS-USAGE-EXAMPLES.md +116 -0
- package/claude-assets/hooks/README-GIT-HOOKS.md +443 -0
- package/claude-assets/hooks/cfn-invoke-post-edit.sh +31 -5
- package/claude-assets/hooks/cfn-post-edit.config.json +9 -2
- package/claude-assets/hooks/install-git-hooks.sh +243 -0
- package/claude-assets/hooks/subagent-start.sh +98 -0
- package/claude-assets/hooks/subagent-stop.sh +93 -0
- package/claude-assets/hooks/validators/credential-scanner.sh +172 -0
- package/claude-assets/root-claude-distribute/CFN-CLAUDE.md +1 -1
- package/claude-assets/skills/cfn-backlog-management/SKILL.md +1 -1
- package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +41 -13
- package/claude-assets/skills/cfn-dependency-ingestion/ingest.sh +237 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/cli-mode-dependencies.txt +73 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/shared-dependencies.txt +57 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-dev-dependencies.txt +82 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-mode-dependencies.txt +80 -0
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +14 -4
- package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +1 -1
- package/claude-assets/skills/cfn-provider-routing/SKILL.md +23 -0
- package/claude-assets/skills/docker-build/build.sh +1 -1
- package/dist/agent/skill-mcp-selector.js +2 -1
- package/dist/agent/skill-mcp-selector.js.map +1 -1
- package/dist/agents/agent-loader.js +165 -146
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/cli/agent-executor.js +470 -26
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +2 -2
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +7 -4
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/agent-spawner.js +51 -4
- package/dist/cli/agent-spawner.js.map +1 -1
- package/dist/cli/agent-token-manager.js +2 -1
- package/dist/cli/agent-token-manager.js.map +1 -1
- package/dist/cli/anthropic-client.js +117 -11
- package/dist/cli/anthropic-client.js.map +1 -1
- package/dist/cli/cfn-context.js +2 -1
- package/dist/cli/cfn-context.js.map +1 -1
- package/dist/cli/cfn-metrics.js +2 -1
- package/dist/cli/cfn-metrics.js.map +1 -1
- package/dist/cli/cfn-redis.js +2 -1
- package/dist/cli/cfn-redis.js.map +1 -1
- package/dist/cli/cli-agent-context.js +2 -0
- package/dist/cli/cli-agent-context.js.map +1 -1
- package/dist/cli/config-manager.js +4 -252
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/conversation-fork-cleanup.js +2 -1
- package/dist/cli/conversation-fork-cleanup.js.map +1 -1
- package/dist/cli/conversation-fork.js +2 -1
- package/dist/cli/conversation-fork.js.map +1 -1
- package/dist/cli/coordination/agent-messaging.js +415 -0
- package/dist/cli/coordination/agent-messaging.js.map +1 -0
- package/dist/cli/coordination/wait-for-threshold.js +232 -0
- package/dist/cli/coordination/wait-for-threshold.js.map +1 -0
- package/dist/cli/iteration-history.js +2 -1
- package/dist/cli/iteration-history.js.map +1 -1
- package/dist/cli/process-lifecycle.js +5 -1
- package/dist/cli/process-lifecycle.js.map +1 -1
- package/dist/cli/spawn-agent-cli.js +41 -6
- package/dist/cli/spawn-agent-cli.js.map +1 -1
- package/dist/coordination/redis-waiting-mode.js +4 -0
- package/dist/coordination/redis-waiting-mode.js.map +1 -1
- package/dist/lib/artifact-registry.js +4 -0
- package/dist/lib/artifact-registry.js.map +1 -1
- package/dist/lib/connection-pool.js +390 -0
- package/dist/lib/connection-pool.js.map +1 -0
- package/dist/lib/environment-contract.js +258 -0
- package/dist/lib/environment-contract.js.map +1 -0
- package/dist/lib/query-optimizer.js +388 -0
- package/dist/lib/query-optimizer.js.map +1 -0
- package/dist/lib/result-cache.js +285 -0
- package/dist/lib/result-cache.js.map +1 -0
- package/dist/mcp/auth-middleware.js +2 -1
- package/dist/mcp/auth-middleware.js.map +1 -1
- package/dist/mcp/playwright-mcp-server-auth.js +2 -1
- package/dist/mcp/playwright-mcp-server-auth.js.map +1 -1
- package/package.json +3 -1
- package/scripts/build-agent-image.sh +1 -1
- package/scripts/cost-allocation-tracker.sh +632 -0
- package/scripts/docker-rebuild-all-agents.sh +2 -2
- package/scripts/reorganize-tests.sh +280 -0
- package/scripts/trigger-dev-setup.sh +12 -0
- package/tests/README.md +45 -0
- package/.claude/commands/cost-savings-status.md +0 -34
- package/.claude/commands/metrics-summary.md +0 -58
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +0 -768
- package/claude-assets/agents/custom/test-mcp-access.md +0 -24
- package/claude-assets/commands/cost-savings-status.md +0 -34
- package/claude-assets/commands/metrics-summary.md +0 -58
- package/tests/test-memory-leak-task-mode.sh +0 -435
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Pool Manager - Phase 6 Performance Optimization
|
|
3
|
+
*
|
|
4
|
+
* Implements enterprise-grade connection pooling for PostgreSQL and Redis
|
|
5
|
+
* to achieve 3-5x throughput improvement over direct connections.
|
|
6
|
+
*
|
|
7
|
+
* Performance targets:
|
|
8
|
+
* - Direct connection: ~50ms overhead per query
|
|
9
|
+
* - Pooled connection: ~5ms overhead per query
|
|
10
|
+
* - 3-5x throughput improvement
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - PostgreSQL pg-pool with configurable max connections (default 20)
|
|
14
|
+
* - Redis ioredis with connection pooling
|
|
15
|
+
* - Connection health checks
|
|
16
|
+
* - Graceful shutdown handlers
|
|
17
|
+
* - Error recovery with reconnection logic
|
|
18
|
+
* - Prometheus metrics exposure
|
|
19
|
+
* - TypeScript strict mode (no any types)
|
|
20
|
+
*/ import { Pool } from 'pg';
|
|
21
|
+
import Redis from 'ioredis';
|
|
22
|
+
/**
|
|
23
|
+
* PostgreSQL connection pool singleton
|
|
24
|
+
*/ export let pgPool = null;
|
|
25
|
+
/**
|
|
26
|
+
* Redis connection pool singleton
|
|
27
|
+
*/ export let redisPool = null;
|
|
28
|
+
/**
|
|
29
|
+
* Initialization state tracking
|
|
30
|
+
*/ let isInitialized = false;
|
|
31
|
+
let isShuttingDown = false;
|
|
32
|
+
let initializationPromise = null;
|
|
33
|
+
/**
|
|
34
|
+
* Metrics tracking
|
|
35
|
+
*/ let postgresCommandsProcessed = 0;
|
|
36
|
+
let redisCommandsProcessed = 0;
|
|
37
|
+
/**
|
|
38
|
+
* Initialize connection pools
|
|
39
|
+
* Thread-safe initialization with promise-based mutex
|
|
40
|
+
*/ export async function initializePools(config) {
|
|
41
|
+
// Return if already initialized
|
|
42
|
+
if (isInitialized && pgPool && redisPool) {
|
|
43
|
+
console.log('Connection pools already initialized');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Wait for in-progress initialization
|
|
47
|
+
if (initializationPromise) {
|
|
48
|
+
console.log('Waiting for in-progress initialization...');
|
|
49
|
+
return initializationPromise;
|
|
50
|
+
}
|
|
51
|
+
// Atomic initialization with promise-based mutex
|
|
52
|
+
initializationPromise = (async ()=>{
|
|
53
|
+
try {
|
|
54
|
+
console.log('Initializing connection pools...');
|
|
55
|
+
// Validate configuration
|
|
56
|
+
validatePoolConfig(config);
|
|
57
|
+
// Initialize PostgreSQL pool
|
|
58
|
+
await initializePostgresPool(config.postgres);
|
|
59
|
+
// Initialize Redis pool
|
|
60
|
+
await initializeRedisPool(config.redis);
|
|
61
|
+
// Register shutdown handlers
|
|
62
|
+
registerShutdownHandlers();
|
|
63
|
+
isInitialized = true;
|
|
64
|
+
console.log('Connection pools initialized successfully');
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Failed to initialize connection pools:', error);
|
|
67
|
+
// Cleanup on failure
|
|
68
|
+
await shutdownPools();
|
|
69
|
+
throw error;
|
|
70
|
+
} finally{
|
|
71
|
+
// Clear lock after initialization completes or fails
|
|
72
|
+
initializationPromise = null;
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
return initializationPromise;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate pool configuration
|
|
79
|
+
*/ function validatePoolConfig(config) {
|
|
80
|
+
// Validate PostgreSQL config
|
|
81
|
+
if (config.postgres.max < 4) {
|
|
82
|
+
throw new Error(`Invalid PostgreSQL max connections: ${config.postgres.max}. Minimum allowed is 4.`);
|
|
83
|
+
}
|
|
84
|
+
if (config.postgres.max > 100) {
|
|
85
|
+
throw new Error(`Invalid PostgreSQL max connections: ${config.postgres.max}. Maximum allowed is 100.`);
|
|
86
|
+
}
|
|
87
|
+
if (config.postgres.idleTimeoutMillis < 1000) {
|
|
88
|
+
throw new Error(`Invalid idle timeout: ${config.postgres.idleTimeoutMillis}ms. Minimum allowed is 1000ms.`);
|
|
89
|
+
}
|
|
90
|
+
if (config.postgres.connectionTimeoutMillis < 1000) {
|
|
91
|
+
throw new Error(`Invalid connection timeout: ${config.postgres.connectionTimeoutMillis}ms. Minimum allowed is 1000ms.`);
|
|
92
|
+
}
|
|
93
|
+
// Validate Redis config
|
|
94
|
+
if (config.redis.maxRetriesPerRequest < 0) {
|
|
95
|
+
throw new Error(`Invalid max retries: ${config.redis.maxRetriesPerRequest}. Must be >= 0.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Initialize PostgreSQL connection pool
|
|
100
|
+
*/ async function initializePostgresPool(config) {
|
|
101
|
+
const poolConfig = {
|
|
102
|
+
host: config.host,
|
|
103
|
+
port: config.port,
|
|
104
|
+
database: config.database,
|
|
105
|
+
user: config.user,
|
|
106
|
+
password: config.password,
|
|
107
|
+
max: config.max,
|
|
108
|
+
idleTimeoutMillis: config.idleTimeoutMillis,
|
|
109
|
+
connectionTimeoutMillis: config.connectionTimeoutMillis,
|
|
110
|
+
// Additional pool settings for production
|
|
111
|
+
allowExitOnIdle: false,
|
|
112
|
+
application_name: 'cfn-connection-pool'
|
|
113
|
+
};
|
|
114
|
+
pgPool = new Pool(poolConfig);
|
|
115
|
+
// Handle pool errors with reconnection logic
|
|
116
|
+
pgPool.on('error', (err, client)=>{
|
|
117
|
+
console.error('Unexpected error on idle PostgreSQL client:', err.message);
|
|
118
|
+
// Error will trigger reconnection on next query
|
|
119
|
+
});
|
|
120
|
+
// Connection event tracking
|
|
121
|
+
pgPool.on('connect', (client)=>{
|
|
122
|
+
console.log('New PostgreSQL client connected to pool');
|
|
123
|
+
});
|
|
124
|
+
pgPool.on('acquire', (client)=>{
|
|
125
|
+
// Connection acquired from pool
|
|
126
|
+
});
|
|
127
|
+
pgPool.on('remove', (client)=>{
|
|
128
|
+
console.log('PostgreSQL client removed from pool');
|
|
129
|
+
});
|
|
130
|
+
// Test connection with timeout
|
|
131
|
+
try {
|
|
132
|
+
const client = await pgPool.connect();
|
|
133
|
+
try {
|
|
134
|
+
await client.query('SELECT 1 AS health_check');
|
|
135
|
+
console.log('PostgreSQL connection pool health check passed');
|
|
136
|
+
} finally{
|
|
137
|
+
client.release();
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
const error = err;
|
|
141
|
+
console.error('Failed to initialize PostgreSQL connection pool:', error.message);
|
|
142
|
+
throw new Error(`PostgreSQL pool initialization failed: ${error.message}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Initialize Redis connection pool
|
|
147
|
+
*/ async function initializeRedisPool(config) {
|
|
148
|
+
const redisOptions = {
|
|
149
|
+
host: config.host,
|
|
150
|
+
port: config.port,
|
|
151
|
+
password: config.password,
|
|
152
|
+
maxRetriesPerRequest: config.maxRetriesPerRequest,
|
|
153
|
+
// Connection pooling settings
|
|
154
|
+
lazyConnect: false,
|
|
155
|
+
keepAlive: 30000,
|
|
156
|
+
connectTimeout: 10000,
|
|
157
|
+
retryStrategy: (times)=>{
|
|
158
|
+
if (times > 10) {
|
|
159
|
+
console.error('Redis connection retry limit exceeded');
|
|
160
|
+
return undefined; // Stop retrying
|
|
161
|
+
}
|
|
162
|
+
// Exponential backoff: 100ms, 200ms, 400ms, 800ms, ...
|
|
163
|
+
const delay = Math.min(100 * Math.pow(2, times), 3000);
|
|
164
|
+
console.log(`Redis reconnection attempt ${times} in ${delay}ms`);
|
|
165
|
+
return delay;
|
|
166
|
+
},
|
|
167
|
+
reconnectOnError: (err)=>{
|
|
168
|
+
// Reconnect on specific errors
|
|
169
|
+
const targetErrors = [
|
|
170
|
+
'READONLY',
|
|
171
|
+
'ECONNREFUSED',
|
|
172
|
+
'ETIMEDOUT'
|
|
173
|
+
];
|
|
174
|
+
return targetErrors.some((target)=>err.message.includes(target));
|
|
175
|
+
},
|
|
176
|
+
enableReadyCheck: true,
|
|
177
|
+
enableOfflineQueue: true
|
|
178
|
+
};
|
|
179
|
+
redisPool = new Redis(redisOptions);
|
|
180
|
+
// Handle Redis events
|
|
181
|
+
redisPool.on('error', (err)=>{
|
|
182
|
+
console.error('Redis connection error:', err.message);
|
|
183
|
+
});
|
|
184
|
+
redisPool.on('ready', ()=>{
|
|
185
|
+
console.log('Redis connection ready');
|
|
186
|
+
});
|
|
187
|
+
redisPool.on('connect', ()=>{
|
|
188
|
+
console.log('Redis client connected');
|
|
189
|
+
});
|
|
190
|
+
redisPool.on('reconnecting', (delay)=>{
|
|
191
|
+
console.log(`Redis reconnecting in ${delay}ms`);
|
|
192
|
+
});
|
|
193
|
+
redisPool.on('close', ()=>{
|
|
194
|
+
console.log('Redis connection closed');
|
|
195
|
+
});
|
|
196
|
+
// Test connection with timeout
|
|
197
|
+
try {
|
|
198
|
+
const result = await redisPool.ping();
|
|
199
|
+
if (result !== 'PONG') {
|
|
200
|
+
throw new Error(`Unexpected ping response: ${result}`);
|
|
201
|
+
}
|
|
202
|
+
console.log('Redis connection pool health check passed');
|
|
203
|
+
} catch (err) {
|
|
204
|
+
const error = err;
|
|
205
|
+
console.error('Failed to initialize Redis connection pool:', error.message);
|
|
206
|
+
throw new Error(`Redis pool initialization failed: ${error.message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Register graceful shutdown handlers
|
|
211
|
+
*/ function registerShutdownHandlers() {
|
|
212
|
+
const shutdownHandler = async (signal)=>{
|
|
213
|
+
if (isShuttingDown) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
console.log(`Received ${signal}, shutting down connection pools...`);
|
|
217
|
+
await shutdownPools();
|
|
218
|
+
process.exit(0);
|
|
219
|
+
};
|
|
220
|
+
process.on('SIGTERM', ()=>shutdownHandler('SIGTERM'));
|
|
221
|
+
process.on('SIGINT', ()=>shutdownHandler('SIGINT'));
|
|
222
|
+
// Handle uncaught errors
|
|
223
|
+
process.on('uncaughtException', async (err)=>{
|
|
224
|
+
console.error('Uncaught exception:', err);
|
|
225
|
+
await shutdownPools();
|
|
226
|
+
process.exit(1);
|
|
227
|
+
});
|
|
228
|
+
process.on('unhandledRejection', async (reason)=>{
|
|
229
|
+
console.error('Unhandled rejection:', reason);
|
|
230
|
+
await shutdownPools();
|
|
231
|
+
process.exit(1);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Gracefully shutdown all connection pools
|
|
236
|
+
*/ export async function shutdownPools() {
|
|
237
|
+
if (isShuttingDown) {
|
|
238
|
+
console.log('Shutdown already in progress');
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
console.log('Initiating graceful connection pool shutdown...');
|
|
242
|
+
isShuttingDown = true;
|
|
243
|
+
const shutdownPromises = [];
|
|
244
|
+
// Shutdown PostgreSQL pool
|
|
245
|
+
if (pgPool) {
|
|
246
|
+
shutdownPromises.push(pgPool.end().then(()=>{
|
|
247
|
+
console.log('PostgreSQL connection pool closed');
|
|
248
|
+
pgPool = null;
|
|
249
|
+
}).catch((err)=>{
|
|
250
|
+
console.error('Error closing PostgreSQL pool:', err.message);
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
// Shutdown Redis pool
|
|
254
|
+
if (redisPool) {
|
|
255
|
+
shutdownPromises.push(redisPool.quit().then(()=>{
|
|
256
|
+
console.log('Redis connection pool closed');
|
|
257
|
+
redisPool = null;
|
|
258
|
+
}).catch((err)=>{
|
|
259
|
+
console.error('Error closing Redis pool:', err.message);
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
await Promise.all(shutdownPromises);
|
|
263
|
+
isInitialized = false;
|
|
264
|
+
isShuttingDown = false;
|
|
265
|
+
console.log('Connection pool shutdown complete');
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get pool metrics for Prometheus monitoring
|
|
269
|
+
*/ export function getPoolMetrics() {
|
|
270
|
+
if (!pgPool || !redisPool) {
|
|
271
|
+
throw new Error('Connection pools not initialized. Call initializePools first.');
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
postgres: {
|
|
275
|
+
totalConnections: pgPool.totalCount,
|
|
276
|
+
idleConnections: pgPool.idleCount,
|
|
277
|
+
waitingRequests: pgPool.waitingCount,
|
|
278
|
+
activeConnections: pgPool.totalCount - pgPool.idleCount
|
|
279
|
+
},
|
|
280
|
+
redis: {
|
|
281
|
+
status: redisPool.status,
|
|
282
|
+
connectedClients: 1,
|
|
283
|
+
commandsProcessed: redisCommandsProcessed
|
|
284
|
+
},
|
|
285
|
+
timestamp: new Date().toISOString()
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Execute PostgreSQL query with pooled connection
|
|
290
|
+
* Automatically handles connection acquisition and release
|
|
291
|
+
*/ export async function executePostgresQuery(query, params) {
|
|
292
|
+
if (!pgPool) {
|
|
293
|
+
throw new Error('PostgreSQL pool not initialized. Call initializePools first.');
|
|
294
|
+
}
|
|
295
|
+
if (isShuttingDown) {
|
|
296
|
+
throw new Error('Connection pool is shutting down');
|
|
297
|
+
}
|
|
298
|
+
let client = null;
|
|
299
|
+
try {
|
|
300
|
+
client = await pgPool.connect();
|
|
301
|
+
const result = await client.query(query, params);
|
|
302
|
+
postgresCommandsProcessed++;
|
|
303
|
+
return result.rows;
|
|
304
|
+
} catch (err) {
|
|
305
|
+
const error = err;
|
|
306
|
+
console.error('PostgreSQL query error:', error.message);
|
|
307
|
+
throw new Error(`PostgreSQL query failed: ${error.message}`);
|
|
308
|
+
} finally{
|
|
309
|
+
if (client) {
|
|
310
|
+
client.release();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Execute Redis command with pooled connection
|
|
316
|
+
* Automatically handles reconnection on failure
|
|
317
|
+
*/ export async function executeRedisCommand(command, ...args) {
|
|
318
|
+
if (!redisPool) {
|
|
319
|
+
throw new Error('Redis pool not initialized. Call initializePools first.');
|
|
320
|
+
}
|
|
321
|
+
if (isShuttingDown) {
|
|
322
|
+
throw new Error('Connection pool is shutting down');
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
// Type-safe command execution using unknown cast
|
|
326
|
+
const redisClient = redisPool;
|
|
327
|
+
const result = await redisClient[command](...args);
|
|
328
|
+
redisCommandsProcessed++;
|
|
329
|
+
return result;
|
|
330
|
+
} catch (err) {
|
|
331
|
+
const error = err;
|
|
332
|
+
console.error(`Redis ${command} command error:`, error.message);
|
|
333
|
+
throw new Error(`Redis command failed: ${error.message}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Health check for all connection pools
|
|
338
|
+
*/ export async function healthCheck() {
|
|
339
|
+
const health = {
|
|
340
|
+
postgres: false,
|
|
341
|
+
redis: false,
|
|
342
|
+
details: {
|
|
343
|
+
postgres: 'not initialized',
|
|
344
|
+
redis: 'not initialized'
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
// Check PostgreSQL
|
|
348
|
+
if (pgPool) {
|
|
349
|
+
try {
|
|
350
|
+
await executePostgresQuery('SELECT 1 AS health_check');
|
|
351
|
+
health.postgres = true;
|
|
352
|
+
health.details.postgres = 'healthy';
|
|
353
|
+
} catch (err) {
|
|
354
|
+
const error = err;
|
|
355
|
+
health.details.postgres = error.message;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Check Redis
|
|
359
|
+
if (redisPool) {
|
|
360
|
+
try {
|
|
361
|
+
await redisPool.ping();
|
|
362
|
+
health.redis = true;
|
|
363
|
+
health.details.redis = 'healthy';
|
|
364
|
+
} catch (err) {
|
|
365
|
+
const error = err;
|
|
366
|
+
health.details.redis = error.message;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return health;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get PostgreSQL pool instance (for advanced use cases)
|
|
373
|
+
* Most code should use executePostgresQuery instead
|
|
374
|
+
*/ export function getPostgresPool() {
|
|
375
|
+
if (!pgPool) {
|
|
376
|
+
throw new Error('PostgreSQL pool not initialized. Call initializePools first.');
|
|
377
|
+
}
|
|
378
|
+
return pgPool;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get Redis pool instance (for advanced use cases)
|
|
382
|
+
* Most code should use executeRedisCommand instead
|
|
383
|
+
*/ export function getRedisPool() {
|
|
384
|
+
if (!redisPool) {
|
|
385
|
+
throw new Error('Redis pool not initialized. Call initializePools first.');
|
|
386
|
+
}
|
|
387
|
+
return redisPool;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
//# sourceMappingURL=connection-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/connection-pool.ts"],"sourcesContent":["/**\r\n * Connection Pool Manager - Phase 6 Performance Optimization\r\n *\r\n * Implements enterprise-grade connection pooling for PostgreSQL and Redis\r\n * to achieve 3-5x throughput improvement over direct connections.\r\n *\r\n * Performance targets:\r\n * - Direct connection: ~50ms overhead per query\r\n * - Pooled connection: ~5ms overhead per query\r\n * - 3-5x throughput improvement\r\n *\r\n * Features:\r\n * - PostgreSQL pg-pool with configurable max connections (default 20)\r\n * - Redis ioredis with connection pooling\r\n * - Connection health checks\r\n * - Graceful shutdown handlers\r\n * - Error recovery with reconnection logic\r\n * - Prometheus metrics exposure\r\n * - TypeScript strict mode (no any types)\r\n */\r\n\r\nimport { Pool, PoolClient, PoolConfig as PgPoolConfig } from 'pg';\r\nimport Redis, { RedisOptions } from 'ioredis';\r\n\r\n/**\r\n * Pool configuration interface\r\n */\r\nexport interface PoolConfig {\r\n postgres: {\r\n host: string;\r\n port: number;\r\n database: string;\r\n user: string;\r\n password: string;\r\n max: number; // Maximum pool size\r\n idleTimeoutMillis: number; // Idle connection timeout\r\n connectionTimeoutMillis: number; // Connection acquisition timeout\r\n };\r\n redis: {\r\n host: string;\r\n port: number;\r\n password?: string;\r\n maxRetriesPerRequest: number; // Max retry attempts per request\r\n };\r\n}\r\n\r\n/**\r\n * Pool metrics for Prometheus monitoring\r\n */\r\nexport interface PoolMetrics {\r\n postgres: {\r\n totalConnections: number;\r\n idleConnections: number;\r\n waitingRequests: number;\r\n activeConnections: number;\r\n };\r\n redis: {\r\n status: string;\r\n connectedClients: number;\r\n commandsProcessed: number;\r\n };\r\n timestamp: string;\r\n}\r\n\r\n/**\r\n * Health check result\r\n */\r\ninterface HealthCheckResult {\r\n postgres: boolean;\r\n redis: boolean;\r\n details: {\r\n postgres: string;\r\n redis: string;\r\n };\r\n}\r\n\r\n/**\r\n * PostgreSQL connection pool singleton\r\n */\r\nexport let pgPool: Pool | null = null;\r\n\r\n/**\r\n * Redis connection pool singleton\r\n */\r\nexport let redisPool: Redis | null = null;\r\n\r\n/**\r\n * Initialization state tracking\r\n */\r\nlet isInitialized = false;\r\nlet isShuttingDown = false;\r\nlet initializationPromise: Promise<void> | null = null;\r\n\r\n/**\r\n * Metrics tracking\r\n */\r\nlet postgresCommandsProcessed = 0;\r\nlet redisCommandsProcessed = 0;\r\n\r\n/**\r\n * Initialize connection pools\r\n * Thread-safe initialization with promise-based mutex\r\n */\r\nexport async function initializePools(config: PoolConfig): Promise<void> {\r\n // Return if already initialized\r\n if (isInitialized && pgPool && redisPool) {\r\n console.log('Connection pools already initialized');\r\n return;\r\n }\r\n\r\n // Wait for in-progress initialization\r\n if (initializationPromise) {\r\n console.log('Waiting for in-progress initialization...');\r\n return initializationPromise;\r\n }\r\n\r\n // Atomic initialization with promise-based mutex\r\n initializationPromise = (async () => {\r\n try {\r\n console.log('Initializing connection pools...');\r\n\r\n // Validate configuration\r\n validatePoolConfig(config);\r\n\r\n // Initialize PostgreSQL pool\r\n await initializePostgresPool(config.postgres);\r\n\r\n // Initialize Redis pool\r\n await initializeRedisPool(config.redis);\r\n\r\n // Register shutdown handlers\r\n registerShutdownHandlers();\r\n\r\n isInitialized = true;\r\n console.log('Connection pools initialized successfully');\r\n } catch (error) {\r\n console.error('Failed to initialize connection pools:', error);\r\n // Cleanup on failure\r\n await shutdownPools();\r\n throw error;\r\n } finally {\r\n // Clear lock after initialization completes or fails\r\n initializationPromise = null;\r\n }\r\n })();\r\n\r\n return initializationPromise;\r\n}\r\n\r\n/**\r\n * Validate pool configuration\r\n */\r\nfunction validatePoolConfig(config: PoolConfig): void {\r\n // Validate PostgreSQL config\r\n if (config.postgres.max < 4) {\r\n throw new Error(\r\n `Invalid PostgreSQL max connections: ${config.postgres.max}. Minimum allowed is 4.`\r\n );\r\n }\r\n if (config.postgres.max > 100) {\r\n throw new Error(\r\n `Invalid PostgreSQL max connections: ${config.postgres.max}. Maximum allowed is 100.`\r\n );\r\n }\r\n if (config.postgres.idleTimeoutMillis < 1000) {\r\n throw new Error(\r\n `Invalid idle timeout: ${config.postgres.idleTimeoutMillis}ms. Minimum allowed is 1000ms.`\r\n );\r\n }\r\n if (config.postgres.connectionTimeoutMillis < 1000) {\r\n throw new Error(\r\n `Invalid connection timeout: ${config.postgres.connectionTimeoutMillis}ms. Minimum allowed is 1000ms.`\r\n );\r\n }\r\n\r\n // Validate Redis config\r\n if (config.redis.maxRetriesPerRequest < 0) {\r\n throw new Error(\r\n `Invalid max retries: ${config.redis.maxRetriesPerRequest}. Must be >= 0.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initialize PostgreSQL connection pool\r\n */\r\nasync function initializePostgresPool(\r\n config: PoolConfig['postgres']\r\n): Promise<void> {\r\n const poolConfig: PgPoolConfig = {\r\n host: config.host,\r\n port: config.port,\r\n database: config.database,\r\n user: config.user,\r\n password: config.password,\r\n max: config.max,\r\n idleTimeoutMillis: config.idleTimeoutMillis,\r\n connectionTimeoutMillis: config.connectionTimeoutMillis,\r\n // Additional pool settings for production\r\n allowExitOnIdle: false,\r\n application_name: 'cfn-connection-pool',\r\n };\r\n\r\n pgPool = new Pool(poolConfig);\r\n\r\n // Handle pool errors with reconnection logic\r\n pgPool.on('error', (err: Error, client: PoolClient) => {\r\n console.error('Unexpected error on idle PostgreSQL client:', err.message);\r\n // Error will trigger reconnection on next query\r\n });\r\n\r\n // Connection event tracking\r\n pgPool.on('connect', (client: PoolClient) => {\r\n console.log('New PostgreSQL client connected to pool');\r\n });\r\n\r\n pgPool.on('acquire', (client: PoolClient) => {\r\n // Connection acquired from pool\r\n });\r\n\r\n pgPool.on('remove', (client: PoolClient) => {\r\n console.log('PostgreSQL client removed from pool');\r\n });\r\n\r\n // Test connection with timeout\r\n try {\r\n const client = await pgPool.connect();\r\n try {\r\n await client.query('SELECT 1 AS health_check');\r\n console.log('PostgreSQL connection pool health check passed');\r\n } finally {\r\n client.release();\r\n }\r\n } catch (err) {\r\n const error = err as Error;\r\n console.error('Failed to initialize PostgreSQL connection pool:', error.message);\r\n throw new Error(`PostgreSQL pool initialization failed: ${error.message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Initialize Redis connection pool\r\n */\r\nasync function initializeRedisPool(config: PoolConfig['redis']): Promise<void> {\r\n const redisOptions: RedisOptions = {\r\n host: config.host,\r\n port: config.port,\r\n password: config.password,\r\n maxRetriesPerRequest: config.maxRetriesPerRequest,\r\n // Connection pooling settings\r\n lazyConnect: false,\r\n keepAlive: 30000, // Keep connections alive\r\n connectTimeout: 10000, // 10s connection timeout\r\n retryStrategy: (times: number): number | void => {\r\n if (times > 10) {\r\n console.error('Redis connection retry limit exceeded');\r\n return undefined; // Stop retrying\r\n }\r\n // Exponential backoff: 100ms, 200ms, 400ms, 800ms, ...\r\n const delay = Math.min(100 * Math.pow(2, times), 3000);\r\n console.log(`Redis reconnection attempt ${times} in ${delay}ms`);\r\n return delay;\r\n },\r\n reconnectOnError: (err: Error): boolean => {\r\n // Reconnect on specific errors\r\n const targetErrors = ['READONLY', 'ECONNREFUSED', 'ETIMEDOUT'];\r\n return targetErrors.some((target) => err.message.includes(target));\r\n },\r\n enableReadyCheck: true,\r\n enableOfflineQueue: true,\r\n };\r\n\r\n redisPool = new Redis(redisOptions);\r\n\r\n // Handle Redis events\r\n redisPool.on('error', (err: Error) => {\r\n console.error('Redis connection error:', err.message);\r\n });\r\n\r\n redisPool.on('ready', () => {\r\n console.log('Redis connection ready');\r\n });\r\n\r\n redisPool.on('connect', () => {\r\n console.log('Redis client connected');\r\n });\r\n\r\n redisPool.on('reconnecting', (delay: number) => {\r\n console.log(`Redis reconnecting in ${delay}ms`);\r\n });\r\n\r\n redisPool.on('close', () => {\r\n console.log('Redis connection closed');\r\n });\r\n\r\n // Test connection with timeout\r\n try {\r\n const result = await redisPool.ping();\r\n if (result !== 'PONG') {\r\n throw new Error(`Unexpected ping response: ${result}`);\r\n }\r\n console.log('Redis connection pool health check passed');\r\n } catch (err) {\r\n const error = err as Error;\r\n console.error('Failed to initialize Redis connection pool:', error.message);\r\n throw new Error(`Redis pool initialization failed: ${error.message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Register graceful shutdown handlers\r\n */\r\nfunction registerShutdownHandlers(): void {\r\n const shutdownHandler = async (signal: string): Promise<void> => {\r\n if (isShuttingDown) {\r\n return;\r\n }\r\n console.log(`Received ${signal}, shutting down connection pools...`);\r\n await shutdownPools();\r\n process.exit(0);\r\n };\r\n\r\n process.on('SIGTERM', () => shutdownHandler('SIGTERM'));\r\n process.on('SIGINT', () => shutdownHandler('SIGINT'));\r\n\r\n // Handle uncaught errors\r\n process.on('uncaughtException', async (err: Error) => {\r\n console.error('Uncaught exception:', err);\r\n await shutdownPools();\r\n process.exit(1);\r\n });\r\n\r\n process.on('unhandledRejection', async (reason: unknown) => {\r\n console.error('Unhandled rejection:', reason);\r\n await shutdownPools();\r\n process.exit(1);\r\n });\r\n}\r\n\r\n/**\r\n * Gracefully shutdown all connection pools\r\n */\r\nexport async function shutdownPools(): Promise<void> {\r\n if (isShuttingDown) {\r\n console.log('Shutdown already in progress');\r\n return;\r\n }\r\n\r\n console.log('Initiating graceful connection pool shutdown...');\r\n isShuttingDown = true;\r\n\r\n const shutdownPromises: Promise<void>[] = [];\r\n\r\n // Shutdown PostgreSQL pool\r\n if (pgPool) {\r\n shutdownPromises.push(\r\n pgPool\r\n .end()\r\n .then(() => {\r\n console.log('PostgreSQL connection pool closed');\r\n pgPool = null;\r\n })\r\n .catch((err: Error) => {\r\n console.error('Error closing PostgreSQL pool:', err.message);\r\n })\r\n );\r\n }\r\n\r\n // Shutdown Redis pool\r\n if (redisPool) {\r\n shutdownPromises.push(\r\n redisPool\r\n .quit()\r\n .then(() => {\r\n console.log('Redis connection pool closed');\r\n redisPool = null;\r\n })\r\n .catch((err: Error) => {\r\n console.error('Error closing Redis pool:', err.message);\r\n })\r\n );\r\n }\r\n\r\n await Promise.all(shutdownPromises);\r\n\r\n isInitialized = false;\r\n isShuttingDown = false;\r\n console.log('Connection pool shutdown complete');\r\n}\r\n\r\n/**\r\n * Get pool metrics for Prometheus monitoring\r\n */\r\nexport function getPoolMetrics(): PoolMetrics {\r\n if (!pgPool || !redisPool) {\r\n throw new Error('Connection pools not initialized. Call initializePools first.');\r\n }\r\n\r\n return {\r\n postgres: {\r\n totalConnections: pgPool.totalCount,\r\n idleConnections: pgPool.idleCount,\r\n waitingRequests: pgPool.waitingCount,\r\n activeConnections: pgPool.totalCount - pgPool.idleCount,\r\n },\r\n redis: {\r\n status: redisPool.status,\r\n connectedClients: 1, // Single connection per pool\r\n commandsProcessed: redisCommandsProcessed,\r\n },\r\n timestamp: new Date().toISOString(),\r\n };\r\n}\r\n\r\n/**\r\n * Execute PostgreSQL query with pooled connection\r\n * Automatically handles connection acquisition and release\r\n */\r\nexport async function executePostgresQuery<T = Record<string, unknown>>(\r\n query: string,\r\n params?: unknown[]\r\n): Promise<T[]> {\r\n if (!pgPool) {\r\n throw new Error('PostgreSQL pool not initialized. Call initializePools first.');\r\n }\r\n\r\n if (isShuttingDown) {\r\n throw new Error('Connection pool is shutting down');\r\n }\r\n\r\n let client: PoolClient | null = null;\r\n try {\r\n client = await pgPool.connect();\r\n const result = await client.query(query, params);\r\n postgresCommandsProcessed++;\r\n return result.rows as T[];\r\n } catch (err) {\r\n const error = err as Error;\r\n console.error('PostgreSQL query error:', error.message);\r\n throw new Error(`PostgreSQL query failed: ${error.message}`);\r\n } finally {\r\n if (client) {\r\n client.release();\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Execute Redis command with pooled connection\r\n * Automatically handles reconnection on failure\r\n */\r\nexport async function executeRedisCommand<T = unknown>(\r\n command: string,\r\n ...args: unknown[]\r\n): Promise<T> {\r\n if (!redisPool) {\r\n throw new Error('Redis pool not initialized. Call initializePools first.');\r\n }\r\n\r\n if (isShuttingDown) {\r\n throw new Error('Connection pool is shutting down');\r\n }\r\n\r\n try {\r\n // Type-safe command execution using unknown cast\r\n const redisClient = redisPool as unknown as Record<\r\n string,\r\n (...args: unknown[]) => Promise<T>\r\n >;\r\n const result = await redisClient[command](...args);\r\n redisCommandsProcessed++;\r\n return result;\r\n } catch (err) {\r\n const error = err as Error;\r\n console.error(`Redis ${command} command error:`, error.message);\r\n throw new Error(`Redis command failed: ${error.message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Health check for all connection pools\r\n */\r\nexport async function healthCheck(): Promise<HealthCheckResult> {\r\n const health: HealthCheckResult = {\r\n postgres: false,\r\n redis: false,\r\n details: {\r\n postgres: 'not initialized',\r\n redis: 'not initialized',\r\n },\r\n };\r\n\r\n // Check PostgreSQL\r\n if (pgPool) {\r\n try {\r\n await executePostgresQuery('SELECT 1 AS health_check');\r\n health.postgres = true;\r\n health.details.postgres = 'healthy';\r\n } catch (err) {\r\n const error = err as Error;\r\n health.details.postgres = error.message;\r\n }\r\n }\r\n\r\n // Check Redis\r\n if (redisPool) {\r\n try {\r\n await redisPool.ping();\r\n health.redis = true;\r\n health.details.redis = 'healthy';\r\n } catch (err) {\r\n const error = err as Error;\r\n health.details.redis = error.message;\r\n }\r\n }\r\n\r\n return health;\r\n}\r\n\r\n/**\r\n * Get PostgreSQL pool instance (for advanced use cases)\r\n * Most code should use executePostgresQuery instead\r\n */\r\nexport function getPostgresPool(): Pool {\r\n if (!pgPool) {\r\n throw new Error('PostgreSQL pool not initialized. Call initializePools first.');\r\n }\r\n return pgPool;\r\n}\r\n\r\n/**\r\n * Get Redis pool instance (for advanced use cases)\r\n * Most code should use executeRedisCommand instead\r\n */\r\nexport function getRedisPool(): Redis {\r\n if (!redisPool) {\r\n throw new Error('Redis pool not initialized. Call initializePools first.');\r\n }\r\n return redisPool;\r\n}\r\n"],"names":["Pool","Redis","pgPool","redisPool","isInitialized","isShuttingDown","initializationPromise","postgresCommandsProcessed","redisCommandsProcessed","initializePools","config","console","log","validatePoolConfig","initializePostgresPool","postgres","initializeRedisPool","redis","registerShutdownHandlers","error","shutdownPools","max","Error","idleTimeoutMillis","connectionTimeoutMillis","maxRetriesPerRequest","poolConfig","host","port","database","user","password","allowExitOnIdle","application_name","on","err","client","message","connect","query","release","redisOptions","lazyConnect","keepAlive","connectTimeout","retryStrategy","times","undefined","delay","Math","min","pow","reconnectOnError","targetErrors","some","target","includes","enableReadyCheck","enableOfflineQueue","result","ping","shutdownHandler","signal","process","exit","reason","shutdownPromises","push","end","then","catch","quit","Promise","all","getPoolMetrics","totalConnections","totalCount","idleConnections","idleCount","waitingRequests","waitingCount","activeConnections","status","connectedClients","commandsProcessed","timestamp","Date","toISOString","executePostgresQuery","params","rows","executeRedisCommand","command","args","redisClient","healthCheck","health","details","getPostgresPool","getRedisPool"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;CAmBC,GAED,SAASA,IAAI,QAAgD,KAAK;AAClE,OAAOC,WAA6B,UAAU;AAsD9C;;CAEC,GACD,OAAO,IAAIC,SAAsB,KAAK;AAEtC;;CAEC,GACD,OAAO,IAAIC,YAA0B,KAAK;AAE1C;;CAEC,GACD,IAAIC,gBAAgB;AACpB,IAAIC,iBAAiB;AACrB,IAAIC,wBAA8C;AAElD;;CAEC,GACD,IAAIC,4BAA4B;AAChC,IAAIC,yBAAyB;AAE7B;;;CAGC,GACD,OAAO,eAAeC,gBAAgBC,MAAkB;IACtD,gCAAgC;IAChC,IAAIN,iBAAiBF,UAAUC,WAAW;QACxCQ,QAAQC,GAAG,CAAC;QACZ;IACF;IAEA,sCAAsC;IACtC,IAAIN,uBAAuB;QACzBK,QAAQC,GAAG,CAAC;QACZ,OAAON;IACT;IAEA,iDAAiD;IACjDA,wBAAwB,AAAC,CAAA;QACvB,IAAI;YACFK,QAAQC,GAAG,CAAC;YAEZ,yBAAyB;YACzBC,mBAAmBH;YAEnB,6BAA6B;YAC7B,MAAMI,uBAAuBJ,OAAOK,QAAQ;YAE5C,wBAAwB;YACxB,MAAMC,oBAAoBN,OAAOO,KAAK;YAEtC,6BAA6B;YAC7BC;YAEAd,gBAAgB;YAChBO,QAAQC,GAAG,CAAC;QACd,EAAE,OAAOO,OAAO;YACdR,QAAQQ,KAAK,CAAC,0CAA0CA;YACxD,qBAAqB;YACrB,MAAMC;YACN,MAAMD;QACR,SAAU;YACR,qDAAqD;YACrDb,wBAAwB;QAC1B;IACF,CAAA;IAEA,OAAOA;AACT;AAEA;;CAEC,GACD,SAASO,mBAAmBH,MAAkB;IAC5C,6BAA6B;IAC7B,IAAIA,OAAOK,QAAQ,CAACM,GAAG,GAAG,GAAG;QAC3B,MAAM,IAAIC,MACR,CAAC,oCAAoC,EAAEZ,OAAOK,QAAQ,CAACM,GAAG,CAAC,uBAAuB,CAAC;IAEvF;IACA,IAAIX,OAAOK,QAAQ,CAACM,GAAG,GAAG,KAAK;QAC7B,MAAM,IAAIC,MACR,CAAC,oCAAoC,EAAEZ,OAAOK,QAAQ,CAACM,GAAG,CAAC,yBAAyB,CAAC;IAEzF;IACA,IAAIX,OAAOK,QAAQ,CAACQ,iBAAiB,GAAG,MAAM;QAC5C,MAAM,IAAID,MACR,CAAC,sBAAsB,EAAEZ,OAAOK,QAAQ,CAACQ,iBAAiB,CAAC,8BAA8B,CAAC;IAE9F;IACA,IAAIb,OAAOK,QAAQ,CAACS,uBAAuB,GAAG,MAAM;QAClD,MAAM,IAAIF,MACR,CAAC,4BAA4B,EAAEZ,OAAOK,QAAQ,CAACS,uBAAuB,CAAC,8BAA8B,CAAC;IAE1G;IAEA,wBAAwB;IACxB,IAAId,OAAOO,KAAK,CAACQ,oBAAoB,GAAG,GAAG;QACzC,MAAM,IAAIH,MACR,CAAC,qBAAqB,EAAEZ,OAAOO,KAAK,CAACQ,oBAAoB,CAAC,eAAe,CAAC;IAE9E;AACF;AAEA;;CAEC,GACD,eAAeX,uBACbJ,MAA8B;IAE9B,MAAMgB,aAA2B;QAC/BC,MAAMjB,OAAOiB,IAAI;QACjBC,MAAMlB,OAAOkB,IAAI;QACjBC,UAAUnB,OAAOmB,QAAQ;QACzBC,MAAMpB,OAAOoB,IAAI;QACjBC,UAAUrB,OAAOqB,QAAQ;QACzBV,KAAKX,OAAOW,GAAG;QACfE,mBAAmBb,OAAOa,iBAAiB;QAC3CC,yBAAyBd,OAAOc,uBAAuB;QACvD,0CAA0C;QAC1CQ,iBAAiB;QACjBC,kBAAkB;IACpB;IAEA/B,SAAS,IAAIF,KAAK0B;IAElB,6CAA6C;IAC7CxB,OAAOgC,EAAE,CAAC,SAAS,CAACC,KAAYC;QAC9BzB,QAAQQ,KAAK,CAAC,+CAA+CgB,IAAIE,OAAO;IACxE,gDAAgD;IAClD;IAEA,4BAA4B;IAC5BnC,OAAOgC,EAAE,CAAC,WAAW,CAACE;QACpBzB,QAAQC,GAAG,CAAC;IACd;IAEAV,OAAOgC,EAAE,CAAC,WAAW,CAACE;IACpB,gCAAgC;IAClC;IAEAlC,OAAOgC,EAAE,CAAC,UAAU,CAACE;QACnBzB,QAAQC,GAAG,CAAC;IACd;IAEA,+BAA+B;IAC/B,IAAI;QACF,MAAMwB,SAAS,MAAMlC,OAAOoC,OAAO;QACnC,IAAI;YACF,MAAMF,OAAOG,KAAK,CAAC;YACnB5B,QAAQC,GAAG,CAAC;QACd,SAAU;YACRwB,OAAOI,OAAO;QAChB;IACF,EAAE,OAAOL,KAAK;QACZ,MAAMhB,QAAQgB;QACdxB,QAAQQ,KAAK,CAAC,oDAAoDA,MAAMkB,OAAO;QAC/E,MAAM,IAAIf,MAAM,CAAC,uCAAuC,EAAEH,MAAMkB,OAAO,EAAE;IAC3E;AACF;AAEA;;CAEC,GACD,eAAerB,oBAAoBN,MAA2B;IAC5D,MAAM+B,eAA6B;QACjCd,MAAMjB,OAAOiB,IAAI;QACjBC,MAAMlB,OAAOkB,IAAI;QACjBG,UAAUrB,OAAOqB,QAAQ;QACzBN,sBAAsBf,OAAOe,oBAAoB;QACjD,8BAA8B;QAC9BiB,aAAa;QACbC,WAAW;QACXC,gBAAgB;QAChBC,eAAe,CAACC;YACd,IAAIA,QAAQ,IAAI;gBACdnC,QAAQQ,KAAK,CAAC;gBACd,OAAO4B,WAAW,gBAAgB;YACpC;YACA,uDAAuD;YACvD,MAAMC,QAAQC,KAAKC,GAAG,CAAC,MAAMD,KAAKE,GAAG,CAAC,GAAGL,QAAQ;YACjDnC,QAAQC,GAAG,CAAC,CAAC,2BAA2B,EAAEkC,MAAM,IAAI,EAAEE,MAAM,EAAE,CAAC;YAC/D,OAAOA;QACT;QACAI,kBAAkB,CAACjB;YACjB,+BAA+B;YAC/B,MAAMkB,eAAe;gBAAC;gBAAY;gBAAgB;aAAY;YAC9D,OAAOA,aAAaC,IAAI,CAAC,CAACC,SAAWpB,IAAIE,OAAO,CAACmB,QAAQ,CAACD;QAC5D;QACAE,kBAAkB;QAClBC,oBAAoB;IACtB;IAEAvD,YAAY,IAAIF,MAAMwC;IAEtB,sBAAsB;IACtBtC,UAAU+B,EAAE,CAAC,SAAS,CAACC;QACrBxB,QAAQQ,KAAK,CAAC,2BAA2BgB,IAAIE,OAAO;IACtD;IAEAlC,UAAU+B,EAAE,CAAC,SAAS;QACpBvB,QAAQC,GAAG,CAAC;IACd;IAEAT,UAAU+B,EAAE,CAAC,WAAW;QACtBvB,QAAQC,GAAG,CAAC;IACd;IAEAT,UAAU+B,EAAE,CAAC,gBAAgB,CAACc;QAC5BrC,QAAQC,GAAG,CAAC,CAAC,sBAAsB,EAAEoC,MAAM,EAAE,CAAC;IAChD;IAEA7C,UAAU+B,EAAE,CAAC,SAAS;QACpBvB,QAAQC,GAAG,CAAC;IACd;IAEA,+BAA+B;IAC/B,IAAI;QACF,MAAM+C,SAAS,MAAMxD,UAAUyD,IAAI;QACnC,IAAID,WAAW,QAAQ;YACrB,MAAM,IAAIrC,MAAM,CAAC,0BAA0B,EAAEqC,QAAQ;QACvD;QACAhD,QAAQC,GAAG,CAAC;IACd,EAAE,OAAOuB,KAAK;QACZ,MAAMhB,QAAQgB;QACdxB,QAAQQ,KAAK,CAAC,+CAA+CA,MAAMkB,OAAO;QAC1E,MAAM,IAAIf,MAAM,CAAC,kCAAkC,EAAEH,MAAMkB,OAAO,EAAE;IACtE;AACF;AAEA;;CAEC,GACD,SAASnB;IACP,MAAM2C,kBAAkB,OAAOC;QAC7B,IAAIzD,gBAAgB;YAClB;QACF;QACAM,QAAQC,GAAG,CAAC,CAAC,SAAS,EAAEkD,OAAO,mCAAmC,CAAC;QACnE,MAAM1C;QACN2C,QAAQC,IAAI,CAAC;IACf;IAEAD,QAAQ7B,EAAE,CAAC,WAAW,IAAM2B,gBAAgB;IAC5CE,QAAQ7B,EAAE,CAAC,UAAU,IAAM2B,gBAAgB;IAE3C,yBAAyB;IACzBE,QAAQ7B,EAAE,CAAC,qBAAqB,OAAOC;QACrCxB,QAAQQ,KAAK,CAAC,uBAAuBgB;QACrC,MAAMf;QACN2C,QAAQC,IAAI,CAAC;IACf;IAEAD,QAAQ7B,EAAE,CAAC,sBAAsB,OAAO+B;QACtCtD,QAAQQ,KAAK,CAAC,wBAAwB8C;QACtC,MAAM7C;QACN2C,QAAQC,IAAI,CAAC;IACf;AACF;AAEA;;CAEC,GACD,OAAO,eAAe5C;IACpB,IAAIf,gBAAgB;QAClBM,QAAQC,GAAG,CAAC;QACZ;IACF;IAEAD,QAAQC,GAAG,CAAC;IACZP,iBAAiB;IAEjB,MAAM6D,mBAAoC,EAAE;IAE5C,2BAA2B;IAC3B,IAAIhE,QAAQ;QACVgE,iBAAiBC,IAAI,CACnBjE,OACGkE,GAAG,GACHC,IAAI,CAAC;YACJ1D,QAAQC,GAAG,CAAC;YACZV,SAAS;QACX,GACCoE,KAAK,CAAC,CAACnC;YACNxB,QAAQQ,KAAK,CAAC,kCAAkCgB,IAAIE,OAAO;QAC7D;IAEN;IAEA,sBAAsB;IACtB,IAAIlC,WAAW;QACb+D,iBAAiBC,IAAI,CACnBhE,UACGoE,IAAI,GACJF,IAAI,CAAC;YACJ1D,QAAQC,GAAG,CAAC;YACZT,YAAY;QACd,GACCmE,KAAK,CAAC,CAACnC;YACNxB,QAAQQ,KAAK,CAAC,6BAA6BgB,IAAIE,OAAO;QACxD;IAEN;IAEA,MAAMmC,QAAQC,GAAG,CAACP;IAElB9D,gBAAgB;IAChBC,iBAAiB;IACjBM,QAAQC,GAAG,CAAC;AACd;AAEA;;CAEC,GACD,OAAO,SAAS8D;IACd,IAAI,CAACxE,UAAU,CAACC,WAAW;QACzB,MAAM,IAAImB,MAAM;IAClB;IAEA,OAAO;QACLP,UAAU;YACR4D,kBAAkBzE,OAAO0E,UAAU;YACnCC,iBAAiB3E,OAAO4E,SAAS;YACjCC,iBAAiB7E,OAAO8E,YAAY;YACpCC,mBAAmB/E,OAAO0E,UAAU,GAAG1E,OAAO4E,SAAS;QACzD;QACA7D,OAAO;YACLiE,QAAQ/E,UAAU+E,MAAM;YACxBC,kBAAkB;YAClBC,mBAAmB5E;QACrB;QACA6E,WAAW,IAAIC,OAAOC,WAAW;IACnC;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeC,qBACpBjD,KAAa,EACbkD,MAAkB;IAElB,IAAI,CAACvF,QAAQ;QACX,MAAM,IAAIoB,MAAM;IAClB;IAEA,IAAIjB,gBAAgB;QAClB,MAAM,IAAIiB,MAAM;IAClB;IAEA,IAAIc,SAA4B;IAChC,IAAI;QACFA,SAAS,MAAMlC,OAAOoC,OAAO;QAC7B,MAAMqB,SAAS,MAAMvB,OAAOG,KAAK,CAACA,OAAOkD;QACzClF;QACA,OAAOoD,OAAO+B,IAAI;IACpB,EAAE,OAAOvD,KAAK;QACZ,MAAMhB,QAAQgB;QACdxB,QAAQQ,KAAK,CAAC,2BAA2BA,MAAMkB,OAAO;QACtD,MAAM,IAAIf,MAAM,CAAC,yBAAyB,EAAEH,MAAMkB,OAAO,EAAE;IAC7D,SAAU;QACR,IAAID,QAAQ;YACVA,OAAOI,OAAO;QAChB;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,eAAemD,oBACpBC,OAAe,EACf,GAAGC,IAAe;IAElB,IAAI,CAAC1F,WAAW;QACd,MAAM,IAAImB,MAAM;IAClB;IAEA,IAAIjB,gBAAgB;QAClB,MAAM,IAAIiB,MAAM;IAClB;IAEA,IAAI;QACF,iDAAiD;QACjD,MAAMwE,cAAc3F;QAIpB,MAAMwD,SAAS,MAAMmC,WAAW,CAACF,QAAQ,IAAIC;QAC7CrF;QACA,OAAOmD;IACT,EAAE,OAAOxB,KAAK;QACZ,MAAMhB,QAAQgB;QACdxB,QAAQQ,KAAK,CAAC,CAAC,MAAM,EAAEyE,QAAQ,eAAe,CAAC,EAAEzE,MAAMkB,OAAO;QAC9D,MAAM,IAAIf,MAAM,CAAC,sBAAsB,EAAEH,MAAMkB,OAAO,EAAE;IAC1D;AACF;AAEA;;CAEC,GACD,OAAO,eAAe0D;IACpB,MAAMC,SAA4B;QAChCjF,UAAU;QACVE,OAAO;QACPgF,SAAS;YACPlF,UAAU;YACVE,OAAO;QACT;IACF;IAEA,mBAAmB;IACnB,IAAIf,QAAQ;QACV,IAAI;YACF,MAAMsF,qBAAqB;YAC3BQ,OAAOjF,QAAQ,GAAG;YAClBiF,OAAOC,OAAO,CAAClF,QAAQ,GAAG;QAC5B,EAAE,OAAOoB,KAAK;YACZ,MAAMhB,QAAQgB;YACd6D,OAAOC,OAAO,CAAClF,QAAQ,GAAGI,MAAMkB,OAAO;QACzC;IACF;IAEA,cAAc;IACd,IAAIlC,WAAW;QACb,IAAI;YACF,MAAMA,UAAUyD,IAAI;YACpBoC,OAAO/E,KAAK,GAAG;YACf+E,OAAOC,OAAO,CAAChF,KAAK,GAAG;QACzB,EAAE,OAAOkB,KAAK;YACZ,MAAMhB,QAAQgB;YACd6D,OAAOC,OAAO,CAAChF,KAAK,GAAGE,MAAMkB,OAAO;QACtC;IACF;IAEA,OAAO2D;AACT;AAEA;;;CAGC,GACD,OAAO,SAASE;IACd,IAAI,CAAChG,QAAQ;QACX,MAAM,IAAIoB,MAAM;IAClB;IACA,OAAOpB;AACT;AAEA;;;CAGC,GACD,OAAO,SAASiG;IACd,IAAI,CAAChG,WAAW;QACd,MAAM,IAAImB,MAAM;IAClB;IACA,OAAOnB;AACT"}
|