viirya commented on PR #50:
URL: 
https://github.com/apache/spark-connect-rust/pull/50#issuecomment-5392429230

   Re-reviewed this as PART 1 of the split, carrying over the findings from #49 
that live in the Rust core. I checked each against this branch rather than 
assuming — all of them still apply here, at shifted line numbers.
   
   Two notes on the split itself before the findings.
   
   **This isn't a pure split of #49 — new work came with it.** Comparing the 
stack's final state against #49 shows roughly +3,800/-900 across 27 files, 
including a new `crates/spark-connect/src/merge.rs` (219 lines) and about +800 
lines in `functions.rs`. That's fine, but it means the split isn't a no-op 
re-shuffle and the added surface hasn't been reviewed before. Calling it out so 
nobody assumes #49's review transfers automatically.
   
   **#51 is not actually stacked on this branch.** It declares 
`split-rust-core` as its base, but the two branches share no lineage — `git 
merge-base --is-ancestor` fails, and both fork independently from `4f43dfa` on 
master. #51 carries its own full copy of the Rust core (all 21 files under 
`crates/spark-connect/src`, plus `release.yml` and `.asf.yaml`). GitHub's diff 
view makes it look like a clean follow-up, but as-is #51 can't merge as one — 
landing this PR then #51 would conflict or silently revert core changes 
depending on merge order. Worth rebasing #51 onto this branch so the stack is 
real.
   
   ## The crate names are already taken on crates.io, so `release.yml` cannot 
work
   
   This blocks the release story regardless of code quality:
   
   - **`spark-connect`** — the name this PR gives its flagship crate — is 
already published on crates.io by an unrelated third party (currently 0.2.2, 
`github.com/franciscoabsampaio/spark-connect`).
   - **`spark-connect-core`** is also taken, by the previous architecture's 
author (0.0.1-beta.5, `github.com/sjrusso8/spark-connect-rs`).
   - Only `spark-connect-proto` is free.
   
   `release.yml` claims to publish the Rust crates to crates.io, but `cargo 
publish` will fail outright until the ASF obtains ownership of those two names. 
That needs either a name-transfer conversation with both owners or a different 
naming scheme (e.g. an `apache-*`-prefixed set) — better settled before this 
lands, since the names are baked into every `Cargo.toml`, doc example, and 
README snippet.
   
   Separately, the outgoing **`spark-connect-rs` (0.0.2) gets no deprecation 
path**. Existing users aren't broken — no new crate reuses that name, and 
`0.0.2` → `4.2.0` won't be picked up by `cargo update` — but they'll also never 
learn the project was restructured under different names. A final `0.0.3` 
marked deprecated and pointing at the successor would close that out.
   
   ## Silent-wrong-result bugs in the plan builder 
(`crates/spark-connect/src/plan.rs`)
   
   All the same shape: an argument is accepted by the API, then dropped before 
it reaches the proto. Nothing errors; the query just returns a wrong answer.
   
   - **`dropna(how=...)` ignores `how` entirely.** `dataframe.rs` computes 
`how_str` and stores it on `LogicalPlan::NADrop`, but `plan.rs:740` 
destructures it as `how: _` and forwards only `thresh` → `min_non_nulls`. 
PySpark derives `min_non_nulls` from `how` client-side; that translation is 
absent, so `dropna(Some("all"), …)` and `dropna(Some("any"), …)` build 
byte-identical plans and both behave as `"any"`.
   - **`hint(name, parameters)` drops all parameters.** `plan.rs:692` 
destructures `parameters: _` and never populates `hint.parameters`, though the 
proto field exists (`relations.proto`, `repeated Expression parameters = 3`). 
`df.hint("REPARTITION", [10])` sends a parameterless hint.
   - **`replace()` silently no-ops on non-numeric values.** `plan.rs:764,772` 
only set `old_value`/`new_value` when the string parses as `f64`; otherwise the 
`Replacement` is pushed with both fields `None`. `df.replace([("foo","bar")])` 
on a string column does nothing at all, with no error.
   - **Explicit-value `pivot` is unimplemented.** `plan.rs:444` sets 
`pivot.col` but never serializes `pivot_values` ("In the future, if 
pivot_values are passed…"), and `group.rs` hardcodes `pivot_values: vec![]` 
with no API to supply them.
   - **`fillna` accepts only `i64`**, so a fractional fill into a double column 
isn't expressible. Lower severity — an API gap rather than a wrong plan — but 
it undercuts the parity claim.
   
   Note these aren't confined to this PR's audience: PART 2 re-exports this 
same plan builder to Python (`from pyspark._pyspark import DataFrame`), so the 
published wheel inherits every one of them. Fixing them here fixes both.
   
   ## Nothing in the test setup can catch that class of bug
   
   The golden tests (`tests/golden/*.jsonl`) are the only thing covering the 
Rust plan builder, and none of the five above fall in their scope — which is 
why they've survived this long. Given `plan.rs` is ~1,400 lines and 
`functions.rs` is now over 4,000, the open question isn't these five; it's how 
many more of the same shape exist with no test that would notice. A golden case 
per DataFrame method that takes a mode/how/parameters argument would close the 
category rather than the instances.
   
   `scripts/audit_no_stubs.sh` compounds this by giving false confidence. It 
runs in CI to enforce "no deferrals," but its regex misses the `plan.rs` pivot 
deferral ("In the future…") and deliberately excludes bare 
"placeholder"/"stub". A passing audit isn't evidence of completeness, and the 
pivot gap is the proof.
   
   ## Retries and reattach are unwired
   
   The description lists the core's responsibilities as "transport: channel, 
retries, reattach," and `reattach.rs` opens with "Implements the full reattach 
protocol." Neither is actually connected:
   
   - `RetryPolicy` / `RetryPolicyState` (`retries.rs`) are only `pub use`d — 
`client.rs` has zero references to them. Every RPC hits the stub directly with 
no retry.
   - `ExecutePlanResponseReattachableIterator` (`reattach.rs`) is a data holder 
with getters/setters and no stream-consumption loop. Nothing drives it.
   
   This is a regression against the crate being replaced. The outgoing 
`spark-connect-rs` did drive reattach for Rust callers — its `client/mod.rs` 
has the `while let Some(_) = stream.message().await` loop that tracks 
`response_id`, watches for `ResultComplete`, and calls `reattach_execute()` on 
interruption. Here, `DataFrame::collect()` (`dataframe.rs:441`) iterates the 
raw stream with no retry and no reattach, so a transient `UNAVAILABLE` or 
`INVALID_CURSOR.DISCONNECTED` aborts a query the old crate would have resumed. 
Either wire these modules in, or drop the "retries/reattach" framing and delete 
the dead iterator so it stops implying coverage that isn't there.
   
   ## Scope and governance
   
   This deletes the entire existing `spark-connect-rs` crate (~19k lines) and 
replaces the project's architecture and identity. A couple of non-code 
decisions ride along that I think belong in a dev@ thread rather than a 
restructure PR:
   
   - **`.asf.yaml` turns GitHub Issues off** (`issues: true` → `false`), 
closing an existing intake channel and redirecting reporters to JIRA.
   - The crates are versioned **4.2.0** to track the Spark release, while the 
README says **"Status: alpha, work in progress"** and treats API parity as a 
goal. Those signals point in opposite directions for anyone deciding whether to 
depend on this.
   
   To be clear on the architecture itself: I think the layering is a genuine 
improvement — splitting proto / transport / DataFrame across crate boundaries 
is a real upgrade over the old single crate's module split. My blocking 
concerns are the crates.io naming (nothing can be published as-is) and the 
plan-builder bugs, since those return wrong answers with no error surfaced.
   
   Happy to file JIRAs for the individual bugs if that's the preferred tracking 
path.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to