2026년 09월 29일 | DBMS Error 가이드
이 글에서 다루는 내용
HV014 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
HV014 fdw too many handles 는?
PostgreSQL 에러 코드 HV014 (fdw_too_many_handles)는 Foreign Data Wrapper(FDW)가 허용된 최대 핸들(handle) 수를 초과했을 때 발생하는 에러입니다. FDW는 외부 데이터 소스(다른 PostgreSQL 서버, MySQL, Oracle, 파일 시스템 등)에 연결하기 위한 인터페이스로, 각 연결 또는 커서마다 핸들을 생성하는데, 이 핸들이 과도하게 누적되면 해당 에러가 발생합니다. 주로 postgres_fdw, oracle_fdw, mysql_fdw 등을 사용하는 환경에서 많은 수의 외부 테이블에 동시에 접근하거나, 트랜잭션이 제대로 닫히지 않은 채 반복적으로 쿼리를 실행할 때 나타납니다.
주요 발생 원인
1. FDW 연결이 해제되지 않고 누적되는 경우 (Connection Leak)
가장 흔한 원인입니다. 애플리케이션에서 외부 테이블에 접근하는 트랜잭션을 열고 적절히 닫지 않거나, 에러 발생 후 롤백 처리 없이 커넥션이 방치될 때 핸들이 계속 쌓입니다. 특히 커넥션 풀링 환경(PgBouncer, pgpool-II 등)에서 세션이 재사용될 때 이전 FDW 핸들이 정리되지 않으면 빠르게 한계에 도달합니다.
2. 동시에 너무 많은 외부 테이블에 접근하는 쿼리
복잡한 JOIN 쿼리나 다수의 foreign table을 참조하는 뷰(View)를 실행할 때, 각 외부 테이블마다 별도의 핸들이 생성됩니다. 예를 들어, 20개 이상의 foreign table을 동시에 참조하는 쿼리는 단일 트랜잭션에서 많은 수의 핸들을 한꺼번에 소비할 수 있습니다. FDW 구현체에 따라 허용 핸들 수의 상한이 다르므로, 특정 FDW에서 더 쉽게 이 문제가 나타날 수 있습니다.
3. FDW 옵션 설정 오류 또는 버그
일부 FDW 구현체에서는 fetch_size, use_remote_estimate, keep_connections 등의 옵션이 잘못 설정되어 있을 때 핸들을 불필요하게 많이 생성하거나 재사용하지 않는 문제가 있습니다. 오래된 버전의 postgres_fdw에서는 keep_connections = on 상태에서 서버가 재시작되거나 네트워크 오류가 발생했을 때 좀비(zombie) 핸들이 남는 버그가 보고된 바 있으며, 이 경우 업그레이드 또는 패치 적용이 필요합니다.
해결 방법
원인 1 해결: 누적된 FDW 연결 정리
현재 열려 있는 FDW 연결 상태를 확인하고, 불필요한 연결을 강제로 닫습니다.
-- 현재 FDW 서버에 연결된 세션 정보 확인
SELECT
s.usename,
s.application_name,
s.state,
s.query,
s.backend_start,
s.state_change
FROM pg_stat_activity s
WHERE s.query ILIKE '%foreign%'
OR s.query ILIKE '%fdw%';
-- postgres_fdw의 경우 열린 연결을 직접 종료
-- (슈퍼유저 권한 필요)
SELECT postgres_fdw_disconnect('foreign_server_name');
-- 모든 FDW 서버 연결 일괄 종료
SELECT postgres_fdw_disconnect_all();
-- 특정 세션 강제 종료 (pid 확인 후 실행)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND backend_start < NOW() - INTERVAL '1 hour';
원인 2 해결: 쿼리 최적화 및 핸들 사용 최소화
한 번에 많은 foreign table에 접근하지 않도록 쿼리를 분리하거나, 중간 결과를 CTE 또는 임시 테이블에 저장합니다.
-- 나쁜 예: 여러 foreign table을 한꺼번에 JOIN
SELECT a.*, b.*, c.*, d.*
FROM foreign_table_a a
JOIN foreign_table_b b ON a.id = b.a_id
JOIN foreign_table_c c ON b.id = c.b_id
JOIN foreign_table_d d ON c.id = d.c_id;
-- 좋은 예: CTE로 단계적으로 분리하여 핸들 부하 분산
WITH step1 AS (
SELECT id, a_id FROM foreign_table_b
WHERE created_at > NOW() - INTERVAL '7 days'
),
step2 AS (
SELECT s.*, c.b_id
FROM step1 s
JOIN foreign_table_c c ON s.id = c.b_id
)
SELECT s2.*, d.c_id
FROM step2 s2
JOIN foreign_table_d d ON s2.b_id = d.c_id;
-- foreign table 접근을 임시 테이블로 분리
CREATE TEMP TABLE tmp_remote_data AS
SELECT * FROM foreign_table_a
WHERE status = 'active'
AND updated_at > NOW() - INTERVAL '1 day';
-- 이후 로컬 임시 테이블과 JOIN
SELECT t.*, b.*
FROM tmp_remote_data t
JOIN local_table b ON t.id = b.foreign_id;
원인 3 해결: FDW 옵션 튜닝
postgres_fdw의 경우 서버 및 사용자 매핑 옵션을 재검토하여 핸들 재사용을 유도합니다.
-- 현재 foreign server 옵션 확인
SELECT
srvname,
srvoptions
FROM pg_foreign_server;
-- 현재 user mapping 옵션 확인
SELECT
umuser::regrole AS "user",
umoptions
FROM pg_user_mappings;
-- keep_connections 옵션 활성화 (연결 재사용으로 핸들 절약)
ALTER SERVER my_foreign_server
OPTIONS (ADD keep_connections 'on');
-- fetch_size를 줄여 한 번에 가져오는 데이터 양 조절
-- (핸들당 부하 감소)
ALTER SERVER my_foreign_server
OPTIONS (ADD fetch_size '500');
-- 특정 사용자 매핑에 옵션 변경
ALTER USER MAPPING FOR app_user
SERVER my_foreign_server
OPTIONS (ADD fetch_size '500');
-- 변경 후 기존 연결 재설정
SELECT postgres_fdw_disconnect('my_foreign_server');
예방 방법
1. FDW 연결 모니터링 및 자동 정리 자동화
정기적으로 FDW 연결 상태를 모니터링하고, 일정 시간 이상 유휴 상태인 연결을 자동으로 정리하는 스크립트를 cron 또는 pg_cron으로 예약합니다.
-- pg_cron 을 이용한 주기적 FDW 연결 정리 예제 (매 시간 실행)
SELECT cron.schedule(
'cleanup_fdw_connections',
'0 * * * *',
$$SELECT postgres_fdw_disconnect_all();$$
);
-- 모니터링: FDW 관련 오류를 pg_stat_activity 에서 추적
SELECT count(*) AS active_fdw_sessions
FROM pg_stat_activity
WHERE query ILIKE '%foreign%'
AND state != 'idle';
2. 애플리케이션 레벨에서 트랜잭션 관리 철저히
FDW를 사용하는 모든 쿼리는 반드시 명시적인 트랜잭션 블록 안에서 실행하고, 예외 발생 시 ROLLBACK이 반드시 수행되도록 애플리케이션 코드를 설계합니다. ORM이나 커넥션 풀을 사용하는 경우, 세션이 반환되기 전에 DISCARD ALL 또는 RESET 명령으로 세션 상태를 초기화하는 것을 권장합니다.
-- 트랜잭션 블록 내에서 FDW 쿼리 실행 예시
BEGIN;
-- FDW 외부 테이블 조회
SELECT * FROM foreign_orders WHERE order_date = CURRENT_DATE;
-- 정상 처리 시 커밋
COMMIT;
-- 애플리케이션 레벨에서 오류 발생 시
-- ROLLBACK 반드시 호출하여 핸들 해제
ROLLBACK;
-- 커넥션 풀 반환 전 세션 상태 초기화
DISCARD ALL;
관련 에러
- HV000 (fdw_error): FDW 관련 일반 에러로, 외부 서버 연결 실패, 인증 오류 등 다양한 FDW 문제의 포괄적 코드입니다.
- HV001 (fdw_out_of_memory): FDW 처리 중 메모리 부족이 발생했을 때 나타나며, 너무 많은 핸들이 메모리를 점유할 때 HV014와 함께 나타날 수 있습니다.
- HV00P (fdw_no_schemas): Foreign server에 스키마가 정의되지 않았을 때 발생하며, FDW 설정 초기 단계에서 자주 보입니다.
- 08006 (connection_failure): FDW 외부 서버와의 TCP 연결이 끊어졌을 때 발생하며, 좀비 핸들이 남아 HV014로 이어질 수 있습니다.
- HV00B (fdw_invalid_handle): 이미 무효화된 핸들을 재사용하려 할 때 발생하며, HV014와 함께 나타나는 경우가 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.