이 챕터는 이 책 전체의 지도입니다. Quipu-Log가 어떤 부품들로 이루어졌는지 5개 크레이트를 조감하고, 그보다 중요한 — DB가 당연하게 제공하던 개념들이 파일 위에서는 어떤 모습으로 나타나는지 — 대응표를 한 번에 보여드립니다. 이 표를 머릿속에 넣으면 뒤 챕터들이 훨씬 빨리 읽힙니다.
Quipu-Log는 DB 없이 파일만으로 WAL·인덱스·락·MVCC·파티션 삭제를 재현한다. DB 개념 → 파일 구현의 대응을 이해하면 이 라이브러리의 모든 설계 결정이 보인다.
5개 크레이트 한눈에 보기
Quipu-Log는 하나의 단일 크레이트가 아닙니다. 역할별로 5개 크레이트로 나뉩니다. 각각이 무슨 일을 하는지 먼저 보겠습니다.
| 크레이트 | 역할 | 핵심 타입 |
|---|---|---|
quipu-core | 동기 저장 엔진 — 세그먼트 파일, 레지스트리, 암호화, 리텐션, 쿼리, 머클 트리 | AuditStore, StoreConfig, Table<T> |
quipu-middleware | 비동기 파이프라인 — 논블로킹 emit, DLQ, 필터, RBAC, tower 레이어, 샤드 라우터 | AuditPipeline, AuditHandle, AuditLayer |
quipu-server | 독립 데몬 — 토큰 인증 HTTP/JSON API, 쓰기 전용 모드 | HTTP 핸들러, 설정 파일 |
quipu-client | 서버 모드 레퍼런스 클라이언트 — 재시도, 지수 백오프, 디스크 스풀 | QuipuClient, Backoff, Spool |
quipu-mcp | LLM 감사관 — MCP 프로토콜로 LLM이 query·history·verify 호출 | MCP 툴 핸들러 |
임베디드 모드 vs 서버 모드
Quipu-Log를 쓰는 방법은 크게 두 가지입니다.
임베디드 모드: quipu-core + quipu-middleware를 여러분의 Rust 서비스에 라이브러리로 추가합니다. 감사 저장소가 서비스 프로세스 안에서 실행됩니다. 별도 데몬, 네트워크, DB가 필요 없습니다. Rust 서비스 한 개에 감사 로그가 필요할 때 가장 간단한 선택입니다.
서버 모드: quipu-server를 독립 프로세스로 실행하고, 여러 서비스(언어 무관)가 HTTP/JSON API로 기록합니다. 한 곳에서 여러 서비스의 감사 기록을 모을 때, 또는 Rust 이외 언어 서비스에서 쓸 때 선택합니다.
관계형 DB에도 비슷한 구분이 있습니다. SQLite(임베디드)처럼 프로세스 안에서 돌거나, PostgreSQL(서버)처럼 별도 프로세스로 돌거나. Quipu-Log도 같은 선택지를 제공합니다 — 다만 내부 저장 엔진은 파일 위에 직접 구현되어 있습니다.
이 책의 뼈대: DB↔파일시스템 대응표
이제 이 챕터의 핵심입니다. DB를 써온 독자라면 다음 개념들을 이미 압니다: WAL, 인덱스, 락 매니저, MVCC, 파티션 삭제, 체크섬. Quipu-Log는 이것들을 DB 엔진 없이 파일 위에서 직접 구현합니다. 대응 관계를 한 번에 보겠습니다.
| DB 개념 | Quipu-Log 파일 구현 | 다루는 장 |
|---|---|---|
| WAL (Write-Ahead Log) 트랜잭션 의도를 먼저 기록하는 순차 파일 |
append-only 세그먼트 파일 WAL이 곧 데이터베이스. 본체(B-tree) 없음. |
7장 |
| 데이터 파일 / 테이블스페이스 테이블 데이터를 담는 파일 집합 |
세그먼트 파일 묶음 (Table<T>)seg-0000000000.log, seg-0000000001.log... |
8장 |
| 페이지 체크섬 / torn page 탐지 디스크 비트 오류·부분 쓰기 탐지 |
프레임 CRC32[len][crc32][ts][payload] — 모든 레코드마다 |
9장 |
| 락 매니저 동시 쓰기 충돌 방지 |
OS 파일 락 (File::try_lock)루트 디렉토리의 LOCK 파일 — 두 번째 프로세스가 열면 즉시 오류 |
13장 |
| MVCC (다중 버전 동시성 제어) 읽기와 쓰기가 서로 안 막히게 |
읽기 스냅샷 (ReadSnapshot) 인메모리 인덱스를 클론 — 불변 세그먼트 + 인덱스 복제 |
14장 |
| B-tree 보조 인덱스 특정 컬럼으로 빠른 조회 |
인메모리 레지스트리 인덱스 + 디스크 토큰 재시작 시 재구축; 블라인드 인덱스로 암호화 필드 검색 |
15장 26장 |
| 파티션 삭제 (partition drop) 오래된 파티션을 파일 통째 삭제 |
세그먼트 unlinksealed 세그먼트 파일을 통째로 삭제 — O(1), 행 단위 재작성 없음 |
17장 |
| WAL replay / 크래시 복구 정전 후 로그를 재실행해 복구 |
세그먼트 skim + 꼬리 truncate CRC 실패 지점까지 유효, 나머지는 잘라낸다. redo-only. |
12장 |
이 표가 이해되면, 뒤 챕터들은 "DB에서 X라는 개념을 파일로는 Y로 구현했구나"라는 흐름으로 읽힙니다. 막힐 때마다 이 표로 돌아오세요.
저장소 레이아웃: 실제 파일 구조
Quipu-Log 스토어를 열면 루트 디렉토리 아래 다음과 같은 구조가 생깁니다.
crates/quipu-core/src/store.rs — AuditStore 레이아웃 주석root/
meta/ type schemas + custom column registry (replayed on open)
logs/ AuditLog rows
relations/ log -> target-entity-version relations
registry/<t>/ one versioned registry table per entity/actor type
access/ meta-audit (opt-in)
LOCK advisory OS lock file
각 디렉토리는 Table<T> 하나에 대응합니다 — 즉, 세그먼트 파일 여러 개로 이루어진 append-only 로그 하나. DB로 치면 테이블 하나가 테이블스페이스(파일 집합)에 저장되는 것과 같습니다. 18장에서 포맷 버저닝까지 포함해 자세히 다룹니다.
DB에서는 데이터 딕셔너리(시스템 카탈로그)가 테이블·컬럼 정보를 관리한다. Quipu-Log에서는 meta/ 디렉토리가 그 역할을 한다 — 타입 스키마 정의가 append-only로 기록되고, 재시작 시 순서대로 재실행(replay)된다. DB의 DDL 로그와 같은 원리다.
① quipu-core와 quipu-middleware의 역할 차이를 한 문장씩 말해보세요.
② DB의 "파티션 drop"이 Quipu-Log에서는 어떻게 구현되나요? 왜 "행 단위 DELETE + vacuum"보다 효율적인가요?
③ DB의 MVCC와 Quipu-Log의 읽기 스냅샷은 어떤 문제를 공통으로 해결하나요?