Commit 8a52de0
Add the queries and the cross-library integration tests
Seventeen tests for #1719, every one running real worker processes, plus
`run_tpch.py` for the same thing against the generated TPC-H data.
The four queries: a Q1-shaped distributed aggregate; the same with `dfx_udfs`'
Rust scalar and aggregate functions; an inline Python UDF; and the storage
library's provider read on the workers. Each compares the distributed answer
against the single-process answer through the same session factory, because
disagreement there is the only reliable signal that a split is wrong.
Tests use a small hand-checked fixture rather than the real dataset.
`tpchgen-cli` writes one file per table, so SF-1 `lineitem` is a single 220 MB
file — one partition, and nothing to fan out. `run_tpch.py` re-shards it
first, which is a fair illustration of the actual constraint: an engine can
only spread work as widely as the data is split.
Three things this pass turned up.
**An empty `shuffle_dir` was writing files into the working directory.** A
registered config extension always *has* an entry, so an unset directory
arrives as `Some("")` rather than `None`, and the planner treated it as
configured. The stage node's paths were then relative to wherever the process
happened to be, so `run_local` scattered `stage-1-part-*.arrow` next to the
caller and later queries read another query's leftovers back out of them —
which is how six tests failed with "Batch has 3 columns but BatchCoalescer
expects 5". Four of those files had already been committed by the previous
change; they are deleted here.
**cloudpickle captures a module attribute as the module, not as its parent.**
I expected `pa.compute` inside a UDF to fail on a worker, since `import
pyarrow` does not bind `pyarrow.compute` and nothing loads it transitively.
It does not fail: cloudpickle resolves the attribute and stores an import of
`pyarrow.compute` itself, so the worker imports the submodule on load. The
real trap is a *function* with a resolvable `module.qualname` — the same
callable is 1106 bytes pickled from `__main__` and 34 bytes from an importable
module, because the second is a pointer. A helper at test-module scope
therefore reaches the worker as `ModuleNotFoundError: No module named
'_test_three_libraries'`, with `traceback: None` and nothing naming a UDF, a
plan, or serialization. Both halves are pinned as tests.
**An FFI query planner encodes its own output on every query.** It returns
proto bytes rather than a plan handle, so both libraries' codecs show one
encode apiece straight after `execution_plan()`, before the driver has asked
for any bytes. Worth knowing before reading an encode counter as "this is what
shipping cost".
`run_tpch.py` compares floats with a tolerance rather than for equality:
splitting a `sum` across partitions changes the order the additions happen in,
and floating point addition is not associative, so the low bits of
`sum_charge` differ legitimately between the two runs. Any distributed engine
has this property, and someone diffing two runs should not conclude the split
is broken.
Verified: 400k rows of real `lineitem` across four worker processes, using the
custom provider, its custom scan node, the engine's stage node, and both Rust
functions, agreeing with the single-process result to 1e-6 relative.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 6571d89 commit 8a52de0
8 files changed
Lines changed: 704 additions & 3 deletions
File tree
- examples/distributed
- engine-library
- python
- dfx_engine
- tests
- src
Lines changed: 19 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| 50 | + | |
50 | 51 | | |
51 | 52 | | |
52 | 53 | | |
| |||
116 | 117 | | |
117 | 118 | | |
118 | 119 | | |
119 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
120 | 123 | | |
121 | 124 | | |
122 | 125 | | |
123 | 126 | | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
124 | 133 | | |
125 | 134 | | |
126 | 135 | | |
127 | 136 | | |
128 | 137 | | |
129 | 138 | | |
| 139 | + | |
| 140 | + | |
130 | 141 | | |
131 | 142 | | |
132 | 143 | | |
| |||
184 | 195 | | |
185 | 196 | | |
186 | 197 | | |
187 | | - | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
188 | 201 | | |
189 | 202 | | |
190 | 203 | | |
191 | | - | |
| 204 | + | |
| 205 | + | |
192 | 206 | | |
193 | 207 | | |
194 | 208 | | |
| |||
197 | 211 | | |
198 | 212 | | |
199 | 213 | | |
| 214 | + | |
| 215 | + | |
200 | 216 | | |
201 | 217 | | |
202 | 218 | | |
| |||
0 commit comments