Repository navigation
Conversation
…mantics Use explicit LongType schema names when constructing Arrow columns. Limit the columnar path to concrete pandas/NumPy arrays and native schema descriptors, including authentic converter aliases retained by already imported modules. Keep nullable values in non-nullable fields on the native row path so local validation still precedes configuration RPCs. Support warm Arrow importers that resolve converters locally. Add mock-RPC regressions for all emitted integer cells, schema metadata and nullability, custom conversion hooks, and repeated warm construction. Co-authored-by: Isaac <no-reply@databricks.com>
Build the subclass-label fallback fixture with an explicit object Index and assert its concrete label type. Pandas 3 otherwise normalizes constructor labels to builtin strings before the unchanged integer helper sees them. The original regression passes on pandas 2.3.3 and fails on 3.1.0; the fixed regression and full CI-selected Arrow/drop-in suites pass on both versions. Runtime Python and native sources remain unchanged. Co-authored-by: Isaac <no-reply@databricks.com>
Decline noncanonical columns indexes and their direct string-storage hooks before the integer fast path inspects labels or normalizes cells. Keep native tolist errors and mutations in their original order and cover public construction on both pandas versions. Co-authored-by: Isaac <no-reply@databricks.com>
Decline class-level columns storage descriptors before reading labels. Keep the single native columns storage read before pandas normalization, and cover public construction with inferred, named and explicit schemas on both pandas versions. Co-authored-by: Isaac <no-reply@databricks.com>
Avoid loading optional Arrow label modules for fallback inputs, and decline integer Arrow conversion when the loaded importer AtomicType alias is absent. Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
dbtsai
force-pushed
the
codex/spark-60720-narrow-publication-v13-candidate
branch
from
November 6, 2026 02:09
d31870b to
e31aad6
Compare
Member
|
Thank you for this. As agreed with the maintainers, it continues in #208: this branch does not allow edits by maintainers, so #208 carries its commits plus the merge with master (which now has #121) and with #207, the replacement of its sibling #143, as the two share the inline IPC bridge and the dispatcher, and the review changes (encoding each string once instead of twice, dropping the converter checks the string path never reaches with the tests that only exercised them, and relaxing the test that pinned master's pandas read counts). It keeps SPARK-60720. #145, stacked on this, stays open. |
HyukjinKwon
added a commit
to dbtsai/spark-connect-rust
that referenced
this pull request
Nov 6, 2026
Stack this on the replacements of apache#143 and apache#144 (apache#207 and apache#208), on the current master, which has apache#121, apache#128, apache#130 and apache#134 that this branch carried copies of: take the stacked branch's files, and add the date hook after the string one, and the date functions and tests after theirs. Co-authored-by: Isaac <no-reply@databricks.com>
HyukjinKwon
added a commit
that referenced
this pull request
Nov 6, 2026
…in Rust ### What changes were proposed in this pull request? This replaces #144 by DB Tsai, which does not allow edits by maintainers, with its commits plus the merge with master and the review changes below. It is stacked on #207 (SPARK-60727), with which it shares the inline IPC bridge and the dispatcher; until #207 merges, its diff includes #207's. `createDataFrame(pandas_df, schema)`, for a pandas DataFrame of at least 10,000 rows whose columns are all object columns of exact `str` values and `None`, with an explicit `StructType` of `StringType` (UTF8_BINARY) fields, now encodes the Arrow IPC in Rust (`_pandas_string_ipc` in `crates/pyspark-rs/src/pandas_input.rs`) from the NumPy object arrays, and sends it inline as the row path does, instead of materializing Python rows. Everything else keeps the row path, with its errors: subclasses of `str`, other objects (including `NaN`, `pd.NA` and numbers), `None` in a non-nullable field, strings with lone surrogates (which cannot be encoded to UTF-8), other collations, smaller frames, other dtypes and schemas, and customized pandas or `pyspark.sql.types` classes, as for #121 (SPARK-60676). Review changes on top of #144: - `_pandas_string_ipc` encoded every string to UTF-8 twice, in a validation pass and while building; it now builds in one pass and falls back on the first string that cannot be encoded. - Dropped the checks of the aliases of the Arrow importer and of `pyspark.sql.pandas.types`: the path calls the Rust encoder with the native schema and never calls them. The two tests that only exercised them went with them. - A test that required a second row conversion to read `Index._data` exactly as often as the first now checks that the path declines without more reads than the row path, as for #121. ### Why are the changes needed? [SPARK-60720](https://issues.apache.org/jira/browse/SPARK-60720): converting each string through Python rows dominates `createDataFrame` of large text frames. Measured on macOS against a local Spark 4.3.0 server, as the client CPU time of `createDataFrame` of 200,000 rows of three string columns (ASCII, empty, NUL, accents, CJK, emoji, combining marks and nulls) with an explicit schema (median of 5): 216 ms on the current master, 60 ms with #144 as it was, and 43 ms with this change (official PySpark 4.3.0: 51 ms). All give the same rows. ### Does this PR introduce _any_ user-facing change? No, other than speed. ### How was this patch tested? - New tests in `python/tests/test_arrow_handoff.py`, against the mock Arrow input service, check every IPC cell (Unicode, NUL, empty strings, nulls, all-null columns), field names, nullability and metadata, the inline transport with any relation-cache setting, that no configuration is read, that surrogates fall back before any configuration read, and that each unsupported or customized input keeps the row path with its errors. - Against the current master, merged into #207 and this branch, and a local Spark 4.3.0 server: the lib and every integration test of `apache-spark-connect` and `apache-spark-connect-core`, e2e included (789 passed), the build of all tests with `wasm-udf`, the offline Python suites (1,614 passed, 8 skipped), the live drop-in and Arrow suites (86 passed) and the live cases (10 passed). `cargo fmt --all --check` passes. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: OpenAI Codex (GPT-6); the merge with master, the review changes and the measurement by Claude Opus 5.5 This pull request and its description were written by Isaac. Closes #208 from HyukjinKwon/SPARK-60720-pandas-strings. Lead-authored-by: DB Tsai <dbtsai@dbtsai.com> Co-authored-by: DB Tsai <db.tsai@databricks.com> Co-authored-by: Hyukjin Kwon <hyukjin.kwon@databricks.com> Signed-off-by: Hyukjin Kwon <hyukjin.kwon@databricks.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current change after the master rebase
Encode eligible pandas NumPy object-string columns directly into Arrow IPC in Rust for frames of at least 10,000 rows with an explicit native UTF8_BINARY string schema and exact str/nullable None cells. Preserve inline LocalRelation transport, Spark schema and supported fallback/error ordering for subclasses, surrogates, missing values and customized hooks. This remains stacked on PR #121 with current Index-iteration coverage and its inline bridge; PR #118/#108 are already in master. Guards cover a finite set of directly skipped hooks rather than arbitrary pandas customization.
Validation of the rebased change
Validated exact head
e31aad6482c1b748924991a8c01aabbea74db385against upstream master014674e44470001f5c184846964c131d43f6e4fa. Formatting, 115 core and 419 API Rust unit tests, all API integration-target compilation with and withoutwasm-udf, the native extension build, and 979 offline/mock Python tests passed (8 skips). Committed whitespace checks passed. Workspace Clippy remains advisory and fails on unmodified master; diagnostics are compared separately in the rebase evidence. Full live-Spark parity and CI for the rewritten head remain pending. No new performance, memory, or RPC-cost measurement is claimed; historical results do not validate this rebased head.Historical pre-rebase description, validation and performance evidence
The complete previous description is preserved below as historical evidence. Every test count, benchmark number, source/dependency/revision ID, RPC observation, source-equivalence claim, and use of “current”, “final”, “fresh” or “new” in this appendix refers to its original pre-rebase context. Master has since changed local inference and conversion, cached schema access and Arrow configuration handling. The current scope above takes precedence over stale runtime, dependency and per-call RPC statements below. None of these results validates or measures the November 5 rebased head. Prior failures, guard/cold-path costs, RPC/memory observations and live-server limitations remain preserved within their stated historical scope.
What changes were proposed in this pull request?
Pandas object columns containing strings currently materialize Python rows during
createDataFrame, even with an explicit string schema. Encode eligible columns directly into Arrow IPC in Rust while preserving the existing inlineLocalRelationtransport and Spark schema.The path requires an ordinary pandas DataFrame with at least 10,000 rows, concrete NumPy object storage, an explicit native UTF8_BINARY string schema, and exact
strvalues or nullableNone. Customized pandas/schema hooks, string subclasses, unsupported missing values and surrogates retain the existing conversion/error path. The required internal IPC bridge is included in this change.Depends on PR121, including its PR118/PR108 prerequisites. The narrow delta against PR121 changes five production files and adds 19 test definitions.
Why are the changes needed?
The frozen client-only benchmark compares published PR121
c4284a3e0b71c8d624a1253c52337419761b2131without the bridge against candidated31870bc4ceb15c3cadc4bc0a44fcb16f7b5e723with the complete fix, using separate fresh wheels/environments and unchanged workloads/validators. All 535 samples completed. An independent passive raw artifact audit cross-checks original validator receipts, hashes and all raw metrics; it does not replay Arrow decoding.Medians below use five worker samples. Each string frame has 10,000 rows and three columns. Each focused warm sample averages 20 measured constructions after three warmups; focused first-construction samples measure one construction after zero warmups.
The middle-surrogate candidate maximum was 21.036 ms versus 13.605 ms baseline. The ordinary pandas 3.1 StringDtype case retains the fallback path and increased 1.95% warm/6.61% first-construction elapsed time. Ordinary Arrow input increased 15.0% in warm elapsed time while CPU decreased 8.4%. Other guarded workloads increased: the million-row pandas float case by 3.93% elapsed/5.04% CPU and UDF construction by 4.27% elapsed/3.92% CPU. Focused CPU medians increased 18.72% for early-surrogate warm errors, 12.17% for middle-surrogate first-construction errors and 7.75% for late-subclass first construction. These five-sample observations do not establish statistical significance or a general overhead bound.
Warm ordinary held-process RSS under pandas 2.3.3 was:
Measured construction starts after interpreter/library/session/input preparation. Ordinary first-construction timing also follows an untimed version call; the focused worker has no explicit version call. First-construction samples measure one constructor call without constructor warmup. Cold-interpreter costs were not measured. Focused sampled-peak/post-GC RSS was not collected; ordinary post-GC RSS includes later correctness work.
The client is 4.3.0 on Python 3.12.14 with pandas 2.3.3/3.1.0, NumPy 2.5.4, PyArrow 26.0.0 and grpcio 1.84.1. A separate official PySpark 5.0.0.dev0 reference at Apache Spark
ca1514a2b4e72f9529871f34d09881cd4921df82is measured under pandas 2.3.3 only. The Rust-client and official PySpark versions differ, so the official result provides separate context. Improvements above compare the two Rust-client revisions; historical minimal/combined-tree timings were not substituted.Does this PR introduce any user-facing change?
Eligible pandas string input uses the column encoder. Tests preserve values, nulls, field names/metadata, error ordering and inline transport/configuration behavior. Paired ordinary 20-counter RPC maps are equal. Focused intercepted RPC method maps are zero; their separate inferred zero 20-counter maps do not establish a complete end-to-end transport observation.
How was this patch tested?
wasm-udffeature test compilation each produce 37 test executables; no WASM-target execution is claimed.test_arrow_handoff.pyandtest_dropin_offline.pyusingpytest --rootdir=python --import-mode=importlib -q -rs. Baseline: 522 passed/9 skipped for each pandas version. Candidate: 681 passed/9 skipped with pandas 2.3.3; 679 passed/11 skipped with pandas 3.1.0. The two extra skips concern unavailable older infer-string storage behavior.Package checks cover existing dependency RECORD-listed non-bytecode files and recorded PySpark import origins; they do not establish continuous isolation, unlisted/shadow dependencies or loaded dependency origins. Probe receipts cover outer role/reference commands; nested internal
check_outputpartial stdout is not separately receipted.Was this patch authored or co-authored using generative AI tooling?
Generated-by: OpenAI Codex (GPT-6)
This pull request and its description were written by Isaac.