Skip to content

docs: DATAFUSION-PERIMETER.md — what a DataFusion-free build would actually cost - #870

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/x265-x266-plans-review-h9osnl
Jul 29, 2026
Merged

docs: DATAFUSION-PERIMETER.md — what a DataFusion-free build would actually cost#870
AdaWorldAPI merged 1 commit into
mainfrom
claude/x265-x266-plans-review-h9osnl

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Docs-only. docs/DATAFUSION-PERIMETER.md, rebased on main (ceca232).

Motivation (operator): a substrate small enough for a Raspberry Pi — home automation, toy robots, anything wanting a breath of AGI without a data-warehouse build. Compiling DataFusion is outside that budget. This answers what it would cost, with measurements rather than estimates.

The finding that governs everything

lance = 7.0.0 reaches DataFusion through three independent paths of its ownlance-datafusion, lance-geo → geodatafusion, lance-index → lance-datafusion. So feature-gating lance-graph's five direct deps would remove zero crates while lance is present: a flag that looks like a win and delivers nothing.

Corollary: Lance storage and a Pi-sized binary are mutually exclusive today.

This is invisible to every grep — it appears only in the resolved dependency graph. It also inverts a conclusion I'd drawn earlier in the session from a module-gating probe, which is exactly why it's written down.

Closures

crate transitive
lance-graph 598
lance-graph-cognitive 70
lance-graph-planner 47
lance-graph-contract 1

The Pi-viable substrate is contract + planner + cognitive + ndarray — reasoning seam, 16 strategies, MUL, thinking, SoA/kanban, NEON already present. No DataFusion edge anywhere in it. Its cost is Lance persistence.

Also measured

  • error.rs does not cascade — 3 lines, both #[cfg]-able, no exhaustive matches. This was the feasibility gate and it came out on the afternoon side rather than the wave side.
  • Module-gate probe: 5 errors = 4 real sites + 1 cascade through lib.rs's pub use query::{…}. Probe reverted; tree unchanged.
  • Public surface is three namesScanStrategy (6 files), GraphPhysicalPlanner (2), DataFusionPlanner (1), for a ~6K-LOC subsystem.
  • Ten of seventeen children have zero pub items — the whole builder/ tree, scan and join ops. The relational machinery is unreachable from outside.
  • vector_ops (15 pub items) and ScanStrategy are the two pieces needing a decision rather than a #[cfg].
  • Not implicated, checked rather than assumed: the batch writer imports neither DataFusion nor Lance (it batches kanban moves; ACID/WAL is Lance's); RaBitQ lives in thinking-engine; cam_pq is V1 6-byte, not the V3 6×(u8:u8) carving despite both involving the number 6.
  • The fossil: lance-graph-planner's [features] datafusion = [] is an empty feature, not a dependency — a reserved slot for something that never arrived, because the planner grew the replacement in parallel. datafusion_planner was hired as the orchestrator for the 4096 0xFFFFFF command shapes before V3 answered that with classid → ClassView. It now serves neither that role nor upstream's traversal-join one.

§9 — proxies that lied

Four of them, recorded because they cost real time today: a single-line Cargo.toml grep that read a [features] entry as a dependency; a grep -c that can't tell sections apart; a grep -lw on module names where mod scored 831 and helpers 58; and a compiler error count read as independent sites when one was a cascade.

Rule extracted: for dependency questions use cargo tree, not grep.

Open questions, in decision order

  1. Can lance-core / lance-file be used without lance-datafusion? — decides versioned storage vs memory-only on Pi.
  2. Full dependency-gate blast radius (--no-default-features --all-targets).
  3. ScanStrategy — contract-shaped or DataFusion-shaped?
  4. vector_ops — substrate side or query side? (Note: the CAM-PQ it serves is V1.)
  5. Call-site census — framed as "who reaches behaviour it can't address directly", not "who uses joins". The second framing measures it as a query engine and misses the dispatch role.

No code change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki


Generated by Claude Code

Summary by CodeRabbit

  • Documentation
    • Added a measurement-driven guide documenting DataFusion’s impact on the Lance Graph dependency graph.
    • Documented current DataFusion references, optional SQL feature feasibility findings, and unaffected system areas.
    • Added guidance on dependency-removal considerations, open architectural questions, and reproducibility details.

Measured, not estimated. Written because the decisive fact is invisible to
grep and would otherwise be re-derived.

The headline kills the simple plan: `lance = 7.0.0` reaches DataFusion through
three independent paths of its own (lance-datafusion, lance-geo/geodatafusion,
lance-index). Feature-gating lance-graph's five DIRECT deps would remove ZERO
crates while lance is present -- a flag that looks like a win and delivers
nothing. Corollary: Lance storage and a Pi-sized binary are mutually exclusive
today.

Closures: lance-graph 598, cognitive 70, planner 47, contract 1. The Pi-viable
substrate (contract + planner + cognitive + ndarray, NEON already there) has no
DataFusion edge at all; its cost is Lance persistence, and whether lance-core /
lance-file work without lance-datafusion is the open question that decides
versioned time-travel vs memory-only state.

Also recorded: error.rs does NOT cascade (3 lines, both cfg-able) which was the
feasibility gate; the module-gate probe measured 5 errors = 4 real sites + 1
cascade; the public surface is three names; ten of seventeen children have zero
pub items; vector_ops and ScanStrategy are the two needing a decision rather
than a cfg; the batch writer imports neither DataFusion nor Lance (ACID/WAL is
Lance's); RaBitQ is in thinking-engine; cam_pq is V1 6-byte, NOT the V3
6x(u8:u8) carving despite sharing the number 6.

Plus the fossil -- planner's `[features] datafusion = []` is an empty feature,
not a dep -- and why it stayed empty: datafusion_planner was hired as the
orchestrator for 4096 command shapes before V3 answered that with
classid -> ClassView, while the planner grew the replacement in parallel.

Section 9 lists the four proxies that lied while producing this, including the
one that matters: for dependency questions use cargo tree, not grep. The
lance -> lance-datafusion edge appears in no grep.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ca922823-f82c-441b-9a1c-6891d95ddfa3

📥 Commits

Reviewing files that changed from the base of the PR and between ceca232 and a50299c.

📒 Files selected for processing (1)
  • docs/DATAFUSION-PERIMETER.md

📝 Walkthrough

Walkthrough

Adds a measurement-driven document describing DataFusion’s dependency closure, source coupling, module-gating feasibility, excluded areas, analysis methodology, and remaining design questions.

Changes

DataFusion perimeter analysis

Layer / File(s) Summary
Dependency scope and measurements
docs/DATAFUSION-PERIMETER.md
Documents DataFusion dependency paths, closure counts, affected source areas, and explicitly excluded subsystems.
Gating feasibility and public surface
docs/DATAFUSION-PERIMETER.md
Describes the module-gating build probe, exposed planner types, Cargo feature status, and LazyLock limitations.
Method notes and open questions
docs/DATAFUSION-PERIMETER.md
Records dependency-analysis methodology, reproducibility details, and unresolved questions about crate independence and API placement.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit reads the graphs at night,
Counting crates by moonbeam light.
DataFusion paths are mapped with care,
Gating questions nibble the air.
“Hop onward,” says the bunny bright—
The perimeter is now in sight!


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b2342599-d9a3-4a71-a78e-a10458a1f9e0)

Copy link
Copy Markdown
Owner Author

@codex review

Docs-only, but the claims are load-bearing for a product decision (can this substrate build on a Raspberry Pi), so please treat it as a factual audit rather than a prose review. Four specific asks, roughly in order of what would change the conclusion:

  1. §10 Q1 — can lance-core / lance-file be used without lance-datafusion? This is the open question that decides whether a Pi build gets versioned storage or memory-only state, and I could not answer it. If you can resolve it from the dep graph, that is worth more than everything else in the doc.

  2. §1 — verify the lance → datafusion edge and my conclusion from it. I claim three independent paths (lance-datafusion, lance-geo → geodatafusion, lance-index → lance-datafusion) and therefore that gating lance-graph's five direct deps removes zero crates. If any of those is feature-gated in lance 7.0.0 such that it does not always apply, my headline is wrong and the doc's central finding collapses.

  3. §6 — the cam_pq V1 claim, which I flagged myself. I concluded cam_pq is V1 6-byte and not the V3 6×(u8:u8) carving. But [u8; 6] came from a test fixture and 6 * 256 from a size estimate — neither is a type definition. The authoritative width may live in ndarray::hpc::cam_pq, which lance-graph implements CamCodecContract against, and I did not check there. Please falsify or confirm.

  4. §4 — is the probe number honest? I gated the module (not the deps), got 5 errors, and read it as 4 real sites + 1 cascade. I state three caveats. Are there more — particularly, does cargo check -p hide anything that --all-targets or a dependent crate would surface?

Standing invitation on §9: it lists four proxies that produced wrong answers while I wrote this (a Cargo.toml grep reading a [features] entry as a dependency; a grep -lw where mod scored 831). If any surviving number in the doc rests on a similar proxy, that is the finding I most want. Today's pattern has been that my mechanism reasoning holds and my measurements do not.


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a50299c1a7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +50 to +52
durable storage*. **Open question, not yet measured:** can `lance-core` /
`lance-file` be used without `lance-datafusion`? That single answer decides
whether a Pi build gets real time-travel or memory-only state.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include lance-table in the persistence decision

This is not actually decided by lance-core/lance-file: in Lance 7.0.0, lance-core's DataFusion dependencies are optional and lance-file has no lance-datafusion dependency (only datafusion-common), while the dataset-versioning primitives—Manifest, Transaction, and CommitHandler—live in lance-table, which likewise has no lance-datafusion edge. The real product question is whether the Pi substrate can build on lance-table or must recreate the higher-level Dataset conveniences; presenting the current decision as versioned storage versus memory-only state can therefore lead to the wrong architecture choice.

Useful? React with 👍 / 👎.


## 2. Dependency closures (measured)

`cargo tree -e normal --prefix none | sort -u | wc -l`:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count packages instead of rendered cargo-tree lines

This pipeline does not count unique crates: default cargo tree de-duplicates shared dependencies by printing later occurrences with a (*) suffix (cargo tree --help describes --no-dedupe as the option that “repeats all shared dependencies”), so sort -u treats foo v1 and foo v1 (*) as two different entries. Because these graphs contain many shared dependencies, the reported 598/70/47 totals are inflated rendered-line counts rather than transitive-crate counts; strip the marker or derive the closure from Cargo metadata before using these measurements for the Pi comparison.

Useful? React with 👍 / 👎.

Comment on lines +54 to +56
Compile *time* is the smaller half of the argument. Compiling DataFusion is
memory-hungry; a 4 GB Pi is more likely to thrash or OOM than to merely take
27 minutes. A 47-crate closure is a Pi Zero 2W conversation, not a Pi 5 one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include build dependencies in the native Pi closure

When the target scenario is compiling natively on a Pi, the 47-crate figure is not the compilation closure because -e normal explicitly excludes Cargo's separate build edge kind. For example, lance-graph-contract is reported as one crate here even though its build script depends on serde_yaml, serde, and glob plus their transitive and proc-macro dependencies; those still compile on a native Pi and also enter the planner/cognitive builds through the contract. Use -e normal,build (with proper package deduplication) before drawing the Pi Zero versus Pi 5 conclusion.

Useful? React with 👍 / 👎.


| files naming it | type |
|---|---|
| 6 | `ScanStrategy` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the unrelated ScanStrategy from this census

The six files counted here belong to lance-graph-planner and use crate::ir::logical_op::ScanStrategy, which is a different type from lance_graph::datafusion_planner::cost_estimation::ScanStrategy. The DataFusion planner's enum is only consumed internally by predicate_pushdown.rs, so the claimed three-name boundary and the subsequent proposal to move this ScanStrategy into contract are based on a same-identifier grep rather than a resolved call-site census.

Useful? React with 👍 / 👎.

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review July 29, 2026 19:50
@AdaWorldAPI
AdaWorldAPI merged commit 9d1e325 into main Jul 29, 2026
2 checks passed
AdaWorldAPI added a commit that referenced this pull request Jul 29, 2026
Codex reviewed the first edition (a50299c, merged as #870) and found four
defects. All four verified; two changed what the document concludes. Superseded
text is retained in place and labelled, never silently overwritten.

1. P1 -- the corollary "Lance storage and a Pi-sized binary are mutually
   exclusive" was WRONG. It equated a capability with a crate. Measured in
   ISOLATED scratch crates (a cargo tree -p inside the workspace resolves under
   feature unification and is not comparable):

       lance       = 7.0.0  ->  571 crates,  32 datafusion
       lance-table = 7.0.0  ->  280 crates,   1 datafusion

   The one is datafusion-common, non-optional, via lance-file; lance-core's
   datafusion deps are behind an optional feature and lance-table's manifest
   names DataFusion nowhere. And lance-table is where the versioning lives --
   Manifest, Transaction, CommitHandler. So the trade is not "versioned storage
   vs memory-only" (the first edition's top open question); it is whether the
   Dataset conveniences this repo actually uses can be re-created on
   lance-table. New section 2a.

2. P1 -- ScanStrategy was never on the datafusion_planner public surface. The
   6 files were crate::ir::logical_op::ScanStrategy in the planner, a different
   type in a different crate; the DataFusion one is used in 2 files, both
   INSIDE the module. Corrected membership: DataFusionPlanner,
   GraphPhysicalPlanner, vector_ops -- four resolved imports, not a name grep.
   The "three names" headline survives; its membership does not, and the
   proposal to move ScanStrategy into contract is withdrawn as a change whose
   reason did not exist.

3. P2 -- -e normal excludes build edges. Added a column: contract goes 1 -> 16
   (serde_yaml, serde, glob + transitives). "Zero deps" stays true of the
   linked artifact and was never true of the build.

4. P2 -- the printed recipe did not reproduce its own numbers. cargo tree marks
   repeat subtrees with (*), so foo and "foo (*)" survive sort -u; the printed
   pipeline yields 828 for lance-graph, not 598. Codex inferred the counts were
   inflated -- they were not, cross-checked two ways. The counts were right and
   the recipe was wrong, which is the worse of the two: a wrong number gets
   challenged, an unreproducible recipe gets trusted.

Also finishes section 10 Q1: the lance call-site census is done and short --
open + append + list-versions, one public signature leaking &lance::Dataset
(lance_vector_search.rs:222, an API break not a refactor), and one genuinely
query-side use. Gated on an unmeasured round-trip: whether hand-rolled
open/append over lance-table reads lance-written datasets byte-identically.

Section 9 gains a round two, because all four defects have one shape: a proxy
that is cheap to compute gets published as the quantity it resembles. A grep
resembles a census, a facade crate resembles a capability, an in-workspace tree
resembles a closure. Section 9 already named the same-identifier-grep failure --
and section 5 was written with one four sections later, so the defence has to be
mechanical, not vigilance.

Board hygiene (EPIPHANIES E-A-CHEAP-PROXY-GETS-PUBLISHED-AS-THE-QUANTITY-IT-
RESEMBLES-1) follows in a second commit: it is a large append-only file and
re-transmitting it whole through this API risks corrupting it, so it waits for
git rather than being rewritten wholesale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki
AdaWorldAPI pushed a commit that referenced this pull request Jul 29, 2026
…on finding

E-A-CHEAP-PROXY-GETS-PUBLISHED-AS-THE-QUANTITY-IT-RESEMBLES-1, owed since #871
(merged via GitHub API push while the Bash permission classifier was
intermittently unavailable in-session -- a 14,447-line append-only file was
not safe to re-transmit whole through that path, so it waited for git).

Status updated to SHIPPED: both #870 (first edition) and #871 (correction
pass) are on main now. Added the process paragraph the entry was missing --
this is the THIRD occurrence this session of a merge outrunning its review
(#866 review-in-flight, #868 merged while DO-NOT-MERGE, #870 merged mid-
correction-pass), and a fourth near-miss (#871 itself merged before
CodeRabbit's rate-limited review could run). Sharpens
E-A-GUARANTEE-WITH-A-RE-EXPORTED-BYPASS-IS-NOT-A-GUARANTEE-1's rule: the
reviewable window is "until the operator reads it," which can be zero, so
correctness has to be front-loaded into the PR rather than caught after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki
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.

2 participants