Files
vexplor/docs/외부_DB_연결_풀_가이드.md
2025-10-13 17:47:24 +09:00

12 KiB

외부 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 연결 풀

{
  max: 10,              // 최대 연결 수
  min: 2,               // 최소 연결 수
  idleTimeoutMillis: 30000,     // 30초 유휴 시 연결 해제
  connectionTimeoutMillis: 30000, // 연결 타임아웃 30초
  statement_timeout: 60000,       // 쿼리 타임아웃 60초
}

MySQL/MariaDB 연결 풀

{
  connectionLimit: 10,   // 최대 연결 수
  waitForConnections: true,
  queueLimit: 0,         // 대기열 무제한
  connectTimeout: 30000, // 연결 타임아웃 30초
}

📊 연결 풀 라이프사이클

1. 풀 생성

// 첫 요청 시 자동 생성
const pool = await poolService.getPool(connectionId);

생성 시점:

  • 외부 DB 소스 노드 첫 실행 시
  • 외부 DB 액션 노드 첫 실행 시

생성 과정:

  1. DB 연결 정보 조회 (external_db_connections 테이블)
  2. 비밀번호 복호화
  3. DB 타입에 맞는 연결 풀 생성 (PostgreSQL, MySQL, MariaDB)
  4. 이벤트 리스너 등록 (연결 획득/해제 추적)

2. 풀 재사용

// 기존 풀이 있으면 재사용
if (this.pools.has(connectionId)) {
  const pool = this.pools.get(connectionId)!;
  pool.lastUsedAt = new Date(); // 사용 시간 갱신
  return pool;
}

재사용 조건:

  • 동일한 connectionId로 요청
  • 풀이 정상 상태 (isHealthy() 통과)

3. 자동 정리

유휴 시간 초과 (10분):

const IDLE_TIMEOUT = 10 * 60 * 1000; // 10분

if (now - pool.lastUsedAt.getTime() > IDLE_TIMEOUT) {
  await this.removePool(connectionId);
}

헬스 체크 실패:

if (!pool.isHealthy()) {
  await this.removePool(connectionId);
}

🔍 헬스 체크 시스템

주기적 헬스 체크

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

isHealthy(): boolean {
  return this.pool.totalCount > 0
    && this.activeConnections < this.maxConnections;
}

MySQL/MariaDB

isHealthy(): boolean {
  return this.activeConnections < this.maxConnections;
}

💻 사용 방법

1. 외부 DB 소스 노드에서 사용

// 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 액션 노드에서 사용

// 기존 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

응답 예시:

{
  "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. 최대 연결 수 제한

// 데이터베이스 설정 기준
max_connections: config.max_connections || 10;

각 외부 DB 연결마다 최대 연결 수를 설정하여 무제한 연결 방지.

2. 연결 재사용

// 동일한 connectionId 요청 시 기존 풀 재사용
const pool = await poolService.getPool(connectionId);

매번 새 연결을 생성하지 않고 기존 풀 재사용.

3. 자동 연결 해제

// PostgreSQL: 30초 유휴 시 자동 해제
idleTimeoutMillis: 30000;

사용되지 않는 연결은 자동으로 해제하여 리소스 절약.

4. 전역 풀 정리

// 10분 이상 미사용 풀 제거
if (idleTime > IDLE_TIMEOUT) {
  await this.removePool(connectionId);
}

장시간 사용되지 않는 풀 자체를 제거.

5. 애플리케이션 종료 시 정리

process.on("SIGINT", async () => {
  await ExternalDbConnectionPoolService.getInstance().closeAll();
  process.exit(0);
});

프로세스 종료 시 모든 연결 정상 종료.


📈 모니터링 및 로깅

연결 이벤트 로깅

// 연결 획득
pool.on("acquire", () => {
  logger.debug(`[PostgreSQL] 연결 획득 (2/10)`);
});

// 연결 반환
pool.on("release", () => {
  logger.debug(`[PostgreSQL] 연결 반환 (1/10)`);
});

// 에러 발생
pool.on("error", (err) => {
  logger.error(`[PostgreSQL] 연결 풀 오류:`, err);
});

정기 상태 로깅

// 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. 비밀번호 보호

// 비밀번호 복호화는 풀 생성 시에만 수행
config.password = PasswordEncryption.decrypt(config.password);

메모리에 평문 비밀번호를 최소한으로 유지.

2. 연결 정보 검증

if (config.is_active !== "Y") {
  throw new Error(`비활성화된 연결입니다 (ID: ${connectionId})`);
}

비활성화된 연결은 사용 불가.

3. 타임아웃 설정

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)

{
  max_connections: 5,
  connection_timeout: 30,
  query_timeout: 60,
}

중규모 시스템 (동시 사용자 50-200)

{
  max_connections: 10,
  connection_timeout: 30,
  query_timeout: 90,
}

대규모 시스템 (동시 사용자 > 200)

{
  max_connections: 20,
  connection_timeout: 60,
  query_timeout: 120,
}

📚 참고 자료


🎯 결론

외부 DB 연결 풀 서비스는 다음을 보장합니다:

효율성: 연결 재사용으로 성능 향상
안정성: 연결 풀 고갈 방지
자동화: 생성/정리/모니터링 자동화
확장성: 다중 DB 및 대규모 트래픽 지원

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