[SPARK-59024][SQL] Use the physical plan id as the cached name for anonymous cached tables - #58314
[SPARK-59024][SQL] Use the physical plan id as the cached name for anonymous cached tables#58314pan3793 wants to merge 6 commits into
Conversation
|
Thanks for working on this — the underlying problem is real, and defaulting the config to 1. Part of this can be fixed without a config
So for anonymous caches, Making it lazy val cachedName: String = tableName.map(n => s"In-memory table $n").getOrElse { ... }One thing to confirm if you take this: This would also narrow what the new config has to justify, down to "large anonymous caches that are materialized". 2. Off-by-one between the doc and the behaviorprivate val _nextCachedRDDId = new AtomicLong(0)
def nextCachedRDDId(): Long = _nextCachedRDDId.getAndIncrement
Minor: the closest precedent in this area is private val nextPlanId = new AtomicInteger(0)
private[execution] def newPlanId(): Int = nextPlanId.getAndIncrement()
3. The config should be
|
|
Thanks for the review! 1. Adopted. 2. Kept 3. Made the conf internal, renamed it to 4. Adopted: 5. Added a short comment noting the sequential id is consumed when the lazy val is first forced. 6. Added the |
|
Thanks for the quick turnaround — all six items from the previous round are addressed, and CI is fully green on Two follow-ups, one of which is a correction of something I said last round. 1. Correction: the
|
dongjoon-hyun
left a comment
There was a problem hiding this comment.
+1, LGTM except the above (1).
|
Thanks for the second pass! 1. Correct -- noted in the PR description: the anonymous cached name is now rendered at materialization time, so for adaptive plans it is derived from the final AQE plan; the user-facing section is adjusted accordingly. 2. Added to the config doc: "The name is resolved when the cache is first materialized." 3. Switched to 4. Added |
dongjoon-hyun
left a comment
There was a problem hiding this comment.
Thanks, this addresses everything from the last round, and CI is green on 57578fe.
The diff is now just a lazy val plus a three-line branch -- no companion object, no extra AtomicLong, no id-consumption caveat. The CachedRDD (plan_id=42) format also lines up with Exchange.stringArgs, which renders the same SparkPlan.id as [plan_id=$id].
Four comments, all non-blocking -- feel free to fold them into a follow-up or address them here.
1. plan_id is an overloaded term
Spark uses plan_id for two unrelated things:
SparkPlan.id, rendered as[plan_id=N]byExchangeandEmptyRelationExec-- what this PR uses.LogicalPlan.PLAN_ID_TAG = TreeNodeTag[Long]("plan_id"), the Spark Connect plan id used for column resolution inColumnResolutionHelper-- a completely separate numbering space.
On top of that, the physical [plan_id=N] suffix is only printed for Exchange / EmptyRelationExec, so the root of a cached plan never shows its id in explain() output. The name therefore suggests a cross-reference that a user cannot actually make.
CachedRDD 42 would carry the same information without the ambiguity. Since I am the one who suggested cachedPlan.id, I brought the ambiguity in -- your call whether the explicit label is worth it.
2. Test comment, and a behavior nuance worth knowing
// Caches of the same plan share the plan id.
val r1Again = InMemoryRelation(StorageLevel.MEMORY_ONLY, d.queryExecution, None)This is the same executedPlan instance (both come from d.queryExecution), not merely the same plan shape. Two separately built but structurally identical plans get different ids -- and those used to share a name under the tree-string scheme, since the string was identical.
Display-only, and arguably better (distinct caches, distinct names), but "the same physical plan instance" would describe what the test actually pins.
3. The new comment does not hold for named caches
// Resolved on first access, which happens at cache materialization; for adaptive plans the
// name therefore reflects the final plan.InMemoryTableScanExec#nodeName is an override val and reads cachedName in the case Some(_) => branch, so for a named cache the lazy val is forced at scan-exec construction, well before materialization. Something like "first access (cache materialization for anonymous caches)" would be accurate.
4. Optional: pin the other direction too
ToStringCountingPlan currently asserts only that nothing is rendered:
assert(plan.toStringCount == 0)Forcing the name and asserting toStringCount == 1 would also lock in that the fallback path really does render the tree string, so a future change cannot quietly break either half.
LGTM.
|
Thank you, @pan3793 . Also, please resolve the merge conflicts. |
…d tables Add spark.sql.useSequentialCacheName. When it is true and the cached table has no name, CachedRDDBuilder uses a sequential number like 'CachedRDD 1' as the cached name instead of the abbreviated plan tree string. Rendering the plan tree string can be expensive for large plans. Assisted-by: Qwen3.8 Max
Remove .internal() from spark.sql.useSequentialCacheName, and pin the disabled case in the test with an explicit conf value instead of relying on the default.
- Make cachedName a lazy val so the plan tree string is only rendered when the name is needed - Rename the config to spark.sql.dataframeCache.sequentialName.enabled, keep it internal, and declare ConfigBindingPolicy.NOT_APPLICABLE - Use cachedPlan.conf instead of cachedPlan.session.conf - Follow the SparkPlan.newPlanId naming for the id generator and document the id-consumption side effect - Prefix the test with SPARK-59024 and assert the disabled-case name exactly
…name - Rename the config to spark.sql.dataframeCache.planIdName.enabled and use 'CachedRDD (plan_id=<id>)', dropping the dedicated id generator - Note in the config doc that the name is resolved at first materialization - Rework the tests: same-plan caches share the name, and the tree string is not rendered at construction
- Reword the cachedName comment: named caches force the name at scan construction - Reword the test comment: the same physical plan instance shares the plan id - Also assert the fallback path renders the tree string exactly once
|
Thanks for the third pass! 1. Keeping the explicit 2. Reworded: "Caches of the same physical plan instance share the plan id." 3. Reworded the comment: "Resolved on first access (cache materialization for anonymous caches)." 4. Added the reverse assertion: forcing the name renders the tree string exactly once. Also rebased onto master and resolved the conflict with the SPARK-59009 test. |
What changes were proposed in this pull request?
Add an internal SQL config
spark.sql.dataframeCache.planIdName.enabled(defaultfalse). When it is true and the cached table has no name,CachedRDDBuilderuses the physical plan id, e.g.CachedRDD (plan_id=42), as the cached name instead of the abbreviated plan tree string.cachedNameis also made alazy val, so the name is only computed when it is actually needed:Why are the changes needed?
For anonymous cached tables, the cached name was built from the plan's tree string (
cachedPlan.toString, abbreviated to 1024 chars) atCachedRDDBuilderconstruction time, even for caches that are never materialized. Rendering the plan tree string can be expensive for large plans, and the name is only used for display.This is another spot, besides the SQL event plan description addressed in SPARK-59023, that hurts the same customer job: it constructs a huge plan whose
treeStringexceeds 280,000 lines, and rendering the plan tree string takes minutes per iteration and contributes to driver OOM.Does this PR introduce any user-facing change?
The new config is internal and defaults to
false. One nuance: becausecachedNameis now evaluated lazily, the name of anonymous caches is rendered at materialization time, so for adaptive plans the Storage tab shows a name derived from the final AQE plan instead of the pre-execution plan. No other behavior changes.How was this patch tested?
New unit tests in
InMemoryRelationSuite:SPARK-59024: plan id cached name for anonymous cached tables-- verifies theCachedRDD (plan_id=<id>)format, that caches of the same plan share the name, that distinct plans get distinct names, that named tables keep theIn-memory table <name>name, and that the abbreviated plan tree string is kept when the config is disabledSPARK-59024: anonymous cached name is not rendered before materialization-- verifies the plan tree string is not rendered at cache constructionWas this patch authored or co-authored using generative AI tooling?
Generated-by: Qwen3.8 Max