Postgres LISTEN/NOTIFY는 실제로 확장 가능함

4 hours ago 2

Postgres LISTEN/NOTIFY의 전역 배타적 잠금은 단순 구현의 처리량을 제한하지만, 알림을 버퍼링해 일괄 전송하면 단일 서버에서 초당 최대 6만 건의 스트림 쓰기를 처리할 수 있음 NOTIFY를 호출한 트랜잭션은 알림의 커밋 순서를 보장하기 위해 커밋과 fsync()가 끝날 때까지 전역 잠금을 유지하며, 이로 인해 커밋이 직렬화되고 그룹 커밋도 활용하지 못함 스트림 테이블의 모든 쓰기마다 트리거로 NOTIFY를 호출한 초기 구현은 낮은 지연 시간을 제공했지만, CPU·메모리·IOPS를 충분히 사용하지 못한 채 초당 2,900건에서 병목을 맞음 알림이 아닌 데이터베이스 테이블을 진실의 원천으로 삼고, 메모리에 모은 알림을 주기적으로 하나의 트랜잭션에서 전송하면 잠금 획득 횟수를 크게 줄일 수 있음 프로세스 장애로 버퍼의 알림이 유실될 가능성은 낮은 빈도의 폴링으로 보완하며, 동시 읽기 환경에서도 15~100ms의 지연 시간과 기존 대비 20배의 처리량을 달성함 Postgres로 구현한 저지연 스트림 Postgres 기반 스트림은 각 스트림 조각을 streams 테이블의 새 행으로 저장하며, LLM 응답 토큰도 하나의 조각이 될 수 있음 읽기 측에서는 다음 조각이 언제 도착할지 알 수 없어 단순 조회만으로 효율적으로 대기하기 어려움 주기적 폴링은 간격이 길면 온라인 채팅 같은 대화형 용도에서 지연 시간이 커지고, 짧으면 동시 폴러가 데이터베이스를 압도함 LISTEN/NOTIFY를 사용하면 읽기 프로세스가 차단 상태로 기다리다가 새 조각이 기록됐다는 알림과 함께 즉시 깨어나므로 불필요한 폴링을 피할 수 있음 쓰기마다 보낸 NOTIFY의 병목 초기 구현에서는 streams 테이블에 새 조각이 기록될 때마다 트리거가 함수를 실행해 NOTIFY를 한 번씩 전송하고, 읽기 프로세스는 알림을 기다린 뒤 새 조각을 읽음 정확성과 낮은 지연 시간은 확보했지만, 큰 Postgres 데이터베이스에서도 초당 2,900건을 넘는 스트림 쓰기를 지속하지 못함 병목이 발생하는 동안 CPU·메모리·IOPS 사용량은 눈에 띄게 높아지지 않았으며, 원인은 NOTIFY 커밋 경로의 전역 잠금이었음 커밋 순서를 보장하는 전역 잠금 NOTIFY를 호출한 트랜잭션은 커밋 시작 시 전역 배타적 잠금을 획득하고, 완전히 커밋되어 내용이 fsync()로 디스크에 기록될 때까지 해제하지 않음 Postgres는 알림이 트랜잭션 커밋 순서대로 전달되...

Read Entire Article