서버리스에서 데이터베이스 연결 수가 폭발한다는 통념은 잘못됐다. 동시 요청 1,000개 기준 서버·Lambda·Vercel 모두 필요 연결 수는 동일하다. 진짜 문제는 함수 suspend 중 유휴 연결의 타임아웃 타이머가 멈춰 연결이 수분간 누출되는 현상이며, Vercel Fluid의 attachDatabasePool이 waitUntil 패턴으로 이를 해결한다.
핵심 포인트- 서버리스가 연결을 더 많이 쓴다는 건 오해. 동시 요청 수가 같으면 모든 컴퓨팅 모델의 필요 연결 수는 동일하다.
- 진짜 문제: 함수가 suspend될 때 유휴 연결의 타임아웃 타이머가 멈춰 연결이 수분간 열려 있다.
- @vercel/functions의 attachDatabasePool(pool)을 전역 스코프에 등록하면 타이머 스케줄링과 waitUntil로 연결 누출을 방지한다.
- Lambda 권고 max pool size=1은 누출 방지가 안 되고 동시 처리 이점을 잃는 잘못된 권장사항이다.
- 트래픽이 낮을 때는 아이들 타임아웃을 5초처럼 짧게 설정해 빠른 연결 해제를 유도한다.
상세 정리- 오해 바로잡기: 단일 서버 1,000 스레드 = 서버리스 1,000 인스턴스 x 1 클라이언트. Vercel Fluid 100 인스턴스 x 10 클라이언트도 같다. 총 연결 수는 동일하다.
- 연결 누출 원인: 함수 suspend 중 타임아웃 타이머가 작동하지 않아 연결이 데이터베이스 서버 측 타임아웃(수분)까지 열려 있다.
- 영향 사례: Supabase 무료 플랜(최대 200 연결)에서 50개 누출만으로도 즉각적인 연결 부족이 발생한다.
- 해결 코드: import { Pool } from 'pg'; import { attachDatabasePool } from '@vercel/functions'; const pool = new Pool(…); attachDatabasePool(pool); 전역 스코프에 한 번만 등록한다.
- 동작 원리: 클라이언트가 풀로 반환될 때 타이머 스케줄링, waitUntil로 함수를 유지해 타이머 완료 후 연결 해제. 새 클라이언트 요청 시 타이머 취소 및 재스케줄링.
- 비용 영향: Active CPU Pricing 모델에서 타이머 대기 중 CPU 미사용(메모리 예약 비용만). 마지막 요청만 추가 시간 소요.
- 추가 권장사항: Pool은 전역 스코프 정의, 아이들 타임아웃 짧게 설정, 롤링 배포로 연결 몰림(thundering herd) 방지.
- 안티패턴: Lambda max pool size=1 권장은 연결 누출을 막지 못하면서 동시 처리 이점까지 잃는다.
왜 읽나서버리스 환경에서 Postgres/RDB 연결 풀 관리 문제를 겪는 백엔드 개발자에게 실제 원인 분석과 코드 수준 해결책을 제공한다.