TechFeedTechFeed
Backend

쿼리 타임아웃, 락 대기, 트랜잭션 유휴 | 문의 저장이 안 끝나고 풀이 차면?

문의 저장이 안 끝나면 풀부터 늘리지 않는다. 포스트그레 기본은 문장 제한, 락 대기, 트랜잭션 안 유휴를 0으로 둔다. 액션이 커밋 전에 메일을 기다리면 세션이 idle in transaction으로 남는다. 슈퍼베이스 역할은 익명 3초, 인증 8초, 대시보드 역할은 전역 2분 상한이다. 개발자, Next.js, 백엔드, API, PostgreSQL, 슈퍼베이스 기준으로 제한 숫자 칸만 보고 풀 글, 오토배큠 글과 자리를 섞지 않는다. 2026년 8월 공식 문서.

by

문의 저장이 안 끝나면 풀부터 늘리지 않습니다. 포스트그레 기본은 문장 제한, 락 대기, 트랜잭션 안 유휴를 모두 0으로 둡니다. 0은 꺼짐이라, 한 문이 끝나지 않아도 서버가 안 자릅니다. 액션이 커밋 전에 메일을 기다리면 세션이 idle in transaction으로 남고, 락과 죽은 튜플이 같이 묶입니다.


저는 12개 사이트 파트너십 저장을 연 채로 리센드를 기다린 날이 있습니다. 화면은 안 풀리고 풀은 찼습니다. 풀 고갈은 피지바운서 글, 응답 뒤 메일은 after 글입니다. 목록이 느린 칸은 페이지네이션 글입니다. 여기는 제한 숫자 칸만 봅니다.


숫자는 2026년 8월 포스트그레 18 클라이언트 기본값슈퍼베이스 타임아웃 문서 기준입니다.


저장이 안 끝나면 어느 제한부터 보나

풀 숫자를 올리기 전에 상태를 봅니다. active면 문장 제한, idle in transaction이면 트랜잭션 안 유휴, 락 대기는 lock 이벤트가 있을 때입니다.


파트너십 문의가 수 분째 스피너를 돌린 날이 있습니다. 저장은 이미 들어갔고, 같은 함수가 메일을 기다리고 있었습니다. 연결은 살아 있는데 쿼리가 없는 상태였습니다. 피지바운서 글은 연결이 반환되지 않아 풀이 찬 기록입니다. 오늘은 그 앞에, 세션이 왜 안 끝나는지 가르는 칸입니다.


기본언제 보나끝나면
문장 제한0, 꺼짐한 문이 길게 돈다그 문만 취소. 57014
락 대기0, 꺼짐다른 세션 락을 기다린다그 문만 취소. 55P03
트랜잭션 안 유휴0, 꺼짐BEGIN 뒤 클라이언트가 멈춤세션 종료, 롤백
세션 유휴0, 꺼짐트랜잭션 없이 놀고 있음세션 종료. 풀러에는 잘 안 켬
트랜잭션 제한0, 꺼짐. 17부터짧은 문이 이어져 BEGIN이 길다세션 종료. 준비된 트랜잭션은 제외

문서도 설정 파일에 문장 제한을 전역으로 넣지 말라고 적습니다. 모든 세션이 같이 잘립니다. 마이그레이션 잠금은 무중단 마이그레이션 글입니다. 재고 동시 쓰기는 낙관적 락 글입니다. 오늘은 초를 어디에 적을지입니다.


풀을 먼저 늘리지 않습니다 | 유휴 트랜잭션이 자리를 차지한 채로 한도만 올리면, 같은 패턴이 더 많은 연결로 늘어납니다. 상태를 본 뒤에 초를 넣습니다.


문장 제한과 락 대기, 유휴는 칸이 다르다

다섯 숫자는 서로 다른 시계입니다. 문장 제한은 한 문의 서버 처리 시간이고, 락 대기는 락을 얻는 동안만 돕니다. 트랜잭션 안 유휴는 클라이언트가 다음 문을 안 보내는 동안입니다.


포스트그레 18 문서는 단위 없이 쓰면 밀리초라고 적습니다. '15s'처럼 단위를 붙이는 편이 읽기 쉽습니다. 락 대기를 문장 제한과 같거나 더 크게 두면, 문서 그대로 문장 제한이 먼저 터져 락 대기는 의미가 없습니다. 트랜잭션 제한이 다른 둘보다 짧거나 같으면, 긴 쪽은 무시됩니다.


죽은 튜플이 안 지워지는 날은 오토배큠 글과 맞닿습니다. 열린 트랜잭션이 최근 죽은 줄을 아직 볼 수 있어서, 배큠이 그 줄을 못 지웁니다. 디스크가 느는 글과 자리를 섞지 않습니다. 여기는 그 트랜잭션을 몇 초에 자를지입니다.


역할에 문장 제한과 트랜잭션 안 유휴를 같이 넣는다
-- https://www.postgresql.org/docs/current/runtime-config-client.html SHOW statement_timeout; SHOW lock_timeout; SHOW idle_in_transaction_session_timeout; SHOW idle_session_timeout; SHOW transaction_timeout; -- 앱이 쓰는 역할. 단위를 붙인다 ALTER ROLE app SET statement_timeout = '15s'; ALTER ROLE app SET lock_timeout = '3s'; ALTER ROLE app SET idle_in_transaction_session_timeout = '20s'; -- idle_session_timeout 은 풀러 연결에 잘 안 켠다
포스트그레 세션 상태에서 문장 실행과 트랜잭션 안 유휴를 구분하는 모니터링 화면
active 와 idle in transaction 은 넣는 초가 다르다

커밋 전에 메일을 기다리면 세션이 남는 자리

BEGIN 뒤에 외부 HTTP를 두면, 쿼리는 끝났는데 세션은 트랜잭션 안 유휴입니다. 메일은 커밋 다음이나 after로 보냅니다.


문의 행을 넣은 뒤 같은 클라이언트에서 리센드를 기다린 적이 있습니다. 수신함이 느린 오후에 연결이 한동안 안 돌아왔습니다. 버튼 잠금은 폼 훅 글이고, 응답을 먼저 보내는 칸은 after 글입니다. 오늘은 그 대기가 데이터베이스 세션을 붙잡는 자리입니다.


주문과 알림을 같은 커밋에 맞춰야 하면 아웃박스 글입니다. 메일 본문을 트랜잭션 안에 넣지 말고, 행만 커밋한 뒤 워커가 보냅니다. 소프트 삭제 유니크는 부분 유니크 글입니다. 제한 숫자와 자리를 섞지 않습니다.


저장만 커밋하고, 메일은 트랜잭션 밖에서 보낸다
// Next.js 서버 액션. 문의 행만 커밋한다 import { after } from 'next/server' export async function saveInquiry(formData) { const row = await db.insert(inquiry).values({ email: String(formData.get('email') || ''), body: String(formData.get('body') || '') }).returning() after(async () => { await sendMail(row[0]) }) return { ok: true } }

SELECT FOR UPDATE 뒤에 HTTP를 두지 않습니다 | 행 락을 잡은 채로 카카오, 토스, 리센드를 기다리면 다른 요청이 그 행에서 멈춥니다. 락 대기가 꺼져 있으면 더 오래 붙습니다.


슈퍼베이스 역할은 3초, 8초, 2분이 기본이다

클라이언트 API는 역할 초가 이미 있습니다. 익명 3초, 인증 8초입니다. 대시보드와 외부 도구가 쓰는 postgres 역할은 전역 2분 상한입니다. 서버 액션이 서비스 롤이면 기본이 비어 있어, 인증자 8초에 기대거나 직접 적어야 합니다.


슈퍼베이스 타임아웃 문서는 대시보드와 클라이언트 쿼리의 설정 상한을 60초로 둡니다. 그 이상은 수퍼바이저 세션 모드나 직접 연결입니다. 세션에서 SET statement_timeout 은 트랜잭션 모드 6543, 대시보드, 클라이언트 API에서는 안 됩니다. 직접 연결이나 세션 모드 5432만 됩니다.


역할문장 제한 기본어디서 쓰나초를 어디에 적나
anon3초브라우저 익명역할. 올리면 노티파이로 재로드
authenticated8초로그인 클라이언트역할. 상한 60초
service_role없음. 비면 인증자 8초서버 액션, 배치역할에 15~30초를 직접 적음
postgres없음. 전역 2분 상한대시보드, 프리즈마, 피에스큐엘긴 인덱스는 세션 모드에서 SET

ORM 고르는 칸은 드리즐과 프리즈마 글입니다. 시각 저장은 타임스탬프 글입니다. 행 권한은 알엘에스 글입니다. 오늘은 역할 초만 봅니다.


슈퍼베이스 서비스 롤에 초를 적고 포스트그레스트를 재로드한다
-- https://supabase.com/docs/guides/database/postgres/timeouts ALTER ROLE service_role SET statement_timeout = '15s'; ALTER ROLE service_role SET idle_in_transaction_session_timeout = '30s'; ALTER ROLE service_role SET lock_timeout = '3s'; SELECT rolname, rolconfig FROM pg_roles WHERE rolname IN ('anon', 'authenticated', 'postgres', 'service_role'); -- 클라이언트 API 역할 초를 바꿨으면 NOTIFY pgrst, 'reload config';
슈퍼베이스 역할별 문장 제한 초를 확인하는 데이터베이스 설정 화면
익명 3초, 인증 8초. 서비스 롤은 직접 적는다

설정 파일에 전역으로 넣으면 마이그레이션이 같이 죽는다

문장 제한을 postgresql.conf 에 넣지 않습니다. 문서가 모든 세션에 영향을 준다고 말립니다. 앱 역할에만 적고, 인덱스 생성 세션은 그 연결에서만 풉니다.


동시 인덱스 생성을 15초 문장 제한 아래에서 돌리면, 테이블이 큰 날 중간에 잘립니다. 무중단 마이그레이션 글이 세션에 문장 제한과 락 대기를 명시하라고 적어 둔 자리입니다. 앱과 마이그레이션이 같은 역할을 쓰면, 앱 초가 배포 문을 자릅니다. 마이그레이션 역할과 앱 역할을 나눕니다.


전역을 4초로 낮추는 슈퍼베이스 예는 모든 역할의 빈 값을 덮습니다. 대시보드 긴 조회도 같이 짧아집니다. 역할 칸이 비어 있는 서비스 롤만 손보는 편이 안전합니다.


인덱스 세션만 문장 제한을 잠시 푼다. 전역 파일은 건드리지 않는다
-- 세션 모드 또는 직접 연결에서만 SET statement_timeout = '30min'; SET lock_timeout = '5s'; CREATE INDEX CONCURRENTLY inquiry_email_live_idx ON inquiry (email) WHERE deleted_at IS NULL; -- 확인 SHOW statement_timeout;

세션 유휴를 풀러에 켜지 않습니다 | 문서가 미들웨어가 예기치 않은 종료를 잘 못 다룰 수 있다고 적습니다. 트랜잭션 안 유휴만 앱 역할에 넣고, 세션 유휴는 대화형 계정에만 둡니다.


상태를 본 뒤에 숫자를 넣는 한 바퀴

먼저 누가 오래 붙잡는지 봅니다. 상태, 트랜잭션 나이, 대기 이벤트를 한 표로 적고, 그다음 역할 초를 넣습니다. 초를 넣은 뒤 같은 조회를 한 번 더 합니다.


관리 목록이 오프셋으로 느려진 칸은 페이지네이션 글입니다. 여기 조회는 그 목록을 고치는 쿼리가 아닙니다. 지금 붙어 있는 세션이 문을 실행 중인지, 트랜잭션만 열어 둔 채 메일 쪽을 보는지 가르는 표입니다.


지금 붙은 세션의 상태와 나이를 본다
-- https://www.postgresql.org/docs/current/monitoring-stats.html SELECT pid, usename, state, wait_event_type, wait_event, now() - xact_start AS xact_age, now() - query_start AS query_age, left(query, 80) AS query FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid() ORDER BY xact_start NULLS LAST;
데이터베이스 활동 뷰에서 트랜잭션 나이와 대기 이벤트를 나란히 보는 표
상태와 나이를 적은 뒤에 역할 초를 넣는다

오늘 앱 역할에 초 하나만 적는다

오늘 할 일은 앱 역할에 문장 제한 15초와 트랜잭션 안 유휴 20초입니다. 락 대기는 3초입니다. 메일은 커밋 밖으로 옮깁니다. 풀 한도와 전역 파일은 그대로 둡니다.


노드 풀이면 새 연결마다 SET 를 한 줄 넣을 수 있습니다. 슈퍼베이스 트랜잭션 모드면 SET 이 안 붙으니 역할에 적습니다. 저는 파트너십 액션부터 이 숫자를 넣었습니다. 화면이 풀린 뒤에야 풀 글의 연결 수를 다시 봤습니다.


오늘 한 줄 | 풀 한도보다 역할 초가 먼저입니다. 문장 제한 15초, 트랜잭션 안 유휴 20초, 메일은 커밋 다음입니다.


참고 자료


내부 연계: 피지바운서, after, 오토배큠, 페이지네이션, 부분 유니크, 무중단 마이그레이션, 낙관적 락, 아웃박스


역할 화면과 활동 뷰가 표보다 우선입니다. 위 숫자는 2026년 8월 공개 문서 기준입니다.


자주 묻는 질문

문의 저장이 안 끝나면 풀을 먼저 늘려야 하나?

아닙니다. 활동 뷰에서 상태가 idle in transaction 이면 연결이 부족한 게 아니라 트랜잭션이 안 닫힌 겁니다. 메일을 커밋 밖으로 옮기고, 앱 역할에 문장 제한과 트랜잭션 안 유휴를 넣습니다. 그래도 남는 연결만 풀 글에서 봅니다.


문장 제한 기본값이 왜 0인가?

포스트그레는 문장 제한, 락 대기, 트랜잭션 안 유휴, 세션 유휴, 트랜잭션 제한을 모두 0으로 둡니다. 0은 꺼짐입니다. 직접 설치한 서버는 앱이 안 적으면 한 문이 끝나지 않습니다. 슈퍼베이스 클라이언트 역할은 이미 3초, 8초가 있습니다.


트랜잭션 모드 풀러에서 SET 이 안 먹는 이유는?

슈퍼베이스는 세션 SET 을 직접 연결이나 세션 모드 5432에만 허용합니다. 트랜잭션 모드 6543, 대시보드, 클라이언트 API는 안 됩니다. 프리즈마가 6543을 쓰면 역할에 ALTER ROLE 로 적습니다. 바꾼 클라이언트 역할은 노티파이로 재로드합니다.


락 대기와 교착 대기는 같은가?

다릅니다. 락 대기는 락을 너무 오래 기다리면 그 문을 취소합니다. 교착 대기는 교착을 검사하는 주기이고 기본 1초입니다. 재고 한 줄을 잠근 채 HTTP를 기다리면 락 대기가 먼저 도움이 됩니다. 동시 재고 패턴은 낙관적 락 글입니다.


마이그레이션도 15초로 자르면 되나?

앱 역할 초를 마이그레이션에 복사하지 않습니다. 동시 인덱스는 분이 걸릴 수 있습니다. 세션 모드에서 그 연결만 문장 제한을 길게 풀거나, 마이그레이션 역할을 나눕니다. 설정 파일 전역은 문서가 말립니다.


부가세나 카드 한도와 관계 있나?

타임아웃 숫자는 과금 칸이 아닙니다. 슈퍼베이스 프로젝트 요금과 연결 수는 플랜 화면이 우선입니다. 국내 카드 부가세 10%는 구독 청구 줄이고, 여기 15초, 20초와 자리가 다릅니다.


statement_timeoutlock_timeoutidle in transactionPostgreSQL슈퍼베이스백엔드Next.jsAPI개발자트랜잭션쿼리 타임아웃드리즐프리즈마

함께 보면 좋은 문제 해결

EXPLORE / Backend

이어서 읽어보기

전체 토픽 둘러보기