ERP-node/docs/외부_DB_연결_풀_가이드.md

492 lines
12 KiB
Markdown

# 외부 DB 연결 풀 관리 가이드
## 📋 개요
외부 DB 연결 풀 서비스는 여러 외부 데이터베이스와의 연결을 효율적으로 관리하여 **연결 풀 고갈을 방지**하고 성능을 최적화합니다.
### 주요 기능
-**자동 연결 풀 관리**: 연결 생성, 재사용, 정리 자동화
-**연결 풀 고갈 방지**: 최대 연결 수 제한 및 모니터링
-**유휴 연결 정리**: 10분 이상 사용되지 않은 풀 자동 제거
-**헬스 체크**: 1분마다 모든 풀 상태 검사
-**다중 DB 지원**: PostgreSQL, MySQL, MariaDB
-**싱글톤 패턴**: 전역적으로 단일 인스턴스 사용
---
## 🏗️ 아키텍처
```
┌─────────────────────────────────────────┐
│ NodeFlowExecutionService │
│ (외부 DB 소스/액션 노드) │
└──────────────┬──────────────────────────┘
┌─────────────────────────────────────────┐
│ ExternalDbConnectionPoolService │
│ (싱글톤 인스턴스) │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Connection Pool Map │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ ID: 1 → PostgresPool │ │ │
│ │ │ ID: 2 → MySQLPool │ │ │
│ │ │ ID: 3 → MariaDBPool │ │ │
│ │ └──────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
│ │
│ - 자동 풀 생성/제거 │
│ - 헬스 체크 (1분마다) │
│ - 유휴 풀 정리 (10분) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ External Databases │
│ - PostgreSQL │
│ - MySQL │
│ - MariaDB │
└─────────────────────────────────────────┘
```
---
## 🔧 연결 풀 설정
### PostgreSQL 연결 풀
```typescript
{
max: 10, // 최대 연결 수
min: 2, // 최소 연결 수
idleTimeoutMillis: 30000, // 30초 유휴 시 연결 해제
connectionTimeoutMillis: 30000, // 연결 타임아웃 30초
statement_timeout: 60000, // 쿼리 타임아웃 60초
}
```
### MySQL/MariaDB 연결 풀
```typescript
{
connectionLimit: 10, // 최대 연결 수
waitForConnections: true,
queueLimit: 0, // 대기열 무제한
connectTimeout: 30000, // 연결 타임아웃 30초
}
```
---
## 📊 연결 풀 라이프사이클
### 1. 풀 생성
```typescript
// 첫 요청 시 자동 생성
const pool = await poolService.getPool(connectionId);
```
**생성 시점**:
- 외부 DB 소스 노드 첫 실행 시
- 외부 DB 액션 노드 첫 실행 시
**생성 과정**:
1. DB 연결 정보 조회 (`external_db_connections` 테이블)
2. 비밀번호 복호화
3. DB 타입에 맞는 연결 풀 생성 (PostgreSQL, MySQL, MariaDB)
4. 이벤트 리스너 등록 (연결 획득/해제 추적)
### 2. 풀 재사용
```typescript
// 기존 풀이 있으면 재사용
if (this.pools.has(connectionId)) {
const pool = this.pools.get(connectionId)!;
pool.lastUsedAt = new Date(); // 사용 시간 갱신
return pool;
}
```
**재사용 조건**:
- 동일한 `connectionId`로 요청
- 풀이 정상 상태 (`isHealthy()` 통과)
### 3. 자동 정리
**유휴 시간 초과 (10분)**:
```typescript
const IDLE_TIMEOUT = 10 * 60 * 1000; // 10분
if (now - pool.lastUsedAt.getTime() > IDLE_TIMEOUT) {
await this.removePool(connectionId);
}
```
**헬스 체크 실패**:
```typescript
if (!pool.isHealthy()) {
await this.removePool(connectionId);
}
```
---
## 🔍 헬스 체크 시스템
### 주기적 헬스 체크
```typescript
const HEALTH_CHECK_INTERVAL = 60 * 1000; // 1분마다
setInterval(() => {
this.pools.forEach(async (pool, connectionId) => {
// 유휴 시간 체크
const idleTime = now - pool.lastUsedAt.getTime();
if (idleTime > IDLE_TIMEOUT) {
await this.removePool(connectionId);
}
// 헬스 체크
if (!pool.isHealthy()) {
await this.removePool(connectionId);
}
});
}, HEALTH_CHECK_INTERVAL);
```
### 헬스 체크 조건
#### PostgreSQL
```typescript
isHealthy(): boolean {
return this.pool.totalCount > 0
&& this.activeConnections < this.maxConnections;
}
```
#### MySQL/MariaDB
```typescript
isHealthy(): boolean {
return this.activeConnections < this.maxConnections;
}
```
---
## 💻 사용 방법
### 1. 외부 DB 소스 노드에서 사용
```typescript
// nodeFlowExecutionService.ts
private static async executeExternalDBSource(
node: FlowNode,
context: ExecutionContext
): Promise<any[]> {
const { connectionId, tableName } = node.data;
// 연결 풀 서비스 사용
const { ExternalDbConnectionPoolService } = await import(
"./externalDbConnectionPoolService"
);
const poolService = ExternalDbConnectionPoolService.getInstance();
const sql = `SELECT * FROM ${tableName}`;
const result = await poolService.executeQuery(connectionId, sql);
return result;
}
```
### 2. 외부 DB 액션 노드에서 사용
```typescript
// 기존 createExternalConnector가 자동으로 연결 풀 사용
const connector = await this.createExternalConnector(connectionId, dbType);
// executeQuery 호출 시 내부적으로 연결 풀 사용
const result = await connector.executeQuery(sql, params);
```
### 3. 연결 풀 상태 조회
**API 엔드포인트**:
```
GET /api/external-db-connections/pool-status
```
**응답 예시**:
```json
{
"success": true,
"data": {
"totalPools": 3,
"activePools": 2,
"pools": [
{
"connectionId": 1,
"dbType": "postgresql",
"activeConnections": 2,
"maxConnections": 10,
"createdAt": "2025-01-13T10:00:00.000Z",
"lastUsedAt": "2025-01-13T10:05:00.000Z",
"idleSeconds": 45
},
{
"connectionId": 2,
"dbType": "mysql",
"activeConnections": 0,
"maxConnections": 10,
"createdAt": "2025-01-13T09:50:00.000Z",
"lastUsedAt": "2025-01-13T09:55:00.000Z",
"idleSeconds": 600
}
]
},
"message": "3개의 연결 풀 상태를 조회했습니다."
}
```
---
## 🚨 연결 풀 고갈 방지 메커니즘
### 1. 최대 연결 수 제한
```typescript
// 데이터베이스 설정 기준
max_connections: config.max_connections || 10;
```
각 외부 DB 연결마다 최대 연결 수를 설정하여 무제한 연결 방지.
### 2. 연결 재사용
```typescript
// 동일한 connectionId 요청 시 기존 풀 재사용
const pool = await poolService.getPool(connectionId);
```
매번 새 연결을 생성하지 않고 기존 풀 재사용.
### 3. 자동 연결 해제
```typescript
// PostgreSQL: 30초 유휴 시 자동 해제
idleTimeoutMillis: 30000;
```
사용되지 않는 연결은 자동으로 해제하여 리소스 절약.
### 4. 전역 풀 정리
```typescript
// 10분 이상 미사용 풀 제거
if (idleTime > IDLE_TIMEOUT) {
await this.removePool(connectionId);
}
```
장시간 사용되지 않는 풀 자체를 제거.
### 5. 애플리케이션 종료 시 정리
```typescript
process.on("SIGINT", async () => {
await ExternalDbConnectionPoolService.getInstance().closeAll();
process.exit(0);
});
```
프로세스 종료 시 모든 연결 정상 종료.
---
## 📈 모니터링 및 로깅
### 연결 이벤트 로깅
```typescript
// 연결 획득
pool.on("acquire", () => {
logger.debug(`[PostgreSQL] 연결 획득 (2/10)`);
});
// 연결 반환
pool.on("release", () => {
logger.debug(`[PostgreSQL] 연결 반환 (1/10)`);
});
// 에러 발생
pool.on("error", (err) => {
logger.error(`[PostgreSQL] 연결 풀 오류:`, err);
});
```
### 정기 상태 로깅
```typescript
// 1분마다 상태 출력
logger.debug(`📊 연결 풀 상태: 총 3개, 활성: 2개`);
```
### 주요 로그 메시지
| 레벨 | 메시지 | 의미 |
| ------- | ---------------------------------------------------------- | --------------- |
| `info` | `🔧 새 연결 풀 생성 중 (ID: 1)...` | 새 풀 생성 시작 |
| `info` | `✅ 연결 풀 생성 완료 (ID: 1, 타입: postgresql, 최대: 10)` | 풀 생성 완료 |
| `debug` | `✅ 기존 연결 풀 재사용 (ID: 1)` | 기존 풀 재사용 |
| `info` | `🧹 유휴 연결 풀 정리 (ID: 2, 유휴: 620초)` | 유휴 풀 제거 |
| `warn` | `⚠️ 연결 풀 비정상 감지 (ID: 3), 재생성 중...` | 헬스 체크 실패 |
| `error` | `❌ 쿼리 실행 실패 (ID: 1)` | 쿼리 오류 |
---
## 🔒 보안 고려사항
### 1. 비밀번호 보호
```typescript
// 비밀번호 복호화는 풀 생성 시에만 수행
config.password = PasswordEncryption.decrypt(config.password);
```
메모리에 평문 비밀번호를 최소한으로 유지.
### 2. 연결 정보 검증
```typescript
if (config.is_active !== "Y") {
throw new Error(`비활성화된 연결입니다 (ID: ${connectionId})`);
}
```
비활성화된 연결은 사용 불가.
### 3. 타임아웃 설정
```typescript
connectionTimeoutMillis: 30000, // 30초
statement_timeout: 60000, // 60초
```
무한 대기 방지.
---
## 🐛 트러블슈팅
### 문제 1: 연결 풀 고갈
**증상**: "Connection pool exhausted" 오류
**원인**:
- 동시 요청이 최대 연결 수 초과
- 쿼리가 너무 오래 실행되어 연결 점유
**해결**:
1. `max_connections` 값 증가 (`external_db_connections` 테이블)
2. 쿼리 최적화 (인덱스, LIMIT 추가)
3. `query_timeout` 값 조정
### 문제 2: 메모리 누수
**증상**: 메모리 사용량 지속 증가
**원인**:
- 연결 풀이 정리되지 않음
- 헬스 체크 실패
**해결**:
1. 연결 풀 상태 확인: `GET /api/external-db-connections/pool-status`
2. 수동 재시작으로 모든 풀 정리
3. 로그에서 `🧹 유휴 연결 풀 정리` 메시지 확인
### 문제 3: 연결 시간 초과
**증상**: "Connection timeout" 오류
**원인**:
- DB 서버 응답 없음
- 네트워크 문제
- 방화벽 차단
**해결**:
1. DB 서버 상태 확인
2. 네트워크 연결 확인
3. `connection_timeout` 값 증가
---
## ⚙️ 설정 권장사항
### 소규모 시스템 (동시 사용자 < 50)
```typescript
{
max_connections: 5,
connection_timeout: 30,
query_timeout: 60,
}
```
### 중규모 시스템 (동시 사용자 50-200)
```typescript
{
max_connections: 10,
connection_timeout: 30,
query_timeout: 90,
}
```
### 대규모 시스템 (동시 사용자 > 200)
```typescript
{
max_connections: 20,
connection_timeout: 60,
query_timeout: 120,
}
```
---
## 📚 참고 자료
- [PostgreSQL Connection Pooling](https://node-postgres.com/features/pooling)
- [MySQL Connection Pool](https://github.com/mysqljs/mysql#pooling-connections)
- [Node.js Best Practices - Database Connection Management](https://github.com/goldbergyoni/nodebestpractices)
---
## 🎯 결론
외부 DB 연결 풀 서비스는 다음을 보장합니다:
**효율성**: 연결 재사용으로 성능 향상
**안정성**: 연결 풀 고갈 방지
**자동화**: 생성/정리/모니터링 자동화
**확장성**: 다중 DB 및 대규모 트래픽 지원
**최소한의 설정**으로 **최대한의 안정성**을 제공합니다! 🚀