2026년 08월 29일 | DBMS Error 가이드
이 글에서 다루는 내용
25P01 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
25P01 no active sql transaction 는?
PostgreSQL 에러 코드 25P01 (no active sql transaction)은 활성화된 트랜잭션이 없는 상태에서 트랜잭션 제어 명령(ROLLBACK, COMMIT, SAVEPOINT 등)을 실행하려 할 때 발생합니다. 쉽게 말해, 트랜잭션을 시작(BEGIN)하지 않았거나 이미 종료된 트랜잭션에 대해 트랜잭션 관련 명령을 호출할 때 나타나는 에러입니다. 이 에러는 개발 환경보다 애플리케이션 코드와 데이터베이스 연결 관리 로직이 복잡해지는 운영 환경에서 자주 목격되며, 간과하면 데이터 정합성 문제로 이어질 수 있습니다.
주요 발생 원인
1. BEGIN 없이 ROLLBACK 또는 COMMIT 실행
가장 흔한 원인으로, 트랜잭션을 명시적으로 시작하지 않은 채 ROLLBACK이나 COMMIT을 호출하는 경우입니다. PostgreSQL은 기본적으로 각 SQL 문을 자동 커밋(autocommit) 모드로 처리하므로, BEGIN 없이 ROLLBACK을 실행하면 “현재 활성 트랜잭션이 없다”는 에러를 반환합니다. 특히 애플리케이션 레벨에서 예외 처리 로직이 잘못 구성된 경우 이 패턴이 반복적으로 발생합니다.
2. 이미 종료된 트랜잭션에서 SAVEPOINT 또는 ROLLBACK TO SAVEPOINT 실행
트랜잭션이 에러로 인해 자동으로 중단(aborted)되거나 이미 COMMIT/ROLLBACK으로 완료된 이후에도 추가적인 트랜잭션 제어 명령을 실행하려는 경우입니다. 애플리케이션 레이어에서 커넥션 풀을 재사용하거나 비동기 처리 흐름이 꼬일 때 발생하기 쉬우며, 로그에 25P01이 반복 출력된다면 이 케이스를 우선 의심해야 합니다.
3. 잘못 구성된 커넥션 풀 또는 미들웨어 트랜잭션 관리
PgBouncer, HikariCP 등 커넥션 풀러나 ORM(예: SQLAlchemy, Hibernate)이 트랜잭션 상태를 잘못 추적하거나 커넥션을 반환하기 전에 트랜잭션을 명시적으로 종료하지 않는 경우입니다. 풀러가 커넥션을 재활용할 때 이전 세션의 트랜잭션 상태가 오염되어 있으면, 다음 요청에서 25P01이 발생할 수 있습니다.
해결 방법
원인 1 해결: BEGIN을 명시적으로 선언하기
트랜잭션이 필요한 모든 작업은 반드시 BEGIN으로 시작해야 합니다.
-- 잘못된 예시: BEGIN 없이 ROLLBACK 호출
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
ROLLBACK; -- 에러 발생: ERROR 25P01: there is no transaction in progress
-- 올바른 예시: BEGIN으로 트랜잭션 명시 시작
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 또는 예외 처리를 포함한 패턴
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 오류 발생 시 롤백
ROLLBACK;
원인 2 해결: 트랜잭션 상태 확인 후 명령 실행
pg_current_xact_id_if_assigned() 또는 클라이언트 라이브러리의 트랜잭션 상태 API를 활용하여 트랜잭션이 활성 상태인지 먼저 확인합니다.
-- 트랜잭션 활성 여부를 확인하는 방법
SELECT pg_current_xact_id_if_assigned();
-- 결과가 NULL이면 현재 활성 트랜잭션 없음
-- SAVEPOINT를 안전하게 사용하는 패턴
BEGIN;
SAVEPOINT my_savepoint;
UPDATE orders SET status = 'processing' WHERE order_id = 101;
-- 문제 발생 시 SAVEPOINT로 복귀
ROLLBACK TO SAVEPOINT my_savepoint;
-- 정상 진행 시 SAVEPOINT 해제
RELEASE SAVEPOINT my_savepoint;
COMMIT;
원인 3 해결: PL/pgSQL에서 예외 처리로 안전하게 트랜잭션 관리
저장 프로시저나 함수 내에서 예외 처리 블록을 활용하여 트랜잭션 오류를 방어적으로 처리합니다.
-- PL/pgSQL 예외 처리 예시
DO $$
BEGIN
-- 트랜잭션 작업 시작
UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 55;
IF NOT FOUND THEN
RAISE EXCEPTION '상품을 찾을 수 없습니다: product_id = 55';
END IF;
INSERT INTO order_log (product_id, action, created_at)
VALUES (55, 'sold', NOW());
EXCEPTION
WHEN OTHERS THEN
-- 에러 정보 기록
RAISE WARNING '트랜잭션 오류 발생: %', SQLERRM;
-- 여기서 추가 ROLLBACK을 호출하지 않아야 함
-- PostgreSQL이 자동으로 블록 내 변경 사항을 롤백함
END;
$$;
커넥션 풀 환경에서의 추가 설정
-- 커넥션 반환 전 트랜잭션 상태 초기화 확인
-- PgBouncer server_reset_query 설정 예시 (postgresql.conf 또는 pgbouncer.ini)
-- server_reset_query = DISCARD ALL
-- 애플리케이션에서 커넥션 상태 확인
SELECT txid_current_if_assigned() IS NOT NULL AS in_transaction;
-- 강제로 트랜잭션 정리가 필요한 경우 (주의해서 사용)
-- 현재 세션의 트랜잭션 상태를 psql에서 확인
\echo :AUTOCOMMIT
예방 방법
1. 트랜잭션 상태를 명시적으로 관리하는 코딩 컨벤션 수립
모든 데이터 변경 작업은 반드시 BEGIN/COMMIT/ROLLBACK 블록 안에서 수행하도록 팀 내 코딩 가이드라인을 확립하세요. ORM을 사용하는 경우 자동 트랜잭션 관리 기능(예: SQLAlchemy의 with session.begin(), Spring의 @Transactional)을 일관되게 사용하고, 수동으로 트랜잭션을 제어하는 로직을 최소화해야 합니다. 코드 리뷰 단계에서 ROLLBACK이나 COMMIT이 단독으로 사용되는지 반드시 점검하세요.
2. PostgreSQL 로그와 모니터링을 통한 조기 감지
log_min_messages = WARNING 이상으로 설정하고, 25P01 에러가 로그에 나타날 때 즉시 알림을 받을 수 있도록 모니터링 시스템(Prometheus + pg_exporter, Datadog 등)을 구성하세요. pg_stat_activity 뷰를 정기적으로 조회하여 비정상적으로 오래 유지되는 idle in transaction 세션이 없는지 확인하고, 필요하다면 idle_in_transaction_session_timeout 파라미터를 설정하여 방치된 트랜잭션을 자동으로 종료시키는 것을 권장합니다.
관련 에러
- 25P02 (in_failed_sql_transaction): 트랜잭션 내에서 에러가 발생한 후, 해당 트랜잭션이 아직
ROLLBACK되지 않은 상태에서 추가 SQL을 실행하려 할 때 발생합니다. 25P01과 반대 방향의 트랜잭션 상태 불일치 에러입니다. - 2D000 (invalid_transaction_termination): 잘못된 컨텍스트에서 트랜잭션 종료 명령을 사용할 때 발생하며, 주로 함수 또는 트리거 내부에서
COMMIT을 직접 호출할 때 나타납니다. - 3B001 (savepoint_exception): SAVEPOINT 관련 작업에서 잘못된 SAVEPOINT 이름을 참조하거나 존재하지 않는 SAVEPOINT로
ROLLBACK TO를 시도할 때 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.