Skip to content

feat: support Parquet export format + bincode zstd compression #131

Description

@c2j

背景

当前 codeweb 的 GraphStore 使用 bincode 作为唯一二进制存储格式(store.rs version=6)。这带来两个问题:

问题 1: 版本兼容性脆弱

历史上每次 GraphStore schema 变更都触发了 version bump,用户必须重新 analyze

版本 变更内容
1→2 ReferencesTableTableAccess 重构
2→3 新增 Table/View/Package/Trigger 等 8+ Node 变体
3→4 Edge 类型丰富(CallScope/DataFlowKind)
4→5 MappedStatement/JavaSql 新增 sql 字段
5→6 新增 JspPage/JspSql Node 变体

bincode 采用位置编码,新增枚举变体或字段会破坏旧文件的解析 → 每次 schema 演进都强制全量重分析

问题 2: 缺少通用数据分析接口

用户希望用 Python/pandas、DuckDB 等外部工具分析调用图数据(统计、聚合、关联查询),当前只能通过 CLI 或 JSON 导出间接获取。


方案

Phase 1: Parquet 导出格式(推荐优先实施)

新增 codeweb export --format parquet,将图谱导出为列式 Parquet 文件,供外部工具消费。

Schema 设计(宽表 + nullable 列,天然支持 schema 演进):

nodes.parquet — 核心标识列 + 各变体特有字段均为 nullable:

node_id: Int64 (NOT NULL)
node_type: Utf8 (NOT NULL)  — "Procedure"/"Function"/"Table"/...
schema_name: Utf8
package_name: Utf8
object_name: Utf8
file_path: Utf8
line: Int64
is_partial: Boolean
is_explicit: Boolean
is_system: Boolean
sql_text: Utf8             — MappedStatement.sql / Procedure.body_sql
namespace: Utf8             — MappedStatement
statement_id: Utf8          — MappedStatement
class_fqn: Utf8             — JavaMethod/JavaClass
method_signature: Utf8      — JavaMethod
columns_json: Utf8          — Table (JSON)
partition_json: Utf8        — Table (JSON)
distribute_json: Utf8       — Table (JSON)
ddl_source: Utf8            — Table
type_kind: Utf8             — Type
properties_json: Utf8       — Custom (JSON string via JsonMap)
...

edges.parquet:

edge_id: Int64
source_node_id: Int64
target_node_id: Int64
edge_type: Utf8             — "DirectCall"/"TableAccess"/...
scope: Utf8                 — DirectCall
flow_kind: Utf8             — TableAccess
access_modes: Int32         — TableAccess (bitflags)
write_kinds_json: Utf8      — TableAccess (JSON array)
column_analysis_json: Utf8  — TableAccess (JSON)
file_path: Utf8
line: Int64

为什么这样设计

  • 新增 Node/Edge 变体 = 新增 nullable 列 → 旧文件自动兼容(列不存在时 reader 返回 NULL)
  • node_type / edge_type 是 string 列而非 enum → 新增变体不改变已有数据的编码
  • 嵌套结构(PartitionInfo, DistributeInfo, ColumnAnalysis)用 JSON string 列 → 灵活但不可列内查询(折中方案)

依赖:

parquet = { version = "54", features = ["arrow", "zstd"] }

CLI 接口:

codeweb export --format parquet --output graph.parquet
# 生成 nodes.parquet + edges.parquet(两个文件)
codeweb export --format parquet --output ./export/

Phase 2: bincode + zstd 压缩(低风险优化)

在不改变存储格式的前提下,对 bincode 输出做 zstd 压缩包装:

// store.rs
pub fn save_bincode(&self, path: &Path) -> Result<()> {
    let bytes = bincode::serialize(self)?;
    let compressed = zstd::encode_all(&bytes[..], 3)?;  // level 3, 平衡速度与压缩率
    std::fs::write(path, compressed)?;
    Ok(())
}

收益: 文件大小减小 30-50%,代码改动 < 10 行,零兼容性风险。

Phase 3(远期): Parquet 作为 store 的可选格式

codeweb.toml 中支持 format = "parquet",GraphStore 直接读写 Parquet。此阶段需要:

  • 完整的 Parquet → GraphStore 反序列化(重建 petgraph + 全部索引)
  • 增量更新的 row group 追加策略
  • Benchmark 对比 bincode+zstd

仅在经过 Phase 1 验证 Parquet 读写性能和 schema 兼容性后再考虑。


验证标准

Phase 1

  • codeweb export --format parquet 生成可通过 DuckDB 查询的 Parquet 文件
  • 导出的节点数和边数与 JSON 导出一致
  • duckdb -c "SELECT node_type, count(*) FROM nodes.parquet GROUP BY 1" 结果与 codeweb stats 一致
  • Python pd.read_parquet("nodes.parquet") 可正常读取

Phase 2

  • 新旧 bincode 文件 roundtrip 测试通过(save → load → stats 一致)
  • 文件大小对比:100K 节点项目的压缩前后大小
  • load_bincode 延迟对比(hyperfine

Phase 3

  • Parquet store 的 schema 演进测试:旧文件可在新版 codeweb 中正常加载
  • 与 bincode+zstd 的性能对比(加载延迟、查询延迟、文件大小)

参考资料

  • 当前 GraphStore 结构: src/graph/store.rs L24-58
  • bincode 版本管理: store.rs L920-928
  • 历史 version bump 清单: git log --oneline -S "version:" -- src/graph/store.rs
  • parquet crate: https://crates.io/crates/parquet (需 features=["arrow", "zstd"])

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions