# 외부 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 및 대규모 트래픽 지원

**최소한의 설정**으로 **최대한의 안정성**을 제공합니다! 🚀
