2026년 11월 2일, PostHog는 DuckDB 기반 warehouse를 자체 Trino 구성과 HogLake로 다시 설계한 이유를 공개했습니다.[18] 예상보다 복잡해진 고객 쿼리, DuckLake catalog의 비용, PostgreSQL 연결 호환성이 각각 다른 부담이었다는 설명입니다.[18]
이 글은 분석 기능이나 데이터 파이프라인을 운영하는 BE·인프라 개발자를 위한 해설입니다. 특정 엔진의 승패보다 쿼리 실행·메타데이터·클라이언트 연결을 나눠 진단하는 방법에 집중합니다.
💡 💡 핵심: 한 회사의 전환은 다른 회사의 교체 명령이 아닙니다. 무엇이 느린지, 어떤 버전에서 발생하는지, 사용자에게 어디까지 보장할지를 먼저 분리해야 합니다.
DuckDB의 in-process read-write 모드는 한 프로세스 안에서 여러 writer thread를 지원합니다.[26] 충돌하지 않는 쓰기는 동시에 진행할 수 있으므로, “단일 프로세스”를 “쿼리 하나만 실행 가능”으로 이해하면 안 됩니다.[26]
또한 DuckDB 2.0 문서는 Quack을 통한 client-server 방식과 PostgreSQL catalog를 사용하는 DuckLake의 다중 프로세스 접근을 별도로 안내합니다.[26] 따라서 “DuckDB는 여러 프로그램이 쓰는 서버로 만들 수 없다”는 설명도 현재 문서와 맞지 않습니다.[26]
DuckLake specification은 두 구성요소를 요구합니다.[30] 하나는 transaction과 primary key를 지원하는 catalog database이고, 다른 하나는 Parquet 파일을 저장하는 storage입니다.[30] Catalog에는 metadata와 statistics 등이 SQL 테이블로 표현됩니다.[30]
이 구조를 이해하면 “S3의 데이터는 그대로인데 왜 작은 쓰기까지 느려질까?”라는 질문을 할 수 있습니다. 대량 데이터 파일을 읽는 경로와, 어떤 파일·schema·snapshot이 유효한지 확인하는 경로는 다른 문제입니다.[30]
PostHog의 이전 설계는 조직별 DuckDB instance, 유휴 시 sleep하는 lifecycle service, PostgreSQL wire endpoint, S3 데이터와 DuckLake catalog를 조합했습니다.[19] 엔진만 선택한 것이 아니라 격리·실행 수명·외부 도구 연결까지 묶어 서비스로 제공한 것입니다.
당시 공식 글은 핵심 analytics 데이터에는 ClickHouse를 계속 사용한다고 명시했습니다.[19] 이번 HN 토론에서도 창업자라고 밝힌 참여자가 warehouse는 주 analytics DB와 별개이며 DuckDB warehouse 사용자는 소수 pilot 고객이었다고 설명했습니다.[24] 이는 해당 참여자의 설명이지 별도 운영 감사 결과는 아닙니다.
💡 ⚠️ 따라서 이번 사건을 “PostHog 전체 분석 DB가 ClickHouse에서 DuckDB로 갔다가 다시 Trino로 옮겨졌다”고 요약하면 범위를 과장하게 됩니다.[19][24]
PostHog는 고객 쿼리가 복잡해지면서 큰 instance가 필요했고, 쿼리 도착 전에 CPU·메모리를 얼마나 배정해야 할지 예측하기 어려웠다고 보고했습니다.[18] Disk를 추가하는 방법도 검토했지만 원하는 응답 시간과 비용을 맞추기 어렵다는 판단이었습니다.[18]
DuckDB 공식 성능 문서도 메모리 요구량에는 데이터셋 크기뿐 아니라 쿼리 형태가 더 큰 영향을 줄 수 있다고 설명합니다.[27] 특히 many-to-many join은 큰 중간 결과를 만들며, DuckDB는 메모리를 넘는 작업에서 disk spill을 지원합니다.[27]
여기서 배울 점은 “메모리보다 데이터가 크면 DuckDB를 버려야 한다”가 아닙니다. 완료할 수 있는지와 서비스가 약속한 시간·비용으로 완료하는지는 다른 질문입니다.
진단 제안: 같은 데이터에서 단순 집계와 큰 join을 따로 측정하고, 단독 실행과 동시 실행도 나누세요. 실행 시간만 보지 말고 peak memory, 임시 disk 사용, 대기 시간과 요청당 비용을 함께 기록하는 편이 좋겠습니다. 이는 이 글의 운영 제안이며 직접 측정한 결과가 아닙니다.
PostHog는 단일 테이블 쓰기에서 catalog 전체 statistics를 다시 읽어 3분 넘게 걸릴 수 있었다고 설명합니다.[18] 더 이상 존재하지 않는 테이블에 속한 statistics 행이 99%를 넘었고, 이를 정리했을 때 쓰기가 30~50배 빨라졌지만 다시 누적됐다는 보고도 포함합니다.[18]
이 수치는 PostHog의 해당 환경에 대한 자체 보고입니다. 전환 시 DuckLake에는 약 2PB, 약 1조 행, 1,530만 snapshot이 있었다고 밝히므로 일반적인 소규모 분석 작업의 성능 수치로 옮겨 쓰면 안 됩니다.[18]
DuckLake의 공개 issue #1847에도 테이블 수가 늘면 commit이 느려진다는 사용자 보고가 있습니다.[39] 보고자는 DuckDB 2.0.0용 DuckLake build 417a9117f5와 DuckDB 1.5.4용 build d318a545를 비교하고, 쓰기 뒤 statistics cache miss가 catalog 전체 조회를 유발하는 경로와 재현 코드를 제시했습니다.[39]
중요한 것은 후속 댓글입니다. 보고자는 commit 자체를 개선한 변경 뒤에도 다음 읽기에는 전체 statistics 재조회가 남았고, 다른 수정 branch를 시험한 뒤에는 그 경로도 개선됐다고 설명했습니다.[39] 이 issue는 확인 시점에 open이었습니다.[39]
이는 외부 사용자가 공개한 재현·branch 시험 보고입니다. 이 글에서 직접 재현한 시험이 아니며, PostHog의 모든 장애가 같은 원인이라는 증거도 아닙니다. 특정 branch의 개선을 사용자의 배포 버전에 반영된 수정으로 간주해서도 안 됩니다.
설계 해석: 작은 쓰기 하나가 전체 catalog의 cache를 무효화하는지 확인해야 합니다. 새 엔진을 고르기 전에 변경한 테이블 수, 조회한 metadata 양, network 전송량이 어떻게 연결되는지 살펴볼 가치가 있습니다.
현재 DuckLake 문서는 time travel 때문에 삭제한 데이터나 테이블도 즉시 물리 제거하지 않는다고 설명합니다.[40] Snapshot expiration은 더 이상 보존할 snapshot이 필요로 하지 않는 obsolete metadata와 삭제된 inlined row도 제거하지만, 참조가 사라진 파일을 즉시 지우는 작업과는 구분됩니다.[40]
따라서 “삭제한 테이블의 기록이 남는다”는 관찰만으로 모두 garbage라고 판단할 수 없습니다. 반대로 보존 기능이 있다는 이유로 PostHog가 보고한 과도한 통계 누적을 정상이라고 단정할 수도 없습니다.
운영 제안: retention 요구, 실제 참조 관계, 사용 중인 extension build, expiration·cleanup 실행 이력을 함께 확인하세요. 이 글은 운영 catalog의 직접 삭제나 내부 statistics 테이블 편집을 권하지 않습니다.
이전 PostHog 설계는 PostgreSQL protocol로 연결하되 실제 쿼리는 DuckDB SQL로 실행하고, 도구의 schema introspection을 위해 PostgreSQL catalog를 변환한다고 설명했습니다.[19]
새 회고에서는 reconnect, idle connection, TTL, load balancing뿐 아니라 클라이언트마다 기대하는 세부 PostgreSQL 동작을 맞추는 데 계속 비용이 들었다고 보고합니다.[18]
이 사례에서 protocol 호환성과 동작 호환성은 별도 계약이라는 점을 읽을 수 있습니다. 연결 성공을 확인한 것만으로 BI 도구의 introspection, transaction 처리, 끊김 뒤 재시도까지 검증한 것은 아닙니다.
적용 제안: “Postgres-compatible”이라는 문구 대신 지원할 client·driver 버전과 실제 workflow를 명시하세요. 연결·schema 조회·쿼리·취소·재연결을 각각 acceptance test로 두면 호환성의 경계를 설명하기 쉬워집니다.
Trino 공식 문서에서 coordinator는 SQL 파싱·계획과 worker 관리를 담당하고, worker는 task 실행과 데이터 처리를 맡습니다.[23] Worker끼리는 중간 결과를 교환합니다.[23]
따라서 Trino를 검토하는 이유와 catalog를 교체하는 이유는 따로 설명해야 합니다. 전자는 쿼리 실행을 분산하는 문제이고, 후자는 metadata의 조회·변경·유지보수를 누가 책임지는지의 문제입니다.[23][48]
확인한 HogLake README는 metadata를 Postgres에 두되 client가 DB를 직접 보지 않고 REST contract를 사용한다고 설명합니다.[48] Catalog SQL과 유지보수 책임을 server에 모으고, backend도 Postgres로 한정하는 설계입니다.[48]
여기서 바뀐 것은 “Postgres를 없앴다”가 아닙니다. Client마다 metadata 구현을 포함하는 방식에서, 하나의 service가 그 구현을 관리하는 방식으로 경계를 옮긴 것입니다.[48]
README는 HogLake가 DuckLake와 wire·SQL·metadata 호환을 목표로 하지 않는다고 명시합니다.[48] 이름과 저장 방식이 비슷하다고 기존 도구를 그대로 연결할 수 있다고 가정하면 안 됩니다.
또한 README의 benchmark는 개발 laptop의 local compose 환경에서 수행한 회귀 검사이며 capacity planning 수치로 읽지 말라고 안내합니다.[48] 이 글에서는 그 수치를 PostHog의 2PB 운영 성능으로 재인용하지 않았습니다.
다음은 문서를 바탕으로 한 도입 판단 제안입니다. 실제 고객 적용이나 benchmark 결과가 아닙니다.
대표 쿼리가 자원·시간 한도 안에서 끝나고 동시 작업도 통제할 수 있다면, 먼저 thread 수와 임시 disk·메모리 사용을 조정하는 편이 단순합니다. 공식 문서에도 thread 제한과 disk spill에 대한 안내가 있습니다.[27]
원격 client가 필요할 때에는 현재 DuckDB 문서의 Quack·DuckLake 접근 방식도 검토 대상입니다.[26] 다만 원격 연결이 된다는 사실만으로 무거운 쿼리의 분산 실행이나 운영 SLA까지 해결됐다고 판단하지는 마세요.
Trino를 직접 운영하면 coordinator·worker와 connector 구성이 필요하고, HogLake 방식에는 catalog service와 Postgres 운영도 더해집니다.[23][48] 이 추가 책임을 감당할 근거가 없다면 유명 회사의 전환을 따라가는 것보다 현재 구성을 다듬는 편을 권합니다.
HN에서는 전환 이후의 비교 결과와 검증 방법이 충분히 제시됐는지 묻는 반응이 있었습니다.[24] 공개 README에서 설계와 benchmark 조건을 더 읽을 수는 있지만, 그것이 독립적인 운영 성능 검증을 대신하지는 않습니다.[48]
보안도 별도입니다. 확인한 HogLake README는 authentication이 opt-in이고 기본 HOGLAKE_AUTH_MODE가 off라고 명시합니다.[48] 공개 예제를 그대로 외부에 노출하는 식의 도입은 권하지 않습니다. 이 글은 설치나 배포 절차가 아닙니다.
💡 🔬 검증 노트: 자료는 2026-11-03 KST에 확인했습니다. DuckDB 2.0 문서, DuckLake specification 1.1 및 현재 expiration 문서, Trino live 문서 484, HogLake commit
615b0119ae93a4238404a016159d53b186828e70의 README를 읽었습니다. 공개 issue는 API 본문과 후속 댓글까지 대조했습니다. 직접 DB 설치·쿼리 실행·성능 재현·운영 환경 감사는 하지 않았습니다.
PostHog 회고에는 사용한 모든 DuckDB·DuckLake build와 tuning 이력, 동일 조건의 전환 전후 비용·지연 비교가 담겨 있지 않습니다.[18] 따라서 “최신 DuckLake에서도 해결 불가능하다”, “Trino가 언제나 더 싸다”, “HogLake로 모든 운영 문제가 해결됐다”는 결론은 내리지 않습니다.
이번 회고의 가치는 새 데이터베이스 이름보다 운영 책임의 경계를 드러낸 데 있습니다. 먼저 어느 경로가 사용자의 시간·비용·신뢰를 깨는지 확인하고, 그 문제를 해결하는 가장 작은 변경부터 비교하는 편이 좋겠습니다.
[18] https://posthog.com/blog/moving-off-duckdb — PostHog: Why we're moving our warehouse off DuckDB — 2026-11-02
[19] https://posthog.com/blog/why-we-rebuilt-our-data-warehouse — PostHog: original DuckDB warehouse design — 2026-06-29
[23] https://trino.io/docs/current/overview/concepts.html — Trino concepts — live documentation 484
[24] https://news.ycombinator.com/item?id=50338747 — Hacker News: PostHog warehouse discussion — opinions and author replies
[26] https://raw.githubusercontent.com/duckdb/duckdb-web/refs/heads/main/docs/2.0/connect/concurrency.md — DuckDB 2.0 official concurrency documentation — Markdown
[27] https://raw.githubusercontent.com/duckdb/duckdb-web/refs/heads/main/docs/2.0/guides/performance/environment.md — DuckDB 2.0 official resource planning documentation — Markdown
[30] https://ducklake.select/docs/stable/specification/introduction — DuckLake specification 1.1: building blocks
[39] https://github.com/duckdb/ducklake/issues/1847 — DuckLake issue #1847 — user report and follow-up branch tests
[40] https://ducklake.select/docs/stable/duckdb/maintenance/expire_snapshots — DuckLake 1.1: snapshot expiration and metadata retention
[48] https://raw.githubusercontent.com/PostHog/hoglake/615b0119ae93a4238404a016159d53b186828e70/README.md — Hoglake README — inspected commit 615b0119ae