Skip to content

grust-ladybug: bulk loads COPY from a temporary CSV; lbug 0.20.4 - #7

Merged
alexy merged 8 commits into
turso-mvcc-concurrencyfrom
ladybug-bulk-copy
Sep 18, 2026
Merged

alexy merged 8 commits into
turso-mvcc-concurrencyfrom
ladybug-bulk-copy

Conversation

@alexy

@alexy alexy commented Sep 18, 2026

Copy link
Copy Markdown
Member

Stacked on #6 (turso-mvcc-concurrency); only the last five commits are this PR.

Why

LadybugDB never loaded cit-Patents in the strain harness: the load did not finish inside the 7,200 s budget at 9.7 GB RSS (2026-09-12-quegee), while the maintainer loads the same graph from CSR parquet in about five seconds. The engine was not the problem; the adapter's bulk path was.

bulk_load.rs registered each chunk's rows as an Arrow table and ran COPY t FROM (MATCH (a)-[r:scratch]->(b) RETURN …). Measured with the new copy_bench example (eigen, 1 M nodes / 5 M edges, lbug 0.20.4):

path nodes edges
registered Arrow + COPY FROM (MATCH …) (before) 1.05 M rows/s 4.1 k rows/s at 500 k; unfinished after 132 min at 5 M
COPY t FROM <registered arrow table> binder rejects binder rejects
COPY t FROM 'file.csv', this adapter's schema (STRING keys, JSON props) 2.2 M rows/s 1.28 M rows/s (5 M in 3.9 s)
file COPY, INT64 keys, no props 7.7 M rows/s 6.1 M rows/s
CSR registration (UINT64 offsets) + COPY FROM (MATCH …) – 231 k rows/s

The relationship probe is superlinear in the engine; the engine's file COPY takes the same rows about 300× faster. The lbug Rust crate has no equivalent of the Python binding's COPY t FROM $df.

The second cost was the per-chunk read-back of every existing node id and (from, to) pair, kept to preserve put_graph's upsert semantics; the harness feeds large graphs in 5 M-edge chunks, so that read-back grew with every chunk and is where the 9.7 GB went.

What changes

  • grust-ladybug: every bulk write (nodes, edges, the metadata index) is written to a temporary CSV with Arrow's writer (RFC 4180 quoting, ESCAPE='"') and loaded with COPY t FROM 'file'. A count query skips the read-back when the target table is empty, so a fresh load costs nothing beyond the copy. Upsert semantics are unchanged: rows whose id or pair already exists still take the per-row MERGE path.
  • lbug 0.20.4 (crates.io, 2026-09-10; the Rust API is identical to 0.20.2).
  • examples/copy_bench.rs: the measurement above, VARIANTS=ABCDE cargo run --release -p grust-ladybug --example copy_bench -- [nodes] [edges].
  • Test: a round trip of properties with commas, double quotes, newlines, backslashes, a quote-only string, an empty string, unicode and nested JSON, then a second put over the same ids that must merge, not duplicate. 19/19 crate tests pass (eigen).

Not in this PR

  • The read-back still runs for non-empty tables, so a multi-chunk load pays it from the second chunk on. The harness chunk size (AG_CHUNK_EDGES) is the lever; a per-load session that remembers what this process copied would remove it.
  • A direct CSR / parquet import path (the maintainer's icebug-disk format, native in 0.21.x) would be a different load path and, in the harness, a separately tagged profile.
  • The workspace gate still excludes grust-ladybug on Linux because of the bundled-zstd duplicate symbols when linked next to zstd-sys; unchanged by 0.20.4.

The strain harness pins this head (6af2006) for the Ladybug ladder that follows the Turso, memory and LanceDB reruns.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sak2FQcdgs5pkL2ruKrSUZ

alexy and others added 8 commits September 17, 2026 21:51
…p the read-back

The registered-Arrow COPY FROM (MATCH …) path is superlinear in the engine on
relationship tables: 4,100 edges/s at 500 k, unfinished after two hours at
5 M (cit-Patents never loaded). The engine's file COPY takes the same rows,
STRING keys and JSON props included, at 1.3 M edges/s and 2.2 M nodes/s, so
nodes, edges and the metadata index are written through a temporary CSV
(Arrow's writer, RFC 4180 quoting, ESCAPE='"'). A count query skips the
read-back of existing ids and pairs when the target table is empty. Upsert
semantics unchanged; the round-trip test covers commas, quotes, newlines,
backslashes and nested JSON, then a second put over the same ids.

lbug 0.20.4 (crates.io, 2026-09-10; no Rust-API change from 0.20.2).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sak2FQcdgs5pkL2ruKrSUZ
@alexy
alexy merged commit da0ddea into turso-mvcc-concurrency Sep 18, 2026
1 of 2 checks passed
alexy added a commit that referenced this pull request Sep 20, 2026
Documentation only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
alexy added a commit that referenced this pull request Sep 20, 2026
Documentation only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
alexy added a commit that referenced this pull request Sep 20, 2026
Verifies the corrected lineage against icecat's own README, records the fixture
generator and independent parity reference on work/simple-rust-algo-bench, and
raises that board #7's first constraint excludes the C++ NetworKit build that
the icebug participant is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant