Quack: DuckDB 클라이언트-서버 프로토콜
요약
Quack은 DuckDB 인스턴스 간 통신을 가능하게 하는 원격 프로토콜로, 이를 통해 클라이언트-서버 구성과 여러 동시 작성자가 같은 데이터베이스를 사용할 수 있게 합니다. HTTP 기반의 요청-응답 패턴을 사용하며, `application/duckdb` MIME 타입으로 직렬화된 데이터를 전송하고 토큰 인증을 요구합니다. Quack은 DuckDB가 인프로세스 아키텍처에 의존해 온 한계를 극복하고 범용 데이터 가공 도구로 확장하는 것을 목표로 하며, 벤치마크에서 대규모 데이터셋 처리 및 높은 트랜잭션 속도를 입증했습니다.
핵심 포인트
- Quack은 DuckDB 인스턴스 간 통신을 위한 원격 프로토콜이며, 클라이언트-서버 아키텍처를 지원합니다.
- HTTP 기반의 요청-응답 패턴과 `application/duckdb` 직렬화를 사용하여 안정적이고 효율적인 데이터 전송을 구현했습니다.
- 여러 동시 작성자가 같은 데이터베이스 파일을 수정할 수 있도록 상태 동기화 문제를 해결하며, 이는 DuckDB의 범용성을 높입니다.
- 클라이언트와 서버 모두 DuckDB 인스턴스가 역할을 수행하며, `quack_serve` 및 `ATTACH` 명령어를 통해 연결을 설정합니다.
- Quack은 DuckLake 통합, Catalog 서버 등 추가 기능을 계획하고 있으며, v2.0 프로덕션 릴리스를 목표로 합니다.
Quack은 DuckDB 인스턴스 간 통신을 제공해 클라이언트-서버 구성과 여러 동시 작성자의 같은 데이터베이스 사용을 가능하게 함
- DuckDB는
인프로세스 아키텍처를 유지하면서, 여러 프로세스가 같은 파일을 수정할 때 필요한 상태 동기화를 원격 프로토콜로 처리함 - Quack은
HTTP 기반 요청-응답 프로토콜이며,application/duckdb
직렬화와 토큰 인증을 쓰고 기본 포트는 9494
임
- 벤치마크에서 Quack은
6천만 행을 4.94초에 전송했고, 작은 append 테스트에서도 8스레드 기준 약 5,434 tx/s를 기록함 - Quack은 DuckLake 통합, 원격
Catalog 서버, 자동 설치·로드, 프로토콜 확장, 복제 프로토콜을 계획하며 DuckDB v2.0 시기 프로덕션 릴리스를 목표로 함
Quack의 목적과 배경
Quack은 DuckDB 인스턴스끼리 통신하게 하는 원격 프로토콜로, DuckDB를 클라이언트-서버 구성에서 실행하고 여러 동시 작성자가 같은 데이터베이스를 사용할 수 있게 함
- DuckDB는 2019년부터
인프로세스 아키텍처를 강조해 왔으며, 별도 서버나 프로토콜 없이 낮은 수준 API 호출로 동작하는 방식은 Python 노트북 같은 대화형 데이터 과학 작업과 애플리케이션 내 SQL 기능 제공에 잘 맞았음 - 같은 DuckDB 데이터베이스 파일을 여러 프로세스가 동시에 수정하려면, DuckDB가 메인 메모리에 유지하는 많은 상태를 프로세스 간에 동기화해야 함
- 기존에는 별도
RPC 프로세스, Arrow Flight SQL protocol을 쓰는 프로젝트, MotherDuck의 자체 프로토콜, PostgreSQL과 pg_duckdb를 조합한 “EleDucken” 같은 우회 방식이 있었음 - DuckDB를
범용 데이터 가공 도구로 확장하려면, 인프로세스 기능에 더해 클라이언트-서버 프로토콜이 새로운 사용 사례를 열 수 있음
Quack 사용 방식
- Quack에서는 두 개 이상의 DuckDB 인스턴스가 서로 통신하며, DuckDB가
클라이언트와 서버 역할을 모두 수행함 - 두 인스턴스는 서로 다른 컴퓨터, 원격 위치, 또는 같은 노트북의 서로 다른 터미널 창에 있을 수 있음
- Quack 확장은 현재
core_nightly
저장소에 있으며, 현재 릴리스 버전인 DuckDB v1.5.2에서 사용할 수 있음
- 서버 측 DuckDB 인스턴스에서는 Quack을 설치·로드한 뒤
quack_serve
를 호출하고, 예시에서는 quack:localhost
와 token = 'super_secret'
을 사용함
INSTALL quack FROM core_nightly;
LOAD quack;
CALL quack_serve(
'quack:localhost',
token = 'super_secret'
);
CREATE TABLE hello AS
FROM VALUES ('world') v(s);
- 클라이언트 측 DuckDB 인스턴스에서도 Quack을 설치·로드하고,
CREATE SECRET
으로 토큰을 설정한 뒤 ATTACH 'quack:localhost' AS remote;
로 원격 인스턴스를 연결함
INSTALL quack FROM core_nightly;
LOAD quack;
CREATE SECRET (
TYPE quack,
TOKEN 'super_secret'
);
ATTACH 'quack:localhost' AS remote;
FROM remote.hello;
- 이 구성으로 DuckDB #2에서 원격 테이블
hello
의 값인 world
를 조회할 수 있음
- 로컬 인스턴스에서 원격 인스턴스로 데이터를 복사할 수도 있으며, 예시에서는 DuckDB #2에서
remote.hello2
테이블을 만들고 DuckDB #1에서 FROM hello2;
로 확인함
- 복잡한 쿼리나 대용량 데이터셋에서는 원격 측으로 쿼리를 그대로 보내는
query
함수로 어떤 작업이 원격에서 실행되는지 더 잘 제어할 수 있음
FROM remote.query(
'SELECT s FROM hello'
);
프로토콜 설계
HTTP 기반
-
Quack은
HTTP 위에 직접 구축됐으며, HTTP는 TCP와 그 아래 계층 위에서 사실상 표준 프로토콜 계층으로 자리 잡았음 -
HTTP 스택은 메시지 스트림 전송에 맞게 효율적으로 최적화돼 있고, 제대로 구현하면 오버헤드가 낮음
-
로드밸런싱, 인증, 방화벽, 침입 탐지 같은 영역에서 HTTP를 다루는 방법이 널리 알려져 있음
-
HTTP를 사용하므로 DuckDB-Wasm 배포판도 Quack을 네이티브로 사용할 수 있음
-
브라우저에서 실행되는 DuckDB가 Quack으로 EC2 서버에서 실행되는 DuckDB 인스턴스에 직접 연결할 수 있음
요청-응답 패턴
-
Quack의 상호작용은 항상 클라이언트가 주도하는
요청-응답 패턴으로 동작함 -
메시지에는 토큰 인증을 위한 연결 요청, 쿼리 실행 요청, 응답의 첫 부분 반환, 대용량 결과를 가져오기 위한 후속 fetch 메시지 등이 들어감
-
대용량 결과는 여러 스레드에서 병렬로 가져올 수 있음
직렬화
- 요청과 응답은 새 MIME 타입인
application/duckdb
로 인코딩됨
-
이 인코딩은 데이터 타입과 결과 집합 같은 복잡한 구조를 위한 DuckDB 내부 직렬화 프리미티브를 활용함
-
같은 프리미티브는 수년 동안 DuckDB의
WAL 파일에서도 사용돼 왔고, 최적화와 검증이 상당히 이뤄졌음
암호화와 노출 방식
- Quack은 서버 시작 시 기본적으로
무작위 인증 토큰을 생성하고, 클라이언트는 이 토큰을 제공해야 함 - Quack 서버는 기본적으로
localhost
에만 바인딩되며, 이 동작은 재정의할 수 있음
- 기본적으로 SSL을 사용하지 않는데,
localhost
통신만을 위해 해당 인프라와 의존성을 추가하는 것은 적절하지 않다고 봄
-
DuckDB Quack 엔드포인트를 인터넷에 직접 여는 것은 권장되지 않음
-
Quack을 웹에 노출해야 한다면 nginx 같은 일반 HTTP 엔드포인트를 사용하고, 해당 프록시가 Let’s Encrypt 등으로 SSL을 종료하도록 강하게 권장함
-
Quack 클라이언트는 비로컬 연결에 대해 SSL이 활성화됐다고 가정하며, 이 동작은 재정의할 수 있음
-
관련 설정은 리버스 프록시 문서에 있음
왕복 횟수 최적화
-
Quack은 쿼리에 필요한
프로토콜 왕복 횟수를 줄이도록 설계됐음 -
연결 이후에는 쿼리 하나를 단일 왕복으로 처리할 수 있어, 지연 시간에 민감한 환경에서 유리함
-
대량 응답 전송도 최적화했으며, DuckDB 팀은 Quack이 현재 소켓을 통해 테이블을 전송하는 가장 빠른 방법이라고 봄
-
수백만 행을 몇 초 안에 전송할 수 있다는 벤치마크 결과를 냄
인증과 권한 부여
-
Quack은 DuckDB의
확장성 철학에 맞춰 인증과 권한 부여 모델을 설계함 -
기본 인증 방식과 제한 없는 기본 권한 부여를 제공하지만, 둘 다 사용자 제공 코드로 바꿀 수 있음
-
서버가 시작할 때 무작위 인증 토큰을 생성하고, 클라이언트는 연결 시 인증 문자열을 제공함
-
서버는 인증 콜백을 호출하며, 기본 콜백은 클라이언트가 제공한 토큰과 서버가 생성한 토큰을 비교함
-
인증 콜백은 설정으로 교체할 수 있어 LDAP 디렉터리 조회, 텍스트 파일 읽기, 임의 로직 등을 사용할 수 있음
-
권한 부여 함수도 바꿀 수 있으며, 기본 함수는 모든 요청을 허용함
-
사용자 권한 부여 함수는 클라이언트가 실행하려는 각 쿼리를 검사하고, 이전에 사용된 인증 문자열과 연결해 판단할 수 있음
-
이런 콜백은 일반
SQL 매크로로도 작성할 수 있음
기본 포트
벤치마크
- 벤치마크는 Ubuntu on Arm을 실행하는 AWS 가상 머신에서 수행됨
- 인스턴스 타입은 m8g.2xlarge로,
8 vCPU, 32GB RAM, “최대 15Gbps” 네트워크 대역폭을 가짐 - 클라이언트와 서버가 같은 데이터센터에 있지만 서로 다른 머신에 있는 실제 시나리오를 재현함
- 두 인스턴스는 같은
가용 영역에 배치됐고, 평균 ping 시간은 약 0.280ms였음
대량 전송
- 첫 번째 벤치마크는 데이터베이스 프로토콜을 통해 많은 행을 전송하는
대량 전송 작업을 측정함 - 비교 대상은 Quack, PostgreSQL 프로토콜, Arrow Flight SQL 프로토콜임
- Arrow Flight는 내부적으로 DuckDB를 사용하는 GizmoSQL 서버로 제공됨
- TPC-H
lineitem
테이블에서 행 수를 늘려가며 전송했고, 최대 6천만 행까지 측정함
- 6천만 행은 CSV 형식으로
76GB에 해당함 - 각 결과는 5회 실행의 중앙값 벽시계 시간으로 보고됨
- 주요 결과는 다음과 같음
100k 행: DuckDB Quack0.07 s
, Arrow Flight 0.07 s
, PostgreSQL 0.20 s
1M 행: DuckDB Quack 0.24 s
, Arrow Flight 0.38 s
, PostgreSQL 2.20 s
10M 행: DuckDB Quack 0.89 s
, Arrow Flight 2.90 s
, PostgreSQL 25.64 s
60M 행: DuckDB Quack 4.94 s
, Arrow Flight 17.40 s
, PostgreSQL 158.37 s
-
Quack은 6천만 행을
5초 미만에 전송해 대량 결과 집합 전송에서 강한 성능을 보임 -
목적 지향적인 Arrow Flight SQL도 이 결과에서는 Quack에 미치지 못했고, PostgreSQL의 행 기반 프로토콜은 전반적으로 불리했음
-
표준 PostgreSQL 클라이언트는 여러 스레드에서 읽기를 병렬화하지 않지만, Quack과 Arrow는 병렬화할 수 있음
-
DuckDB의 PostgreSQL client도 일부 경우 병렬 읽기를 할 수 있음
작은 쓰기
- 두 번째 벤치마크는
작은 append 작업을 측정함 - 관측 가능성 데이터를 중앙 DuckDB 인스턴스에 모으는 구성처럼, 작은 쓰기를 중앙화하는 상황에 해당함
- 단일 트랜잭션을 완료하는 데 여러 번의 클라이언트-서버 왕복이 필요한 프로토콜에는 불리한 테스트임
- TPC-H
lineitem
과 같은 구조의 빈 테이블을 만들고, 무작위 값을 한 행씩 각자 별도의 INSERT
트랜잭션으로 삽입함
- 병렬 스레드 수를 늘려가며 5초 동안 실행했고, 실험을 5회 반복해 중앙값 초당 트랜잭션 수를 보고함
- 주요 결과는 다음과 같음
1 스레드: DuckDB Quack1,038 tx/s
, Arrow Flight 469 tx/s
, PostgreSQL 839 tx/s
2 스레드: DuckDB Quack 1,956 tx/s
, Arrow Flight 799 tx/s
, PostgreSQL 1,094 tx/s
4 스레드: DuckDB Quack 3,504 tx/s
, Arrow Flight 1,224 tx/s
, PostgreSQL 2,180 tx/s
8 스레드: DuckDB Quack 5,434 tx/s
, Arrow Flight 1,358 tx/s
, PostgreSQL 4,320 tx/s
- Quack은 8개 병렬 스레드까지 PostgreSQL을 앞섰고, 최대 약
5,500 tx/s의 트랜잭션 처리율을 보임 - 그 이상에서는 같은 테이블에 대한 동시 삽입 초당 횟수에서 DuckDB 자체의 현재 한계에 도달함
- PostgreSQL은 이 영역에서 더 잘 확장되며, DuckDB 팀은 이를 가까운 미래에 살펴볼 대상으로 봄
- Arrow Flight는 예상대로 좋지 않았고, PostgreSQL의 대략 절반 수준 성능을 보임
- 벤치마크 스크립트가 공개돼 있음
사용 사례와 DuckDB에서의 의미
- Quack은 여러 개별 프로세스가 로컬 또는 원격에서 같은 테이블 내용을 병렬로 수정할 수 있는
멀티플레이어 DuckDB 사용을 가능하게 함 - 일부 기능은 DuckLake로도 가능했지만, Quack은 이를 더 단순하게 만들고 훨씬 높은 성능을 제공함
- 중앙 상태가 초근접 로컬 쿼리보다 중요한 사용 사례에서 DuckDB가 활용될 수 있는 범위가 넓어짐
- 데이터 레이크의 확산으로 데이터가 항상 로컬에 있지는 않다는 점이 이미 드러났고, Quack은 이런 방향에 맞는 기능으로 자리 잡음
- Quack은 DuckLake에 통합될 예정이며, DuckDB 자체가 원격 접근 가능한
Catalog 서버가 될 수 있게 함 - 이 통합은 데이터 인라이닝 같은 새로운 기능을 열 수 있음
- 추가 질문은 Quack FAQ에 있음
- DuckDB는 대화형 분석을 위한 인프로세스 데이터베이스라는 초기 틈새에서 벗어나, 현대 데이터 아키텍처의 핵심 구성 요소 쪽으로 더 이동하고 있음
Arrow Flight SQL을 사용하지 않은 이유
- Arrow와 ADBC 같은 관련 프로젝트는 ODBC와 JDBC처럼 시스템 간 데이터 교환의 마찰을 줄이기 위한
교환 API로 가치가 있음 - 다만 DuckDB 내부에서 Arrow 같은 교환 형식을 사용하는 데에는 신중함
- DuckDB의 쿼리 중간 결과 내부 구조는 어떤 면에서는 Arrow와 가깝지만, 다른 면에서는 상당히 다름
- 데이터 시스템 혁신을 계속하려면 외부에서 통제되는 형식에 제약받을 수 없다고 보고, Quack에서는 자체 직렬화를 사용함
- 자체 직렬화를 쓰면 새 데이터 타입이나 프로토콜 메시지가 필요할 때 바로 배포할 수 있음
- Arrow Flight SQL에는 모든 쿼리가 최소 두 번의 프로토콜 왕복, 즉
CommandStatementQuery
와 DoGet
을 필요로 하는 설계가 있음
- 이 방식은 작은 업데이트 작업, 특히 지연 시간이 더 큰 환경에서는 이상적이지 않음
- Quack은 작은 쿼리에 대해 단일 왕복으로 쿼리 실행과 결과 fetch가 가능하도록 설계됨
향후 계획
- Quack은 DuckLake에 통합돼 원격 DuckDB 서버를
DuckLake catalog로 사용할 수 있게 될 예정임 - 이 통합은 특히 인라이닝에서 성능을 크게 개선할 것으로 기대됨
- 앞으로 몇 달 동안 Quack을 다듬고, 올해 가을 예정된 DuckDB v2.0과 함께 첫 프로덕션 릴리스를 낼 계획임
- Quack 확장이 필요할 때
자동 설치와 자동 로드가 가능하도록 할 계획이 있음 - 새 parser를 사용해 DuckDB에서 원격 SQL 데이터베이스와 대화하는 문법도 개선할 계획임
- DuckDB 코어 측면에서는 초당 트랜잭션 수를 크게 늘려, 8개 병렬 스레드를 훨씬 넘어 트랜잭션을 확장할 수 있게 할 계획임
- 인증과 권한 부여를 넘어, DuckDB 확장이 새로운 프로토콜 메시지와 처리 코드를 추가할 수 있도록
Quack 프로토콜 확장을 허용하는 방안도 검토 중임 - Quack 위에 복제 프로토콜을 추가해 DuckDB 인스턴스의 변경 사항을 다른 서버로 복제하고, 읽기 복제본 클러스터를 구성하는 방안도 검토 중임
- Quack과 초기 도입 내용은 6월 24일 커뮤니티 컨퍼런스 DuckCon #7에서도 다뤄질 예정임
- 별도 Quack project 페이지도 제공됨
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기