Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 17 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,32 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

# storage
redis = { version = "1.0", features = ["tokio-comp"] }
rusqlite = { version = "0.32", features = ["bundled", "backup"] }
redis = { version = "1.0", features = ["tokio-comp"] }
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] }

# embedded memory-mapped KV (bundled C — no system lib needed)
lmdb-rkv = "0.14"

# tree-sitter core
tree-sitter = "0.25"

# tree-sitter grammars (one feature per lang on extract crate)
tree-sitter-typescript = "0.23"
tree-sitter-javascript = "0.23"
tree-sitter-python = "0.23"
tree-sitter-rust = "0.23"
tree-sitter-go = "0.23"
tree-sitter-java = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-c-sharp = "0.23"
tree-sitter-ruby = "0.23"
tree-sitter-php = "0.23"
tree-sitter-scala = "0.26"
tree-sitter-swift = "0.7"
tree-sitter-kotlin = "0.3"
tree-sitter-lua = "0.5"
tree-sitter-python = "0.23"
tree-sitter-rust = "0.23"
tree-sitter-go = "0.23"
tree-sitter-java = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-c-sharp = "0.23"
tree-sitter-ruby = "0.23"
tree-sitter-php = "0.23"
tree-sitter-scala = "0.26"
tree-sitter-swift = "0.7"
tree-sitter-kotlin = "0.3"
tree-sitter-lua = "0.5"

# cli / async / fs
clap = { version = "4", features = ["derive", "wrap_help"] }
Expand Down
8 changes: 4 additions & 4 deletions crates/codegraph-api/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ async fn api(path: &str) -> GraphApi {
async fn search_and_symbol_by_id() {
let dir = tempfile::tempdir().unwrap();
let db_path = dir.path().join("db.sqlite");
let db_str = db_path.to_string_lossy().into_owned();
let db_str = format!("sqlite://{}", db_path.to_string_lossy());
let (caller, _, _) = seed_index(&db_str).await;
let api = api(&db_str).await;

Expand All @@ -87,7 +87,7 @@ async fn search_and_symbol_by_id() {
async fn callers_callees_and_flow() {
let dir = tempfile::tempdir().unwrap();
let db_path = dir.path().join("db.sqlite");
let db_str = db_path.to_string_lossy().into_owned();
let db_str = format!("sqlite://{}", db_path.to_string_lossy());
let (caller, callee, helper) = seed_index(&db_str).await;
let api = api(&db_str).await;

Expand Down Expand Up @@ -117,7 +117,7 @@ async fn callers_callees_and_flow() {
async fn search_flow_pattern_and_references() {
let dir = tempfile::tempdir().unwrap();
let db_path = dir.path().join("db.sqlite");
let db_str = db_path.to_string_lossy().into_owned();
let db_str = format!("sqlite://{}", db_path.to_string_lossy());
let (caller, callee, _) = seed_index(&db_str).await;
let api = api(&db_str).await;

Expand Down Expand Up @@ -147,7 +147,7 @@ async fn search_flow_pattern_and_references() {
async fn files_stats_and_context() {
let dir = tempfile::tempdir().unwrap();
let db_path = dir.path().join("db.sqlite");
let db_str = db_path.to_string_lossy().into_owned();
let db_str = format!("sqlite://{}", db_path.to_string_lossy());
seed_index(&db_str).await;
let api = api(&db_str).await;

Expand Down
8 changes: 6 additions & 2 deletions crates/codegraph-bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description = "Benchmark codegraph-extract + codegraph-graph trên các repo th

[dependencies]
codegraph-extract = { path = "../codegraph-extract" }
codegraph-graph = { path = "../codegraph-graph" }
codegraph-graph = { path = "../codegraph-graph", features = ["sqlite", "lmdb"] }
codegraph-core = { path = "../codegraph-core" }

anyhow = { workspace = true }
Expand Down Expand Up @@ -36,4 +36,8 @@ codspeed = ["dep:codspeed-criterion-compat"]

[[bench]]
name = "codspeed"
harness = false
harness = false

[[bench]]
name = "storage"
harness = false
85 changes: 85 additions & 0 deletions crates/codegraph-bench/STORAGE_PERF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Báo cáo hiệu năng storage backend

So sánh 3 backend mà `codegraph-graph` hỗ trợ cho việc persist index:

- `in_memory` — `GraphIndex::in_memory()` (baseline RAM, không persist)
- `sqlite` — backend hiện tại, qua `sqlx` (`sqlite://<dir>/db.sqlite`)
- `lmdb` — backend mới thêm, qua `lmdb-rkv` (`lmdb://<dir>`)

> Redis bị loại khỏi phạm vi vì đã chạy trên RAM, không phải "disk-backed".

## Cách đo

Benchmark chạy **đúng pipeline thật** như `codspeed.rs` (extract → index → query)
thay vì micro-benchmark gọi trực tiếp từng `Storage`. Với mỗi repo:

1. **extract** một lần (`codegraph-extract`: walk + parse → `Vec<ParseResult>`).
2. **index**: với mỗi backend, mỗi iteration dựng **storage mới** (tempdir/file
mới) rồi `GraphIndex::open(dsn)` + `ingest` — đo chi phí open+ingest, không bị
tích luỹ giữa các iteration. Backend được chọn bằng **DSN scheme**
(`sqlite://` / `lmdb://` / `None` = in-memory), đúng cơ chế
`GraphIndex::open(dsn)` trong `lib.rs`.
3. **query**: chạy bộ truy vấn mẫu trên index in-memory sau ingest (engine query
nằm in-memory, backend không ảnh hưởng phase này).

Repo đo: toàn bộ `crates/` (chính workspace này). Lệnh:

```bash
cargo bench -p codegraph-bench --bench storage
```

## Kết quả

### index: open + ingest (mỗi iteration storage mới)

| Backend | lần 1 (median) | lần 2 (median) | lần 3 (median) | ghi chú |
|-------------|---------------|----------------|----------------|---------|
| `in_memory` | 13.75 µs | 12.09 µs | 8.99 µs | không persist, không I/O |
| `sqlite` | 42.73 ms | 40.55 ms | 13.46 ms | biến động cao |
| `lmdb` | 20.01 ms | 28.40 ms | 15.88 ms | biến động cao |

**Nhận xét**: biến động giữa các lần chạy lớn (máy đo còn chia tải). Trung bình
LMDB nhanh hơn SQLite khoảng **1.4–2.1×**; có lần chạy về ngang nhau. Lợi thế
của LMDB đến từ: viết 1 transaction duy nhất cho toàn bộ commit (không
WAL/journal riêng, không parser SQL mỗi op), và mapping file theo trang B+tree
kiểu B-tree copy-on-write.

### Dung lượng trên đĩa (corpus `crates/`)

| Backend | kích thước | ghi chú |
|---------|-----------|---------|
| `sqlite` | ~590–690 KB | file db.sqlite |
| `lmdb` | ~270 KB | thư mục chứa data.mdb |

**Nhận xét**: LMDB chiếm **ít hơn ~2.2×** so với SQLite trên cùng dữ liệu — bản
thân LMDB chứa trang metadata + dữ liệu compact; SQLite lưu cả schema, WAL
overhead và trang trống.

### query (index in-memory, backend không ảnh hưởng)

| Nhóm | median |
|-------|--------|
| `sample` (search_symbol + callees + flow × 200 tên) | ~84–90 ns / op |

Query không bị ảnh hưởng bởi backend vì sau `ingest` engine đọc từ graph
in-memory.

## Khuyến nghị

- **LMDB đáng dùng khi cần persist nhanh hơn + nhỏ hơn** (cùng mức API
`GraphIndex::open(dsn)`), đặc biệt cho index lớn: chi phí open+ingest thấp hơn
và footprint ~2.2× nhỏ hơn SQLite.
- **SQLite vẫn là lựa chọn an toàn** nếu cần tooling/quen thuộc với file `.db`
đơn, hoặc dùng query ad-hoc bên ngoài. Độ lệch hiệu năng giữa 2 backend nằm
trong tầm 1.4–2.1× tuỳ tải máy.
- `in_memory` là baseline nhanh nhất (không I/O), dùng cho trường hợp không cần
persist (CLI một lần).
- Redis giữ vai trò dành cho triển khai cần chia sẻ index giữa nhiều process.

Chọn backend bằng DSN scheme:

```rust
GraphIndex::open("sqlite:///tmp/db.sqlite").await?; // sqlite
GraphIndex::open("lmdb:///tmp/db").await?; // lmdb
GraphIndex::in_memory(); // RAM
```
Loading
Loading