PostgreSQL HV014 오류 원인과 해결 방법 완벽 가이드

HV014
2026년 07월 26일 | DBMS Error 가이드

이 글에서 다루는 내용

HV014 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

HV014 fdw too many handles 는?

PostgreSQL 에러 코드 HV014 (fdw_too_many_handles)는 Foreign Data Wrapper(FDW)를 사용할 때 동시에 열 수 있는 핸들(handle)의 최대 개수를 초과했을 때 발생하는 에러입니다. FDW는 외부 데이터 소스(다른 PostgreSQL 인스턴스, Oracle, MySQL, 파일 시스템 등)에 접근하기 위한 인터페이스로, 각 연결 및 쿼리 실행 시 내부적으로 핸들을 생성하여 관리합니다. 핸들이 제때 해제되지 않거나, 동시 연결이 과도하게 많아지는 경우 이 에러가 발생하며, 특히 postgres_fdw, oracle_fdw, file_fdw 등 다양한 FDW 구현체에서 공통적으로 나타날 수 있습니다.


주요 발생 원인

1. FDW 연결 핸들의 누수(Connection Handle Leak)

FDW를 통해 외부 데이터 소스에 쿼리를 실행할 때, 각 연결 및 커서는 내부적으로 핸들을 생성합니다. 트랜잭션이 비정상적으로 종료되거나, 애플리케이션 레벨에서 커넥션을 명시적으로 닫지 않으면 핸들이 반환되지 않아 점진적으로 누적됩니다. 특히 장시간 실행되는 배치 작업이나 반복적인 FDW 쿼리 루프에서 이 문제가 두드러지게 나타납니다.

2. 과도한 동시 FDW 세션 및 커서 사용

여러 PostgreSQL 클라이언트 세션이 동시에 FDW를 통해 외부 소스에 접근할 때, 각 세션은 독립적인 핸들을 생성합니다. 커넥션 풀링(Connection Pooling)을 제대로 설정하지 않으면 세션 수가 폭발적으로 늘어나고, 이에 비례하여 FDW 핸들 수도 증가하여 한계치를 초과하게 됩니다. pgBouncer나 애플리케이션 레벨의 커넥션 풀 설정이 미흡한 환경에서 특히 자주 발생합니다.

3. FDW 서버 또는 드라이버의 핸들 제한 설정 미흡

일부 FDW 구현체(예: oracle_fdw, ODBC 기반 FDW)는 외부 드라이버나 라이브러리 수준에서 최대 핸들 수를 제한합니다. PostgreSQL 인스턴스의 max_connections 설정과 FDW 외부 서버의 max_connections 옵션이 불일치하거나, 드라이버 레벨의 핸들 풀이 너무 작게 설정된 경우 이 에러가 발생합니다. FDW 서버 정의 시 use_remote_estimate, fdw_startup_cost 등 옵션과 함께 연결 수 제한 옵션을 반드시 검토해야 합니다.


해결 방법

원인 1 해결: 핸들 누수 확인 및 정리

현재 활성 FDW 연결 및 세션 상태를 확인합니다.

-- 현재 활성 연결 및 FDW 관련 세션 확인
SELECT pid, usename, application_name, state, query_start, state_change, query
FROM pg_stat_activity
WHERE query ILIKE '%fdw%' OR query ILIKE '%foreign%'
ORDER BY query_start;

-- 오래된 idle 상태의 FDW 연결 강제 종료 (10분 이상 idle)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
  AND state_change < NOW() - INTERVAL '10 minutes'
  AND query ILIKE '%fdw%';

FDW 핸들 누수를 방지하기 위해 명시적으로 트랜잭션을 관리합니다.

-- 올바른 FDW 사용 패턴: 트랜잭션 명시적 관리
BEGIN;

-- FDW 테이블 쿼리
SELECT * FROM foreign_table_name WHERE condition = 'value';

-- 작업 완료 후 반드시 커밋 또는 롤백
COMMIT;

-- 커서를 사용하는 경우 반드시 명시적 CLOSE 수행
BEGIN;
DECLARE fdw_cursor CURSOR FOR
    SELECT * FROM foreign_orders WHERE order_date > '2024-01-01';

FETCH 100 FROM fdw_cursor;
-- 데이터 처리...
CLOSE fdw_cursor;  -- 반드시 커서 명시적 종료
COMMIT;

원인 2 해결: FDW 서버의 연결 수 제한 설정

postgres_fdw를 사용하는 경우 서버 옵션에서 연결 수를 제어합니다.

-- 기존 FDW 서버 옵션 확인
SELECT srvname, srvoptions
FROM pg_foreign_server;

-- FDW 서버에 연결 수 제한 옵션 설정
ALTER SERVER my_foreign_server
OPTIONS (ADD fetch_size '1000');

-- 사용자 매핑 수준에서 연결 재사용 활성화 (postgres_fdw)
ALTER USER MAPPING FOR current_user
SERVER my_foreign_server
OPTIONS (ADD keep_connections 'on');

-- FDW 연결 캐시 강제 초기화 (연결 문제 발생 시)
SELECT postgres_fdw_disconnect('my_foreign_server');

-- 모든 FDW 서버 연결 초기화
SELECT postgres_fdw_disconnect_all();

원인 3 해결: FDW 외부 서버 핸들 제한 조정

-- 현재 등록된 FDW 및 서버 목록 확인
SELECT f.fdwname, s.srvname, s.srvoptions
FROM pg_foreign_data_wrapper f
JOIN pg_foreign_server s ON s.srvfdw = f.oid;

-- oracle_fdw 또는 기타 FDW의 경우 연결 옵션 재설정
ALTER SERVER oracle_remote_server
OPTIONS (
    SET dbserver '//oracle-host:1521/ORCL'
);

-- FDW 서버별 접속 가능한 최대 연결 확인 및 조정
-- (postgres_fdw 기준)
CREATE SERVER optimized_foreign_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
    host 'remote-pg-host',
    port '5432',
    dbname 'remote_db',
    fetch_size '500',          -- 한 번에 가져올 행 수 조정
    connect_timeout '30'       -- 연결 타임아웃 설정
);

-- 기존 foreign table의 통계 갱신 (쿼리 플래너 최적화)
ANALYZE foreign_table_name;

예방 방법

1. FDW 연결 모니터링 및 자동 정리 루틴 구축

FDW 핸들 문제를 사전에 예방하려면 주기적으로 연결 상태를 모니터링하고, 오래된 유휴 연결을 자동으로 정리하는 루틴을 구성해야 합니다. pg_cron 확장을 활용하면 주기적인 정리 작업을 자동화할 수 있습니다.

-- pg_cron을 이용한 주기적 FDW 연결 정리 (5분마다)
SELECT cron.schedule(
    'cleanup-fdw-idle-connections',
    '*/5 * * * *',
    $$
    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE state = 'idle'
      AND state_change < NOW() - INTERVAL '5 minutes'
      AND backend_type = 'client backend';
    $$
);

-- FDW 핸들 수 모니터링 뷰 생성
CREATE OR REPLACE VIEW fdw_connection_monitor AS
SELECT
    usename,
    application_name,
    state,
    COUNT(*) AS connection_count,
    MAX(NOW() - state_change) AS max_idle_time
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY usename, application_name, state
ORDER BY connection_count DESC;

2. 애플리케이션 레벨 커넥션 풀 및 FDW 사용 패턴 표준화

애플리케이션에서 FDW를 사용할 때는 반드시 커넥션 풀(예: PgBouncer)을 도입하고, FDW 쿼리는 가능한 짧은 트랜잭션 내에서 처리하도록 코딩 표준을 수립해야 합니다. 대량 데이터 처리 시에는 fetch_size 옵션을 적절히 조정하여 단일 핸들이 오래 점유되지 않도록 해야 합니다.

-- fetch_size 최적화 예시 (대용량 데이터 처리 시)
ALTER FOREIGN TABLE large_foreign_table
OPTIONS (SET fetch_size '200');

-- FDW 테이블 접근 시 불필요한 컬럼 제한으로 핸들 부하 최소화
SELECT order_id, order_date, total_amount
FROM foreign_orders
WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31'
  AND status = 'COMPLETED';
-- (SELECT * 대신 필요한 컬럼만 명시적으로 지정)

관련 에러

  • HV000 (fdw_error): FDW 일반 에러로, HV014 발생 전 선행될 수 있는 기본 FDW 오류입니다.
  • HV001 (fdw_out_of_memory): FDW 작업 중 메모리 부족 시 발생하며, 핸들 누수와 함께 나타날 수 있습니다.
  • HV00B (fdw_invalid_handle): 이미 해제되었거나 유효하지 않은 핸들에 접근할 때 발생하는 에러로, HV014와 연관된 핸들 관리 문제입니다.
  • 08006 (connection_failure): FDW 연결이 과도하게 많아져 외부 서버 연결이 실패할 때 함께 나타날 수 있습니다.
  • 53300 (too_many_connections): PostgreSQL 인스턴스 자체의 최대 연결 수 초과 에러로, FDW 핸들 폭증과 함께 복합적으로 발생하는 경우가 많습니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기