Skip to content

Add einfold for Python: an extension for datafusion-python sessions - #75

Merged
alxmrs merged 9 commits into
mainfrom
m2/python
Oct 29, 2026
Merged

alxmrs merged 9 commits into
mainfrom
m2/python

Conversation

@alxmrs

@alxmrs alxmrs commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

🪭 Claude (einfold)

For the maintainer's request to integrate einfold with xarray-sql's demos. python/ is a Python package, einfold, that installs einfold in any session of datafusion-python (Apache DataFusion's Python bindings), and so in xarray-sql's Context and ddxdb's:

from datafusion import SessionContext
from einfold.datafusion import EinFold

einfold = EinFold()
ctx = SessionContext().with_extensions(einfold)   # every later query is planned by einfold
einfold.plans_with_einfold(), einfold.plans_with_pipelines()

The package is organized by engine: einfold.datafusion now, with datafusion an optional extra (pip install "einfold[datafusion]"), so that einfold.duckdb can sit beside it. The README is written for someone who first meets einfold on PyPI.

It's spike S29's bridge (docs/spikes/s29-python-planner), made fit to keep:

  • Detection includes pipelines. The planner calls DetectFolds::detect, so demo 1's attention runs fused.
  • The codecs no longer leak.
    • A table is held only from its encoding, on the session's side, to its decoding on einfold's, within one planning call. The spike kept every table forever, and holding them weakly doesn't work: the codec is handed a fresh FFI wrapper per encode.
    • Plans are held the same way, from encoding to decoding. A plan that has run keeps what its operators hold, such as a hash join's table, so keeping plans any longer kept every query's tables: in xarray-sql's GPT demo, about 2 GB per query.
  • MAX is relinked as well as SUM, in aggregates and windows. Built-ins cross the FFI as foreign wrappers, and detection matches them by type, so the attention pipeline wasn't found until MAX was relinked too.
  • The returned plan can be reset, for recursive queries, by resetting the plan inside it.
  • No debug switches. The extension counts what it planned instead.
  • Plans of any depth decode (fixes A logical plan deeper than 100 protobuf messages fails to decode, so einfold fails queries DataFusion answers (the GPT's loss) #77). The session hands each logical plan over as protobuf, and the decoder, prost, stopped at 100 nested messages, which a chain of 15 joins exceeds. The decoder is this library's copy of prost, inside DataFusion's FFI, so the crate turns on prost's no-recursion-limit feature. xarray-sql's GPT demo (in Add an ml extra for gradients in SQL, and two ML demos xarray-sql#269) now plans and trains with einfold installed, matching DataFusion's loss to 2e-16. Deeper plans then overflowed the stack, from about 250 joins, where DataFusion alone plans about 1,350. datafusion-proto decodes a plan, on the thread that asks for it, by recursing without growing its stack (DataFusion 55: datafusion-proto decodes a logical plan without growing its stack, so a deep plan crashes the process — draft for upstream #95). So installing einfold raises the process's soft stack limit to 64 MB, as ulimit -s would: Linux grows the main thread's stack on demand up to that limit. A chain of 1,300 joins now plans.

From Python (datafusion-python 55.0.0 from PyPI, i7-8700):

query without einfold with einfold
demo 1's causal attention, 1,024 tokens 2.07 s 17.5 ms
demo 1's causal attention, 4,096 tokens 109 s 46 ms
matrix product, 50,000 × 16 · 16 × 8 37.9 ms 20.9 ms
SELECT 1 0.23 ms 1.69 ms

The last row is the fixed cost of crossing the FFI, 1.5–3 ms per query. That's for datafusion-python and its FFI to fix (#58).

Packaging. The crate is outside the Cargo workspace, like the benchmarks. It needs unsafe to read datafusion-python's FFI capsules, which the workspace forbids, and pyo3 and DataFusion's FFI crates. Each unsafe block carries a SAFETY comment, and unsafe_op_in_unsafe_fn is denied. A new CI job builds the wheel with maturin, runs clippy on the crate, and runs the tests against datafusion-python 55.0.0 from PyPI.

Tests (python/tests/test_einfold.py): each query gives the same rows with einfold installed as without, to 1e-12, and einfold runs what it should:

All 9 pass locally against xarray-sql's environment. CI has neither xarray-sql nor ddxdb, so it runs 7.

Next, in xarray-sql: an EinFold host in benchmarks/ml/_hosts.py, a DataFusion host whose context is xql.Context().with_extensions(EinFold()). That's a PR there, once xarray-sql#269 merges.

After CodeRabbit's review

  • Each foreign FFI struct's version is checked before it's cloned: the session's query planner, and both codecs.
  • The README has a Limits section. Very deep plans can overflow a thread's stack outside Linux's main thread. A function registered as sum or max is replaced by DataFusion's, since nothing crossing the FFI tells them apart (Keep a session's own sum and max in plans einfold plans from Python #111).
  • The deep-plan test sets its core limit in the child, on Linux only, and is skipped elsewhere.

🤖 Generated with Claude Code

https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Files ignored due to path filters: 1
  • python/Cargo.lock is excluded by !**/*.lock
Run configuration
Commits

Reviewing files that changed from the base of the PR and between b6a7706 and 90e78f4.

Files ignored due to path filters: 1
  • python/Cargo.lock is excluded by !**/*.lock

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
Walkthrough

Adds a Python package and native extension for DataFusion sessions. The extension provides einfold planning, FFI codecs, plan counters, and deep-plan stack handling. Python tests and CI cover wheel installation, query results, integrations, and deep join plans.

Changes: Python DataFusion integration

Layer / File(s) Summary
Python package and session hooks
python/Cargo.toml, python/pyproject.toml, python/einfold/*, python/src/lib.rs, python/README.md, python/Cargo.lock.license, .gitignore
Adds Python package configuration and native exports for DataFusion session components, planner hooks, codecs, plan counters, and the DataFusion version. The README documents supported query patterns and session integration.
FFI codecs and planner execution
python/src/codec.rs, python/src/bridge.rs
Adds codecs that transfer table providers and physical plans by reference. The planner bridge relinks matching sum and max functions, tracks plan counts, plans through EinFoldPlanner, and wraps execution in the library runtime.
Deep-plan handling and validation
python/src/stack.rs, python/tests/test_einfold.py, .github/workflows/ci.yml
Adds Linux stack-limit handling and tests for query results, repeated queries, optional integrations, and deep join plans. CI builds and installs a release wheel before running the Python tests.

Priority: Pending latest changes

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant DataFusionSession
  participant EinFold
  participant Bridge
  participant EinFoldPlanner
  DataFusionSession->>EinFold: Request session planner
  EinFold->>Bridge: Create planner with shared counters
  DataFusionSession->>Bridge: Plan logical query
  Bridge->>EinFoldPlanner: Plan relinked query with session rules
  EinFoldPlanner-->>Bridge: Return physical plan
  Bridge-->>DataFusionSession: Return opaque execution plan
Loading

Possibly related issues

Possibly related PRs

  • xqlsystems/einfold#40 — Pins the workspace to DataFusion 55.2.0, which this Python bridge also uses.
  • xqlsystems/einfold#110 — Continues the Python package work using the same package, native modules, and bridge.
  • xqlsystems/einfold#37 — Adds the fold-planning and execution types that the Python bridge plans and executes.
Merge Risk: Moderate · previous review c6c5e

Custom aggregates can return incorrect results, and failed queries can retain table data. These risks should be addressed before merging.

Security architecture risk: Moderate · up to c6c5e

Installing the extension makes query planning part of the host process’s availability boundary. Deep queries can terminate the process on some threads, and interrupted plan transfers lack a complete resource-release guarantee. Security exposure depends on whether the application accepts untrusted SQL and shares a process across users.

Retained concerns

  • Medium · security · inferred: The new planner routes session queries through recursive FFI decoding while disabling the protobuf recursion limit. The documented worker-thread and macOS behavior includes process termination for sufficiently deep plans. Raising Linux’s soft stack limit only accommodates the main thread; it does not turn excessive depth into a contained query error. If a host accepts less-trusted SQL, one enabled session can therefore threaten availability of the entire process. The negative subprocess test does not independently attribute the crash to the enabled planner because it runs the baseline first.
  • Medium · reliability · inferred: Table and plan references are stored in process-global registries and released only when their identifiers are decoded. There is no per-transfer owner or abort cleanup. Published DataFusion 55.2.0 logical serialization encodes a LIMIT input before rejecting a non-literal limit, providing an encode-before-error path that cannot be cleaned inside Bridge. Such aborted transfers can accumulate identifiers and keep table providers alive after session replacement or disposal, weakening process-level resource containment. Supported Python-query reproduction and exact wheel dependency resolution remain unresolved; successful-query cleanup does not establish failure cleanup.
Security review details

Security Blast Radius

  • inferred — The availability boundary is the hosting process, not an individual query or session: decoding can terminate the process, and reference registries are shared across sessions. A malicious-SQL attack requires an application to grant query submission to an enabled session; external endpoint, tenant, credential, and deployment exposure were not established.

Security Findings and Attack Paths

  • inferred — The supported security concern is failure containment: attacker-selected query depth can reach recursive decoding through the installed planner. A second conditional path is failed serialization after reference registration, which can retain resources. Neither cross-session data theft nor privilege escalation was demonstrated.

Trust Boundaries and Controls

  • observed — Capsule names and FFI version checks constrain the supported native interface. Reference decoding rejects malformed and unknown identifiers and consumes valid entries once, but identifiers have no session namespace. No inspected SQL path gives callers control over these encoded identifiers, so their predictability alone does not establish an authorization bypass.

Resilience and Maintainability Implications

  • observed — Mutex protection serializes registry mutations and successful decoding releases entries. It does not associate entries with a transfer’s terminal state. Opaque prevents outbound traversal of inner physical nodes, but cannot protect inbound logical serialization that fails before the planner callback.

Hardening Proposals

  • proposed — Give each transfer an owner spanning outer serialization through terminal decoding, with error and cancellation cleanup limited to that transfer’s identifiers. Clearing shared registries globally would interfere with concurrent transfers.
  • proposed — For hosts accepting untrusted SQL, enforce a plan-complexity bound before recursive decoding or use decoding with bounded or growable stack behavior. Process isolation is an alternative containment boundary; a larger main-thread stack alone is not one.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: adding a Python einfold extension for datafusion-python sessions.
Description check Passed The description is detailed and on topic. It explains the user-facing API, design decisions, packaging, tests, performance results, limitations, and follow-up work. It does not use every template head…
Linked Issues check Passed [#77] requires einfold to plan logical plans deeper than prost's default 100-message recursion limit. python/Cargo.toml enables prost's no-recursion-limit feature. python/tests/test_einfold.py c…
Out of Scope Changes check Passed The changes support the stated DataFusion Python package and [#77]. The prost configuration and stack handling support deep logical-plan decoding. The bridge, codecs, counters, packaging, CI, README, …
Full details: Description check

Explanation

The description is detailed and on topic. It explains the user-facing API, design decisions, packaging, tests, performance results, limitations, and follow-up work. It does not use every template heading or include a completed issue-closing line, but the required information is mostly present.

Full details: Linked Issues check

Explanation

[#77] requires einfold to plan logical plans deeper than prost's default 100-message recursion limit. python/Cargo.toml enables prost's no-recursion-limit feature. python/tests/test_einfold.py covers join depths 15 and 60 and a 350-join subprocess case. The PR also reports successful planning at greater depth. The issue's fallback suggestion is optional, so its absence is not a gap.

Full details: Out of Scope Changes check

Explanation

The changes support the stated DataFusion Python package and [#77]. The prost configuration and stack handling support deep logical-plan decoding. The bridge, codecs, counters, packaging, CI, README, and integration tests support installing and validating EinFold in datafusion-python. No unrelated change is demonstrated by the supplied whole-PR summary.

Full details: Docstring Coverage

Explanation

Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 27 functions across 7 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@alxmrs alxmrs left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Initial thoughts.

Comment thread python/README.md Resolved
Comment thread python/README.md Resolved
Comment thread python/README.md Resolved
Comment thread python/README.md Resolved
Comment thread python/einfold/__init__.py Resolved
Comment thread python/einfold/__init__.py Resolved
Comment thread python/einfold/__init__.py Resolved
Comment thread python/README.md Resolved

@alxmrs alxmrs left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

LGTM.

Comment thread python/README.md Resolved
alxmrs added a commit that referenced this pull request Oct 29, 2026
🪤 Claude (fuzz adversary)

This adds a simulation test of einfold in DataFusion, a soak mode that
runs it on fresh seeds for a time budget, and a nightly workflow that
runs the soak and files an issue when a property fails. It's modeled on
the soaks of ddx, the XQL Systems project for automatic differentiation
of SQL queries, and shares their log format and their triage script, so
a failure in either project is reported the same way.

`detect_vs_host.rs` checks that detection fires on the shapes it should,
over a handful of spellings each. `simulation.rs` generates the
spellings instead: joins written with `ON` or as comma joins, sides
swapped, operands behind subqueries and CTEs, filters and `HAVING`,
factors that are expressions, keys of eight types (integers of several
widths, unsigned keys past `BIGINT`, text, dictionary text, and floats
with `-0.0`, NaNs of both signs and two payloads), NULL slots holding
garbage values, empty batches, domains with duplicate keys, the
regroup's `CASE` testing columns other than the keys, two folds under
`UNION ALL`, a fold of a fold's result, and `ORDER BY … LIMIT` above.
Each case runs on plain DataFusion and on DataFusion with einfold, with
different partition counts and batch sizes. It also varies the session's
settings, the same for both: sort-merge rather than hash joins, no
repartitioning, no dynamic filters, and small memory pools (8 KB, 32 KB,
256 KB) that DataFusion's operators can spill from. A sixth shape is a
masked fold: a `WHERE` comparing two of a fold's outputs, or a
look-alike comparing an output with a summed column, half of them under
a domain.

The invariants it checks, each tagged in failure reports:

- `[error]`: einfold answers wherever DataFusion does. `[masks-error]`:
and fails wherever DataFusion fails.
- `[schema]`: the same columns, names, types, nullability and metadata,
both planned and in the batches.
- `[rows]`: the same rows as a multiset, bit for bit with exact values.
With ill-conditioned values, within the summation bound `2·(n −
1)·u·Σ|terms|`, where DataFusion computes `n` and `Σ|terms|` by running
the same query over twin tables holding 1.0 and `|v|`.
- `[host-settings]`: DataFusion gives the same exact answer under the
case's settings as under the defaults. Where it doesn't, the bug is
DataFusion's, and einfold can't match both.
- `[rerun]`: the physical plan, reset with `reset_plan_states` and run
again, gives the same rows. DataFusion's own plan, rerun, is the
control. Reruns use sessions of their own without dynamic filters, which
`reset_plan_states` doesn't support.
- `[threads]`: where einfold computed every sum itself, the same bits on
another partition count and batch size.
- `[partition]`: a mask comparing two of a fold's outputs decides each
group whole. So the groups of the mask, of its negation and of its `IS
NULL` case are, between them, exactly the unmasked fold's, with the same
values. An exact check that needs no oracle.
- `[hash]`: every fold detection finds, run through einfold's own
kernels (`DetectFolds::without_fallback`), also matches.
- `[stall]` (soak only): no case runs longer than two minutes. A
watchdog reports a hang as a failure of its seed.

Under a small pool, either side may run out of memory where the other
doesn't. Those cases are tallied rather than failed. Coverage is
asserted on long runs: each shape fires, and the dense algorithm, the
hash algorithm and the host's plan each run.

Findings, each filed with a minimal reproduction:

- #60 (#52): a fold joined back to a domain with duplicate keys gave
wrong answers on the host-plan fallback. Fixed on main.
- #61 (#48): factors were evaluated below the join, so einfold failed
queries DataFusion answers. Fixed on main.
- #62 (#48): the fallback wasn't a child of `EinFoldExec`, so a fold in
`WITH RECURSIVE` panicked, and a reset plan reran wrongly. Fixed on
main.
- #66 (#48, from #62's fix): with the fallback now a child, DataFusion's
physical optimizer runs over it twice, and some folds fail to plan.
- #63 (upstream, already apache/datafusion#26618): DataFusion's TopK
gives timing-dependent answers over `-0.0` and `0.0`.
- #67 (upstream): DataFusion's sort-merge join on a float key and
another key drops a match when a side holds both `-0.0` and `0.0`.
- apache/datafusion#24414, fixed after 55.2: a sort-merge join's filter
reads stale columns once a projection is pushed into it.
- #71 (upstream): DataFusion's plans can hang under a small memory pool
when they repartition.
- #21, a new spelling: an `=` in `WHERE` over an `IS NOT DISTINCT FROM`
join matches NULL to NULL. The shrinker in #104 cut it down to one row
per table.
- #88 (#78, on M2's stack): a masked fold left-joined from a domain
ignores its mask.

The upstream ones are pinned in `datafusion_behaviors.rs`, and each pin
fails once DataFusion fixes its bug. The simulation steps around each
open bug (`Known` in `simulation.rs`), so the soak keeps finding new
ones. `EINFOLD_SIM_KNOWN=1` brings them back, to check a fix. It passes
150 cases in a release build (60 in CI's debug build).

An ignored `export` test writes generated cases to files, so another
host can run them. The Python package's differential runs (#75) used it.

#104 adds shrinking on top, through #103's test kit. The nightly
workflow runs three ten-minute regions on disjoint seeds, and keeps at
most one open `fuzz-finding` issue: a recurrence comments on it rather
than opening another. Reproduce any reported failure with
`EINFOLD_SEED=<seed> cargo test -p einfold-datafusion --test simulation
-- --nocapture simulation`, which prints the case's SQL and tables.

<!--
- [x] cargo fmt / clippy / test / doc / reuse lint
-->

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01SCMAk87qC9VKCk22P7HDH1


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Tests**
* Expanded coverage for query behavior involving joins, filters,
sorting, signed zeros, and varying partition and batch sizes.
* Added randomized comparisons of query results and schemas,
repeatability checks, and longer-running simulations.
* Nightly and manually started soak runs now test multiple seed regions
and summarize failures with reproduction details when available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@alxmrs
alxmrs force-pushed the m2/python branch 3 times, most recently from f1c2ce8 to e0f455a Compare October 29, 2026 07:23
@alxmrs
alxmrs force-pushed the m2/python branch 2 times, most recently from 3cf61a3 to 2e7e1a2 Compare October 29, 2026 07:54
alxmrs and others added 3 commits October 29, 2026 15:52
…ts, and follow the style guide in the bridge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
…erms mean

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
@alxmrs
alxmrs merged commit 9eb7bde into main Oct 29, 2026
@alxmrs
alxmrs deleted the m2/python branch October 29, 2026 22:53
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ython does (#83)

🪭 Claude (einfold)

For #79 (peak memory as a goal). With einfold installed, a session that
ran xarray-sql's GPT loss again and again kept growing. (The GPT is
`benchmarks/ml/02_nanogpt.py`, in xqlsystems/xarray-sql#269.) Two
causes:

- **The codec kept executed plans.** A plan that has run keeps what its
operators hold, such as a hash join's table, and the codec kept the last
1,024 plans. That cost about 2.2 GB per query. It's fixed on #75 ("Hold
a plan from its encoding to its decoding"), since the code is there.
- **The allocator** is this PR. This library allocated with the system
allocator, glibc's, which keeps memory per thread, and einfold's kernels
run on blocking threads. A session still grew by about 150 MB per query.
With `MALLOC_ARENA_MAX=2` the growth stopped, so this is fragmentation,
not retention. datafusion-python links mimalloc, and its sessions don't
grow, so this library now allocates with mimalloc too. A buffer one
library allocates is freed by its own release callback, through Arrow's
C data interface, so the two allocators never free each other's memory.

The GPT's loss at batch 8, 64 channels, context 32, run ten times in one
session, from xarray-sql's environment:

| | after the 1st | after the 5th | after the 10th |
|---|---:|---:|---:|
| plans kept | 2.9 GB | 9.9 GB (killed at 10 GB) | |
| plans freed, glibc | 2.8 GB | 3.4 GB | |
| plans freed, mimalloc | 3.3 GB | 3.1 GB | 3.1 GB |

DataFusion alone: 2.7 GB after the 1st, 2.3 GB after the 5th.

The whole demo at that size, three steps, now peaks at 3.6 GB with
einfold against 2.7 GB without; before both fixes it reached 5.9 GB in
two. With einfold, a training step takes 1.3 s against 2.5 s. The loss
still peaks about 600 MB higher with einfold, which #79 is about.

**Tests.** The Python suite passes (12 tests). #75's test of running
queries again now checks that the codec holds no tables or plans after
each query.

## After CodeRabbit's review

- **mimalloc 2, not 3:** mimalloc 3 crashes on macOS arm64 when another
copy of it runs in the process, as PyArrow 24's does
(apache/datafusion-python#1607). #110's release workflow tests the macOS
arm64 wheel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M96xSez6PZwTmfaGh2eJgS
alxmrs added a commit that referenced this pull request Oct 29, 2026
…ir items

The capsule helpers move to capsule.rs, EinFold to ein_fold.rs, the codecs
datafusion-python installs to logical_codec.rs, and codec.rs becomes
by_reference.rs, after ByReference. Found by CodeRabbit's review of #75.

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

A logical plan deeper than 100 protobuf messages fails to decode, so einfold fails queries DataFusion answers (the GPT's loss)

1 participant