요약
ClickBench 관리자가 pgrust 기록이 질의 43개 전용 코드와 결과 재사용으로 만든 것이라며 삭제했습니다.
ClickBench를 운영하는 클릭하우스의 알렉세이 밀로비도프가 올리고 직접 병합한 변경 요청(PR)입니다. 그는 러스트로 만든 PostgreSQL 호환 데이터베이스 pgrust의 v0.4-preview 실행 파일과 공개된 v0.3 소스를 분석했습니다.
왜 중요한가
- 43개 답이 모두 맞아도 문제 목록에 맞춘 조회표라면 데이터베이스 성능으로 볼 수 없다는 기준을 분명히 했습니다.
- 소스가 공개되지 않은 실행 파일도 내장된 경로와 오류 메시지, EXPLAIN 출력만으로 검증할 수 있음을 보여 줬습니다.
핵심 내용
- 결과 캐시를 꺼야 한다는 규칙을 어겼고, 특정 질의에 맞췄는데도 튜닝 안 함(tuned: no)으로 표시했다고 판단했습니다.
- 실행 파일 안에 질의 번호마다 손으로 짠 커널(그 질의만 처리하는 코드)의 소스 경로가 들어 있었습니다.
- Q20은 캐시를 비운 첫 실행에 10.8초, 다음 실행에 11ms가 걸려 앞 실행의 판정을 다시 쓴 것으로 봤습니다.
- 캐시 설정을 모두 꺼도 같은 LIKE 질의를 반복하면 3~4ms 만에 끝났습니다.
SELECT MIN(URL) FROM hits처럼 조금만 바꾼 질의는 지원하지 않는다는 오류와 함께 거부됐습니다.
HN 반응
- pgrust 제작자는 커널이 벤치마크 중에는 돌지 않는 시험 코드이고 캐시도 허용되는 종류라고 반박했지만, 성급한 제출은 인정했습니다.
- 여러 이용자는 v0.4-preview 소스부터 공개하라고 요구했고, 새로 공개한 0.4-beta가 같은 성능을 내면 그만이라는 의견도 있었습니다.
안녕하세요! pgrust를 만든 사람입니다. 무슨 일이 있었는지 설명드리겠습니다.
제기된 의혹은 대부분 사실이 아닙니다. 이 제출물은 pgrust 0.4의 초기 버전인 pgrust 0.4-preview였습니다. 저희는 어제 더 새로운 버전인 pgrust 0.4-beta를 오픈소스로 공개했고, 여기[0]에서 전부 감사해 보실 수 있습니다. GitHub에서 의혹을 하나하나 반박하기보다 pgrust 0.4를 완성해서 오픈소스로 내놓는 편이 저희가 정직하다는 걸 보여 주기에 더 쉽겠다고 판단했습니다. 말씀드린 내용은 저희가 ClickBench에 제출한 0.4-preview 바이너리[1]로 직접 확인하셔도 됩니다. 이 바이너리가 제출한 것과 정확히 같다는 걸 증명할 방법은 없지만, ClickHouse 쪽에서 테스트한 바이너리의 SHA를 제공한다면 일치할 겁니다.
주요 의혹 세 가지는 이렇습니다. 1) 벤치마크의 쿼리마다 맞춰 최적화한 손으로 짠 코드를 썼다. 2) 쿼리를 실행하는 사이에 결과를 캐시했다. 3) ClickBench 쿼리 43개 외의 쿼리는 지원하지 않았다.
먼저, 손으로 짠 쿼리는 쓰지 않았습니다. Alexey가 짚은 코드는 벤치마크 중에 실행되지 않습니다. 0.4의 설계는 JIT 컴파일러에 기반합니다. SQL을 Lisp 기반 중간 표현으로 컴파일하고, 이걸 저희는 Gravity IR이라고 부르는데, 그다음 x86과 ARM 어셈블리로 변환합니다. 0.4를 개발하는 동안 저는 벤치마크용으로 이 IR에 맞춰 손으로 코드를 짰습니다. 우리 IR로 쓴 쿼리가 빠르고, 충분히 좋은 IR을 빠르게 컴파일할 수 있다면 새 쿼리 엔진도 빠를 거라는 발상이었습니다. 그 "커널"들이 들어 있는 디렉터리 이름이 "irprobe"인 것도 이 때문입니다. IR이 충분히 빠른지 확인하려고 찔러 본 용도였습니다. 0.4-preview 제출물은 SQL 쿼리 -> Gravity IR -> 어셈블리 파이프라인을 거쳤습니다.
둘째, Alexey가 본 "result-cache"는 쿼리 결과가 아니라, 사전 압축에 쓰는 사전(dictionary)에서 어떤 조건이 어떤 문자열과 일치하는지를 캐시해 둔 것입니다. Alexey는 이 캐시를 몰랐고 저희가 쿼리 결과를 캐시했다고 짐작했습니다. ClickBench에서는 이런 중간 데이터를 담는 캐시를 써도 됩니다[2]. ClickHouse에도 이와 비슷한 Condition Cache[3]라는 자체 캐시가 있고요. 이번 새 제출물에서는 저희가 쓰는 캐시를 모두 솔직하게 밝히고, 각각을 끄는 방법도 적어 두었습니다[4].
셋째, ClickBench 쿼리 43개 외의 쿼리는 지원하지 않았다는 주장은 일부만 거짓이고, 근거가 있는 부분도 있습니다. 앞서 말씀드렸듯 pgrust 0.4-preview는 pgrust 0.4의 초기 버전이었습니다. 새 JIT 컴파일러가 ClickBench 쿼리를 처리할 만큼 SQL을 지원하게 되자 ClickBench에 제출하기로 했습니다. 제가 아는 한 ClickBench에는 SQL을 얼마나 지원해야 하는지에 관한 규칙이 없습니다. 게다가 0.4-preview에는 기존 쿼리 엔진도 함께 들어 있었습니다. 코드 두 줄만 바꾸면 거기로 폴백해서 새 엔진이 지원하지 못하는 훨씬 넓은 범위의 쿼리를 처리할 수 있었을 겁니다. 그런 쿼리도 빠르긴 했겠지만 새 엔진만큼은 아니었겠죠. 새로 낸 0.4-beta는 이제 (CTE와 MERGE 관련 버그 몇 개를 빼면) 모든 Postgres SQL을 지원합니다.
정리하면, pgrust 0.4-preview는 pgrust 0.4를 개발하던 중의 초기 빌드였습니다. 진짜 쿼리 엔진이었고 실제로 쿼리를 실행했습니다. 지원하는 SQL이 좁은 상태에서 너무 일찍 제출한 것은 인정합니다. 제출하기 전에 SQL 지원 범위를 더 넓혔어야 했습니다. 이런 문제를 해결한 새 제출물을 냈으니 pgrust가 복권되기를 바랍니다. 제출물에 이상한 점이 있다면 알려 주세요. 기꺼이 고치겠습니다.
[0] https://github.com/malisper/pgrust/releases/tag/v0.4-beta
[1] https://pgrust.com/downloads/archive/2026-09-clickbench-snapshot/pgrust-v0.4-preview-aarch64-neoverse-v2.tar.gz
[2] https://github.com/ClickHouse/ClickBench/blob/main/README.md
[3] https://clickhouse.com/docs/concepts/features/performance/caches/query-condition-cache
[4] https://github.com/ClickHouse/ClickBench/pull/2751
설명 감사합니다. 사람들이 무슨 일이 있었는지 이해할 수 있도록 0.4-preview 소스를 공개해 주실 수 있나요?
가능한지 알아보겠습니다. 민감한 내용이 없는지 쭉 확인해 봐야 해서요.
바이너리 안에 q00.rs부터 q36.rs까지의 경로가 들어 있는 건 어떻게 설명하실 건가요?
실수로 들어갔습니다. 저장소에 있던 코드인데 테스트 코드라고 명시해 두지 않아서 바이너리에 포함됐습니다.
어떻게요? 컴파일러는 안 쓰는 코드를 제거하잖아요.
제가 조금 잘못 말씀드렸네요. 환경 변수 PGRUST_GRAVITY_IRPROBE로 실행할 수 있는 코드였습니다. 그래도 실제 바이너리에 들어갈 의도는 아니었습니다.
이 댓글 체인 진짜 웃기네요. 혹시 Claude나 다른 LLM에게 그 파일이 뭐냐고 물어보고, 그다음 위 댓글의 질문(어떻게 바이너리에 들어갔냐)을 그대로 던졌더니 자기가 틀렸다고 인정한 거 아닌가요?
LLM이랑 일할 때 너무 흔한 패턴이라서요. 딱 그런 모양새인데요.
아닙니다. 예전에 저희가 처음 부정행위 의혹을 받았을 때 이걸 살펴본 기억이 있습니다. 그 코드가 죽은 코드이고 쓰이지 않는다는 건 알고 있었는데, 이유를 잘못 기억해서 다시 확인해야 했습니다.
설명을 들어도 이 모든 게 이상하고 사기 같습니다. 이렇게 미완성인 데다 이 벤치마크 하나에 심하게 맞춰진 물건을 제출하는 게 자기 홍보 말고 무슨 의미가 있나요?
벤치마크는 범용 쿼리 엔진을 측정하는 데 써야지, 그 자체가 목적이 되어서는 안 됩니다.
굿하트의 법칙의 전형적인 사례 같네요. 이 벤치마크는 이제 자기 홍보용 목표물로만 쓰이고 있습니다.
네, 그건 실수였습니다. 더 완성된 다음에 제출했어야 했어요.
저희가 만든 게 너무 자랑스럽고 얼마나 빠른지에 들떠서 빨리 제출하고 싶었습니다.
이건 어떤 기준으로 봐도 냄새가 납니다. 본인 입으로 테스트 파일을 "실수로" 포함시켰다고 했고, 벤치마크에 필요한 최소한의 SQL만 겨우 통과했고, "내부 벤치마킹"용 환경 변수까지 넣었고, 민감한 데이터가 들어 있을 수 있어서 바로 공개할 수 없는 프리뷰 버전이라고요? 포스트그레스 슬롭 포크에 대체 어떤 민감한 데이터를 넣어 뒀기에 바로 공개를 못 합니까? 부정행위를 했다는 뻔한 증거 말고요. 전부 악취가 진동합니다.
믿기지 않으시면 바이너리를 직접 실행해 보시면 됩니다. 숫자가 ClickBench 수치와 일치하고, 그 파일들이 벤치마크 중에는 죽은 코드라는 걸 확인하실 수 있을 겁니다.
그 바이너리의 소스 코드를 주시면 우리가 직접 확인할 수 있을 텐데요?
https://github.com/malisper/pgrust
지금 공개된 오픈소스 버전이 거의 같은 성능을 내고 이상한 동작도 없다면... 문제는 해결된 거 아닌가요?
이미 수상한 행동에 대해 할 수 있는 최선의 시정 조치를 제출했잖아요.
베타 바이너리가 항상 벤치마크 결과를 충족한다면 프리뷰 바이너리가 실제로 부정행위를 했는지는 별로 중요하지도 않아요. 어차피 100점 받을 시험에서 커닝한 셈이니까요.
맞아요!
여기서 수사관 놀이를 하는 사람들이 있네요. 이 사람은 너무 들뜬 나머지 미완성 작업을 제출했고, 정확히 어느 빌드였는지도 기억 못 한다고 인정했어요. 게다가 점수가 더 좋은 현재 소스 코드를 오픈소스로 공개해서 모든 의혹을 해소했고요.
"베타를 먼저 내고 코드는 몇 주 뒤에 공개한다"는 방식은 직관에 어긋나 보이는데요. 오픈소스에서 흔한 일인가요? 그렇게 하는 이유가 있나요?
예전에 저희는 큰 리팩터링을 자주 했고, 그중 상당수는 도로 철회했기 때문에 그렇게 했습니다. 그래서 공개 버전보다 앞서 있는 내부 개발 버전을 따로 유지해 왔습니다. 그러다 개발 중이던 것이 공개해도 될 만큼 준비되면 오픈소스로 공개하는 식입니다.
죄송하지만 그 논리가 잘 이해가 안 됩니다.
그 내부 버전을 main과 분리된 개발 브랜치로 두면 안 되나요? 한동안 master/main에 반영하지 않으려는 건 이해가 되지만, 그 개발 과정 자체를 숨겨야 하는 이유는 모르겠습니다.
무엇보다 왜 소스는 두고 바이너리만 내놓나요? 공개할 준비가 안 됐다면서 결과물만 나와 있으면 최악의 조합 아닌가요. 미완성 코드는 나와 있는데 사람들은 그 코드가 뭘 하는지 확인할 수 없으니까요.
따지려는 건 아닙니다. 다만 바이너리의 동작을 일부러 숨기려던 게 아니라면, 그런 흐름을 정당화할 만한 다른 장점이 있을 텐데 제가 그걸 놓치고 있는 것 같아서요.
이 개발 방식은 저희가 몇 주마다 코드베이스를 처음부터 계속 다시 쓰던 시절[0]부터 이어져 왔습니다. 실험을 많이 했고 대부분 버렸습니다. 그중 일부만 "pgrust"의 일부가 됐고요. 어차피 80%는 버려질 걸 아는데 개발 노력을 전부 담은 공개 브랜치를 유지할 가치가 있어 보이지 않았습니다.
[0] https://malisper.me/postgres-in-rust-regression-suite/
하지만 실험용 브랜치를 유지보수하겠다고 약속하지 않고도 그 브랜치를 공개할 수는 있잖아요. 말이 안 되는데요.
그런 개발 방식 때문에 에릭 레이먼드의 기념비적인 에세이 『성당과 시장(The Cathedral and the Bazaar)』이 나왔습니다.
"성당"은 문을 닫고 개발하는 방식입니다. 릴리스가 나올 때 소스 덤프가 함께 나오는 식이죠.
"시장"은 공개적으로 개발하는 방식입니다. 최전선의 코드에 언제든 접근할 수 있죠.
에세이가 쓰일 당시 ESR이 성당식 개발의 예로 든 것은 GCC와 Emacs였고, 시장식의 대표 사례는 리눅스였습니다.
성당식은 세월이 지나면서 훨씬 드물어졌지만, 예를 들어 SQLite는 아마 지금도 그렇게 운영될 겁니다. 구글이 AOSP를 그런 식으로 운영하는 것도 분명하지만, 이건 오픈소스 약속을 악의적으로 준수하는 데서 나온 결과라고 보는 편이 더 맞겠죠.
그렇게 하는 프로젝트들이 있습니다. 예를 들어 Lua 프로그래밍 언어는 릴리스 사이에 비공개로 작업합니다.
그렇게 하되 별로 성공적이지 못한 프로젝트도 있습니다. 거창한 주장을 늘어놓고 나중에 반쯤만 동작하는 상태로 내놓는 경우인데, V 프로그래밍 언어가 그렇습니다.
아주 흔한 방식은 아니지만, 제가 바로 떠올린 두 가지 예만 봐도 전례가 없지는 않습니다.
2차 라운드: https://github.com/ClickHouse/ClickBench/pull/2751
만일을 대비해서 아예 퇴출해야 합니다.
부정행위 코드가 릴리스에 들어간 경로는 두 가지가 가능합니다. 코딩 에이전트가 추가했는데 아무도 들여다보지 않았거나, 그것도 나쁘지만 더 나쁜 건 누군가 알고도 승인한 경우인데 이건 악의적인 겁니다.
어느 쪽이든 책임감이 전혀 보이지 않으니 이 제품에 지면을 내줘서는 안 됩니다.
아니면 세 번째 설명은 저희가 부정행위를 하지 않았다는 겁니다: https://news.ycombinator.com/item?id=50209450
0.4-preview 소스 코드는 어디 있나요?
가능한지 알아보겠습니다. 공개해도 되는지 감사해 봐야 해서요.
왜요?