본문으로 건너뛰기
5분 AI.
GITHUB TRENDING RUST WEEKLY #8 딥다이브 · 2026-10-23 분석

omnigraph 딥다이브
— 데이터에도 브랜치가 필요한 순간

데이터베이스

빠른 판단

무엇
타입·관계·벡터·전문 검색을 결합하고 그래프 변경을 브랜치·병합으로 관리하는 Rust 그래프 DB
이럴 때
여러 에이전트가 공유 지식을 바꿀 때 각 브랜치의 변경을 검토하고 타입·관계 제약에 맞춰 합칠 때
입력
타입이 있는 개체·관계·문서와 브랜치별 변경
결과
버전 관리된 지식 그래프와 복합 검색·질의 결과
객체 저장소 위의 여러 테이블을 하나의 그래프로 묶고, 타입·검색·브랜치·권한을 함께 다루는 Rust 데이터베이스입니다. 이 글은 작은 근거 그래프에서 출발해 컴파일러, 실행 계획, 원자적 공개, 장애 테스트까지 연결합니다. ModernRelay/omnigraph · ⭐1,679 · 포크 237 · Rust · MIT (2026-10-23 GitHub 기준). 소스 설명은 커밋 484e334, 최신 배포판은 v0.14.0 기준입니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

사람·문서·관계뿐 아니라, 그 지식의 변경 과정까지 저장한다

omnigraph는 타입이 있는 그래프를 로컬 디스크나 객체 저장소에 보관하고, 그래프 전체의 버전을 브랜치로 관리하는 데이터베이스입니다. 문서를 벡터로 찾고, 단어로 찾고, 관계를 따라 좁히는 작업도 같은 쿼리 실행 환경에서 조합합니다.

예를 들어 Source는 실험 보고서, Claim은 검증할 주장, Supports는 “이 자료가 이 주장을 뒷받침한다”는 연결입니다. “지연 시간이 줄었다는 주장의 근거를 찾아라”는 질문은 문자열 검색을 넘어 관계 탐색이 됩니다.

여러 에이전트가 같은 지식을 읽되, 각자의 변경은 브랜치에서 검토한 뒤 그래프 전체에 반영하도록 만드는 것이 핵심입니다.

용어부터 이해하기
타입이 있는 속성 그래프와 온톨로지
노드는 사람·문서 같은 대상, 간선은 대상 사이의 관계입니다. 속성은 제목·날짜 같은 값입니다. 온톨로지는 “어떤 대상과 관계를 허용하는가”를 정한 도메인 설계도입니다. 여기서는 .pg 스키마로 타입, 키, 제약을 선언하고 엔진이 쓰기 때 검사합니다.
// schema.pg: 공식 빠른 시작의 근거 그래프
node Source {
  slug: String @key
  title: String
}
node Claim {
  slug: String @key
  statement: String
}
edge Supports: Source -> Claim

이 선언을 읽는 출발점은 crates/omnigraph-compiler/src/schema/schema.pest와 crates/omnigraph-compiler/src/schema/parser.rs입니다. 스키마를 파일로 선언하는 이유는 문서화뿐 아니라 쿼리 검사와 데이터 검증이 동일한 모델을 사용하게 하기 위해서입니다.

한 문장 비유

공용 지식 노트에 Git의 작업 브랜치와 정식 발행 절차를 붙인 것

여러 사람이 같은 원고를 고칠 때 바로 정본을 덮어쓰면 누가 무엇을 바꿨는지 판단하기 어렵습니다. 각자 사본에서 제안하고 차이를 확인한 뒤 합치면 검토가 쉬워집니다.

omnigraph의 사본은 텍스트 파일 복제가 아니라 그래프 스냅샷과 테이블 버전의 참조입니다. 파일 저장소인 Git과 동일한 구현이라는 뜻은 아니며, 충돌 판단에는 그래프의 키·간선·제약도 관여합니다.

초보자가 먼저 구분할 세 가지

이름역할여기서 배울 질문
Lance열 기반 파일, 단일 데이터셋 버전·인덱스·트랜잭션이미 있는 저장 엔진 위에 무엇을 더해야 하는가?
omnigraph 엔진여러 데이터셋을 하나의 그래프 상태로 조정테이블 둘을 바꿀 때 절반만 보이는 일을 어떻게 막는가?
CLI·HTTP·MCP사람·앱·에이전트가 사용하는 진입점입구가 달라도 같은 규칙을 어떻게 강제하는가?

학습 목표는 데이터베이스를 통째로 재구현하는 것이 아닙니다. 먼저 작은 쿼리 하나가 어떤 타입 검사를 거쳐 어떤 스냅샷을 읽는지, 변경 하나가 언제부터 다른 독자에게 보이는지 설명할 수 있으면 충분합니다.

2왜 주목받는가

인기 순위보다 중요한 것은, 서로 다른 문제를 어떤 경계에서 결합했는가

2026-10-23 선정 기준은 GitHub Trending Rust Weekly #8입니다. 스타 1,679와 포크 237은 관심의 크기를 보여주지만 채택 규모나 성능의 증거는 아닙니다. 아래의 주목 요인은 README의 지향점과 구현을 연결한 해석이며, 유입 경로를 측정한 통계가 아닙니다.

① 에이전트의 기억을 “수정 가능한 공유 상태”로 본다

README는 장시간 동작하는 여러 에이전트가 타입이 있는 세계 모델을 공유하는 워크로드를 내세웁니다. 단순 대화 기록 저장과 달리, 관계·키·변경 이력을 유지해야 합니다. 브랜치는 서로 다른 작업의 제안을 분리하고, 병합은 승인한 결과를 한 상태로 합치는 수단입니다.

구현에서도 crates/omnigraph/src/exec/merge.rs가 공통 조상·원본·대상 스냅샷을 비교하고, 합칠 후보에 제약 검증을 적용합니다. 브랜치는 이름만 붙인 복사본에 머무르지 않습니다.

② 검색 세 종류를 같은 스냅샷에서 조합한다

벡터 검색은 의미가 비슷한 대상을, 전문 검색은 단어가 맞는 대상을, 그래프 탐색은 관계를 만족하는 대상을 찾습니다. 검색 시스템을 따로 두면 동기화와 결과 결합을 앱이 맡아야 합니다. omnigraph는 nearest, bm25, rrf와 탐색을 실행 계획 안에서 다룹니다.

다만 “검색을 통합했다”가 “모든 검색을 정확 탐색으로 수행한다”는 뜻은 아닙니다. 근사 벡터 검색의 후보 수와 탐색 범위에는 비용·재현율의 절충이 있고, 인덱스 상태에 따른 거부 조건도 있습니다. 이 구분 자체가 좋은 학습 주제입니다.

③ 데이터의 정본과 검색 가속 장치를 분리한다

로컬 파일과 S3 계열 저장소를 같은 설계로 다루면서, 인덱스·캐시가 늦어져도 논리적인 데이터 의미를 바꾸지 않도록 경계를 세웁니다. 이 원칙은 docs/dev/invariants.md에 명시되어 있고, 쓰기와 인덱스 조정 경로가 분리되어 있습니다.

이 저장소의 교육적 가치는 기능 수보다, 정답을 결정하는 상태와 속도를 높이는 상태를 구분하는 방법에 있습니다.

비슷한 선택지와 비교하기

선택지중심 모델·활용점omnigraph의 차별점·선택 비용
PostgreSQL + pgvector관계형 데이터와 SQL에 벡터 유사도 검색을 결합그래프 전체 브랜치·병합을 제품의 핵심 계약으로 제공. 기존 SQL·확장 생태계를 그대로 쓰는 접근과는 도입 비용이 다름
Neo4j노드·관계·속성과 Cypher를 중심으로 그래프를 질의객체 저장소의 Lance 테이블 조정과 .pg·.gq·그래프 버전 관리의 결합에 초점. 쿼리 언어 호환을 가정하면 안 됨
Lance를 직접 사용버전이 있는 데이터셋과 검색·저장 기능을 직접 조합여러 노드·간선 테이블의 공개, 스키마, 그래프 제약과 정책을 위에서 조정. 그만큼 추가 계층을 이해해야 함
omnigraph타입·관계·검색·변경 제안을 한 그래프 계약으로 관리브랜치 기반 지식 관리가 강점. 젊은 0.x 프로젝트의 계약·업그레이드 경계를 함께 감수해야 함

이 표는 처리량 순위가 아닙니다. 비교의 기준 모델은 pgvector 공식 저장소, Neo4j 공식 개념 문서, Lance 버전 관리 문서에서 확인할 수 있습니다. 같은 데이터·하드웨어의 직접 성능 비교는 제시하지 않습니다.

공부할 가치
데이터베이스를 사용하던 개발자가 설계자의 질문을 배우기 좋다

“요청이 성공했다”의 정확한 의미, “새 버전”과 “공개된 버전”의 차이, 권한 검사 위치, 최적화의 안전 조건을 한 저장소에서 연결할 수 있습니다. 작은 예제부터 읽고 해당 기능의 테스트로 이동하는 순서가 효과적입니다.

3기술 스택 전체 지도

버전 범위는 Cargo.toml, 실제 선택된 버전은 Cargo.lock에서 읽는다

루트 Cargo.toml은 22개 워크스페이스 멤버를 선언합니다. 기본 빌드 대상은 엔진·CLI·서버 세 패키지입니다. 엔진의 디렉터리는 crates/omnigraph, Cargo 패키지 이름은 omnigraph-engine, Rust 라이브러리 이름은 omnigraph이므로 명령과 경로에서 이름이 다르게 보입니다.

백엔드·데이터 처리

기술선언 → 잠금 버전담당 기능·확인 위치
Rustedition 2024 · toolchain 1.97.1패키지별 Cargo.toml · rust-toolchain.toml
Lance 계열=13.0.0 → 13.0.0데이터셋·버전·인덱스. 루트에서 동일 버전으로 고정, AWS·Azure 기능 활성화
Apache Arrow58 → 58.3.0arrow-array 등. 열 배열·스키마·배치 표현
DataFusion54 → 54.0.0관계 연산과 실행 계획. 기본 기능을 끄고 문자열·중첩 표현식 활성화
Tokio1 → 1.52.3비동기 런타임, 네트워크·시간·동기화
Pest2 → 2.8.7.pest 문법에서 스키마·쿼리 파서 생성
Cedar Policy4.9 → 4.11.2행위자·행동·범위에 대한 정책 평가
object_store0.14.1 → 0.14.2파일·AWS·Azure 공용 저장소 접근
Serde1 → 1.0.228설정·HTTP 데이터·실행 계획 직렬화
Reqwest0.12 → 0.12.28HTTP 클라이언트, JSON·rustls TLS

표에서 4.9처럼 보이는 선언은 일반적으로 정확한 고정값이 아닙니다. Cargo의 호환 범위 안에서 잠금 파일이 4.11.2를 선택할 수 있습니다. 반대로 Lance의 =13.0.0은 정확히 그 버전을 요구합니다. Cargo.lock에는 전이 의존성용 object_store 0.13.2와 reqwest 0.13.4도 있으므로, 이름 하나당 버전 하나라고 가정하면 안 됩니다.

데이터 처리의 공통 언어
Arrow RecordBatch
행마다 객체를 따로 만드는 대신 “제목 열의 배열, 점수 열의 배열”을 같은 행 수로 묶는 단위입니다. 스키마가 각 열의 타입을 설명합니다. Lance 스캔 결과와 DataFusion 연산자가 같은 표현을 주고받아, 저장·계산 계층을 연결하기 쉽습니다.

사용자·앱과 만나는 계층

표면실제 버전의미
CLI / Clap4.6 → 4.6.1명령 해석·도움말. 로컬 엔진과 원격 서버를 선택해 사용
HTTP / Axum0.8 → 0.8.9멀티 그래프 라우팅·인증·요청 수용 제어
OpenAPI / Utoipa5 → 5.5.0서버 계약과 루트 openapi.json
MCP / rmcp=3.3.0 → 3.3.0crates/omnigraph-mcp/Cargo.toml에서 고정한 서버 SDK
프론트엔드이 저장소에는 해당 앱 없음package.json·TS/JS/HTML 앱 소스 없음. React나 웹 빌드 도구를 스택에 넣을 근거가 없음
TypeScript SDK이 저장소의 의존성으로 고정되지 않음별도 저장소의 클라이언트. HTTP 계약 호환 확인 필요

crates/omnigraph-server/Cargo.toml의 aws 기능은 기본값이 꺼져 있으며, 선택적으로 AWS Secrets Manager 기반 토큰 소스를 더합니다. 이것은 루트 Lance 의존성의 AWS 저장소 기능과 별개입니다.

인프라·빌드·검증

# 공식 소스 빌드 문서의 예시. 테스트 전용 플래그를 대체한다.
RUSTFLAGS= cargo build --release --locked \
  -p omnigraph-cli \
  -p omnigraph-server \
  -p omnigraph-azure-admission

.cargo/config.toml의 --cfg tokio_unstable은 테스트의 시드 기반 스케줄링에 사용됩니다. 공식 릴리스 빌드는 환경의 RUSTFLAGS로 이를 대체합니다. “unstable 설정이 있으니 nightly 제품이다”라고 읽으면 잘못된 결론입니다.

버전 읽기의 함정
최신 릴리스와 분석한 소스는 같은 버전이 아니다

최신 배포판은 v0.14.0(2026-10-22)이지만, 커밋 484e334의 엔진·서버 Cargo.toml은 0.14.1입니다. 특히 최신 커밋의 최적화는 배포판에 포함되었다고 단정할 수 없습니다. docs/user/install.md는 HTTP 계약 0.14를 요구하고, 0.14 릴리스 시점의 npm 패키지 발행은 대기 상태라고 설명합니다.

4아키텍처 심화 분석

질문 하나와 변경 하나를 끝까지 따라가며 네 가지 설계 패턴을 익힌다
사람 / 애플리케이션 / 에이전트 | +-----+--------------------------+ | | omnigraph CLI omnigraph-server | HTTP + /mcp | 인증 · 수용 제어 +-----------+--------------------+ | Session / Omnigraph 엔진 | +-----------+------------------------------+ | | 읽기 경로 쓰기 경로 .pg / .gq 파싱 WriteTxn 캡처 타입 검사 -> typed IR 정책 · 타입 · 관계 검증 논리 계획 -> 물리 계획 변경을 임시 준비 계획 검증 -> AcceptedPlan 테이블별 detached commit 값 바인딩 -> DataFusion 실행 __manifest 조건부 공개 | | +------------------+-----------------------+ | __manifest: 스키마 · 테이블 버전 · 계보 | Lance 노드 / 간선 데이터셋 | 로컬 파일 / S3 계열 / Azure Blob 별도 제어 경로: cluster.yaml -> cluster plan/apply -> 배포 원장 -> 서버의 불변 런타임 뷰

이 그림은 crate 간 책임을 묶어 그린 논리 구조입니다. 각 상자가 반드시 별도 프로세스라는 뜻은 아닙니다. CLI는 엔진을 내장해 로컬 그래프를 열 수 있고, 원격 대상에서는 서버 API로 요청을 보냅니다. 서버는 적용된 클러스터 구성을 바탕으로 여러 그래프를 제공합니다.

패턴 1. 타입으로 실행의 입장권을 만든다

읽기의 출발점인 Session::query_with_head()는 crates/omnigraph/src/exec/query_doors.rs에 있습니다. 먼저 설정을 정하고 capture_read_view()로 스냅샷과 카탈로그를 함께 잡은 뒤, 이름이 있는 쿼리를 컴파일합니다. 반환하는 그래프 커밋 ID도 실제로 읽은 스냅샷에서 가져옵니다.

  1. crates/omnigraph-compiler/src/query/parser.rs::parse_query()가 Pest 문법으로 문자열을 구문 구조로 바꿉니다.
  2. crates/omnigraph-compiler/src/query/typecheck.rs가 바인딩·속성·검색 인자의 타입을 확인하고 검사된 쿼리를 만듭니다.
  3. crates/omnigraph-compiler/src/ir/lower.rs::lower_query()가 타입을 유지한 중간 표현 QueryIR로 변환합니다.
  4. 플래너 crates/omnigraph-planner/src/gate.rs::accept_query()가 실행 계획을 만들고, 원래 쿼리의 요구를 보존했는지 검사합니다.
  5. 엔진 crates/omnigraph/src/engine/bind.rs::bind()가 승인된 계획에 매개변수·검색 벡터를 묶어 실행에 넘깁니다.
// crates/omnigraph-planner/src/validate/mod.rs 발췌
pub struct AcceptedPlan {
    plan: PhysicalPlan,
    evidence: Evidence,
}

// 필드는 비공개이며 역직렬화로 이 타입을 만들지 않는다.
// 검증 성공 뒤 bind()가 AcceptedBoundPlan을 만든다.

배울 점: “검증했으니 조심해서 실행하자”를 관례로 남기지 않고, 실행에 필요한 타입을 검증기만 만들도록 합니다. API 입력의 검증 전·후 타입을 나누는 작은 서비스에도 적용할 수 있는 방식입니다.

검증 범위도 과장하지 않습니다. ValidationScope::ExactSubset은 제한된 쿼리 형태에서 재작성 과정을 재구성해 비교하고, InvariantsOnly는 검색·조건·점수·정렬·잘라내기 등의 불변식을 확인합니다. 후자가 모든 쿼리의 결과 동등성을 증명한다는 뜻은 아닙니다. explain과 실제 실행도 같은 승인 경로를 공유합니다.

최신 커밋에서 보는 작은 최적화

484e334의 주제는 상위 계획이 불필요한 삭제 확인을 생략하도록 하는 것입니다. crates/omnigraph-planner/src/optimizer.rs는 CSV 덮어쓰기 로드의 Validate 노드를 만들 때 다음 조건을 기록합니다. 아래 코드는 핵심 분기의 발췌입니다.

let removal = if overwrite && retained_edge_checks > 0 {
    ValidateRemoval::Scan
} else {
    ValidateRemoval::None
};

간선이 참조하는 기존 노드를 교체하면 사라질 ID를 알아야 관계 무결성을 확인할 수 있습니다. 반면 참조 간선 검사가 없거나, 대상 노드 테이블이 비어 해당 검사가 제거되면 그 스캔이 필요 없습니다. 간선 테이블의 교체도 노드 삭제 탐지가 필요 없는 경우입니다.

결정은 validate_removal 통계와 PhysicalNode::Validate.removal에 남고, crates/omnigraph/src/engine/operators/validate.rs의 LoadJudge로 전달됩니다. 실행기가 저장소 상태를 다시 추측하지 않도록, 무엇을 검사할지 계획이 먼저 결정하고 실행은 그 결정을 따른다는 사례입니다. 정량적인 속도 향상 수치는 이 분기만으로 산출할 수 없습니다.

패턴 2. 준비와 공개를 분리해 그래프를 원자적으로 바꾼다

노드와 간선은 서로 다른 Lance 데이터셋입니다. 노드 테이블만 바뀐 순간을 독자가 볼 수 있다면 간선이 존재하지 않는 노드를 가리킬 수 있습니다. 단일 테이블의 트랜잭션만으로는 그래프 전체의 일관성을 설명할 수 없는 이유입니다.

엔진은 WriteTxn의 기준 스냅샷에서 검증하고 테이블별 변경을 detached commit으로 준비합니다. 이 버전은 내구성 있게 저장되어도 아직 그래프의 정본에서 참조되지 않습니다. 마지막에 카탈로그의 __manifest를 한 번 바꾸어 모든 버전 참조와 스키마·계보를 함께 공개합니다.

예시: 새 Source와 Supports를 한 번에 추가 이전 __manifest M Source@A1, Supports@B1 | 준비 | Source@A2 생성 | Supports@B2 생성 | 아직 독자는 A1 + B1을 읽음 v 조건부 공개 성공 __manifest M' = A2 + B2 + 새 계보 | +-------------> 이후 독자는 A2 + B2를 읽음 공개 전 실패: 이전 그래프 유지, 준비된 파일은 미공개 상태 공개 후 성공: 새 그래프의 전체 참조가 함께 보임 A1/A2/B1/B2는 서로 독립인 테이블 버전의 설명용 기호

crates/omnigraph-catalog/src/lib.rs의 ManifestCoordinator::commit_changes_with_lineage_and_precondition()는 조건을 포함해 publisher를 호출한 뒤 성공한 상태를 설치합니다. crates/omnigraph-catalog/src/commit.rs::overwrite_probed()는 Lance Operation::Overwrite를 사용하며, 선형 공개의 CommitBuilder에는 with_max_retries(0)을 설정합니다. 상위 publisher가 공개 전제조건과 테이블 버전 조건을 재확인하며 경합을 다룹니다.

파일에 새 값이 쓰였다는 사실과, 그 값이 그래프의 정식 상태로 보인다는 사실은 서로 다릅니다.

동시성의 핵심
CAS와 스냅샷
CAS는 “내가 읽은 기준이 여전히 맞을 때만 바꾸기”입니다. 누군가 먼저 상태를 바꾸었으면 이전 가정으로 덮어쓰지 않습니다. 스냅샷은 읽는 동안 고정한 상태입니다. 같은 쿼리가 노드는 옛 버전, 간선은 새 버전에서 섞어 읽는 일을 막습니다.

등록된 테이블의 일반적인 쓰기는 선형 HEAD를 계속 올리는 방식이 아닙니다. 생성 시의 선형 버전을 유지하고, 그래프가 참조할 detached 버전을 만듭니다. 따라서 raw Lance HEAD를 임의로 읽거나 직접 쓰면 그래프의 공개 규약을 우회합니다. docs/dev/invariants.md와 docs/dev/writes.md가 이 금지 경계를 설명합니다.

공개 전 장애가 나면 독자는 이전 상태를 읽습니다. 미공개 버전은 나이가 오래됐다는 이유만으로 삭제하지 않고, 그것을 공개할 수 있는 권한 상태가 사라졌는지까지 확인해 회수합니다. 응답이 유실되어 공개 여부가 불확실한 경우에는 성공·실패를 임의로 단정하거나 쓰기를 무조건 재전송하지 않습니다.

패턴 3. 버전 참조를 공유하고 세 방향으로 병합한다

브랜치는 부모의 정확한 테이블 버전 참조를 상속합니다. 현재 방식에서는 그래프 브랜치마다 노드·간선의 전체 파일을 복제하거나 모든 테이블의 Lance 브랜치를 새로 만들지 않습니다. 이후 각 그래프 브랜치가 만든 detached 버전 체인은 자신의 __manifest 계보가 소유합니다.

공통 조상 base / \ v v source의 상태 target의 상태 \ / v v 세 방향 비교 + 제약 검증 | target에 한 번 공개

crates/omnigraph/src/exec/merge.rs::branch_merge_as_with_options()가 정책과 옵션의 진입점이며, resolve_merge_base()·branch_merge_impl()이 조상 탐색과 병합 흐름을 맡습니다. plan_lineage_merge()는 파일 동일성과 버전 핀 등의 조건을 확인해 변경 후보를 줄이고, 조건을 증명하지 못하면 일반 비교 경로로 돌아갑니다.

기준에서 일어난 변화판단의 예
source만 한 엔터티를 바꿈해당 변경을 후보에 반영한 뒤 제약 확인
양쪽이 서로 다른 엔터티를 바꿈함께 합칠 후보를 만들 수 있으나, 최종 그래프 제약도 확인
같은 엔터티를 서로 다르게 바꿈divergent_update 같은 구조화된 충돌
한쪽은 삭제, 다른 쪽은 수정delete_vs_update 충돌 가능

엔터티 단위 충돌이므로 “속성 이름이 다르면 Git처럼 자동으로 합쳐질 것”이라고 추측하면 안 됩니다. 데이터가 충돌하지 않아도 유일 키나 간선 제약을 어기면 병합이 실패할 수 있습니다. branch_merge_dry_run()은 분류·검증까지 수행하고 최초 효과 전에 멈춥니다.

패턴 4. 인증은 경계에서, 쓰기 권한은 엔진에서 검사한다

인증은 “누구인가”, 인가는 “이 그래프·브랜치에서 이 행동을 허용하는가”입니다. 서버의 authenticate_bearer_token()은 토큰에서 행위자를 결정합니다. 정적 토큰 경로는 SHA-256 해시를 비교하며, ct_eq()와 전체 항목 순회로 일치 위치에 따른 시간 차이를 줄입니다. 이후 서명 토큰 검증 경로도 존재합니다.

쓰기 권한의 중심은 crates/omnigraph/src/db/omnigraph.rs::enforce()입니다. 정책이 설정되어 있는데 행위자가 없으면 거부하고, 있으면 PolicyChecker::check(action, scope, actor)를 호출합니다. crates/omnigraph/src/exec/mutation.rs::enforce_change()도 이 문을 통과합니다.

비유로 이해하기

건물 로비에서 신분증을 확인해도 금고를 여는 권한까지 생기지는 않습니다. HTTP 입구는 신원을 확인하고, 엔진의 쓰기 입구는 실제로 바꾸려는 행동의 권한을 확인합니다. CLI·내장 SDK라는 다른 문으로 들어와도 같은 금고 규칙을 적용하는 구조입니다.

단, 정책이 설치되지 않은 내장 개발 환경에서 enforce()는 허용 경로입니다. 또한 현재 ResourceScope를 일반적인 행 단위 RLS로 설명할 근거는 없습니다. 타입에는 그래프·브랜치·대상 브랜치·브랜치 전환 범위가 표현되어 있습니다.

5디렉토리 구조 해부

22개 멤버를 한꺼번에 읽지 말고, 요청 하나의 책임선을 따라간다
omnigraph/ ├── Cargo.toml / Cargo.lock 의존성 선언과 선택된 버전 ├── rust-toolchain.toml Rust 도구 체인 고정 ├── crates/ │ ├── omnigraph-compiler/ 스키마·쿼리·타입·IR │ ├── omnigraph-planner/ 계획·최적화·승인·explain │ ├── omnigraph/ 엔진과 공개 라이브러리 │ │ ├── src/db/ 핸들·스냅샷·쓰기 권한 │ │ ├── src/exec/ 쿼리 입구·변경·병합 │ │ ├── src/engine/ DataFusion 연산자와 실행 │ │ ├── src/graph_index/ CSR 인접 관계 자료구조 │ │ └── tests/ 통합·비용·장애·구조 검사 │ ├── omnigraph-catalog/ __manifest·계보·공개 │ ├── omnigraph-core/ 오류·Lance 접근·참조 제어 │ ├── omnigraph-storage/ 공용 제어 객체 저장소 │ ├── omnigraph-policy/ Cedar 정책과 검사 인터페이스 │ ├── omnigraph-cluster/ 설정·배포 원장·plan/apply │ ├── omnigraph-api-types/ 공용 HTTP 계약 │ ├── omnigraph-cli/ 명령과 로컬·원격 전달 │ ├── omnigraph-server/ HTTP·인증·수용 제어 │ ├── omnigraph-mcp/ MCP 도구·자원·어댑터 │ ├── omnigraph-azure-admission/ Azure 쓰기 소유권 래퍼 │ ├── omnigraph-seams/ 장애 주입 지점 공통화 │ ├── omnigraph-reference-engine/ 고정된 v1 비교 엔진 │ ├── omnigraph-gqt*/ 언어 사례·공용 실행·서버 검증 │ ├── omnigraph-dst/ 시드 기반 장애 시뮬레이션 │ └── omnigraph-bench/ 계측과 벤치마크 ├── skills/ CLI·MCP에 포함할 에이전트 안내 ├── tools/ 어휘·소스 경계 검사 도구 ├── docs/user/ · docs/dev/ 사용법과 현재 설계 ├── docs/rfcs/ · docs/releases/ 설계 이유와 릴리스 기록 ├── deploy/ · docker/ 배포 자료·진입 스크립트 ├── scripts/ 빌드·문서·계약 검사 └── .github/workflows/ CI·배포·야간 검증

omnigraph-gqt*는 설명을 위해 omnigraph-gqt·omnigraph-gqt-core·omnigraph-gqt-served를 묶어 쓴 표기입니다. 실제 폴더 이름에 별표가 들어가지는 않습니다.

첫 독해 경로 세 가지

궁금한 질문읽을 순서독해 결과물
쿼리는 어떻게 실행되는가?crates/omnigraph/src/exec/query_doors.rs → 컴파일러 crates/omnigraph-compiler/src/query/ → 플래너 crates/omnigraph-planner/src/gate.rs → crates/omnigraph/src/engine/bind.rs문자열부터 승인된 계획까지의 흐름도
변경은 언제 보이는가?crates/omnigraph/src/exec/staging.rs → 카탈로그 crates/omnigraph-catalog/src/publisher.rs → crates/omnigraph-catalog/src/commit.rs준비·공개·응답의 세 경계
노드를 어떻게 빠르게 잇는가?crates/omnigraph/src/graph_index/mod.rs → 플래너 crates/omnigraph-planner/src/cost.rs → crates/omnigraph/src/engine/graph.rsCSR과 인덱스 탐색을 선택하는 조건

코드 규모를 읽는 법

대상파일 수물리적 줄 수
Rust .rs649618,109
Python .py279,440
Shell .sh121,920
YAML .yml + .yaml286,829
쿼리·스키마 .gq + .pg8360
GQT 사례 .gqt549159,316

커밋 484e334의 확장자별 파일 집계이며 .git은 제외합니다. 테스트·주석·빈 줄을 포함한 물리적 줄 수이므로 제품 구현만의 LOC가 아닙니다. Rust 중 tests 경로는 172,708줄이고 나머지에도 인라인 테스트가 있습니다. Markdown 코드 블록은 이 표에 포함되지 않습니다.

엔진 crate만 203,048줄, 컴파일러 36,951줄, 플래너 28,351줄입니다. 분량의 차이는 중요도의 순위가 아닙니다. 작은 컴파일러 경계부터 읽으면 저장소 호출을 이해하지 않고도 타입·IR 설계를 배울 수 있습니다.

6학습 포인트 (기술별)

각 기술에서 하나의 질문을 고르고, 작은 그림이나 사례로 답한다

Rust: 공유 소유권과 검증된 상태 표현

crates/omnigraph/src/session.rs의 Session은 Arc<Omnigraph>와 요청의 설정·설정 출처를 가집니다. Arc는 엔진을 여러 사용자가 공유하게 하고, 세션 설정은 호출별로 보관합니다. 기준 설정도 따로 있어 reset의 의미를 유지합니다.

작은 실습: Session::from_defaults()·rebind()·detach()의 인자와 반환 타입을 적어 보세요. 엔진 핸들을 다시 열어도 어떤 설정이 남는지, Clone이 대규모 데이터 복제를 뜻하는지 구분하는 연습입니다.

컴파일러: 문법·의미·실행 방법을 나누기

crates/omnigraph-compiler/src/query/query.pest는 표현의 형태를, crates/omnigraph-compiler/src/query/typecheck.rs는 타입과 사용 가능성을, crates/omnigraph-compiler/src/ir/lower.rs는 실행 계층에 전달할 구조를 담당합니다. 문법상 맞는 $source.typo도 스키마에 없는 속성이면 의미 검사에서 거부할 수 있습니다.

작은 실습: 유효한 쿼리와 속성 오타가 있는 쿼리를 나란히 적고, 토큰화·파싱·타입 검사 중 어디서 갈리는지 표시하세요. “문자열이 들어왔다”에서 바로 저장소 호출로 뛰어들지 않는 이유를 설명해 보세요.

자료구조: 문자열 ID를 조밀한 정수로 바꾸기

crates/omnigraph/src/graph_index/mod.rs::TypeIndex는 문자열 ID와 u32 인덱스를 대응시키고, CsrIndex::build()는 간선을 두 배열로 묶습니다. neighbors()의 핵심은 다음처럼 짧습니다.

// CsrIndex::neighbors()의 배열 접근 부분
let start = self.offsets[node as usize] as usize;
let end = self.offsets[node as usize + 1] as usize;
&self.targets[start..end]
간선: 0 -> 1, 0 -> 2, 2 -> 1 offsets = [0, 2, 2, 3] targets = [1, 2, 1] 0의 이웃: targets[0..2] = [1, 2] 1의 이웃: targets[2..2] = [] 2의 이웃: targets[2..3] = [1]

작은 실습: 노드 3의 간선 두 개를 추가할 때 배열이 어떻게 바뀌는지 손으로 써 보세요. 이어서 from_parts()가 오프셋의 시작·단조 증가·마지막 위치를 왜 검증하는지 설명하세요. 저장한 가속 자료도 읽을 때 구조 검증이 필요합니다.

쿼리 엔진: 후보를 일찍 줄이는 것과 정답을 잃는 것 구분하기

“먼저 가장 가까운 10개를 찾고, 그중 특정 팀의 문서만 남기기”와 “특정 팀의 문서에서 가까운 10개 찾기”는 다릅니다. 플래너의 필터 위치와 후보 확장 정책은 이 차이를 다룹니다. crates/omnigraph/src/engine/search.rs, crates/omnigraph/src/engine/plan_source.rs, 플래너 crates/omnigraph-planner/src/physical.rs를 함께 읽어야 합니다.

작은 실습: 후보 5개 중 앞의 3개가 팀 필터에서 탈락하는 표를 만들어 보세요. 후보 부족을 막는 재탐색과 ANN 순위 자체의 정확도는 다른 문제라는 점을 표시하세요. 결과 개수가 충분해도 근사 검색이 정확 순위를 보증하지는 않습니다.

검색: 단위가 다른 점수를 합칠 때 순위를 사용하기

bm25의 관련도와 벡터 거리는 같은 숫자 척도가 아닙니다. RRF는 점수를 직접 더하지 않고 각 목록의 순위를 사용합니다. docs/user/search/index.md의 기본값은 k=60입니다. 아래 식의 r은 1부터 시작하는 순위입니다.

RRF(d) = Σ 1 / (60 + r_i(d))

문서 A: 단어 검색 1위, 벡터 검색 5위
문서 B: 단어 검색 4위, 벡터 검색 2위

A = 1/61 + 1/65 ≈ 0.03178
B = 1/64 + 1/62 ≈ 0.03175

작은 실습: 두 검색이 같은 문서를 서로 다른 위치에 둘 때 k를 바꾸며 순위 민감도를 계산하세요. 그래프 필터를 언제 적용하는지도 따로 적어야 합니다. RRF 안의 벡터 검색은 top-k 창이라는 한계가 남습니다.

보안·API: 동일 규칙을 여러 입구에서 유지하기

omnigraph-policy의 PolicyChecker는 엔진과 정책 구현 사이의 경계입니다. 서버의 /mcp 어댑터도 같은 HTTP 처리 계층과 인증·수용 제어를 사용하며, MCP crate 자체가 그래프를 별도로 열어 쓰지 않습니다.

작은 실습: “토큰 없음”, “인증은 됐지만 권한 없음”, “정책이 있는데 엔진 행위자 없음”의 세 사례를 입력→검사 위치→거부 이유 표로 만드세요. HTTP 상태 코드와 내부 오류 타입을 같은 개념으로 섞지 않는 연습입니다.

테스트: 결과만이 아니라 장애 창과 비용도 검증하기

GQT 파일은 --- schema·--- seed·--- query·--- expect 계열 구획으로 입력과 기대 결과를 표현합니다. 일부 사례는 결과 타입과 실행 계획까지 검증합니다. crates/omnigraph-gqt/cases/v2/planner/switch_ran_on_the_report.gqt는 hash_join과 indexed_scan 중 무엇이 실행됐는지도 확인합니다.

DST는 하나의 시드에서 스케줄·시간·ID·저장소 장애를 만들어 실패를 재현합니다. 다만 crates/omnigraph-dst/README.md는 실제 시간의 동시 실행과 프로세스 강제 종료 사례까지 모두 정확 재생된다고 주장하지 않습니다. 재현 가능 범위를 명시하는 태도를 배울 수 있습니다.

좋은 테스트는 정상 결과뿐 아니라, 실패한 순간 무엇이 보이고 어떤 작업이 끝나지 않았는지까지 설명합니다.

작은 실습: crates/omnigraph/tests/detached_commit_matrix.rs의 “테이블 준비 후·공개 전”과 crates/omnigraph/tests/failpoints.rs의 “공개 후·응답 유실” 사례를 비교해 기대 상태를 적으세요. 저장소 요청 비용을 기록한 crates/omnigraph-dst/cost_table.txt와 비교하면 결과의 정당성과 비용 계약도 구분할 수 있습니다.

7하드웨어 / 시스템 요구사항

지원 플랫폼, 빌드 조건, 데이터에 따라 달라지는 자원 비용을 나눠 본다

공식 문서에서 확인할 수 있는 조건

항목확인된 내용학습할 때의 의미
배포 아카이브Linux x86_64·arm64, macOS arm64, Windows x86_64docs/user/install.md와 릴리스 빌드 행렬 기준
Linux 아카이브glibc 2.39 이상오래된 배포판에서 단순히 압축을 풀어 실행된다고 가정하지 않기
다른 플랫폼Intel macOS 등을 위한 소스 빌드 안내도구 체인·네이티브 의존성을 별도로 준비
소스 빌드Rust 1.97.1, Cargo, protoc실행 파일만 사용하는 독자에게 Rust 설치가 필수인 것은 아님
최소 CPU·RAM고정된 공식 최솟값 미기재데이터 크기·검색·동시성에 맞춰 계측 필요
GPU기본 그래프·문자열·벡터 연산에 GPU를 필수로 선언하지 않음외부 임베딩 제공자나 별도 로컬 모델의 요구량은 따로 계산
첫 실습의 저장소로컬 파일 기반 그래프 지원객체 저장소 계정 없이 작은 예제로 시작 가능

컨테이너 기본 이미지가 Debian bookworm이라고 해서 배포 아카이브도 그 환경에 호환된다는 뜻은 아닙니다. Dockerfile은 빌드된 바이너리를 복사하며, 주석은 이미지와 바이너리의 glibc 조합을 맞추도록 설명합니다. 컨테이너 배포와 아카이브 설치의 조건을 구분하세요.

소스의 기본 예산으로 감 잡기

설정기본값무엇을 제한하는가
query_memory_bytes157,286,400바이트 = 150 MiB쿼리의 추적 대상 메모리와 로드의 일부 재사용 버퍼
write_max_bytes33,554,432바이트 = 32 MiB쓰기의 행 데이터·Blob 물질화 등에 각기 적용되는 예산
load_source_max_bytes32 MiBload csv가 읽는 단일 소스 객체 크기
stage_write_concurrency8, 허용 범위 1~64준비할 테이블·CSV 파싱 스트림·Lance 쓰기의 폭
traversal_work_limit1,000,000한 쿼리의 탐색 작업량. 메모리를 반환해도 작업량은 환급되지 않음
ann_nprobes20, 0은 상한 해제ANN에서 인덱스 델타별로 탐색할 파티션 수

출처는 crates/omnigraph-compiler/src/settings.rs::DEFINITIONS입니다. 같은 파일이 설정 이름·범위·기본값·환경 변수 대응을 함께 선언하므로, 문서 표와 구현을 비교하기 좋은 구조입니다.

쿼리 하나의 메모리 예산은 서버 프로세스 전체의 메모리 상한이 아닙니다. 예산을 끝까지 쓰는 쿼리가 10개 동시에 실행되면 추적 대상만 산술적으로 약 1.46 GiB입니다. 여기에 캐시·그래프 인덱스·네이티브 버퍼·입력·런타임 비용이 더해집니다. 이 계산은 용량을 보장하는 측정 결과가 아닙니다.

.cargo/config.toml은 Cargo가 띄우는 개발 프로세스에 LANCE_MEM_POOL_SIZE=1073741824(1 GiB)를 제공합니다. 이 값은 제품 바이너리에 영구히 박히는 설정이 아니며, 위의 쿼리별 예산과도 다른 풀입니다.

저장 비용과 실행 비용

서류를 창고에 싸게 쌓아 둘 수 있어도, 서류를 펼칠 책상과 찾으러 갈 시간은 필요합니다. 객체 저장소는 보관 장소이고, CPU·RAM·네트워크 요청은 질의하는 동안 드는 비용입니다. 브랜치와 과거 버전을 많이 유지하면 회수 정책도 저장 비용에 영향을 줍니다.

CSR 배열만 보면 한 방향에서 대략 4 × (노드 수 + 1) + 4 × 간선 수바이트입니다. 노드 100만·간선 1,000만이면 약 42 MiB이지만, 이는 u32 배열 두 개만의 산술값입니다. 문자열 ID 사전·역방향·타입별 구조·구축 임시 메모리는 포함하지 않습니다.

처음에는 기존 64비트 개발 기기와 수십 개 노드로 흐름을 익히고, 이후 데이터·질의·동시성을 고정해 측정하세요. 기본 쿼리 실습에 임베딩 API나 클라우드 저장소를 먼저 연결할 필요는 없습니다. Azure 쓰기는 admission 래퍼가 필요한 별도 검증 프리뷰 범위입니다.

8직접 해볼 수 있는 실습 과제

작은 로컬 그래프에서 시작해, 성공 조건과 실패 조건을 함께 확인한다

아래 명령은 docs/user/quickstart.md의 CLI 계약에 맞춘 실습 예시입니다. omnigraph CLI가 설치된 환경에서 별도의 로컬 연습 디렉터리를 사용하세요. 출력 설명은 예상 결과와 확인 기준이며, 소스 커밋의 동작을 이해하기 위한 것입니다.

각 과제의 완료 기준은 명령이 끝나는 것이 아니라, 결과가 왜 그렇게 나왔는지 소스와 연결해 설명하는 것입니다.

과제 1

작은 근거 그래프 만들기 입문

배울 것: 타입·키·간선·한 번의 그래프 공개. 1절의 스키마를 schema.pg로 저장하고, 아래 내용을 evidence.jsonl로 준비합니다.

{"type":"Claim","data":{"slug":"lower-latency","statement":"마이그레이션 뒤 지연 시간이 줄었다."}}
{"type":"Source","data":{"slug":"load-test","title":"부하 테스트 보고서"}}
{"type":"Source","data":{"slug":"production-metrics","title":"운영 지표"}}
{"edge":"Supports","from":"load-test","to":"lower-latency"}
{"edge":"Supports","from":"production-metrics","to":"lower-latency"}
omnigraph init --schema schema.pg graph.omni
omnigraph load --data evidence.jsonl --mode overwrite --target ./graph.omni

확인 기준: 노드 3개·간선 2개로 모델을 그릴 수 있고, @key의 slug가 간선의 노드 ID로 쓰인다는 점을 설명합니다. JSONL 한 줄은 노드나 간선 하나를 나타내는 JSON 객체입니다.

소스 연결: crates/omnigraph/src/loader/mod.rs, docs/dev/ingestion.md. 그래프 배치의 엄격한 입력 경계와 공개 단위를 구분해 읽습니다.

과제 2

근거를 찾고 타입 오류를 읽기 입문

배울 것: 이름이 있는 쿼리·매개변수·관계 탐색. 다음을 queries.gq로 저장합니다.

query sources_for_claim($claim: String) {
  match {
    $source: Source
    $claim_node: Claim { slug: $claim }
    $source supports $claim_node
  }
  return { $source.title as source }
  order { source asc }
}
omnigraph query sources_for_claim --query queries.gq \
  --params '{"claim":"lower-latency"}' --format table --target ./graph.omni

확인 기준: 제목 두 개가 나옵니다. 속성 $source.title을 $source.typo로 바꾼 별도 쿼리는 거부되어야 합니다. 다시 원래 쿼리로 돌아오면 데이터가 변하지 않았음을 확인합니다.

생각할 질문: Supports라는 선언과 supports라는 탐색 표기를 어떻게 연결하는가? 매개변수 이름과 노드 바인딩을 왜 $claim·$claim_node로 나누었는가?

소스 연결: crates/omnigraph-compiler/src/query/parser.rs, crates/omnigraph-compiler/src/query/typecheck.rs의 typecheck_query().

과제 3

브랜치에서 근거를 추가하고 검토하기 중급

배울 것: 같은 질문이 브랜치에 따라 다른 스냅샷을 읽는 이유. 아래를 benchmark.jsonl로 저장합니다.

{"type":"Source","data":{"slug":"independent-benchmark","title":"독립 벤치마크"}}
{"edge":"Supports","from":"independent-benchmark","to":"lower-latency"}
omnigraph branch create review/add-benchmark --target ./graph.omni
omnigraph load --data benchmark.jsonl --mode append \
  --branch review/add-benchmark --target ./graph.omni
omnigraph query sources_for_claim --query queries.gq \
  --params '{"claim":"lower-latency"}' --branch main --target ./graph.omni
omnigraph query sources_for_claim --query queries.gq \
  --params '{"claim":"lower-latency"}' \
  --branch review/add-benchmark --target ./graph.omni
omnigraph branch merge review/add-benchmark --into main \
  --dry-run --json --target ./graph.omni
omnigraph branch merge review/add-benchmark --into main --target ./graph.omni

확인 기준: 병합 전 main에는 제목 2개, review에는 3개입니다. 미리보기는 공개하지 않고, 실제 병합 뒤 main에서도 3개를 읽습니다. 같은 입력을 append로 재적재하면 기존 ID 충돌이므로 단순 재실행 성공을 기대하지 않습니다.

소스 연결: crates/omnigraph/src/exec/merge.rs의 branch_merge_dry_run(). dry run 분기가 검증 후, 최초 효과 전에 위치하는지 확인하세요.

과제 4

같은 엔터티의 서로 다른 수정을 충돌시키기 중급

배울 것: 단순 파일 덮어쓰기와 세 방향 병합의 차이. main에서 review/title 브랜치를 새로 만들고, 같은 load-test 노드에 서로 다른 제목을 적재합니다.

# title-main.jsonl
{"type":"Source","data":{"slug":"load-test","title":"부하 테스트 요약"}}

# title-review.jsonl
{"type":"Source","data":{"slug":"load-test","title":"수정된 부하 보고서"}}

위 구획의 파일 이름 주석은 설명용입니다. 실제 JSONL 파일에는 해당 JSON 한 줄만 저장합니다.

omnigraph branch create review/title --target ./graph.omni
omnigraph load --data title-main.jsonl --mode merge \
  --branch main --target ./graph.omni
omnigraph load --data title-review.jsonl --mode merge \
  --branch review/title --target ./graph.omni
omnigraph branch merge review/title --into main \
  --dry-run --json --target ./graph.omni

확인 기준: divergent_update 계열의 충돌과 해당 엔터티를 찾고, 미리보기 뒤 main의 제목은 유지됨을 확인합니다. 각 브랜치의 제목을 같은 값으로 조정했을 때의 결과도 비교해 보세요.

소스 연결: crates/omnigraph/src/exec/merge.rs::branch_merge_on_captured_target(), crates/omnigraph/tests/integration/merge_truth_table.rs. 단순한 표와 실제 충돌 분류를 대조합니다.

과제 5

CSV 덮어쓰기 로드 계획과 장애 창을 종이에 재생하기 고급

배울 것: 최적화가 생략하는 작업과 여전히 지켜야 할 계약. crates/omnigraph-planner/src/optimizer.rs의 Validate 분기와 docs/user/queries/explain-load.md를 나란히 읽습니다.

덮어쓰기 대상예상 removal설명할 근거
간선 타입none노드 제거를 찾는 검사가 아님
간선이 참조하지 않는 노드 타입noneretained_edges 검사 없음
간선이 참조하며 기존 행이 있는 노드 타입scan제거될 ID를 알아야 간선 검증 가능
간선이 참조하지만 기존 행이 0인 노드 타입none빈 대상의 해당 검사를 계획에서 제거

확인 기준: 네 경우의 조건식을 추적하고, “이 스캔이 생략돼도 그래프 제약이 유지되는 이유”를 각 한 문장으로 씁니다. 이어서 테이블 준비 뒤 장애가 난 경우와 manifest 공개 뒤 응답이 유실된 경우의 관측 상태를 표로 나눕니다.

추가 도전: crates/omnigraph/tests/failpoints.rs에서 공개 후 응답 유실 사례를 찾고, 실패 재현에는 왜 단순한 성공 쿼리 테스트만으로 부족한지 설명합니다. 최신 커밋의 분기를 실습할 때는 v0.14.0과의 소스 차이도 구분하세요.

9관련 기술 심화 학습 로드맵

6주 동안 문법에서 일관성·장애·운영까지 범위를 넓힌다

Rust를 처음 배우는 독자라면 소유권·참조·열거형·Result·기본 비동기 문법을 먼저 익히세요. 아래 주차는 속도 경쟁이 아니라, 매 단계에서 설명할 수 있어야 하는 결과물을 기준으로 잡았습니다.

주차공부할 개념읽을 코드·문서완료 결과물
1주그래프 모델, 키와 간선, Rust의 struct·enumdocs/user/quickstart.md, crates/omnigraph-compiler/src/schema/ast.rs근거 그래프와 잘못된 입력 3개
2주파서·타입 검사·IR, 오류 위치crates/omnigraph-compiler/src/query/, crates/omnigraph-compiler/src/ir/lower.rs쿼리 한 개의 표현 변환 지도
3주논리·물리 계획, Arrow 배치, CSRcrates/omnigraph-planner/src/gate.rs, crates/omnigraph/src/graph_index/mod.rs탐색의 배열 예제와 계획 승인 설명
4주스냅샷·낙관적 동시성·조건부 공개docs/dev/writes.md, crates/omnigraph-catalog/src/commit.rs공개 전·후 장애의 관측 상태 표
5주세 방향 병합·제약·인증과 인가crates/omnigraph/src/exec/merge.rs, crates/omnigraph-policy/src/lib.rs병합 충돌표와 권한 검사 위치 지도
6주검색 재현율·비용·결정적 테스트·배포docs/user/search/index.md, crates/omnigraph-dst/README.md, .github/workflows/ci.yml질의·예산·장애 조건이 명시된 검증 계획

매주 읽은 코드의 입력·불변식·실패 결과를 한 장으로 남기면, 다른 데이터 시스템에도 옮겨 쓸 수 있는 설계 지식이 됩니다.

난도가 높을 때의 우회 경로

심화 단계에서는 docs/rfcs/0047-search-plan-truth.md와 docs/rfcs/README.md에서 검색 계획의 의미 보존에 관한 설계 논의를 찾아보세요. 현재 규약은 개발 가이드, 결정의 이유는 RFC, 정확한 예외는 코드와 테스트에서 확인하는 습관을 들이는 편이 좋습니다.

10핵심 키워드 사전

한 단어가 저장·실행·운영에서 서로 다른 뜻으로 쓰이지 않도록 정리한다
용어쉬운 설명
속성 그래프대상인 노드와 연결인 간선이 각각 값을 가질 수 있는 데이터 모델
온톨로지업무 세계에 어떤 종류의 대상·관계·속성이 있는지 정한 설계도
.pg / .gq각각 그래프 스키마와 쿼리·변경을 작성하는 언어 파일
객체 저장소이름 붙은 파일 같은 객체를 API로 읽고 쓰는 저장 공간. 연산용 CPU·RAM까지 제공한다는 뜻은 아님
Lance 데이터셋열 기반 데이터 파일과 메타데이터·버전·인덱스를 함께 관리하는 저장 단위
Arrow / RecordBatch타입이 있는 열 배열과, 같은 행 수를 가진 열들의 묶음
AST문자열로 쓴 문법의 구조를 트리로 표현한 것
타입 검사존재하는 속성인지, 비교·검색에 맞는 값인지 실행 전에 확인하는 과정
IR문법과 실제 실행 사이에서 의미를 구조로 전달하는 중간 표현
논리 계획 / 물리 계획무엇을 계산할지와, 어떤 연산·접근 경로로 계산할지를 나눈 표현
AcceptedPlan플래너의 검사를 통과했다는 증거와 함께 포장된 실행 계획
스냅샷 / 버전 핀읽는 동안 고정한 그래프 상태와, 그 상태가 사용할 정확한 테이블 버전 참조
__manifest어떤 스키마·테이블 버전·계보가 공개된 그래프를 이루는지 정하는 카탈로그
detached commit테이블의 선형 HEAD를 움직이지 않고 준비한 내구성 있는 버전. 그래프에서 보이려면 공개 참조가 필요
원자적 공개여러 테이블의 변경을 독자에게 하나의 완성된 그래프 상태로 보이게 하는 경계
CAS / 낙관적 동시성준비 중 동시 작업을 허용하되, 기준 상태가 그대로일 때만 최종 변경을 받아들이는 방식
세 방향 병합공통 조상·source·target을 비교해 각자의 변경과 충돌을 판단하는 과정
CSR각 노드 이웃의 시작 위치와 목적지 목록을 배열 두 개로 저장하는 인접 관계 표현
BM25단어 빈도·희소성·문서 길이 등을 이용하는 전문 검색의 관련도 점수
ANN / IVF전부 비교하는 대신 후보 영역을 줄여 가까운 벡터를 찾는 근사 검색과, 이를 위한 분할 인덱스 방식
RRF단위가 다른 원점수 대신 검색 목록의 순위로 결과를 합치는 방식
수용 제어 / 배압처리 가능한 작업만 받아들이거나, 과부하가 앞단으로 전달되도록 제한하는 방식
Cedar행위자·행동·대상에 대한 허용 규칙을 코드로 작성하고 평가하는 정책 언어
MCP에이전트가 도구·자원을 발견하고 호출하는 공통 프로토콜. 자체 데이터베이스 엔진은 아님
GQT / DST쿼리의 입력·기대 결과 사례와, 시드로 스케줄·장애를 제어하는 시뮬레이션 테스트
내구성 / 응답 유실변경이 저장되어 살아남는 성질과, 저장은 성공했어도 호출자가 응답을 받지 못할 수 있는 상황

헷갈리기 쉬운 세 쌍은 그래프 브랜치와 Lance native ref, 정책 인가와 쓰기 소유권 admission, 결과 개수 충족과 ANN 순위 정확성입니다. 서로 관련은 있지만 같은 계약이 아닙니다.

11참고 링크

사용법·현재 설계·역사적 이유·정확한 구현을 서로 연결해서 읽는다

공식 저장소와 사용자 문서

본문의 핵심 소스 바로 읽기

기반 기술을 더 공부하려면

Lance 문서의 데이터셋 버전·태그 개념은 기반 저장 계층을 이해하는 자료입니다. 여러 데이터셋의 그래프 공개 계약은 omnigraph의 카탈로그가 추가하므로, 두 문서를 동일한 수준의 보장으로 읽지 않아야 합니다. 의존성의 실제 동작을 추적할 때는 13.0.0 버전도 함께 확인하세요.

현재 상태 (2026-10-23 확인)

항목상태
선정GitHub Trending Rust Weekly #8 · 2026-10-23 분석
스타 / 포크⭐1,679 / 237 · 2026-10-23 GitHub 기준
설명객체 저장소를 기반으로 브랜치와 타입이 있는 온톨로지를 제공하는 그래프 데이터베이스
생성일2026-04-10
최근 push2026-10-23
최신 릴리스v0.14.0 · 2026-10-22
최근 커밋484e334 · 2026-10-23 · perf(planner): make an overwrite's removal a plan decision (#1141) — 덮어쓰기의 제거 확인 여부를 계획이 결정하도록 개선
소스 패키지 버전엔진·서버 등 0.14.1 · 릴리스 버전과 구분
주 언어·구성Rust · Cargo 워크스페이스 멤버 22개
코드 규모Rust 649파일·618,109줄 / Python 9,440줄 / Shell 1,920줄 / YAML 6,829줄
검증 사례.gqt 549파일·159,316줄 · 일반 코드 줄 수와 별도 집계
규모 집계 기준커밋 484e334의 물리적 줄 수, 테스트·주석·빈 줄 포함, .git 제외
라이선스MIT · LICENSE의 저작권 표기는 EQTR, Inc.와 OmniGraph contributors
쓰기 전에 알아둘 한계
도입 판단에 남겨야 할 네 가지 경계

① 버전·성숙도. 생성 약 6개월의 0.x 프로젝트입니다. 릴리스와 main의 소스 버전이 다르고 HTTP 계약도 맞춰야 합니다. docs/user/install.md에 적힌 npm 발행 대기 상태를 현재 설치 가능성의 보증으로 읽으면 안 됩니다. 호환·업그레이드 문서를 먼저 확인하세요.

② 쓰기·스키마의 지원 범위. 서버는 클러스터 전용입니다. 스키마 적용은 main과 단일 활성 브랜치 제약을 유지하며, 일부 제어·유지보수는 단일 변경 프로세스 경계를 갖습니다. 프로세스 내부 락이 있다고 분산 다중 작성자 안전성이 자동으로 생기지는 않습니다. Azure 쓰기는 admission 래퍼가 필요한 검증 프리뷰입니다.

③ 검색·임베딩. ANN에는 비용과 재현율의 절충이 남습니다. 전문 검색은 선언·인덱스·분석기 호환 조건을 만족해야 하며, 준비되지 않은 경우 조용히 다른 검색으로 바뀌지 않고 거부될 수 있습니다. 일반 Load는 @embed를 보고 자동 임베딩하지 않습니다. 벡터를 공급하거나 별도 embed 흐름을 사용해야 하고, 외부 제공자를 사용하면 텍스트 전송·호출 비용도 생깁니다.

④ 운영·성능. 기본 메모리 예산은 전체 RSS 상한이 아닙니다. 객체 저장소의 요청 비용, 인덱스 유지보수, 버전 보존·회수, 백업과 권한 정책을 운영자가 설계해야 합니다. 이 글은 실제 처리량·지연·장애 복구 시간을 측정한 성능 보증을 제공하지 않습니다.