You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Spark physically executes a GlobalLimit through a single partition. When that limited plan is used as the left input of a lateral vector_search, all query vectors are therefore processed by one task unless users add a repartition manually.
This change:
detects a GlobalLimit in the lateral vector-search input;
inserts a shuffle repartition above the limit when no repartition already exists above it;
preserves an explicit user repartition placed after the limit;
adds spark.paimon.vector-search.lateral-join.parallelism, defaulting to 16, to control the generated parallelism.
A repartition below the limit does not help because the global limit gathers its result into one partition, so the rule adds a repartition above that shape.
Tests
Added plan tests covering:
default repartition for a limited input;
configured parallelism;
repartition below a global limit;
preservation of an explicit repartition above the limit.
All four added plan tests pass locally. Apache CI will run the complete Spark test matrix.
The new VECTOR_SEARCH_LATERAL_JOIN_PARALLELISM option is missing from docs/generated/spark_connector_configuration.html, which causes ConfigOptionsDocsCompletenessITCase to fail in two CI jobs. Please regenerate and commit the option documentation with mvn package -Pgenerate-docs -pl paimon-docs -nsu -DskipTests -am, after installing the changed jars as noted in paimon-docs/README.md.
Comment 2 — broadcast join bypasses the repartition
hasUnrepartitionedGlobalLimit stops at every non-unary node, so a broadcast join above the limited subquery bypasses this optimization even though the streamed side, and therefore the join output, still has one partition. I reproduced this with a limited left side and a broadcast right side: the physical plan contains GlobalLimit -> Exchange SinglePartition -> BroadcastHashJoin, no generated round-robin exchange, and LateralVectorSearchExec receives an RDD with one partition. Could we handle partition-preserving binary shapes such as broadcast joins, or make this decision where the effective child partitioning is available?
Comment 3 — COALESCE is incorrectly considered sufficient
This treats every RepartitionOperation as sufficient, including a non-shuffle COALESCE. A COALESCE(16) above a GlobalLimit cannot increase its single-partition input: the physical plan remains Coalesce 16 -> GlobalLimit -> Exchange SinglePartition, and I verified that LateralVectorSearchExec still receives an RDD with one partition. Please only stop at an operation that can actually restore parallelism, such as a shuffle repartition, or explicitly continue through non-shuffle repartitions.
The rule only recognizes explicit/resolved BROADCAST hints.
Spark’s normal statistics-based automatic broadcasting is determined during the physical planning phase; since the logical plan contains no hints, the limited streamed side will not be repartitioned, and there may still be only one partition after the physical BroadcastHashJoin.
Existing tests also only cover /*+ BROADCAST(d) */. It is recommended to add cases without hints that rely on autoBroadcastJoinThreshold, or to handle them at a stage where physical partitioning can be observed.
The current TableValuedFunctionsTest passes 20 out of 20 locally, indicating that the implementation within the existing test coverage is correct; the issue is insufficient coverage.
Addressed the statistics-based automatic broadcast case in commit 31bf5d0de1.
Added a physical-plan fallback in LateralVectorSearchExec.requiredChildDistribution, where Spark's selected broadcast build side is available.
Followed the streamed side of BroadcastHashJoinExec and BroadcastNestedLoopJoinExec through partition-preserving unary operators.
Restored the configured lateral-search parallelism only when that streamed path still contains an unrepartitioned GlobalLimit.
Preserved existing streamed-side parallelism for BuildLeft and stopped when a shuffle already exists.
Added no-hint, statistics-based broadcast tests for both build sides with AQE enabled and disabled.
Added a version shim for ClusteredDistribution because Spark 3.2 uses the two-argument constructor.
Validation:
TableValuedFunctionsTest: 22/22 passed.
Spark 3.2 and 3.3 compile with Scala 2.12 and 2.13.
Spark 4.0 compiles with JDK 17.
Verified the Spark 3.2 shaded bytecode calls the two-argument ClusteredDistribution constructor.
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
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.
Purpose
Spark physically executes a
GlobalLimitthrough a single partition. When that limited plan is used as the left input of a lateralvector_search, all query vectors are therefore processed by one task unless users add a repartition manually.This change:
GlobalLimitin the lateral vector-search input;spark.paimon.vector-search.lateral-join.parallelism, defaulting to 16, to control the generated parallelism.A repartition below the limit does not help because the global limit gathers its result into one partition, so the rule adds a repartition above that shape.
Tests
Added plan tests covering:
All four added plan tests pass locally. Apache CI will run the complete Spark test matrix.