2026년 06월 23일 | DBMS Error 가이드
이 글에서 다루는 내용
25001 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
25001 active sql transaction 는?
PostgreSQL 에러 코드 25001 (active_sql_transaction)은 특정 명령어가 트랜잭션 블록 외부에서만 실행될 수 있음에도 불구하고, 현재 활성화된 트랜잭션 블록 내부에서 해당 명령어를 실행하려 할 때 발생합니다. 쉽게 말해, BEGIN 또는 START TRANSACTION으로 트랜잭션이 이미 시작된 상태에서 트랜잭션 컨텍스트를 허용하지 않는 명령어를 호출했을 때 나타나는 에러입니다. 주로 트랜잭션 격리 수준 변경, 특정 세션 설정 명령, 또는 일부 DDL 명령을 잘못된 위치에서 실행했을 때 실무 환경에서 자주 목격됩니다.
주요 발생 원인
1. 트랜잭션 블록 내에서 격리 수준(Isolation Level) 변경 시도
가장 흔한 원인 중 하나로, SET TRANSACTION ISOLATION LEVEL 명령어는 트랜잭션이 시작되기 전 또는 트랜잭션 내 첫 번째 쿼리 실행 전에만 유효합니다. 이미 BEGIN 이후에 DML(SELECT, INSERT, UPDATE, DELETE) 쿼리가 하나라도 실행된 상태에서 격리 수준을 변경하려 하면 이 에러가 발생합니다. 애플리케이션 레이어에서 커넥션 풀을 통해 트랜잭션을 재사용하는 경우 특히 주의해야 합니다.
2. 활성 트랜잭션 내에서 SET SESSION 또는 특정 세션 명령 실행
일부 세션 수준의 설정 명령어(SET SESSION AUTHORIZATION, SET ROLE 등)는 활성 트랜잭션 블록 내에서 실행될 수 없도록 제한되어 있습니다. 개발자가 세션 설정을 변경하려는 로직과 데이터 처리 로직을 하나의 트랜잭션 블록에 묶어 작성하는 경우 이 에러가 발생합니다. 이는 보안 및 일관성 보장을 위한 PostgreSQL의 의도적인 설계로, 트랜잭션 중간에 권한이나 역할이 변경되는 것을 막기 위한 것입니다.
3. 커넥션 풀(Connection Pool) 환경에서의 트랜잭션 상태 오염
PgBouncer, HikariCP 등의 커넥션 풀을 사용하는 환경에서, 이전 클라이언트가 트랜잭션을 COMMIT 또는 ROLLBACK 하지 않고 커넥션을 반환한 경우 다음 클라이언트가 오염된(dirty) 트랜잭션 상태의 커넥션을 받아 이 에러를 경험하게 됩니다. 이 경우 직접적인 코드 문제가 아니기 때문에 원인을 파악하기가 더 어렵고, 간헐적으로 발생하여 디버깅이 까다롭습니다. 커넥션 풀 설정의 reset_query 또는 server_reset_query 옵션을 통해 방지할 수 있습니다.
해결 방법
원인 1 해결: 트랜잭션 시작 직후, 첫 쿼리 이전에 격리 수준 설정
격리 수준은 반드시 트랜잭션 시작 직후, 실제 데이터 조작 쿼리 이전에 설정해야 합니다.
-- ❌ 잘못된 방법: 이미 DML 실행 후 격리 수준 변경 시도
BEGIN;
SELECT * FROM orders WHERE status = 'pending'; -- DML 실행됨
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- 에러 25001 발생!
-- ✅ 올바른 방법 1: BEGIN과 함께 격리 수준 지정
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM orders WHERE status = 'pending';
UPDATE orders SET status = 'processing' WHERE status = 'pending';
COMMIT;
-- ✅ 올바른 방법 2: SET TRANSACTION을 첫 DML 이전에 실행
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM accounts WHERE user_id = 101;
UPDATE accounts SET balance = balance - 500 WHERE user_id = 101;
COMMIT;
원인 2 해결: 세션 설정은 트랜잭션 외부에서 실행
세션 관련 명령어는 트랜잭션 블록 밖에서 실행하고, 이후에 트랜잭션을 시작해야 합니다.
-- ❌ 잘못된 방법: 트랜잭션 내에서 세션 권한 변경
BEGIN;
INSERT INTO audit_log (action) VALUES ('login');
SET SESSION AUTHORIZATION 'report_user'; -- 에러 25001 발생!
COMMIT;
-- ✅ 올바른 방법: 세션 설정을 트랜잭션 외부에서 먼저 실행
SET SESSION AUTHORIZATION 'report_user';
BEGIN;
INSERT INTO audit_log (action) VALUES ('login');
SELECT * FROM sales_report;
COMMIT;
-- 작업 완료 후 원래 권한으로 복구
RESET SESSION AUTHORIZATION;
-- ✅ 현재 트랜잭션 상태 확인 방법
SELECT current_setting('transaction_isolation'),
txid_current_if_assigned() AS current_txid;
원인 3 해결: 커넥션 풀 오염 상태 감지 및 초기화
커넥션을 받아올 때 현재 트랜잭션 상태를 확인하고, 필요시 롤백 후 재사용하는 로직을 구현합니다.
-- 현재 커넥션의 트랜잭션 상태 확인
SELECT pg_backend_pid(),
state,
backend_xid,
backend_xmin
FROM pg_stat_activity
WHERE pid = pg_backend_pid();
-- 트랜잭션 상태가 'idle in transaction'인 경우 강제 롤백
ROLLBACK;
-- PgBouncer 사용 시 server_reset_query 설정 예시
-- pgbouncer.ini 파일에 아래 설정 추가:
-- server_reset_query = DISCARD ALL
-- 또는 더 가벼운 초기화:
-- server_reset_query = RESET ALL; SET SESSION AUTHORIZATION DEFAULT;
-- 애플리케이션 레벨에서 커넥션 상태 검증 쿼리
DO $$
BEGIN
IF (SELECT COUNT(*) FROM pg_stat_activity
WHERE pid = pg_backend_pid()
AND state = 'idle in transaction') > 0 THEN
RAISE NOTICE '트랜잭션이 열려 있습니다. ROLLBACK을 실행합니다.';
ROLLBACK;
END IF;
END;
$$;
예방 방법
1. 트랜잭션 래퍼 함수 또는 프로시저로 표준화
트랜잭션을 직접 BEGIN/COMMIT으로 관리하는 대신, 팀 내 공통 트랜잭션 래퍼를 만들어 격리 수준 설정, 예외 처리, 롤백 로직을 표준화하세요. 아래 예시처럼 격리 수준을 파라미터로 받아 처리 초기에 설정하도록 강제하면 실수를 방지할 수 있습니다.
-- 트랜잭션 관리 표준화 예시 (저장 프로시저)
CREATE OR REPLACE PROCEDURE safe_transaction(
p_isolation_level TEXT DEFAULT 'READ COMMITTED'
)
LANGUAGE plpgsql AS $$
BEGIN
-- 혹시 열려있는 트랜잭션이 있다면 먼저 정리
-- (실제 환경에서는 에러 로깅 추가 권장)
EXECUTE format('SET TRANSACTION ISOLATION LEVEL %s', p_isolation_level);
RAISE NOTICE '트랜잭션 격리 수준: % 로 설정되었습니다.', p_isolation_level;
END;
$$;
2. 커넥션 반환 전 트랜잭션 상태 검증 자동화
애플리케이션에서 커넥션을 풀에 반환하기 전에 항상 트랜잭션 상태를 검증하고, idle in transaction 상태인 커넥션은 ROLLBACK 후 반환하는 정책을 코드 레벨에서 강제하세요. PostgreSQL의 idle_in_transaction_session_timeout 파라미터를 설정하면 장시간 방치된 트랜잭션을 자동으로 종료시켜 오염을 방지할 수 있습니다.
-- postgresql.conf 또는 ALTER SYSTEM으로 설정
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';
SELECT pg_reload_conf();
-- 현재 설정값 확인
SHOW idle_in_transaction_session_timeout;
관련 에러
- 25000 (invalid_transaction_state): 25001의 상위 분류 에러로, 트랜잭션 상태가 유효하지 않을 때 발생하는 에러 계열의 기본 코드입니다.
- 25002 (branch_transaction_already_active): 분산 트랜잭션 환경에서 브랜치 트랜잭션이 이미 활성 상태일 때 발생합니다.
- 25008 (held_cursor_requires_same_isolation_level): 커서가 동일한 격리 수준을 요구할 때 발생하며, 격리 수준 관련 에러와 함께 자주 나타납니다.
- 2D000 (invalid_transaction_termination): 트랜잭션 종료가 잘못된 컨텍스트에서 발생했을 때 나타나는 관련 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.