sparkforensics-server 0.1.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/index.js +41 -50
- package/lib/static-files.js +15 -10
- package/package.json +23 -3
- package/public/assets/AutoscalingChurn-B96FKx97.js +1 -0
- package/public/assets/CacheUtilization-lKua3J1x.js +1 -0
- package/public/assets/CachingOpportunity-U0g-8HPV.js +1 -0
- package/public/assets/ColdStart-w8QYTNKo.js +1 -0
- package/public/assets/ConfigAudit-ClZp5BpV.js +1 -0
- package/public/assets/CoreUsageArea-o3kqQwyi.js +1 -0
- package/public/assets/Dashboard-Bjl-MYMm.js +1 -0
- package/public/assets/DuplicatePlanSubtree-Cm_UU3lY.js +1 -0
- package/public/assets/DurationHistogram-ltF-7M0A.js +1 -0
- package/public/assets/EmptyState-BCA3xioa.js +1 -0
- package/public/assets/ExecutorUtilization-Cn69i0uq.js +1 -0
- package/public/assets/GcPressure-CYsmU1d7.js +1 -0
- package/public/assets/ImpactEstimate-BGt-4WX5.js +1 -0
- package/public/assets/IncompleteRun-s6uQ8OLf.js +1 -0
- package/public/assets/JobFailures-DJ2dDvPA.js +1 -0
- package/public/assets/MemoryUtilization-C9G7P94X.js +1 -0
- package/public/assets/OverBroadcast-DYauoYWQ.js +1 -0
- package/public/assets/PartitionSizing-Cz_B0-uG.js +1 -0
- package/public/assets/PlanExplorer-_2oB5ouy.js +1 -0
- package/public/assets/PlanGraphRoute-Cj-GWM8W.js +1 -0
- package/public/assets/RetryWaste-D_m8ASwP.js +1 -0
- package/public/assets/RowPagination-BLsm8I6i.js +1 -0
- package/public/assets/ShuffleIO-pyDhxdaz.js +1 -0
- package/public/assets/Skew-BGLJuZZR.js +1 -0
- package/public/assets/SlowHost-ChdWVvGO.js +1 -0
- package/public/assets/SmallFiles-gTilBtjW.js +1 -0
- package/public/assets/SpeculationWaste-CVzgJnSj.js +1 -0
- package/public/assets/Spill-NGJGQaTB.js +1 -0
- package/public/assets/StageFailed-sGhki53z.js +1 -0
- package/public/assets/StageFindingGroup-AyI7BMd3.js +1 -0
- package/public/assets/StageHeader-BTzGkK8k.js +1 -0
- package/public/assets/StagePill-BlMOW_pU.js +1 -0
- package/public/assets/StageShape-DfOW93Z1.js +1 -0
- package/public/assets/StageSlowness-BMZ-K_5e.js +1 -0
- package/public/assets/Straggler-dffcAN7t.js +1 -0
- package/public/assets/TaskFailures-DGDwUK2A.js +1 -0
- package/public/assets/TinyTask-CfT8zZDr.js +1 -0
- package/public/assets/UnderBroadcast-CiOiJ0k1.js +1 -0
- package/public/assets/WidgetLeadSummary-BdUUi7L5.js +1 -0
- package/public/assets/charts-vendor-0VMaLVLN.js +59 -0
- package/public/assets/downsample-WmNM1qc9.js +1 -0
- package/public/assets/finding-anchor-DEV8JvgH.js +1 -0
- package/public/assets/impact-sort-DxaUheWV.js +1 -0
- package/public/assets/inbox-_QCYkBsP.js +1 -0
- package/public/assets/index-3vlFVNCg.js +9 -0
- package/public/assets/index-CjVklAdW.css +2 -0
- package/public/assets/parser-worker-B2bc0lWe.js +112 -0
- package/public/assets/plan-graph-vendor-B0sEms9Z.js +7 -0
- package/public/assets/plan-graph-vendor-DLioOiRN.css +1 -0
- package/public/assets/react-vendor-D2TLlSzI.js +9 -0
- package/public/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/public/assets/select-kuy1MiAo.js +1 -0
- package/public/assets/toggle-group-CUd-KWkp.js +1 -0
- package/public/assets/usePagedRows-jNUWgVTD.js +1 -0
- package/public/assets/useRecentFiles-D0OA_EOK.js +11 -0
- package/public/assets/useSortMode-BaeAA5cw.js +1 -0
- package/public/docs/404.html +3 -3
- package/public/docs/assets/{app.Bxy4_HVA.js → app.APoq_UBs.js} +1 -1
- package/public/docs/assets/aqe-loop.IwQSATHw.svg +1 -0
- package/public/docs/assets/aqe-loop.dark.DGbaxqJE.svg +1 -0
- package/public/docs/assets/broadcast-vs-shuffle.Db4WY1XK.svg +1 -0
- package/public/docs/assets/broadcast-vs-shuffle.dark.C7Bxs0mG.svg +1 -0
- package/public/docs/assets/cache-lifecycle.dark.B-hS7AgU.svg +1 -0
- package/public/docs/assets/cache-lifecycle.rEOVYQNU.svg +1 -0
- package/public/docs/assets/chunks/@localSearchIndexroot.CvM5fuxe.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.L5jNOIR3.js → VPLocalSearchBox.B3TPzlHq.js} +1 -1
- package/public/docs/assets/chunks/duplicate-plan-subtree.dark.Cdp70QhV.js +1 -0
- package/public/docs/assets/chunks/{framework.CNDaxUod.js → framework.DSg0KOwT.js} +1 -1
- package/public/docs/assets/chunks/retry-escalation-ladder.dark.DHipdJgZ.js +1 -0
- package/public/docs/assets/chunks/theme.PavXO_vz.js +2 -0
- package/public/docs/assets/cold-start-timeline.DxC_Sc7w.svg +1 -0
- package/public/docs/assets/cold-start-timeline.dark.CZ17YcAG.svg +1 -0
- package/public/docs/assets/columnar-layout.PghGeOEA.svg +1 -0
- package/public/docs/assets/columnar-layout.dark.BVNlz0ff.svg +1 -0
- package/public/docs/assets/container-memory.DIO0AnIm.svg +1 -0
- package/public/docs/assets/container-memory.dark.CP-5zuCl.svg +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CWpj01WU.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CWpj01WU.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CgzUsQ6W.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CgzUsQ6W.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.BtPdlM7r.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.BtPdlM7r.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CzlXOnCq.js → contributor-guide_architecture_impact-estimation.md.CooslVJt.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CzlXOnCq.lean.js → contributor-guide_architecture_impact-estimation.md.CooslVJt.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_index.md.94lNbF-4.js → contributor-guide_architecture_index.md.3TO9ic6w.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_index.md.94lNbF-4.lean.js → contributor-guide_architecture_index.md.3TO9ic6w.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_overview.md.mQFlN66o.js → contributor-guide_architecture_overview.md.CehiRmGn.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_overview.md.mQFlN66o.lean.js → contributor-guide_architecture_overview.md.CehiRmGn.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history.md.C-xxn0q7.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history.md.C-xxn0q7.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.R27gQrgY.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.R27gQrgY.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BaS_L10g.js → contributor-guide_architecture_worker-protocol.md.IbnfNrV3.js} +2 -2
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BaS_L10g.lean.js → contributor-guide_architecture_worker-protocol.md.IbnfNrV3.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_contributing.md.CvRsdr6J.js +1 -0
- package/public/docs/assets/contributor-guide_contributing.md.CvRsdr6J.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_development-setup.md.CpUMVtIK.js → contributor-guide_development-setup.md.DvAN_9mK.js} +1 -1
- package/public/docs/assets/{contributor-guide_development-setup.md.CpUMVtIK.lean.js → contributor-guide_development-setup.md.DvAN_9mK.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_testing.md.C0q-BJfl.js → contributor-guide_testing.md.6rIKqSyY.js} +1 -1
- package/public/docs/assets/{contributor-guide_testing.md.C0q-BJfl.lean.js → contributor-guide_testing.md.6rIKqSyY.lean.js} +1 -1
- package/public/docs/assets/dag-stages.DSz_S937.svg +1 -0
- package/public/docs/assets/dag-stages.dark.F72UzxH4.svg +1 -0
- package/public/docs/assets/driver-executor.D5pQ7YN1.svg +1 -0
- package/public/docs/assets/driver-executor.dark.BmX9cPvh.svg +1 -0
- package/public/docs/assets/duplicate-plan-subtree.B4cvN6fj.svg +1 -0
- package/public/docs/assets/duplicate-plan-subtree.dark.Dw8wS0Ag.svg +1 -0
- package/public/docs/assets/{index.md.CETnDZzR.js → index.md.CHJVslga.js} +1 -1
- package/public/docs/assets/{index.md.CETnDZzR.lean.js → index.md.CHJVslga.lean.js} +1 -1
- package/public/docs/assets/join-strategy.C_FvrCEo.svg +1 -0
- package/public/docs/assets/join-strategy.dark.ChMLnNII.svg +1 -0
- package/public/docs/assets/memory-borrowing.BqQRJg0u.svg +1 -0
- package/public/docs/assets/memory-borrowing.dark.Yhh20O9C.svg +1 -0
- package/public/docs/assets/memory-regions.XHvO7jHG.svg +1 -0
- package/public/docs/assets/memory-regions.dark.D4TP9_08.svg +1 -0
- package/public/docs/assets/repartition-vs-coalesce.BovLRrpj.svg +1 -0
- package/public/docs/assets/repartition-vs-coalesce.dark.BhAczKZQ.svg +1 -0
- package/public/docs/assets/retry-escalation-ladder.DyTKJJmZ.svg +1 -0
- package/public/docs/assets/retry-escalation-ladder.dark.BdsabtU3.svg +1 -0
- package/public/docs/assets/shuffle-map-reduce.KuOEZVmg.svg +1 -0
- package/public/docs/assets/shuffle-map-reduce.dark.BgQZnFSb.svg +1 -0
- package/public/docs/assets/spill-classification.BU2euYDO.svg +1 -0
- package/public/docs/assets/spill-classification.dark.D7i1M40d.svg +1 -0
- package/public/docs/assets/{style.D2mY0HUs.css → style.DSixAiZE.css} +1 -1
- package/public/docs/assets/tuning-reference_anti-patterns.md.Df1YMIHu.js +1 -0
- package/public/docs/assets/tuning-reference_anti-patterns.md.Df1YMIHu.lean.js +1 -0
- package/public/docs/assets/tuning-reference_aqe.md.BIsCtLzm.js +1 -0
- package/public/docs/assets/tuning-reference_aqe.md.BIsCtLzm.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-broadcast-sizing.md.CEstB3Ia.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-broadcast-sizing.md.CEstB3Ia.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-cold-start.md.CEuy-72y.js +7 -0
- package/public/docs/assets/tuning-reference_bottleneck-cold-start.md.CEuy-72y.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-duplicate-plan-subtree.md.CIohQDfn.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-duplicate-plan-subtree.md.CIohQDfn.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-failures.md.4z5BXGJ2.js +6 -0
- package/public/docs/assets/tuning-reference_bottleneck-failures.md.4z5BXGJ2.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-gc.md.DSxzZRK7.js +6 -0
- package/public/docs/assets/tuning-reference_bottleneck-gc.md.DSxzZRK7.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-job-failure-rate.md.BaJl__1W.js +8 -0
- package/public/docs/assets/tuning-reference_bottleneck-job-failure-rate.md.BaJl__1W.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-memory-utilization.md.DbP-SJZc.js +7 -0
- package/public/docs/assets/tuning-reference_bottleneck-memory-utilization.md.DbP-SJZc.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-retry-waste.md.D5JMjVOt.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-retry-waste.md.D5JMjVOt.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-shuffle.md.CM-nTmIH.js +12 -0
- package/public/docs/assets/tuning-reference_bottleneck-shuffle.md.CM-nTmIH.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-skew.md.BdUwiDhn.js +14 -0
- package/public/docs/assets/tuning-reference_bottleneck-skew.md.BdUwiDhn.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-slow-host.md.BlIo6UDW.js +7 -0
- package/public/docs/assets/tuning-reference_bottleneck-slow-host.md.BlIo6UDW.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-small-files.md.B8kloyx8.js +5 -0
- package/public/docs/assets/tuning-reference_bottleneck-small-files.md.B8kloyx8.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-spill.md.PNH7mITt.js +6 -0
- package/public/docs/assets/tuning-reference_bottleneck-spill.md.PNH7mITt.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-straggler.md.DY36fHN5.js +7 -0
- package/public/docs/assets/tuning-reference_bottleneck-straggler.md.DY36fHN5.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-tiny-tasks.md.QTV7O8kU.js +8 -0
- package/public/docs/assets/tuning-reference_bottleneck-tiny-tasks.md.QTV7O8kU.lean.js +1 -0
- package/public/docs/assets/tuning-reference_bottleneck-utilization.md.DTiueZC3.js +5 -0
- package/public/docs/assets/tuning-reference_bottleneck-utilization.md.DTiueZC3.lean.js +1 -0
- package/public/docs/assets/tuning-reference_caching.md.B7aQ8asB.js +1 -0
- package/public/docs/assets/tuning-reference_caching.md.B7aQ8asB.lean.js +1 -0
- package/public/docs/assets/tuning-reference_cluster-config.md.ZVmDGsQ3.js +1 -0
- package/public/docs/assets/tuning-reference_cluster-config.md.ZVmDGsQ3.lean.js +1 -0
- package/public/docs/assets/tuning-reference_config.md.UvveiWG3.js +1 -0
- package/public/docs/assets/tuning-reference_config.md.UvveiWG3.lean.js +1 -0
- package/public/docs/assets/tuning-reference_data-formats.md.bjCAWH3N.js +1 -0
- package/public/docs/assets/tuning-reference_data-formats.md.bjCAWH3N.lean.js +1 -0
- package/public/docs/assets/tuning-reference_index.md.BQ_NooMV.js +1 -0
- package/public/docs/assets/tuning-reference_index.md.BQ_NooMV.lean.js +1 -0
- package/public/docs/assets/tuning-reference_intro.md.CobD-lGB.js +1 -0
- package/public/docs/assets/tuning-reference_intro.md.CobD-lGB.lean.js +1 -0
- package/public/docs/assets/tuning-reference_joins.md.BtKs_CuW.js +1 -0
- package/public/docs/assets/tuning-reference_joins.md.BtKs_CuW.lean.js +1 -0
- package/public/docs/assets/tuning-reference_memory-model.md.DhT-n4y3.js +1 -0
- package/public/docs/assets/tuning-reference_memory-model.md.DhT-n4y3.lean.js +1 -0
- package/public/docs/assets/tuning-reference_metrics.md.mLOh7Apj.js +1 -0
- package/public/docs/assets/tuning-reference_metrics.md.mLOh7Apj.lean.js +1 -0
- package/public/docs/assets/tuning-reference_partitioning.md.q0zKF_8X.js +1 -0
- package/public/docs/assets/tuning-reference_partitioning.md.q0zKF_8X.lean.js +1 -0
- package/public/docs/assets/tuning-reference_pyspark.md.DDCfvN9t.js +6 -0
- package/public/docs/assets/tuning-reference_pyspark.md.DDCfvN9t.lean.js +1 -0
- package/public/docs/assets/tuning-reference_shuffle.md.BZZ7R4Ix.js +1 -0
- package/public/docs/assets/tuning-reference_shuffle.md.BZZ7R4Ix.lean.js +1 -0
- package/public/docs/assets/tuning-reference_spark-architecture.md.Dwzm5avO.js +1 -0
- package/public/docs/assets/tuning-reference_spark-architecture.md.Dwzm5avO.lean.js +1 -0
- package/public/docs/assets/tuning-reference_table-formats.md.D6wj-2dX.js +1 -0
- package/public/docs/assets/tuning-reference_table-formats.md.D6wj-2dX.lean.js +1 -0
- package/public/docs/assets/udf-execution-models.BUFDICuG.svg +1 -0
- package/public/docs/assets/udf-execution-models.dark.YTNS6GDq.svg +1 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval.md.B4tPGIal.js +1 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval.md.B4tPGIal.lean.js +1 -0
- package/public/docs/assets/user-guide_getting-started.md.BJvwLEIM.js +3 -0
- package/public/docs/assets/user-guide_getting-started.md.BJvwLEIM.lean.js +1 -0
- package/public/docs/assets/{user-guide_mcp-tools.md.DwYI1d6C.js → user-guide_mcp-tools.md.Vi3RoflJ.js} +35 -5
- package/public/docs/assets/{user-guide_mcp-tools.md.DwYI1d6C.lean.js → user-guide_mcp-tools.md.Vi3RoflJ.lean.js} +1 -1
- package/public/docs/assets/user-guide_run-comparison.md.CQc1aoU8.js +1 -0
- package/public/docs/assets/user-guide_run-comparison.md.CQc1aoU8.lean.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.DL1UDhvR.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.DL1UDhvR.lean.js +1 -0
- package/public/docs/contributor-guide/architecture/board-widgets.html +7 -7
- package/public/docs/contributor-guide/architecture/detector-contract.html +7 -7
- package/public/docs/contributor-guide/architecture/drill-down.html +7 -7
- package/public/docs/contributor-guide/architecture/impact-estimation.html +7 -7
- package/public/docs/contributor-guide/architecture/index.html +7 -7
- package/public/docs/contributor-guide/architecture/overview.html +7 -7
- package/public/docs/contributor-guide/architecture/state-and-history.html +7 -7
- package/public/docs/contributor-guide/architecture/widget-rendering.html +7 -7
- package/public/docs/contributor-guide/architecture/worker-protocol.html +8 -8
- package/public/docs/contributor-guide/contributing.html +7 -7
- package/public/docs/contributor-guide/development-setup.html +7 -7
- package/public/docs/contributor-guide/testing.html +7 -7
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +7 -7
- package/public/docs/tuning-reference/anti-patterns.html +29 -0
- package/public/docs/tuning-reference/aqe.html +29 -0
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +29 -0
- package/public/docs/tuning-reference/bottleneck-cold-start.html +35 -0
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +30 -0
- package/public/docs/tuning-reference/bottleneck-failures.html +35 -0
- package/public/docs/tuning-reference/bottleneck-gc.html +34 -0
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +37 -0
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +35 -0
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +30 -0
- package/public/docs/tuning-reference/bottleneck-shuffle.html +40 -0
- package/public/docs/tuning-reference/bottleneck-skew.html +42 -0
- package/public/docs/tuning-reference/bottleneck-slow-host.html +35 -0
- package/public/docs/tuning-reference/bottleneck-small-files.html +33 -0
- package/public/docs/tuning-reference/bottleneck-spill.html +34 -0
- package/public/docs/tuning-reference/bottleneck-straggler.html +35 -0
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +36 -0
- package/public/docs/tuning-reference/bottleneck-utilization.html +34 -0
- package/public/docs/tuning-reference/caching.html +29 -0
- package/public/docs/tuning-reference/cluster-config.html +29 -0
- package/public/docs/tuning-reference/config.html +29 -0
- package/public/docs/tuning-reference/data-formats.html +29 -0
- package/public/docs/tuning-reference/index.html +29 -0
- package/public/docs/tuning-reference/intro.html +29 -0
- package/public/docs/tuning-reference/joins.html +29 -0
- package/public/docs/tuning-reference/memory-model.html +29 -0
- package/public/docs/tuning-reference/metrics.html +29 -0
- package/public/docs/tuning-reference/partitioning.html +29 -0
- package/public/docs/tuning-reference/pyspark.html +34 -0
- package/public/docs/tuning-reference/shuffle.html +29 -0
- package/public/docs/tuning-reference/spark-architecture.html +29 -0
- package/public/docs/tuning-reference/table-formats.html +29 -0
- package/public/docs/user-guide/alternative-log-retrieval.html +29 -0
- package/public/docs/user-guide/getting-started.html +8 -8
- package/public/docs/user-guide/mcp-tools.html +41 -11
- package/public/docs/user-guide/run-comparison.html +7 -7
- package/public/docs/user-guide/understanding-findings.html +7 -7
- package/public/index.html +8 -7
- package/public/sample-runs/sample-run.ndjson.gz +0 -0
- package/vendor-core/analyzer.js +74 -74
- package/vendor-core/cli/budgets.js +13 -27
- package/vendor-core/cli/collect-run.js +43 -19
- package/vendor-core/core-count.js +25 -27
- package/vendor-core/core-locality-ratio.js +4 -11
- package/vendor-core/core-time-series.js +6 -12
- package/vendor-core/core-usage-locality.js +3 -4
- package/vendor-core/detectors.js +395 -389
- package/vendor-core/docs-config.js +69 -21
- package/vendor-core/docs-content/chapters/01-intro.md +32 -0
- package/vendor-core/docs-content/chapters/02-spark-architecture.md +76 -0
- package/vendor-core/docs-content/chapters/03-memory-model.md +73 -0
- package/vendor-core/docs-content/chapters/04-partitioning.md +65 -0
- package/vendor-core/docs-content/chapters/05-joins.md +62 -0
- package/vendor-core/docs-content/chapters/06-shuffle.md +59 -0
- package/vendor-core/docs-content/chapters/07-data-formats.md +81 -0
- package/vendor-core/docs-content/chapters/07b-table-formats.md +56 -0
- package/vendor-core/docs-content/chapters/08-caching.md +58 -0
- package/vendor-core/docs-content/chapters/09-pyspark.md +78 -0
- package/vendor-core/docs-content/chapters/10-aqe.md +167 -0
- package/vendor-core/docs-content/chapters/11-cluster-config.md +170 -0
- package/vendor-core/docs-content/chapters/12-anti-patterns.md +171 -0
- package/vendor-core/docs-content/chapters/14-metrics.md +87 -0
- package/vendor-core/docs-content/chapters/15-config.md +93 -0
- package/vendor-core/docs-content/chapters/nav-index.json +370 -0
- package/vendor-core/docs-content/detection/cache.md +7 -0
- package/vendor-core/docs-content/detection/cfg.md +15 -0
- package/vendor-core/docs-content/detection/chrn.md +9 -0
- package/vendor-core/docs-content/detection/cold.md +4 -0
- package/vendor-core/docs-content/detection/cstor.md +4 -0
- package/vendor-core/docs-content/detection/fail.md +5 -0
- package/vendor-core/docs-content/detection/gc.md +4 -0
- package/vendor-core/docs-content/detection/host.md +5 -0
- package/vendor-core/docs-content/detection/incmp.md +6 -0
- package/vendor-core/docs-content/detection/jobs.md +4 -0
- package/vendor-core/docs-content/detection/local.md +6 -0
- package/vendor-core/docs-content/detection/mem.md +10 -0
- package/vendor-core/docs-content/detection/part.md +5 -0
- package/vendor-core/docs-content/detection/plan.md +14 -0
- package/vendor-core/docs-content/detection/retry.md +4 -0
- package/vendor-core/docs-content/detection/sfail.md +5 -0
- package/vendor-core/docs-content/detection/shape.md +5 -0
- package/vendor-core/docs-content/detection/shfl.md +4 -0
- package/vendor-core/docs-content/detection/skew.md +6 -0
- package/vendor-core/docs-content/detection/slow.md +6 -0
- package/vendor-core/docs-content/detection/spec.md +8 -0
- package/vendor-core/docs-content/detection/spill.md +7 -0
- package/vendor-core/docs-content/detection/strag.md +5 -0
- package/vendor-core/docs-content/detection/tiny.md +4 -0
- package/vendor-core/docs-content/detection/util.md +4 -0
- package/vendor-core/docs-content/diagrams/aqe-loop.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/aqe-loop.svg +1 -0
- package/vendor-core/docs-content/diagrams/broadcast-vs-shuffle.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/broadcast-vs-shuffle.svg +1 -0
- package/vendor-core/docs-content/diagrams/cache-lifecycle.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/cache-lifecycle.svg +1 -0
- package/vendor-core/docs-content/diagrams/cold-start-timeline.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/cold-start-timeline.svg +1 -0
- package/vendor-core/docs-content/diagrams/columnar-layout.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/columnar-layout.svg +1 -0
- package/vendor-core/docs-content/diagrams/container-memory.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/container-memory.svg +1 -0
- package/vendor-core/docs-content/diagrams/dag-stages.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/dag-stages.svg +1 -0
- package/vendor-core/docs-content/diagrams/driver-executor.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/driver-executor.svg +1 -0
- package/vendor-core/docs-content/diagrams/duplicate-plan-subtree.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/duplicate-plan-subtree.svg +1 -0
- package/vendor-core/docs-content/diagrams/join-strategy.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/join-strategy.svg +1 -0
- package/vendor-core/docs-content/diagrams/memory-borrowing.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/memory-borrowing.svg +1 -0
- package/vendor-core/docs-content/diagrams/memory-regions.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/memory-regions.svg +1 -0
- package/vendor-core/docs-content/diagrams/repartition-vs-coalesce.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/repartition-vs-coalesce.svg +1 -0
- package/vendor-core/docs-content/diagrams/retry-escalation-ladder.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/retry-escalation-ladder.svg +1 -0
- package/vendor-core/docs-content/diagrams/shuffle-map-reduce.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/shuffle-map-reduce.svg +1 -0
- package/vendor-core/docs-content/diagrams/spill-classification.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/spill-classification.svg +1 -0
- package/vendor-core/docs-content/diagrams/udf-execution-models.dark.svg +1 -0
- package/vendor-core/docs-content/diagrams/udf-execution-models.svg +1 -0
- package/vendor-core/docs-content/tuning/broadcast-sizing.md +78 -0
- package/vendor-core/docs-content/tuning/cold-start.md +81 -0
- package/vendor-core/docs-content/tuning/duplicate-plan-subtree.md +45 -0
- package/vendor-core/docs-content/tuning/failures.md +124 -0
- package/vendor-core/docs-content/tuning/gc.md +110 -0
- package/vendor-core/docs-content/tuning/job-failure-rate.md +101 -0
- package/vendor-core/docs-content/tuning/memory-utilization.md +58 -0
- package/vendor-core/docs-content/tuning/retry-waste.md +90 -0
- package/vendor-core/docs-content/tuning/shuffle.md +154 -0
- package/vendor-core/docs-content/tuning/skew.md +123 -0
- package/vendor-core/docs-content/tuning/slow-host.md +117 -0
- package/vendor-core/docs-content/tuning/small-files.md +99 -0
- package/vendor-core/docs-content/tuning/spill.md +114 -0
- package/vendor-core/docs-content/tuning/straggler.md +103 -0
- package/vendor-core/docs-content/tuning/tiny-tasks.md +94 -0
- package/vendor-core/docs-content/tuning/utilization.md +90 -0
- package/vendor-core/docs-site-config.js +10 -17
- package/vendor-core/efficiency-model.js +7 -13
- package/vendor-core/etl-phases.js +3 -5
- package/vendor-core/event-handlers.js +232 -134
- package/vendor-core/event-schemas.js +48 -114
- package/vendor-core/evidence-availability.js +5 -10
- package/vendor-core/evidence-report.js +73 -123
- package/vendor-core/export-data.js +48 -0
- package/vendor-core/finding-action-label.js +4 -10
- package/vendor-core/finding-filter-predicate.js +3 -7
- package/vendor-core/finding-generic-recommendation.js +112 -0
- package/vendor-core/finding-names.js +51 -0
- package/vendor-core/format-utils.js +112 -38
- package/vendor-core/impact-band.js +18 -24
- package/vendor-core/impact-estimator.js +38 -74
- package/vendor-core/ingest.js +7 -13
- package/vendor-core/job-groups.js +3 -6
- package/vendor-core/list-runs.js +278 -0
- package/vendor-core/load-vendored.js +6 -12
- package/vendor-core/log-header-peek.js +81 -0
- package/vendor-core/lz4-block.js +4 -6
- package/vendor-core/mcp-server-factory.js +38 -8
- package/vendor-core/mcp-tools.js +105 -76
- package/vendor-core/model-assembler.js +8 -16
- package/vendor-core/occupancy.js +5 -9
- package/vendor-core/parser-worker.js +20 -29
- package/vendor-core/plan-dot.js +2 -5
- package/vendor-core/plan-duration-attribution.js +78 -29
- package/vendor-core/plan-graph-model.js +126 -69
- package/vendor-core/plan-node-detail.js +31 -17
- package/vendor-core/plan-summary.js +19 -8
- package/vendor-core/recommendation-rollup.js +35 -39
- package/vendor-core/redact.js +72 -16
- package/vendor-core/rolling-log-reassembly.js +4 -6
- package/vendor-core/run-comparison.js +86 -72
- package/vendor-core/scaling-sim.js +5 -7
- package/vendor-core/session-snapshot.js +1 -1
- package/vendor-core/shs-fetch.js +4 -6
- package/vendor-core/shs-load.js +9 -13
- package/vendor-core/shs-request.js +1 -1
- package/vendor-core/stage-quantiles.js +14 -0
- package/vendor-core/types.js +78 -18
- package/vendor-core/wasted-core-hours.js +7 -12
- package/public/assets/AutoscalingChurn-CP5OjN6v.js +0 -1
- package/public/assets/CacheUtilization-BjyawXL6.js +0 -1
- package/public/assets/CachingOpportunity-DV2ABG2G.js +0 -1
- package/public/assets/ConfigAudit-DMiBhAoG.js +0 -1
- package/public/assets/CoreUsageArea-CGYIgPnt.js +0 -1
- package/public/assets/Dashboard-D1_IrWRs.js +0 -12
- package/public/assets/ExecutorTimeline-DkaAGqQT.js +0 -1
- package/public/assets/Failures-Crngoicu.js +0 -1
- package/public/assets/GcPressure-CFXm4BnA.js +0 -1
- package/public/assets/IncompleteRun-BWEJTb13.js +0 -1
- package/public/assets/JobFailures-BmjxCA-h.js +0 -1
- package/public/assets/MemoryUtilization-Bu2uQJbd.js +0 -1
- package/public/assets/PlanExplorer-DKUwF0uz.js +0 -1
- package/public/assets/PlanGraphRoute-BjPSugIG.js +0 -1
- package/public/assets/ShuffleIO--Rur3neu.js +0 -1
- package/public/assets/Spill-DIMpt8c2.js +0 -1
- package/public/assets/TaskSkew-BDr1DbeI.js +0 -1
- package/public/assets/charts-vendor-NMqKV0H4.js +0 -59
- package/public/assets/index-4id52G-A.js +0 -9
- package/public/assets/index-D9kwVYIO.css +0 -1
- package/public/assets/parser-worker-00O9WEQC.js +0 -65
- package/public/assets/plan-graph-vendor-BnuhLJ6X.css +0 -1
- package/public/assets/plan-graph-vendor-vZQHG2EJ.js +0 -7
- package/public/assets/react-vendor-u4pyvC5Y.js +0 -9
- package/public/assets/useRecentFiles-46iBU9zJ.js +0 -5
- package/public/docs/assets/chunks/@localSearchIndexroot.DclgKtlT.js +0 -1
- package/public/docs/assets/chunks/theme.CoT6y5bf.js +0 -2
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.BtjoE_1k.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.BtjoE_1k.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.DQyCHQNM.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.DQyCHQNM.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.BKfyDQck.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.BKfyDQck.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DaqttKgQ.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DaqttKgQ.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.Bk6Iyj9S.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.Bk6Iyj9S.lean.js +0 -1
- package/public/docs/assets/contributor-guide_contributing.md.C-lKs70I.js +0 -1
- package/public/docs/assets/contributor-guide_contributing.md.C-lKs70I.lean.js +0 -1
- package/public/docs/assets/user-guide_getting-started.md.BJdD7a7_.js +0 -3
- package/public/docs/assets/user-guide_getting-started.md.BJdD7a7_.lean.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.5-oaOfTu.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.5-oaOfTu.lean.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.CK2WZ0Tf.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.CK2WZ0Tf.lean.js +0 -1
- package/public/vendor/spark-doc/anchors.json +0 -205
- package/public/vendor/spark-doc/index.html +0 -4202
- package/public/vendor/spark-doc/landing.html +0 -418
- package/public/vendor/spark-doc/meta.html +0 -970
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg id="gd11" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" viewBox="-8 -17.3125 1232.421875 904.5421875" role="graphics-document document" aria-roledescription="flowchart-v2" style="max-width: 1232.421875px;"><style>#gd11{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;fill:#151b1e;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#gd11 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#gd11 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#gd11 .error-icon{fill:#eef1f6;}#gd11 .error-text{fill:#110e09;stroke:#110e09;}#gd11 .edge-thickness-normal{stroke-width:1px;}#gd11 .edge-thickness-thick{stroke-width:3.5px;}#gd11 .edge-pattern-solid{stroke-dasharray:0;}#gd11 .edge-thickness-invisible{stroke-width:0;fill:none;}#gd11 .edge-pattern-dashed{stroke-dasharray:3;}#gd11 .edge-pattern-dotted{stroke-dasharray:2;}#gd11 .marker{fill:#4f5f60;stroke:#4f5f60;}#gd11 .marker.cross{stroke:#4f5f60;}#gd11 svg{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;}#gd11 p{margin:0;}#gd11 .label{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;color:#151b1e;}#gd11 .cluster-label text{fill:#151b1e;}#gd11 .cluster-label span{color:#151b1e;}#gd11 .cluster-label span p{background-color:transparent;}#gd11 .label text,#gd11 span{fill:#151b1e;color:#151b1e;}#gd11 .node rect,#gd11 .node circle,#gd11 .node ellipse,#gd11 .node polygon,#gd11 .node path{fill:#ffffff;stroke:#9aa8a3;stroke-width:1px;}#gd11 .rough-node .label text,#gd11 .node .label text,#gd11 .image-shape .label,#gd11 .icon-shape .label{text-anchor:middle;}#gd11 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#gd11 .rough-node .label,#gd11 .node .label,#gd11 .image-shape .label,#gd11 .icon-shape .label{text-align:center;}#gd11 .node.clickable{cursor:pointer;}#gd11 .root .anchor path{fill:#4f5f60!important;stroke-width:0;stroke:#4f5f60;}#gd11 .arrowheadPath{fill:rgba(255, 255, 255, 0);}#gd11 .edgePath .path{stroke:#4f5f60;stroke-width:1px;}#gd11 .flowchart-link{stroke:#4f5f60;fill:none;}#gd11 .edgeLabel{background-color:#eef1f6;text-align:center;}#gd11 .edgeLabel p{background-color:#eef1f6;}#gd11 .edgeLabel rect{opacity:0.5;background-color:#eef1f6;fill:#eef1f6;}#gd11 .labelBkg{background-color:rgba(238, 241, 246, 0.5);}#gd11 .cluster rect{fill:#f2f5f9;stroke:#dbe2df;stroke-width:1px;}#gd11 .cluster text{fill:#151b1e;}#gd11 .cluster span{color:#151b1e;}#gd11 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:12px;background:#eef1f6;border:1px solid hsl(217.5, 0%, 84.9019607843%);border-radius:2px;pointer-events:none;z-index:100;}#gd11 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#151b1e;}#gd11 rect.text{fill:none;stroke-width:0;}#gd11 .icon-shape,#gd11 .image-shape{background-color:#eef1f6;text-align:center;}#gd11 .icon-shape p,#gd11 .image-shape p{background-color:#eef1f6;padding:2px;}#gd11 .icon-shape .label rect,#gd11 .image-shape .label rect{opacity:0.5;background-color:#eef1f6;fill:#eef1f6;}#gd11 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#gd11 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#gd11 .node .neo-node{stroke:#9aa8a3;}#gd11 [data-look="neo"].node rect,#gd11 [data-look="neo"].cluster rect,#gd11 [data-look="neo"].node polygon{stroke:url(#gd11-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd11 [data-look="neo"].swimlane.cluster rect{filter:none;}#gd11 [data-look="neo"].node path{stroke:url(#gd11-gradient);stroke-width:1px;}#gd11 [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd11 [data-look="neo"].node .neo-line path{stroke:#9aa8a3;filter:none;}#gd11 [data-look="neo"].node circle{stroke:url(#gd11-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd11 [data-look="neo"].node circle .state-start{fill:#000000;}#gd11 [data-look="neo"].icon-shape .icon{fill:url(#gd11-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd11 [data-look="neo"].icon-shape .icon-neo path{stroke:url(#gd11-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd11 :root{--mermaid-font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;}</style><g><marker id="gd11_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd11_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd11_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></path></marker><marker id="gd11_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></polygon></marker><marker id="gd11_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd11_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd11_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd11_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd11_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd11_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd11_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5"></path></marker><marker id="gd11_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5;stroke-dasharray:1,0"></path></marker><g class="root"><g class="clusters"><g class="cluster " id="gd11-CONTAINER" data-look="classic"><rect x="8" y="8" width="1208.421875" height="116.225"></rect><g class="cluster-label " transform="translate(470.10546875, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Requested</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> container</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> pod</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> memory</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M169.66,99.225L169.66,103.392C169.66,107.558,169.66,115.892,169.66,124.225C169.66,132.558,169.66,140.892,214.197,151.139C258.734,161.386,347.808,173.546,392.345,179.627L436.882,185.707" id="gd11-L_HEAP_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_HEAP_SUM_0" data-points="W3sieCI6MTY5LjY2MDE1NjI1LCJ5Ijo5OS4yMjV9LHsieCI6MTY5LjY2MDE1NjI1LCJ5IjoxMjQuMjI1fSx7IngiOjE2OS42NjAxNTYyNSwieSI6MTQ5LjIyNX0seyJ4Ijo0NDAuODQ1NzAzMTI1LCJ5IjoxODYuMjQ3OTU3MjgzOTE4ODh9XQ==" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M448.961,99.225L448.961,103.392C448.961,107.558,448.961,115.892,448.961,124.225C448.961,132.558,448.961,140.892,458.835,148.979C468.71,157.066,488.459,164.908,498.333,168.828L508.208,172.749" id="gd11-L_OVER_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_OVER_SUM_0" data-points="W3sieCI6NDQ4Ljk2MDkzNzUsInkiOjk5LjIyNX0seyJ4Ijo0NDguOTYwOTM3NSwieSI6MTI0LjIyNX0seyJ4Ijo0NDguOTYwOTM3NSwieSI6MTQ5LjIyNX0seyJ4Ijo1MTEuOTI1NTg3MTU1ODM5OTMsInkiOjE3NC4yMjQ5OTk5OTk5OTk5N31d" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M741.684,99.225L741.684,103.392C741.684,107.558,741.684,115.892,741.684,124.225C741.684,132.558,741.684,140.892,731.809,148.979C721.935,157.066,702.186,164.908,692.311,168.828L682.437,172.749" id="gd11-L_OFF_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_OFF_SUM_0" data-points="W3sieCI6NzQxLjY4MzU5Mzc1LCJ5Ijo5OS4yMjV9LHsieCI6NzQxLjY4MzU5Mzc1LCJ5IjoxMjQuMjI1fSx7IngiOjc0MS42ODM1OTM3NSwieSI6MTQ5LjIyNX0seyJ4Ijo2NzguNzE4OTQ0MDk0MTYwMSwieSI6MTc0LjIyNDk5OTk5OTk5OTk3fV0=" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M1056.594,99.225L1056.594,103.392C1056.594,107.558,1056.594,115.892,1056.594,124.225C1056.594,132.558,1056.594,140.892,1006.123,151.417C955.652,161.942,854.71,174.659,804.239,181.018L753.767,187.376" id="gd11-L_PY_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_PY_SUM_0" data-points="W3sieCI6MTA1Ni41OTM3NSwieSI6OTkuMjI1fSx7IngiOjEwNTYuNTkzNzUsInkiOjEyNC4yMjV9LHsieCI6MTA1Ni41OTM3NSwieSI6MTQ5LjIyNX0seyJ4Ijo3NDkuNzk4ODI4MTI1LCJ5IjoxODcuODc2MDM0MTU1MzM2NTh9XQ==" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M595.322,240.45L595.322,244.617C595.322,248.783,595.322,257.117,595.322,264.783C595.322,272.45,595.322,279.45,595.322,282.95L595.322,286.45" id="gd11-L_SUM_GRANT_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_SUM_GRANT_0" data-points="W3sieCI6NTk1LjMyMjI2NTYyNSwieSI6MjQwLjQ1fSx7IngiOjU5NS4zMjIyNjU2MjUsInkiOjI2NS40NX0seyJ4Ijo1OTUuMzIyMjY1NjI1LCJ5IjoyOTAuNDV9XQ==" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M595.322,374.275L595.322,378.442C595.322,382.608,595.322,390.942,595.322,398.608C595.322,406.275,595.322,413.275,595.322,416.775L595.322,420.275" id="gd11-L_GRANT_CHK_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_GRANT_CHK_0" data-points="W3sieCI6NTk1LjMyMjI2NTYyNSwieSI6Mzc0LjI3NTAwMDAwMDAwMDAzfSx7IngiOjU5NS4zMjIyNjU2MjUsInkiOjM5OS4yNzV9LHsieCI6NTk1LjMyMjI2NTYyNTAwMDEsInkiOjQyNC4yNzQ5OTk5OTk5OTk5fV0=" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M524.286,669.343L509.68,687.235C495.074,705.126,465.862,740.909,451.256,764.186C436.65,787.463,436.65,798.234,436.65,803.619L436.65,809.005" id="gd11-L_CHK_OOM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_CHK_OOM_0" data-points="W3sieCI6NTI0LjI4NTg1MzgzMjYxNTksInkiOjY2OS4zNDMyNzU3MDc2MTYyfSx7IngiOjQzNi42NTAzOTA2MjUsInkiOjc3Ni42OTIxODc1fSx7IngiOjQzNi42NTAzOTA2MjUsInkiOjgxMy4wMDQ2ODc1fV0=" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path><path d="M666.359,669.343L680.965,687.235C695.57,705.126,724.782,740.909,739.388,765.653C753.994,790.396,753.994,804.101,753.994,810.953L753.994,817.805" id="gd11-L_CHK_OK_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_CHK_OK_0" data-points="W3sieCI6NjY2LjM1ODY3NzQxNzM4NDEsInkiOjY2OS4zNDMyNzU3MDc2MTY0fSx7IngiOjc1My45OTQxNDA2MjUsInkiOjc3Ni42OTIxODc1fSx7IngiOjc1My45OTQxNDA2MjUsInkiOjgyMS44MDQ2ODc1fV0=" data-look="classic" marker-end="url(#gd11_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_HEAP_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_OVER_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_OFF_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_PY_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_SUM_GRANT_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_GRANT_CHK_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel" transform="translate(436.650390625, 776.6921875)"><g class="label" data-id="L_CHK_OOM_0" transform="translate(0, -10.4609375)"><g><rect class="background" x="-15.9765625" y="-0.8515625" width="31.953125" height="22.625"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Yes</tspan></tspan></text></g></g></g><g class="edgeLabel" transform="translate(753.994140625, 776.6921875)"><g class="label" data-id="L_CHK_OK_0" transform="translate(0, -10.4609375)"><g><rect class="background" x="-12.87890625" y="-0.8515625" width="25.7578125" height="22.625"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">No</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd11-flowchart-HEAP-0" data-look="classic" transform="translate(169.66015625, 66.1125)"><rect class="basic label-container" x="-126.66015625" y="-33.1125" width="253.3203125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">executorMemory</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> (heap)</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">8192</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-OVER-1" data-look="classic" transform="translate(448.9609375, 66.1125)"><rect class="basic label-container" x="-102.640625" y="-33.1125" width="205.28125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">memoryOverhead</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">819</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-OFF-2" data-look="classic" transform="translate(741.68359375, 66.1125)"><rect class="basic label-container" x="-140.08203125" y="-33.1125" width="280.1640625" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">spark.memory.offHeap.size</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">1024</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-PY-3" data-look="classic" transform="translate(1056.59375, 66.1125)"><rect class="basic label-container" x="-124.828125" y="-33.1125" width="249.65625" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">pyspark.memory</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">512</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> (only</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> when</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> set)</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-SUM-5" data-look="classic" transform="translate(595.322265625, 207.33749999999998)"><rect class="basic label-container" x="-154.4765625" y="-33.1125" width="308.953125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Sum</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> of</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> terms</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> =</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> requested</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> size</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">=</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> 10,547</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-GRANT-13" data-look="classic" transform="translate(595.322265625, 332.3625)"><rect class="basic label-container" x="-141.35546875" y="-41.9125" width="282.7109375" height="83.825"></rect><g class="label" transform="translate(0, -26.9125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Resource</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> manager</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> grants</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> a</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">container</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> of</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> that</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> size</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(YARN</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Kubernetes)</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-CHK-15" data-look="classic" transform="translate(595.322265625, 582.32734375)"><polygon points="158.05234375,0 316.1046875,-158.05234375 158.05234375,-316.1046875 0,-158.05234375" class="label-container" transform="translate(-157.55234375, 158.05234375)"></polygon><g class="label" transform="translate(0, -35.712500000000006)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Actual</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> runtime</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> footprint</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">></tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> container</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> limit?</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(native</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Python</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> use</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> spills</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="3.2em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">past</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> the</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> overhead</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> budget)</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-OOM-17" data-look="classic" transform="translate(436.650390625, 846.1171875)"><rect class="basic label-container" x="-159.76171875" y="-33.1125" width="319.5234375" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">OOMKilled</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> by</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> resource</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> manager</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(YARN</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Kubernetes</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> kubelet)</tspan></tspan></text></g></g></g><g class="node default " id="gd11-flowchart-OK-19" data-look="classic" transform="translate(753.994140625, 846.1171875)"><rect class="basic label-container" x="-107.58203125" y="-24.3125" width="215.1640625" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Runs</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> within</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> budget</tspan></tspan></text></g></g></g></g></g></g><defs><filter id="gd11-drop-shadow" height="130%" width="130%"><feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><defs><filter id="gd11-drop-shadow-small" height="150%" width="150%"><feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><linearGradient id="gd11-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#9aa8a3" stop-opacity="1"></stop><stop offset="100%" stop-color="hsl(214.2857142857, 0%, 86.2745098039%)" stop-opacity="1"></stop></linearGradient></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg id="gd12" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" viewBox="-8 -17.3125 1232.421875 904.5421875" role="graphics-document document" aria-roledescription="flowchart-v2" style="max-width: 1232.421875px;"><style>#gd12{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;fill:#f4f4f5;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#gd12 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#gd12 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#gd12 .error-icon{fill:#1a232f;}#gd12 .error-text{fill:#e5dcd0;stroke:#e5dcd0;}#gd12 .edge-thickness-normal{stroke-width:1px;}#gd12 .edge-thickness-thick{stroke-width:3.5px;}#gd12 .edge-pattern-solid{stroke-dasharray:0;}#gd12 .edge-thickness-invisible{stroke-width:0;fill:none;}#gd12 .edge-pattern-dashed{stroke-dasharray:3;}#gd12 .edge-pattern-dotted{stroke-dasharray:2;}#gd12 .marker{fill:#a9b5b4;stroke:#a9b5b4;}#gd12 .marker.cross{stroke:#a9b5b4;}#gd12 svg{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;}#gd12 p{margin:0;}#gd12 .label{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;color:#f4f4f5;}#gd12 .cluster-label text{fill:#f4f4f5;}#gd12 .cluster-label span{color:#f4f4f5;}#gd12 .cluster-label span p{background-color:transparent;}#gd12 .label text,#gd12 span{fill:#f4f4f5;color:#f4f4f5;}#gd12 .node rect,#gd12 .node circle,#gd12 .node ellipse,#gd12 .node polygon,#gd12 .node path{fill:#17202c;stroke:#3a4a52;stroke-width:1px;}#gd12 .rough-node .label text,#gd12 .node .label text,#gd12 .image-shape .label,#gd12 .icon-shape .label{text-anchor:middle;}#gd12 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#gd12 .rough-node .label,#gd12 .node .label,#gd12 .image-shape .label,#gd12 .icon-shape .label{text-align:center;}#gd12 .node.clickable{cursor:pointer;}#gd12 .root .anchor path{fill:#a9b5b4!important;stroke-width:0;stroke:#a9b5b4;}#gd12 .arrowheadPath{fill:rgba(255, 255, 255, 0);}#gd12 .edgePath .path{stroke:#a9b5b4;stroke-width:1px;}#gd12 .flowchart-link{stroke:#a9b5b4;fill:none;}#gd12 .edgeLabel{background-color:#11171a;text-align:center;}#gd12 .edgeLabel p{background-color:#11171a;}#gd12 .edgeLabel rect{opacity:0.5;background-color:#11171a;fill:#11171a;}#gd12 .labelBkg{background-color:rgba(17, 23, 26, 0.5);}#gd12 .cluster rect{fill:#11171a;stroke:#233035;stroke-width:1px;}#gd12 .cluster text{fill:#f4f4f5;}#gd12 .cluster span{color:#f4f4f5;}#gd12 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:12px;background:#1a232f;border:1px solid hsl(214.2857142857, 0%, 4.3137254902%);border-radius:2px;pointer-events:none;z-index:100;}#gd12 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#f4f4f5;}#gd12 rect.text{fill:none;stroke-width:0;}#gd12 .icon-shape,#gd12 .image-shape{background-color:#11171a;text-align:center;}#gd12 .icon-shape p,#gd12 .image-shape p{background-color:#11171a;padding:2px;}#gd12 .icon-shape .label rect,#gd12 .image-shape .label rect{opacity:0.5;background-color:#11171a;fill:#11171a;}#gd12 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#gd12 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#gd12 .node .neo-node{stroke:#3a4a52;}#gd12 [data-look="neo"].node rect,#gd12 [data-look="neo"].cluster rect,#gd12 [data-look="neo"].node polygon{stroke:url(#gd12-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd12 [data-look="neo"].swimlane.cluster rect{filter:none;}#gd12 [data-look="neo"].node path{stroke:url(#gd12-gradient);stroke-width:1px;}#gd12 [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd12 [data-look="neo"].node .neo-line path{stroke:#3a4a52;filter:none;}#gd12 [data-look="neo"].node circle{stroke:url(#gd12-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd12 [data-look="neo"].node circle .state-start{fill:#000000;}#gd12 [data-look="neo"].icon-shape .icon{fill:url(#gd12-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd12 [data-look="neo"].icon-shape .icon-neo path{stroke:url(#gd12-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd12 :root{--mermaid-font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;}</style><g><marker id="gd12_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd12_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd12_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></path></marker><marker id="gd12_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></polygon></marker><marker id="gd12_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd12_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd12_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd12_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd12_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd12_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd12_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5"></path></marker><marker id="gd12_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5;stroke-dasharray:1,0"></path></marker><g class="root"><g class="clusters"><g class="cluster " id="gd12-CONTAINER" data-look="classic"><rect x="8" y="8" width="1208.421875" height="116.225"></rect><g class="cluster-label " transform="translate(470.10546875, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Requested</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> container</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> pod</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> memory</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M169.66,99.225L169.66,103.392C169.66,107.558,169.66,115.892,169.66,124.225C169.66,132.558,169.66,140.892,214.197,151.139C258.734,161.386,347.808,173.546,392.345,179.627L436.882,185.707" id="gd12-L_HEAP_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_HEAP_SUM_0" data-points="W3sieCI6MTY5LjY2MDE1NjI1LCJ5Ijo5OS4yMjV9LHsieCI6MTY5LjY2MDE1NjI1LCJ5IjoxMjQuMjI1fSx7IngiOjE2OS42NjAxNTYyNSwieSI6MTQ5LjIyNX0seyJ4Ijo0NDAuODQ1NzAzMTI1LCJ5IjoxODYuMjQ3OTU3MjgzOTE4ODh9XQ==" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M448.961,99.225L448.961,103.392C448.961,107.558,448.961,115.892,448.961,124.225C448.961,132.558,448.961,140.892,458.835,148.979C468.71,157.066,488.459,164.908,498.333,168.828L508.208,172.749" id="gd12-L_OVER_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_OVER_SUM_0" data-points="W3sieCI6NDQ4Ljk2MDkzNzUsInkiOjk5LjIyNX0seyJ4Ijo0NDguOTYwOTM3NSwieSI6MTI0LjIyNX0seyJ4Ijo0NDguOTYwOTM3NSwieSI6MTQ5LjIyNX0seyJ4Ijo1MTEuOTI1NTg3MTU1ODM5OTMsInkiOjE3NC4yMjQ5OTk5OTk5OTk5N31d" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M741.684,99.225L741.684,103.392C741.684,107.558,741.684,115.892,741.684,124.225C741.684,132.558,741.684,140.892,731.809,148.979C721.935,157.066,702.186,164.908,692.311,168.828L682.437,172.749" id="gd12-L_OFF_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_OFF_SUM_0" data-points="W3sieCI6NzQxLjY4MzU5Mzc1LCJ5Ijo5OS4yMjV9LHsieCI6NzQxLjY4MzU5Mzc1LCJ5IjoxMjQuMjI1fSx7IngiOjc0MS42ODM1OTM3NSwieSI6MTQ5LjIyNX0seyJ4Ijo2NzguNzE4OTQ0MDk0MTYwMSwieSI6MTc0LjIyNDk5OTk5OTk5OTk3fV0=" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M1056.594,99.225L1056.594,103.392C1056.594,107.558,1056.594,115.892,1056.594,124.225C1056.594,132.558,1056.594,140.892,1006.123,151.417C955.652,161.942,854.71,174.659,804.239,181.018L753.767,187.376" id="gd12-L_PY_SUM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_PY_SUM_0" data-points="W3sieCI6MTA1Ni41OTM3NSwieSI6OTkuMjI1fSx7IngiOjEwNTYuNTkzNzUsInkiOjEyNC4yMjV9LHsieCI6MTA1Ni41OTM3NSwieSI6MTQ5LjIyNX0seyJ4Ijo3NDkuNzk4ODI4MTI1LCJ5IjoxODcuODc2MDM0MTU1MzM2NTh9XQ==" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M595.322,240.45L595.322,244.617C595.322,248.783,595.322,257.117,595.322,264.783C595.322,272.45,595.322,279.45,595.322,282.95L595.322,286.45" id="gd12-L_SUM_GRANT_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_SUM_GRANT_0" data-points="W3sieCI6NTk1LjMyMjI2NTYyNSwieSI6MjQwLjQ1fSx7IngiOjU5NS4zMjIyNjU2MjUsInkiOjI2NS40NX0seyJ4Ijo1OTUuMzIyMjY1NjI1LCJ5IjoyOTAuNDV9XQ==" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M595.322,374.275L595.322,378.442C595.322,382.608,595.322,390.942,595.322,398.608C595.322,406.275,595.322,413.275,595.322,416.775L595.322,420.275" id="gd12-L_GRANT_CHK_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_GRANT_CHK_0" data-points="W3sieCI6NTk1LjMyMjI2NTYyNSwieSI6Mzc0LjI3NTAwMDAwMDAwMDAzfSx7IngiOjU5NS4zMjIyNjU2MjUsInkiOjM5OS4yNzV9LHsieCI6NTk1LjMyMjI2NTYyNTAwMDEsInkiOjQyNC4yNzQ5OTk5OTk5OTk5fV0=" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M524.286,669.343L509.68,687.235C495.074,705.126,465.862,740.909,451.256,764.186C436.65,787.463,436.65,798.234,436.65,803.619L436.65,809.005" id="gd12-L_CHK_OOM_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_CHK_OOM_0" data-points="W3sieCI6NTI0LjI4NTg1MzgzMjYxNTksInkiOjY2OS4zNDMyNzU3MDc2MTYyfSx7IngiOjQzNi42NTAzOTA2MjUsInkiOjc3Ni42OTIxODc1fSx7IngiOjQzNi42NTAzOTA2MjUsInkiOjgxMy4wMDQ2ODc1fV0=" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path><path d="M666.359,669.343L680.965,687.235C695.57,705.126,724.782,740.909,739.388,765.653C753.994,790.396,753.994,804.101,753.994,810.953L753.994,817.805" id="gd12-L_CHK_OK_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_CHK_OK_0" data-points="W3sieCI6NjY2LjM1ODY3NzQxNzM4NDEsInkiOjY2OS4zNDMyNzU3MDc2MTY0fSx7IngiOjc1My45OTQxNDA2MjUsInkiOjc3Ni42OTIxODc1fSx7IngiOjc1My45OTQxNDA2MjUsInkiOjgyMS44MDQ2ODc1fV0=" data-look="classic" marker-end="url(#gd12_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_HEAP_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_OVER_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_OFF_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_PY_SUM_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_SUM_GRANT_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_GRANT_CHK_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel" transform="translate(436.650390625, 776.6921875)"><g class="label" data-id="L_CHK_OOM_0" transform="translate(0, -10.4609375)"><g><rect class="background" x="-15.9765625" y="-0.8515625" width="31.953125" height="22.625"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Yes</tspan></tspan></text></g></g></g><g class="edgeLabel" transform="translate(753.994140625, 776.6921875)"><g class="label" data-id="L_CHK_OK_0" transform="translate(0, -10.4609375)"><g><rect class="background" x="-12.87890625" y="-0.8515625" width="25.7578125" height="22.625"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">No</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd12-flowchart-HEAP-0" data-look="classic" transform="translate(169.66015625, 66.1125)"><rect class="basic label-container" x="-126.66015625" y="-33.1125" width="253.3203125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">executorMemory</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> (heap)</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">8192</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-OVER-1" data-look="classic" transform="translate(448.9609375, 66.1125)"><rect class="basic label-container" x="-102.640625" y="-33.1125" width="205.28125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">memoryOverhead</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">819</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-OFF-2" data-look="classic" transform="translate(741.68359375, 66.1125)"><rect class="basic label-container" x="-140.08203125" y="-33.1125" width="280.1640625" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">spark.memory.offHeap.size</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">1024</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-PY-3" data-look="classic" transform="translate(1056.59375, 66.1125)"><rect class="basic label-container" x="-124.828125" y="-33.1125" width="249.65625" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">pyspark.memory</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">512</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> (only</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> when</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> set)</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-SUM-5" data-look="classic" transform="translate(595.322265625, 207.33749999999998)"><rect class="basic label-container" x="-154.4765625" y="-33.1125" width="308.953125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Sum</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> of</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> terms</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> =</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> requested</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> size</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">=</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> 10,547</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> MB</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-GRANT-13" data-look="classic" transform="translate(595.322265625, 332.3625)"><rect class="basic label-container" x="-141.35546875" y="-41.9125" width="282.7109375" height="83.825"></rect><g class="label" transform="translate(0, -26.9125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Resource</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> manager</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> grants</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> a</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">container</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> of</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> that</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> size</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(YARN</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Kubernetes)</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-CHK-15" data-look="classic" transform="translate(595.322265625, 582.32734375)"><polygon points="158.05234375,0 316.1046875,-158.05234375 158.05234375,-316.1046875 0,-158.05234375" class="label-container" transform="translate(-157.55234375, 158.05234375)"></polygon><g class="label" transform="translate(0, -35.712500000000006)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Actual</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> runtime</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> footprint</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">></tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> container</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> limit?</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(native</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Python</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> use</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> spills</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="3.2em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">past</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> the</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> overhead</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> budget)</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-OOM-17" data-look="classic" transform="translate(436.650390625, 846.1171875)"><rect class="basic label-container" x="-159.76171875" y="-33.1125" width="319.5234375" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">OOMKilled</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> by</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> resource</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> manager</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(YARN</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Kubernetes</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> kubelet)</tspan></tspan></text></g></g></g><g class="node default " id="gd12-flowchart-OK-19" data-look="classic" transform="translate(753.994140625, 846.1171875)"><rect class="basic label-container" x="-107.58203125" y="-24.3125" width="215.1640625" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Runs</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> within</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> budget</tspan></tspan></text></g></g></g></g></g></g><defs><filter id="gd12-drop-shadow" height="130%" width="130%"><feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><defs><filter id="gd12-drop-shadow-small" height="150%" width="150%"><feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><linearGradient id="gd12-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#3a4a52" stop-opacity="1"></stop><stop offset="100%" stop-color="hsl(200, 0%, 0%)" stop-opacity="1"></stop></linearGradient></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as o,o as t,c as d,a5 as c}from"./chunks/framework.DSg0KOwT.js";const u=JSON.parse('{"title":"Board widgets","description":"","frontmatter":{},"headers":[],"relativePath":"contributor-guide/architecture/board-widgets.md","filePath":"contributor-guide/architecture/board-widgets.md"}'),a={name:"contributor-guide/architecture/board-widgets.md"};function i(n,e,r,s,l,h){return t(),d("div",null,[...e[0]||(e[0]=[c('<h1 id="board-widgets" tabindex="-1">Board widgets <a class="header-anchor" href="#board-widgets" aria-label="Permalink to "Board widgets""></a></h1><h2 id="board-widgets-beyond-the-fixed-six" tabindex="-1">Beyond the fixed six <a class="header-anchor" href="#board-widgets-beyond-the-fixed-six" aria-label="Permalink to "Beyond the fixed six {#board-widgets-beyond-the-fixed-six}""></a></h2><p>(Components below live in <code>src/view/widgets/</code>, one file per widget name, e.g. <code>JobFailures.tsx</code>, <code>MemoryUtilization.tsx</code>.)</p><p>Six extra cards render in the Findings tab's active grid alongside the fixed spec §5 six: Incomplete Run, Job Failures, Caching Opportunities, Config Audit, Plan Advisor, and Autoscaling Churn, all <code>region: 'action'</code> in <code>detector-registry.tsx</code>:</p><ul><li><p><strong>Incomplete Run</strong> (tag <code>INCMP</code>, <code>IncompleteRun.tsx</code>): app-level "the capture never finished" caveat (DETECTORS entry <code>incompleteRun</code>). Fires whenever <code>app.startTime</code> was observed but <code>app.endTime</code> was not, i.e. no <code>SparkListenerApplicationEnd</code> in the log: an in-flight job, a rotated-away log, or a capture cut short. Order 5, ahead of every other detector, so its card is first among the Findings tab's <code>action</code>-region active widgets when present. The Findings tab's recommendation rollup (rendered by <code>FixTheseFirst.tsx</code>) excludes <code>incompleteRun</code> from that list outright (see <a href="./widget-rendering.html#widget-rendering-order-fixed-spec-§5">Widget rendering order</a>): it's a pipeline-completeness caveat, not an addressable fix, so it never competes with other findings for a ranked slot there. Self-gates to <code>null</code> (no card in the DOM) once the run completed normally. Unrelated to <code>evidence-availability.ts</code>'s own <code>trustworthy</code> gate (see <a href="./worker-protocol.html#evidence-availability-contract-v1">Evidence-availability contract</a>): that ledger only downgrades <em>absence</em> conclusions for individual evidence categories, never becomes a <code>DETECTORS</code> finding itself, and this card does not read it. No <code>docAnchor</code> is set, the same deviation as Autoscaling Churn below: this is a tool-specific signal with no upstream <code>spark-tuning-reference</code> section.</p></li><li><p><strong>Job Failures</strong> (tag <code>JOBS</code>): app-level job-failure-rate rollup (DETECTORS entry <code>jobFailureRate</code> in <code>packages/core/src/detectors.ts</code>), computed from <code>app.jobs</code> (<code>SparkListenerJobEnd</code> results): ≥10% info, ≥30% warning, ≥50% critical. Complements the per-stage, task-level Failed Tasks card (tag <code>FAIL</code>).</p></li><li><p><strong>Caching Opportunities</strong> (tag <code>CACHE</code>, <code>CachingOpportunity.tsx</code>): app-level SQL relation-reuse detector (DETECTORS entry <code>cachingOpportunity</code>), computed from <code>ctx.sql</code>. It walks each execution's <code>planTree</code> and keys every scan by its stable pre-AQE identity via <code>scanRelationId</code> (<code>plan-summary.ts</code>): <code>parquet:<db.table></code>, <code>delta:<db.table></code>, <code>jdbc:<schema.table></code>. That dedupes relations within one execution (self-joins count once) and flags any relation scanned by <code>>= minExecutions</code> (2) distinct executions. Relation identity comes from the catalog-qualified scan name (nodeName, e.g. <code>Scan parquet spark_catalog.db.t</code>), not the <code>Location:</code> path, since Spark truncates that path at ~100 chars and points it at <code>_delta_log</code> for Delta tables; internal Delta-log metadata scans are dropped. Read bytes come from the scan's <code>size of files read</code> metric (<code>0</code>/unknown for JDBC). Findings carry <code>executionReuse</code> (<code>value</code> = distinct-execution count), <code>relation</code>, <code>format</code>, <code>executionIds</code>, <code>totalReadBytes</code>, and a size-aware <code>recommendation</code> (<code>confidence</code> scaled <code>low</code>/<code>medium</code>/<code>high</code> via <code>cachingReuseConfidence</code> off reuse-execution count, plus <code>validationRequired</code>). Renders nothing when clean (no card in the DOM). One row per relation: name + format badge, reuse count, <code>Data read</code> (<code>formatBytes</code>, em-dash when unknown), recommendation, sorted by <code>totalReadBytes</code> then reuse count descending. Rows reveal 6 initially, then up to 30 more per click. Pure-RDD-API apps (no SQL executions) produce no finding: a deliberate trade-off replacing the former RDD-lineage heuristic, which surfaced only internal query-engine RDDs on DataFrame/SQL workloads.</p><p>It also detects composite reuse. When the same join/union subtree (not just a leaf scan) recurs across <code>>= minExecutions</code> distinct SQL executions, the detector emits one <code>variant:'composite'</code> finding recommending caching the derived join/union result instead of two independent leaf-relation rows, and suppresses the leaf findings for relations fully covered by it. A relation reused beyond the composite's executions keeps a residual leaf finding for just the uncovered executions. Composite identity is structural: an anchor plan-shape fingerprint (<code>findCompositeCandidates</code>/<code>computePlanShapes</code>'s <code>opts.includeDetail</code> path, <code>detectors.ts</code>) folds the join/union node's own normalized <code>detail</code> (join type, columns, literals, with expr ids, <code>plan_id=</code>, codegen-stage numbers, and AQE's BuildLeft/BuildRight stripped, and commutative equality operands canonicalized) plus its children's detail-free shapes. Descendant nodes never contribute detail text, only structural shape, so a shared join reused across differently-filtered pre-join scans still matches (a deliberate trade-off) while a different join <em>condition</em> on the same tables does not collide. Nested composites (an inner join reused both standalone and inside an outer join) dedupe one level at a time: an inner composite fully covered by a qualifying outer composite's execution set is suppressed entirely, and a superset recomputes a residual finding over just its extra executions. Reuse via pure projection/aggregation with no join/union underneath stays leaf-level, not modeled as composite. <code>variant:'composite'</code> findings carry <code>operator</code> (<code>'join'</code>|<code>'union'</code>), <code>relations</code> (leaf relations under the composite, for display), and <code>format:'derived'</code> (a sentinel: composites have no scan storage format). They render a <code>JOIN</code>/<code>UNION</code> operator badge (styled distinct from the format badges) plus a confidence marker (<code>confidence</code> scaled via <code>cachingReuseConfidence</code>, <code>validationRequired</code>) in <code>CachingOpportunity.tsx</code>.</p></li><li><p><strong>Autoscaling Churn</strong> (tag <code>CHRN</code>, <code>AutoscalingChurn.tsx</code>): app-level short-lived-executor detector (DETECTORS entry <code>autoscalingChurn</code> in <code>packages/core/src/detectors.ts</code>; reuses the same <code>executorsAdded</code>/<code>executorsRemoved</code> matching logic as <code>utilization</code>). For each added executor, finds its matching removal event (falling back to <code>app.endTime</code> for an executor still alive when the log ends) and flags it short-lived if its lifetime is under 2 minutes (<code>thresholds.shortLivedMs</code>). Warns above 30% short-lived, escalates to critical above 60% (<code>confidence</code> scales <code>low</code>/<code>medium</code>/<code>high</code> via <code>autoscalingChurnConfidence</code>, off how far the short-lived share sits past <code>warningPct</code>/<code>criticalPct</code>: these thresholds are still an unvalidated design-spike estimate, not yet checked against real autoscaling-heavy logs). Returns no finding below 5 total executors (noise floor) or when <code>app.endTime</code> is missing (truncated/still-running log). The widget itself is unchanged apart from a finding-driven verdict banner (impact dot + <code>CHRN</code> tag + recommendation) above its existing add/remove chart. The chart's muted scale-down bar coloring is untouched. No <code>docAnchor</code> is set yet: the upstream <code>shuffle-works/spark-tuning-reference</code> docs repo has no <code>bottleneck-autoscaling-churn</code> section (verified 2026-08-19), so the <code>CHRN</code> tag currently renders with no docs-panel link (a tracked follow-up).</p></li><li><p><strong>Config Audit</strong> (tag <code>CFG</code>): static Spark-config sanity findings derived from <code>app.config</code>/<code>app.resources</code> (parsed from <code>SparkListenerEnvironmentUpdate</code>): dynamic-allocation vs. shuffle-service mismatch, inverted/missing autoscaling bounds, non-Kryo serializer, low executor <code>memoryOverhead</code>. Computed outside the runtime bottleneck catalog: its findings live in the separate <code>configFindings</code> stream, not <code>catalog</code>. But <code>FixTheseFirst</code>/<code>Alerts</code> both merge <code>catalog</code> and <code>configFindings</code> (neither reads <code>region</code> any more), so a Config Audit finding is still eligible for a row in the Findings tab's recommendation rollup and still gets its own card in the active grid, alongside genuine bottleneck-catalog findings.</p></li><li><p><strong>Plan Advisor</strong> (tag <code>PLAN</code>): SQL-plan-level findings computed from <code>appModel.sql</code>'s resolved <code>planTree</code> (DETECTORS entries <code>duplicatePlanSubtree</code>, <code>smallFiles</code>, <code>broadcastSizing</code> in <code>packages/core/src/detectors.ts</code>): repeated plan subtrees (≥3 nodes, ≥2 occurrences, critical if the repeated root is an <code>Exchange</code>), small-files read/write (>100 files averaging ❤️ MiB), and broadcast-join sizing in both directions (missed- broadcast info finding, over-broadcast warning at >1 GB). Each of the four emitted types now renders as its own card (<code>DuplicatePlanSubtree.tsx</code>, <code>SmallFiles.tsx</code>, <code>UnderBroadcast.tsx</code>, <code>OverBroadcast.tsx</code>; the 2026-09 widget/finding-type 1:1 mapping redesign split what used to be one shared <code>PlanFindings.tsx</code> card): all four still share the <code>PLAN</code> tag and the same <code>--plan-aggregate</code> badge tint (<code>src/view/plan-finding-shared.ts</code>). Their <code>docAnchor</code>s (<code>#bottleneck-duplicate-plan-subtree</code>, <code>#bottleneck-small-files</code>, <code>#bottleneck-broadcast-sizing</code>) are not yet live in the vendored docs site, so <code>npm run update-docs</code> will abort until upstream <code>spark-tuning-reference</code> adds them.</p></li></ul><p><code>detector-registry.tsx</code>'s <code>REGISTRY</code> still carries <code>region: 'reference'</code> on four entries, each its own component now: <code>memoryUtilization</code> (<code>MemoryUtilization</code>), <code>utilization</code> (<code>ExecutorUtilization</code>), <code>cacheUtilization</code> (<code>CacheUtilization</code>), and <code>coreLocality</code> (<code>CoreUsageArea</code>). <code>broadcastSizing</code> is gone from <code>REGISTRY</code> entirely (the 2026-09 widget/finding-type 1:1 mapping redesign dropped it): it never backed a real <code>Finding</code> (the <code>broadcastSizing</code> <code>DETECTORS</code> entry only ever emits <code>underBroadcast</code>/<code>overBroadcast</code>, both <code>region: 'action'</code>, each now its own Plan Advisor card), so there is no key left for it to occupy or a clean-check line for it to render. Autoscaling Churn, the other executor-provisioning-lifecycle detector alongside <code>utilization</code>/<code>memoryUtilization</code>, was deliberately given <code>action</code> rather than <code>reference</code> (see "Beyond the fixed six" above).</p><p><code>region</code> decides one thing (see <a href="./widget-rendering.html#widget-rendering-order-fixed-spec-§5">Widget rendering order</a>): whether a widget always mounts. <code>isAlwaysMountedType()</code> flags exactly one of the four <code>reference</code>-region types: Core Usage by Locality. That one mounts unconditionally from <code>appModel</code> in its own small grid inside the Findings tab, regardless of finding state. Memory Utilization, Executor Utilization, and Cache Storage are <code>reference</code> too, but all three are excluded by product decision, not a component-sharing constraint (<code>ALWAYS_MOUNTED_EXCEPTIONS</code> in <code>src/view/detector-registry.tsx</code> carries <code>cacheUtilization</code>, <code>memoryUtilization</code>, and <code>utilization</code>): a clean run on any of them isn't evidence worth surfacing unconditionally, so each collapses to an ordinary <code>CleanCheckRow</code> like any other action-region type on a clean run. Every other <code>REGISTRY</code> widget still renders unconditionally as either an active card or a clean-check line, and <code>region</code> still sets <code>orderedWidgets()</code>'s sort order within the active grid (<code>action</code> components first, <code>reference</code> ones after). The Full app report tab is structural-only and reads no <code>REGISTRY</code> entry. So Core Usage by Locality, the one widget still tagged <code>region: 'reference'</code> and exempt from <code>ALWAYS_MOUNTED_EXCEPTIONS</code>, renders in the Findings tab's always-visible grid rather than inside a separate Reference region (Memory Utilization, Executor Utilization, and Cache Storage all render through the ordinary active/clean paths instead):</p><ul><li><strong>Memory Utilization</strong> (tag <code>MEM</code>): app-level card combining three sub-findings from the <code>memoryUtilization</code> DETECTORS entry (<code>packages/core/src/detectors.ts</code>): idle-cores rate (busy-core-time from the worker's run-aggregates sweep vs. peak-cores × wall-clock), per-executor memory bands (peak heap vs. allocated, gated on <code>spark.eventLog.logStageExecutorMetrics</code>: a distinct <code>dataUnavailable</code> finding renders when that config was off), and an unverified memory-waste model (<code>confidence</code> scales <code>low</code>/<code>medium</code>/<code>high</code> via <code>memoryWasteConfidence</code>, off how far the wasted/used ratio sits past the 1.5× buffer). The driver-memory half of the original spec is dropped: the worker only extracts <em>allocated</em> <code>spark.driver.memory</code>, never a driver actual-usage metric, so there is nothing to band against. The separate <code>utilization</code> DETECTORS entry (an <code>avgUtilization</code> info finding: active-executor-time fraction below 60%) has its own card, <strong>Executor Utilization</strong> (tag <code>UTIL</code>, <code>ExecutorUtilization.tsx</code>): a <code>reference</code>-region widget in its own right that renders only with an active finding, split out of this same combined widget in the 2026-09 widget/finding-type 1:1 mapping redesign (it used to render inline here; the former <code>ExecutorTimeline.tsx</code> never owned this type, despite the tag's letters).</li><li><strong>Cache Storage</strong> (tag <code>CSTOR</code>): app-level card driven by the <code>cacheUtilization</code> DETECTORS entry (<code>packages/core/src/detectors.ts</code>), evaluating two per-RDD proxies over <code>ctx.app.rddInfo</code> storage snapshots since Spark event logs carry no runtime block-access/read-count data: partial caching (<code>numCachedPartitions / numPartitions < 0.90</code>, <code>< 0.50</code> for the warning tier) and disk spillover for <code>MEMORY_AND_DISK*</code> RDDs (<code>diskSize / (memorySize + diskSize) > 0.15</code>, <code>> 0.40</code> for the warning tier; <code>DISK_ONLY</code> RDDs are never flagged). <code>confidence</code> scales <code>low</code>/<code>medium</code>/<code>high</code> via <code>cacheSampleConfidence(rdd.numPartitions)</code>, because the ratio is a point-in-time storage snapshot from stage-submission events, not a runtime read-count, and more partitions average that snapshot noise into a more stable ratio. The existing RDD table (ported from the legacy <code>src/widgets/cache-utilization.js</code> canvas widget) still renders unconditionally; flagged rows get an inline <code>CSTOR</code> tag next to the RDD name, and every flagged RDD's recommendation renders below the table, worst-first.</li><li><strong>Core Usage by Locality</strong> (tag <code>LOCAL</code>, <code>CoreUsageArea.tsx</code>): app-level non-local-task-ratio threshold (DETECTORS entry <code>coreLocality</code>; the other half of a wasted-cores-ratio check, the idle-core half already covered by Memory Utilization's <code>idleCores</code> variant above). Sums <code>RACK_LOCAL</code> + <code>ANY</code> task counts against total task count across every stage's <code>stage.localityStats</code> (pure reducer <code>computeCoreLocalityRatio</code>, <code>packages/core/src/core-locality-ratio.ts</code>). <code>NO_PREF</code> stays in the denominator only, since it's what shuffle-read stages legitimately report with no locality problem. Below 50 total tasks or below a 15% non-local ratio: no finding; 15%-35%: warning; >= 35%: critical, still unvalidated design-spike thresholds (<code>confidence</code> scales <code>low</code>/<code>medium</code>/<code>high</code> via <code>coreLocalityConfidence</code>, off whichever is weaker of the non-local ratio and the sampled task count, the same evidence-strength convention as the memory-waste model above). The widget's always-rendered stacked-area chart (<code>packages/core/src/core-usage-locality.ts</code>) is unaffected. The finding only adds a threshold/impact-band section above it, a per-stage non-local breakdown below it (shown whenever any non-local tasks exist at all, independent of whether the aggregate crossed threshold), and a one-line cross-reference to Memory Utilization when its <code>idleCores</code> finding also fired (idle-core ratio itself is not duplicated here).</li></ul><p>Documented ALL-CAPS tag vocabulary: <code>SKEW</code>, <code>SHFL</code>, <code>SPILL</code>, <code>GC</code>, <code>COLD</code>, <code>UTIL</code>, <code>MEM</code> (Memory Utilization), <code>CSTOR</code> (Cache Storage), <code>LOCAL</code> (Core Usage by Locality), plus <code>FAIL</code> (Failed Tasks), <code>JOBS</code> (Job Failures), <code>CFG</code> (Config Audit), <code>PLAN</code> (Plan Advisor), <code>SFAIL</code> (stage failed outright), <code>PART</code> (partition sizing), <code>SLOW</code> (stage overall slowness), <code>SHAPE</code> (stage shape smells), <code>CACHE</code> (caching opportunity) and <code>CHRN</code> (autoscaling churn).</p><p>ETL Phase Attribution (<code>packages/core/src/etl-phases.ts</code> + <code>EtlPhases.tsx</code>), What-If Executor Scaling (<code>packages/core/src/scaling-sim.ts</code> + <code>ScalingSim.tsx</code>, design spike: makespan predictions are unvalidated and carry a Model Error indicator), and Compute Efficiency (<code>packages/core/src/efficiency-model.ts</code> + <code>EfficiencyModel.tsx</code>, design spike: driver-vs-executor waste split, two theoretical floors, and the §6 right-sizing copy) are main-thread report modules, not <code>DETECTORS</code> entries: descriptive lenses with no impact-band threshold, rendered unconditionally into the Full app report tab rather than participating in the bottleneck catalog. <code>packages/core/src/job-groups.ts</code> (<code>checkConcurrentJobGroups</code>) is likewise a report helper: it flags when concurrent SQL-execution job groups make wall-clock-based estimates unreliable, and both the scaling simulator and the efficiency model gate their precision on it, showing a caveat banner rather than suppressing the estimate.</p><h2 id="confidence-metadata" tabindex="-1">Confidence metadata <a class="header-anchor" href="#confidence-metadata" aria-label="Permalink to "Confidence metadata""></a></h2><p>Best-effort (non-deterministic) findings carry a <code>confidence</code> + <code>validationRequired</code> marker, rendered inline by each consuming widget (e.g. <code>Spill.tsx</code>, <code>DuplicatePlanSubtree.tsx</code>, <code>SmallFiles.tsx</code>, <code>UnderBroadcast.tsx</code>, <code>OverBroadcast.tsx</code>, <code>EfficiencyModel.tsx</code>) when <code>confidence</code> is present and not <code>'high'</code>. There is no shared helper: each widget renders its own muted "N confidence: verify" text, with the <code>title</code> attribute carrying <code>validationRequired</code> as a tooltip. Spill classification is <code>medium</code> when classified (skew/volume) and <code>low</code> when unclassified; plan-summary warnings are <code>low</code>. Deterministic detectors stay unmarked, treated as high confidence.</p><h2 id="visual-system" tabindex="-1">Visual system <a class="header-anchor" href="#visual-system" aria-label="Permalink to "Visual system""></a></h2><p>Tailwind CSS v4 + shadcn/ui (Base UI primitives). The design tokens (colors, including the telemetry-console dark palette) are defined as CSS variables in an <code>@theme inline</code> block and consumed via Tailwind utility classes; there is no more hand-authored BEM CSS. Theme polarity is unchanged from the legacy app: dark = bare <code>:root</code> (no attribute), light = <code>:root[data-theme="light"]</code>, toggled by <code>src/theme/ThemeProvider.tsx</code> and persisted to <code>localStorage</code>. <code>index.html</code>'s inline FOUC-prevention script still runs before React mounts. Mono is still the typeface for numerics (<code>--font-mono</code>), applied via a <code>.num</code>-equivalent Tailwind utility per call site rather than one global class. Charts are Recharts (<code>src/view/charts/ChartTheme.tsx</code>'s <code>CHART_COLORS</code>, read from the same CSS tokens) instead of Chart.js. React re-renders on theme toggle, so chart colors update live with the theme, unlike the old Chart.js canvases, which were built once per parse. <code>ChartFrame</code> can also expose the underlying rows through a toggleable accessible table and copy them to the clipboard as TSV.</p><h2 id="templating-xss-safe" tabindex="-1">Templating (XSS-safe) <a class="header-anchor" href="#templating-xss-safe" aria-label="Permalink to "Templating (XSS-safe)""></a></h2><p>JSX auto-escapes every interpolated value by default. The hand-rolled auto-escaping <code> html</code> `` tagged template (<code>src/widgets/utils.js</code>) and its <code>no-raw-innerhtml</code> guard test are gone, and no <code>.innerHTML</code> assignment is left anywhere in the view layer.</p><p>One Base UI-specific gotcha carried no equivalent in the legacy app: <code>WidgetCard.tsx</code> passes <code>aria-expanded={String(open) as 'true' | 'false'}</code> rather than the raw boolean, because Base UI's <code>Collapsible.Trigger</code> otherwise overrides a boolean <code>aria-expanded</code> prop with its own internal state via prop merging. The explicit <code>String()</code> cast keeps the rendered attribute in sync with this app's own <code>open</code> state.</p>',17)])])}const p=o(a,[["render",i]]);export{u as __pageData,p as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as o,o as t,c as d,a5 as c}from"./chunks/framework.DSg0KOwT.js";const u=JSON.parse('{"title":"Board widgets","description":"","frontmatter":{},"headers":[],"relativePath":"contributor-guide/architecture/board-widgets.md","filePath":"contributor-guide/architecture/board-widgets.md"}'),a={name:"contributor-guide/architecture/board-widgets.md"};function i(n,e,r,s,l,h){return t(),d("div",null,[...e[0]||(e[0]=[c("",17)])])}const p=o(a,[["render",i]]);export{u as __pageData,p as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as t,o,c as d,a5 as a}from"./chunks/framework.DSg0KOwT.js";const p=JSON.parse('{"title":"Detector contract","description":"","frontmatter":{},"headers":[],"relativePath":"contributor-guide/architecture/detector-contract.md","filePath":"contributor-guide/architecture/detector-contract.md"}'),c={name:"contributor-guide/architecture/detector-contract.md"};function s(i,e,r,n,l,h){return o(),d("div",null,[...e[0]||(e[0]=[a('<h1 id="detector-contract" tabindex="-1">Detector contract <a class="header-anchor" href="#detector-contract" aria-label="Permalink to "Detector contract""></a></h1><p><code>packages/core/src/detectors.ts</code> is the single source of Spark-optimization logic: one declarative <code>DETECTORS</code> entry per pattern, each carrying <code>type</code>, <code>scope</code> (<code>stage</code> / <code>app</code> / <code>config</code> / <code>sql</code>), <code>order</code>, <code>fixEffort</code>, a <code>thresholds</code> object, impactBand/copy, a <code>docAnchor</code>, and a co-located <code>detect()</code> method. Both consumers are thin loops over that array:</p><ul><li><p><code>packages/core/src/analyzer.ts</code>: <code>analyze()</code> runs every entry regardless of scope, skipping only <code>inScorecard:false</code> ones; <code>auditConfig()</code> separately runs the <code>scope:'config'</code> entries. The four <code>configAudit</code> entries stay out of the bottleneck catalog because each sets <code>inScorecard:false</code>, not because of <code>scope:'config'</code>: a future config-scope detector without that flag would run through <code>analyze()</code> too. Each finding is stamped with its entry's <code>docAnchor</code>.</p></li><li><p><code>src/view/detector-registry.tsx</code>: a <code>REGISTRY: Record<findingType, {component, region}></code> replaces <code>dashboard-renderer.js</code>'s <code>render:</code> bindings, one entry per emitted finding type. <code>orderedWidgets()</code> walks <code>DETECTORS</code> ascending by <code>order</code>, then sorts <code>action</code>-region components before <code>reference</code>-region ones. Every <code>finding.type</code> maps to its own component now (the 2026-09 widget/finding-type 1:1 mapping redesign split six components that used to multiplex several types each: <code>TaskSkew</code> into <code>Skew</code>/<code>StageShape</code>/<code>TinyTask</code>; <code>ShuffleIO</code> narrowed to <code>shuffle</code> only, plus a new <code>PartitionSizing</code>; <code>Failures</code> into <code>StageFailed</code>/ <code>TaskFailures</code>/<code>RetryWaste</code>; <code>ExecutorTimeline</code> into <code>SlowHost</code>/ <code>StageSlowness</code>/<code>Straggler</code>/<code>SpeculationWaste</code>/<code>ColdStart</code> (its non-finding-driven executor-count chart moved to <code>ExecutorCountChart</code>, a <code>ReferenceSection</code> tile, not a <code>REGISTRY</code> entry); <code>MemoryUtilization</code> narrowed to <code>memoryUtilization</code> only, plus a new <code>ExecutorUtilization</code> for <code>utilization</code>; <code>PlanFindings</code> into <code>DuplicatePlanSubtree</code>/<code>SmallFiles</code>/ <code>UnderBroadcast</code>/<code>OverBroadcast</code>, dropping the dead <code>broadcastSizing</code> key entirely). No two <code>REGISTRY</code> entries share a <code>component</code> value any more.</p><p>Each widget component receives the full catalog and self-gates when it has nothing to show, rendering <code>null</code> or a muted "no issue" card for the always-visible ones. <code>orderedWidgets()</code> itself has no empty/non-empty branching, since it iterates the static <code>DETECTORS</code> import, not the runtime <code>catalog</code>.</p></li></ul><p>Thresholds live only in each entry's <code>thresholds</code>; see <a href="#bottleneck-thresholds-spec-§4">Bottleneck thresholds</a>.</p><h2 id="confidence-disclosure" tabindex="-1">Confidence disclosure <a class="header-anchor" href="#confidence-disclosure" aria-label="Permalink to "Confidence disclosure""></a></h2><p>A <code>Detector</code> entry (or the <code>Finding</code> it returns) may carry <code>confidence: 'low' | 'medium' | 'high'</code> plus a <code>validationRequired</code> string. <code>RowStatusCluster</code> (<code>src/view/RowStatusCluster.tsx</code>) is the one place that renders it, gated to Advanced density: a plain "<confidence> confidence" badge whose tooltip carries the full <code>validationRequired</code> text. A finding with no <code>confidence</code> field renders identically to a fully-validated one, so every detector whose thresholds are our own unvalidated noise floor (marked <code>NOT SOURCED</code> in a code comment) should set both fields, not just the ones that happen to already have <code>RowStatusCluster</code> wired into their widget. <code>skew</code>, <code>straggler</code>, and <code>gc</code> set <code>confidence</code> for exactly this reason: their runtime-floor thresholds carry the same kind of unvalidated-noise-floor caveat <code>coreLocality</code>, <code>autoscalingChurn</code>, and <code>memoryUtilization</code>'s <code>wasteModel</code> variant already disclose. None of these hardcode a single confidence value: each scales <code>'low' | 'medium' | 'high'</code> off how far the finding sits past its own detector's threshold, via a small named helper placed just above the <code>DETECTORS</code> array (e.g. <code>skewConfidence</code>, <code>coreLocalityConfidence</code>, <code>cachingReuseConfidence</code>) rather than an inline literal.</p><h2 id="the-fixeffort-field" tabindex="-1">The <code>fixEffort</code> field <a class="header-anchor" href="#the-fixeffort-field" aria-label="Permalink to "The `fixEffort` field""></a></h2><p>Each <code>Detector</code> entry also carries <code>fixEffort: 'config' | 'code' | 'rearchitect'</code>, alongside <code>order</code> and <code>thresholds</code>: a rough estimate of how much work resolving the finding takes.</p><p>No view currently reads it. The quadrant impact/effort bucketing this field was meant to feed (<code>bucketFinding</code>/<code>effortTier</code>/<code>computeImpactMagnitude</code> in a since-deleted <code>src/quadrant-bucket.ts</code>, gated behind a <code>FIX_EFFORT_MAPPING_REVIEWED</code> flag that never flipped to <code>true</code>) was removed as dead code in the recommendations-consolidation redesign: <code>FixTheseFirst</code> (<code>src/view/widgets/FixTheseFirst.tsx</code>) ranks purely by impact magnitude. See <a href="./widget-rendering.html#widget-rendering-order-fixed-spec-§5">Widget rendering order</a> for how it ranks findings today.</p><p>Two shared helpers back multiple detectors and reports. <code>packages/core/src/plan-tree-walk.ts</code>'s <code>walkPlanTree(root, visit, {dedupe})</code> is the iterative pre-order plan-tree traversal used by <code>detectors.ts</code> and every <code>plan-*.ts</code> module (<code>plan-summary.ts</code>, <code>plan-duration-attribution.ts</code>, <code>plan-node-detail.ts</code>, <code>plan-dot.ts</code>). <code>packages/core/src/core-count.ts</code>'s <code>computeTotalCores(app, executorsAdded)</code> is the shared core-count logic used by <code>efficiency-model.ts</code>, <code>scaling-sim.ts</code>, and <code>wasted-core-hours.ts</code>. <code>detectors.ts</code>'s own <code>utilization</code> and <code>memoryUtilization</code> entries use the same file's <code>computePeakConcurrentCores</code>/<code>computePeakConcurrentExecutorCount</code> instead: <code>computeTotalCores</code> sums every <code>ExecutorAdded</code> event with no regard for overlap, so under executor churn (spot preemption, <code>dynamicAllocation</code> replacement) it double-counts a churned executor's capacity against its replacement's; the peak-concurrent sweeps don't.</p><h2 id="cross-detector-suppression" tabindex="-1">Cross-detector suppression <a class="header-anchor" href="#cross-detector-suppression" aria-label="Permalink to "Cross-detector suppression""></a></h2><p>An entry may declare an optional <code>suppressWhen(finding, out)</code> method. <code>analyzer.ts</code>'s <code>push()</code>, the single choke point every finding passes through, calls it per-finding, after the null guard and before the push, and drops the finding silently when it returns <code>true</code>. <code>out</code> is the findings accumulated so far. Since <code>analyze()</code>'s loop is detector-outer / stage-inner, every finding from a detector declared earlier in <code>DETECTORS</code> is already in <code>out</code> by the time a later detector runs, for every stage. That makes the pattern purely declaration-order-driven: the suppressing detector must be declared earlier in the <code>DETECTORS</code> array than the suppressed one.</p><p><code>stageSlowness</code> uses this to defer to <code>slowHost</code>. It is spliced immediately after the <code>slowHost</code> entry regardless of its <code>order</code> field (<code>order</code> only controls render sequencing, not evaluation order), and <code>tests/analyzer.test.js</code>'s "detector contract" suite asserts the array-index ordering so a future reorder can't silently break the suppression. The mechanism is deliberately minimal: a same-array, predicate-in-<code>push()</code> filter, not a general dependency graph. <code>auditConfig()</code>'s own <code>push()</code> call is unaffected, since <code>scope:'config'</code> entries declare no <code>suppressWhen</code>.</p><h2 id="per-operator-duration-attribution" tabindex="-1">Per-operator duration attribution <a class="header-anchor" href="#per-operator-duration-attribution" aria-label="Permalink to "Per-operator duration attribution""></a></h2><p><code>packages/core/src/plan-duration-attribution.ts</code> (entry <code>attributeStageDurationToPlan(planTree, stagesById, sqlExec)</code>) approximates how a SQL execution's stage wall-time splits across plan operators, returning a <code>Map<planNode, milliseconds></code>. It cuts the plan tree at Exchange boundaries into connected components: since the Exchange write/read split (<code>resolvePlanTree</code> in <code>event-handlers.ts</code> always synthesizes a <code>read</code> node wrapping a <code>write</code> node for every raw <code>Exchange</code>/<code>BroadcastExchange</code>), the cut is keyed off <code>PlanNode.exchangeRole === 'read'</code> on the parent, not a name regex: the write half starts the new component, the read half stays in its parent's. A node with no <code>exchangeRole</code> at all (for example <code>ReusedExchange</code>, which is never split) never starts a new component on its own, unlike the old name-based regex, which matched any Exchange-family name regardless of split state. Each component receives a stable pre-order identity and separate parent/depth/traversal metadata; the identity itself does not encode its count of Exchange ancestors. It zips components deepest-first by that explicit depth against submission-ordered stage IDs, then apportions each matched stage's wall-time across that component's nodes by timing-metric weight, falling back to an even split when no node carries a timing metric.</p><p>This is best-effort inference, not measurement. Spark's event model exposes no ground truth for per-operator time within a stage; the Exchange-boundary segmentation and deepest-component-to-earliest-stage zip are heuristics. Treat the per-operator numbers as directional hints, never as authoritative timings, and do not build hard thresholds or findings on top of them.</p><h2 id="stage-id-attribution-for-plan-advisor-findings" tabindex="-1">Stage-ID attribution for Plan Advisor findings <a class="header-anchor" href="#stage-id-attribution-for-plan-advisor-findings" aria-label="Permalink to "Stage-ID attribution for Plan Advisor findings""></a></h2><p>The Plan Advisor detectors (<code>duplicatePlanSubtree</code>, <code>smallFiles</code>, <code>broadcastSizing</code> in <code>packages/core/src/detectors.ts</code>) each attribute their finding to a narrowed <code>stageIds</code> set rather than the whole SQL execution: <code>PlanNode.stageIds</code> is resolved once per plan tree at parse time by unioning, per node, every metric's accumulator ID against a <code>taskAccumStages: Map<accumulatorId, Set<stageId>></code> built while parsing <code>TaskEnd</code> events, then clipping the result to the execution's own stage set. An accumulator ID occasionally points to a <em>different</em> execution's stages, e.g. a <code>ReusedSubquery</code> computed once and reused verbatim, and the clip prevents misattributing that other execution's work. Each detector unions its implicated node(s)' <code>stageIds</code> and falls back to the execution-wide set only when no implicated node has any coverage; a finding never partially blends a narrowed set with the execution-wide one. When an execution has no stage universe at all (no jobs ever recorded against it, which is true for 42% of real-log SQL executions with a plan tree, typically job-less/driver-only executions), the clip drops every candidate stage ID instead of passing them through: every node in that execution's tree ends up with no <code>stageIds</code> anywhere, same "coverage is partial" framing as below. (An earlier version of this clip treated "no stage universe" as "no clip," which let a foreign accumulator ID collision, e.g. the <code>ReusedSubquery</code> case above, leak another execution's stages into a job-less execution's nodes; the clip is now unconditional on <code>executionStageIds</code> being present.)</p><p>Coverage is partial by Spark's own design: whole-stage-codegen wrapper nodes (<code>InputAdapter</code>, and other purely structural passthrough markers) carry no accumulators at all, and <code>BroadcastExchangeExec</code>'s own metrics are computed entirely on the driver and never appear on any <code>TaskEnd</code> (real Spark behavior). Since the Exchange write/read split, those driver-computed metrics live specifically on the synthesized <em>write</em> half (<code>exchangeRole: 'write'</code>); the <em>read</em> half always carries <code>metrics: []</code>. The write half's immediate child, which does carry executor-side metrics, is unioned in instead, see <code>overBroadcast</code>'s wiring. A <code>TaskEnd</code> arriving after its stage has already been finalized is also silently excluded from <code>taskAccumStages</code>, consistent with the parser's existing out-of-order tolerance elsewhere.</p><p><code>planTree</code> itself is kept current against Spark's adaptive query execution (AQE) re-plans: <code>SparkListenerSQLAdaptiveExecutionUpdate</code> events overwrite the execution's <code>sparkPlanInfo</code>/<code>physicalPlanDescription</code> last-write-wins, so accumulator-ID evidence is matched against the plan that actually ran rather than a stale pre-AQE snapshot.</p><p>No eviction/pruning is added to <code>taskAccumStages</code>, a deliberate choice, not an oversight: measured on real logs, it holds roughly 1,050 keys per compressed MB (9,850 keys on an 11.6 MB fixture, about 29,000 keys on a 28.1 MB fixture). Extrapolated to a 240MB+ log, the scale this tool targets (see <code>CLAUDE.md</code>), that is roughly 250,000 keys, around 45 MB of heap for an equivalent synthetic <code>Map<number, Set<number>></code>. This heap estimate is still small relative to this tool's other in-memory state. It is higher, though, than the fixture-only measurements taken when this mechanism was built suggested.</p><p>Per-execution pruning (e.g. dropping a <code>taskAccumStages</code> entry once its stage finalizes or its owning SQL execution resolves, mirroring how <code>accumState</code> is cleared in <code>endSqlExecution</code>) is deliberately not done either: unlike <code>accumState</code>, <code>taskAccumStages</code> is one global, un-scoped map read by every execution's <code>resolvePlanTree</code> call, and the <code>ReusedSubquery</code> case above depends on a stage recorded under one execution still being visible when a later execution resolves. Pruning on any single execution's lifecycle would break that cross-execution lookup. What is bounded is the growth from a single pathological event: <code>TaskEndEventSchema</code>'s <code>Accumulables</code> array is capped at <code>MAX_ACCUMULABLES_PER_TASK</code> (10,000, <code>event-schemas.ts</code>), well above any real plan's per-task metric count, so a single crafted <code>TaskEnd</code> can't grow the map past that per-event bound; a <code>TaskEnd</code> exceeding it fails schema validation and the line is skipped (counted in <code>skippedLines</code>) like any other malformed event.</p><h2 id="bottleneck-thresholds-spec-§4" tabindex="-1">Bottleneck thresholds (spec §4) <a class="header-anchor" href="#bottleneck-thresholds-spec-§4" aria-label="Permalink to "Bottleneck thresholds (spec §4)""></a></h2><p>Every change to <code>packages/core/src/detectors.ts</code> should reference this table.</p><p>Every finding's <code>impactBand</code> comes from one of two places. For any finding whose <code>impactEstimate</code> carries a <code>wallClock</code> estimate (the common case for most rules below), <code>analyzer.ts</code> calls <code>deriveImpactBand()</code> (<code>packages/core/src/impact-band.ts</code>) immediately after <code>estimateImpact()</code>, which sets <code>.impactBand</code> purely from <code>wallClock.high</code> as a fraction of the app's total duration (<code>>= 2%</code> critical, <code>>= 0.5%</code> warning, else info: the same <code>floorPctWarn</code>/<code>floorPctCrit</code> values <code>skew</code>/<code>straggler</code> use for their own thresholds below). For those rules, the table below documents their firing gate plus their fixed fallback constant, which surfaces only when this run's finding of that type didn't get a wallClock estimate (a stage excluded from the occupancy sweep). For rules whose finding type never gets a wallClock estimate (<code>resourceOnly</code>/<code>informational</code> basis, e.g. <code>configAudit</code>, or a rule that keeps its own ratio-tiered classification per the design's Decision 2, e.g. <code>failures</code>), the full threshold table below is the real, displayed classification: <code>detectors.ts</code> sets <code>impactBand</code> directly and nothing overwrites it. <code>partitionSizing</code>'s <code>maxPartitionTooBig</code> rule is a third case: it does carry a <code>wallClock</code> estimate but is explicitly exempted in <code>deriveImpactBand()</code> because it's a hardcoded-critical OOM/crash-risk safety signal, not a time-recovery one, so <code>detectors.ts</code>'s own classification stands regardless of how small that estimate is relative to the run.</p><h3 id="fixed-fallback-only-usually-wallclock-derived-instead" tabindex="-1">Fixed fallback only (usually wallClock-derived instead) <a class="header-anchor" href="#fixed-fallback-only-usually-wallclock-derived-instead" aria-label="Permalink to "Fixed fallback only (usually wallClock-derived instead)""></a></h3><p>These rules' <em>band</em> tiers were deleted from <code>packages/core/src/detectors.ts</code> (they were always overwritten by <code>deriveImpactBand</code> whenever a wallClock estimate was available); the constant in the last column is only a floor-case fallback. Their <em>firing</em> gate is untouched and still lives in each entry's <code>thresholds</code> object: it decides whether the rule reports anything, so it stays documented here in full.</p><table tabindex="0"><thead><tr><th>Rule</th><th>Fires when</th><th>Fallback</th></tr></thead><tbody><tr><td>Task skew</td><td><code>taskDurationP95 / taskDurationP50 > 3×</code> (<code>taskDurationMax / P50</code> for stages under <code>minTasksForP95</code> = 20 tasks), <strong>and</strong> the occupancy-clipped P95−P50 (or max−P50) delta is ≥ <code>floorPctWarn</code> = 0.5% of app runtime</td><td><code>warning</code></td></tr><tr><td>Shuffle read</td><td><code>shuffleReadBytes > minBytes</code> = 50 MiB</td><td><code>info</code></td></tr><tr><td>Partition sizing: skew</td><td><code>shuffleReadMax > 5×</code> <code>shuffleReadP50</code> <strong>and</strong> <code>shuffleReadMax > 256 MiB</code></td><td><code>warning</code></td></tr><tr><td>Partition sizing: low parallelism</td><td><code>shuffleReadBytes ≥ 1 GiB</code> <strong>and</strong> <code>taskCount ≤ 7</code></td><td><code>warning</code></td></tr><tr><td>Partition sizing: oversized partition</td><td><code>shuffleReadMax ≥ 5 GiB</code></td><td><code>critical</code></td></tr><tr><td>GC</td><td><code>executorRunTime ≥ minRunTimeMs</code> = 10 s <strong>and</strong> <code>gcPct > 10%</code></td><td><code>warning</code></td></tr><tr><td>GC (low / cost)</td><td><code>executorRunTime ≥ 10 s</code> <strong>and</strong> <code>gcPct < lowInfoPct100</code> = 5% (checked only when the GC row above did not fire)</td><td><code>info</code></td></tr><tr><td>Spill (magnitude v2)</td><td>any non-zero <code>memoryBytesSpilled</code>. The magnitude sub-table below classifies <em>how much</em>, but does not gate firing</td><td><code>warning</code></td></tr><tr><td>Cold start</td><td><code>firstStageSubmittedAt − app.startTime > gapSeconds</code> = 30 s</td><td><code>warning</code></td></tr><tr><td>Slow host: mean-duration ratio</td><td>stage has ≥ <code>minHosts</code> = 3 hosts (or executors) and ≥ <code>minTasks</code> = 15 tasks; then per host: mean task duration / overall median ≥ <code>ratioWarn</code> = 2.0× <strong>and</strong> host task-share ≥ <code>minShare</code> = 20% <strong>and</strong> host mean ≥ <code>floorMs</code> = 1000 ms (absolute-magnitude floor, rules out sub-second noise)</td><td><code>warning</code></td></tr><tr><td>Slow host: duration-share</td><td>same stage gate as the row above; then per host: ≥ <code>shareWarn</code> = 75% of the stage's total task-duration <strong>and</strong> ≥ <code>taskShareWarn</code> = 50% of its task count</td><td><code>warning</code></td></tr><tr><td>Stage slowness: absolute fallback, suppressed when <code>slowHost</code> already fired</td><td>stage wall-clock duration ≥ <code>infoMin</code> = 15 min</td><td><code>info</code></td></tr><tr><td>Straggler / speculative-execution</td><td><code>taskCount ≥ minTasks</code> = 10, <strong>and</strong> either any speculative task ran <strong>or</strong> straggler share > <code>shareWarn</code> = 5%. <code>warnPct</code>/<code>critPct</code> (10%/20% speculative share) and <code>floorPctWarn</code>/<code>floorPctCrit</code> (0.5%/2% of app runtime) no longer set the band; they rank the straggler-vs-speculative tiers that pick which <em>metric</em> the finding reports</td><td><code>info</code></td></tr><tr><td>Speculation waste (new)</td><td><code>speculationWastedAttempts ≥ minWasted</code> = 5 <strong>and</strong> <code>speculationWasteMs ≥ minWasteMs</code> = 60 s</td><td><code>warning</code></td></tr><tr><td>Retry waste</td><td><code>wastedAttempts ≥ minWasted</code> = 3 <strong>and</strong> <code>retryWasteMs ≥ minWasteMs</code> = 30 s, on a stage that still completed</td><td><code>warning</code></td></tr><tr><td>Tiny tasks</td><td><code>taskCount ≥ minTasks</code> = 100 <strong>and</strong> <code>taskDurationP50 ≤ maxP50</code> = 500 ms <strong>and</strong> <code>taskDurationP95 ≤ maxP95</code> = 1000 ms</td><td><code>info</code></td></tr><tr><td>Duplicate plan subtree</td><td>a subtree of ≥ <code>minSubtreeSize</code> = 3 nodes whose shape fingerprint repeats ≥ <code>minOccurrences</code> = 2× in the plan</td><td><code>warning</code></td></tr><tr><td>Small files read/write</td><td>per read/write side: file count > <code>minFiles</code> = 100 <strong>and</strong> average file size < <code>maxAvgFileSizeMB</code> = 3 MiB</td><td><code>warning</code></td></tr><tr><td>Broadcast sizing: missed</td><td>a 2-child <code>SortMergeJoin</code> whose smaller side is < 10 MiB (unconditional), or < 100 MiB with the larger side > 10 GiB, or < 1 GiB with larger > 300 GiB, or < 5 GiB with larger > 1 TiB (<code>broadcastTiers</code> × <code>comparisonTiers</code>)</td><td><code>info</code></td></tr><tr><td>Broadcast sizing: oversized</td><td>a <code>BroadcastExchange</code> node whose <code>data size</code> metric > <code>overBroadcastBytes</code> = 1 GiB</td><td><code>warning</code></td></tr></tbody></table><h4 id="spill-magnitude-tiers" tabindex="-1">Spill magnitude tiers <a class="header-anchor" href="#spill-magnitude-tiers" aria-label="Permalink to "Spill magnitude tiers""></a></h4><p>The spill row's band is a fixed <code>warning</code> fallback, but <code>computeSpillMagnitude</code> (<code>packages/core/src/detectors.ts</code>) still runs on every spill finding and sets its <code>spillMagnitude</code> field, which the Spill widget displays. Its tiers, in evaluation order (first match wins, <code>null</code> when nothing matches):</p><table tabindex="0"><thead><tr><th>Condition</th><th>Magnitude</th></tr></thead><tbody><tr><td>Single-task stage: <code>spillDiskMax ≥ singleTaskDiskGiB</code> = 1 GiB <strong>or</strong> <code>spillMemMax ≥ singleTaskMemGiB</code> = 4 GiB</td><td><code>severe</code></td></tr><tr><td>Multi-task stage: <code>spillDiskMax ≥ highDiskGiB</code> = 1 GiB, <strong>or</strong> <code>spillDiskMax / taskCount ≥ highTaskDiskMB</code> = 512 MiB (per-task proxy), <strong>or</strong> <code>spillMemMax ≥ highMemGiB</code> = 4 GiB</td><td><code>high</code></td></tr><tr><td>Multi-task stage: <code>spillDiskMax ≥ medDiskMB</code> = 256 MiB <strong>or</strong> <code>spillMemMax ≥ medMemGiB</code> = 1 GiB</td><td><code>medium</code></td></tr><tr><td>Skew (<code>taskCount ≥ skewMinTasks</code> = 10): <code>spillDiskMax / spillDiskP50 > skewRatio</code> = 5× <strong>and</strong> <code>spillDiskMax ≥ skewDiskFloorMB</code> = 128 MiB</td><td><code>high</code></td></tr><tr><td>Skew (<code>taskCount ≥ 10</code>): <code>spillMemMax / spillMemP50 > 5×</code> <strong>and</strong> <code>spillMemMax ≥ skewMemFloorMB</code> = 256 MiB</td><td><code>medium</code></td></tr></tbody></table><p>Disk spill is weighted worse than memory spill by design: the memory thresholds sit well above their disk counterparts at every tier.</p><h3 id="full-threshold-table-never-wallclock-derived" tabindex="-1">Full threshold table (never wallClock-derived) <a class="header-anchor" href="#full-threshold-table-never-wallclock-derived" aria-label="Permalink to "Full threshold table (never wallClock-derived)""></a></h3><table tabindex="0"><thead><tr><th>Rule</th><th>Warning</th><th>Critical</th></tr></thead><tbody><tr><td>Stage shape: PRatio</td><td><code>taskCount / totalCores < 0.5</code> (info, under-parallelized)</td><td>none</td></tr><tr><td>Stage shape: OIRatio</td><td><code>outputBytes / inputBytes > 10×</code> (info, data explosion)</td><td>none</td></tr><tr><td>Stage shape: TaskStageSkew</td><td><code>taskDurationMax / stageDuration > 3×</code> (info)</td><td>none</td></tr><tr><td>Failed tasks</td><td>failure rate > 5% (min 10 tasks)</td><td>> 20%</td></tr><tr><td>Stage failed outright</td><td>none</td><td>any <code>stageFailureReason</code> present</td></tr><tr><td>Slow host: multi-dimensional</td><td>max/median ratio across taskTime/inputBytes/shuffleBytes/storageMemory ≥ 1.33× (info); each dimension's sample must also clear an absolute floor (1000 ms for taskTime, 64 MiB for the byte dimensions)</td><td>≥ 3.16× warning, ≥ 10× critical</td></tr><tr><td>Utilization</td><td>avg active executors / peak < 60% (info)</td><td>none</td></tr><tr><td>Autoscaling churn: short-lived executors (design spike, unvalidated thresholds)</td><td>> 30% of executors alive under 2 min (min 5 executors)</td><td>> 60%</td></tr><tr><td>Job failure rate</td><td>≥ 30% (≥ 10% info)</td><td>≥ 50%</td></tr><tr><td>Idle cores</td><td>busy-core-time / (peak cores × wall-clock) idle > 50% (warning)</td><td>none</td></tr><tr><td>Memory band</td><td>peak heap / allocated > 95% too-small (warning); < 70% over-provisioned (info)</td><td>none</td></tr><tr><td>Caching opportunity</td><td>RDD read across ≥3 stages without <code>.persist()</code></td><td>none (single tier, info)</td></tr><tr><td>Cache utilization: partial caching (this repo)</td><td><code>numCachedPartitions / numPartitions < 0.90</code> (info)</td><td><code>< 0.50</code> (warning)</td></tr><tr><td>Cache utilization: disk spillover (this repo)</td><td><code>diskSize / (memorySize + diskSize) > 0.15</code> (info), <code>MEMORY_AND_DISK*</code> only</td><td><code>> 0.40</code> (warning)</td></tr></tbody></table><p>Spill classification: ≥80% tasks with zero spill → <code>skew</code>; <20% zero → <code>volume</code>; else <code>unclassified</code>. The classification badge is always shown in both compact and expanded spill widget states. This is independent of the magnitude tiers above: classification says <em>what kind</em> of spill, magnitude says <em>how much</em>.</p><h4 id="evidence-fields-stagefailed-retrywaste" tabindex="-1">Evidence fields (<code>stageFailed</code> / <code>retryWaste</code>) <a class="header-anchor" href="#evidence-fields-stagefailed-retrywaste" aria-label="Permalink to "Evidence fields (`stageFailed` / `retryWaste`)""></a></h4><p>Neither entry's <code>detect()</code> used to put anything beyond a scalar <code>metric</code>/ <code>value</code> on its finding. Both now also attach:</p><ul><li><code>numTasks</code>: <code>stage.taskCount</code> at detection time.</li><li><code>memoryBytesSpilled</code>: <code>stage.memoryBytesSpilled</code> at detection time.</li><li><code>stageFailed</code> only: <code>failedTaskDetails</code>: up to 20 <code>FailedTaskSample</code> records (<code>taskId</code>, <code>attemptNumber</code>, <code>host</code>, <code>executorId</code>, <code>reason</code>, <code>peakExecMem</code>, <code>memSpilled</code>, <code>shuffleWrite</code>) for tasks still marked failed when the stage was finalized (<code>finalizeStage</code>, <code>stage-quantiles.ts</code>).</li><li><code>retryWaste</code> only: <code>retriedTaskDetails</code>: up to 20 <code>FailedTaskSample</code> records for attempts discarded by the retry-dedup logic in <code>accumulateTask</code> (<code>event-handlers.ts</code>): captured at the moment they'd otherwise be thrown away, since by finalize time only the winning attempt survives.</li></ul><p>Both sample arrays are capped at 20 entries, filled in first-encountered order (finalize order for <code>failedTaskDetails</code>, discard order for <code>retriedTaskDetails</code>), not spread across distinct hosts/executors: a stage with failures clustered on one bad host could fill the cap before a more informative failure elsewhere in the stage is ever sampled. This is the first evidence-shape documentation in this file; no other finding type has one yet.</p>',39)])])}const g=t(c,[["render",s]]);export{p as __pageData,g as default};
|
package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CgzUsQ6W.lean.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as t,o,c as d,a5 as a}from"./chunks/framework.DSg0KOwT.js";const p=JSON.parse('{"title":"Detector contract","description":"","frontmatter":{},"headers":[],"relativePath":"contributor-guide/architecture/detector-contract.md","filePath":"contributor-guide/architecture/detector-contract.md"}'),c={name:"contributor-guide/architecture/detector-contract.md"};function s(i,e,r,n,l,h){return o(),d("div",null,[...e[0]||(e[0]=[a("",39)])])}const g=t(c,[["render",s]]);export{p as __pageData,g as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as o,o as t,c as a,a5 as d}from"./chunks/framework.DSg0KOwT.js";const g=JSON.parse('{"title":"Drill-down","description":"","frontmatter":{},"headers":[],"relativePath":"contributor-guide/architecture/drill-down.md","filePath":"contributor-guide/architecture/drill-down.md"}'),n={name:"contributor-guide/architecture/drill-down.md"};function s(c,e,i,r,l,h){return t(),a("div",null,[...e[0]||(e[0]=[d('<h1 id="drill-down" tabindex="-1">Drill-down <a class="header-anchor" href="#drill-down" aria-label="Permalink to "Drill-down""></a></h1><p><code>StageDetailProvider</code> (<code>src/view/StageDetailContext.tsx</code>) replaces the legacy <code>openStageDetail</code> <code>window</code> <code>CustomEvent</code> with React context. Any component calls <code>useStageDetail().openStage(stageId)</code> to open <code>StageDetailDialog.tsx</code>, a Radix/shadcn <code>Dialog</code>, for that stage. The callers are StagePill, Timeline, StageTable, and (indirectly, via an embedded <code>StageHeader</code>/<code>StagePill</code>) Skew's per-stage rows.</p><p>The legacy <code>drillDownToStage</code> force-expand-and-<code>scrollIntoView</code> event has no replacement. No code ever dispatched it, so it was dormant even before the migration.</p><h2 id="plan-dot-serialization" tabindex="-1">Plan DOT serialization <a class="header-anchor" href="#plan-dot-serialization" aria-label="Permalink to "Plan DOT serialization""></a></h2><p><code>packages/core/src/plan-dot.ts</code> (entry <code>planTreeToDot(planTree, { title })</code>) serializes a resolved <code>planTree</code> to a Graphviz DOT string. It is dependency-free string building. A first pass walks the tree assigning stable node ids and <code>label</code> (name, plus <code>detail</code> on a second line when it differs). A second pass emits the parent→child edges once ids are known. The graph is laid out <code>rankdir=BT</code>, leaves at the bottom, matching Spark's own plan orientation.</p><p>It carries no metric annotation: pure structure. Adding metric annotation is a deferred roadmap item. A null plan returns an empty string. There is no download/export UI for this output anymore (removed); <code>PlanView.tsx</code> calls it only to decide whether a stage's plan tree can render as a graph at all, and a non-empty result gates the "View plan graph" button.</p><h2 id="plan-graph-view" tabindex="-1">Plan graph view <a class="header-anchor" href="#plan-graph-view" aria-label="Permalink to "Plan graph view""></a></h2><p><code>buildPlanGraphModel(planTree, opts)</code> (<code>packages/core/src/plan-graph-model.ts</code>) flattens a resolved <code>planTree</code> into a <code>{ nodes, edges, segmentIndex, segmentCount, scope, segmentStageIds }</code> graph shape. <code>src/view/PlanGraphRoute.tsx</code> renders it with <code>@xyflow/react</code> (React Flow, pan/zoom/viewport/MiniMap) and <code>@dagrejs/dagre</code> (node layout, <code>src/view/plan-graph/dagre-layout.ts</code>, <code>rankdir: 'RL'</code>).</p><p>Every <code>PlanGraphEdge</code> points <code>source: parentId, target: id</code> (parent/consumer → child/producer), and dagre places an edge's source at the higher-rank end. So <code>RL</code>, rather than the more intuitive-looking <code>LR</code>, is what lands reads/scans (targets, computed first) on the left and the final write/root (source) on the right, matching the left-to-right reading order of the plan's data flow. Each node's React Flow <code>Handle</code>s follow the same horizontal routing: <code>type="target"</code> on <code>Position.Right</code> (its parent sits to the right) and <code>type="source"</code> on <code>Position.Left</code> (its children sit to the left), rather than the top/bottom anchors a vertical <code>TB</code>/<code>BT</code> layout would use.</p><p><code>PlanGraphNode.tsx</code> renders every node at a fixed <code>NODE_WIDTH × NODE_HEIGHT</code> (220×90, also what dagre lays the graph out around) with <code>truncate</code>/<code>title</code> on every text field. An operator with an unusually long label/detail/metric string can't inflate the box and overlap neighbors; it ellipsizes instead, full text on hover.</p><p>Every raw <code>Exchange</code>/<code>BroadcastExchange</code> plan node is split by <code>resolvePlanTree</code> (<code>packages/core/src/event-handlers.ts</code>) into paired write/read halves sharing a <code>sourceNodeId</code> once flattened into the graph. <code>ReusedExchange</code> is classified as an exchange for display purposes but is never split: it carries no <code>exchangeRole</code> and stays a single node. The write half is grouped with its children's producer component; the read half is grouped with its parent's consumer component. Both halves get their own entry in <code>buildDurationMap</code>'s duration share now (the write half no longer hard-codes to null): <code>PlanGraphNode.tsx</code> only suppresses the displayed value for the read half, since the read half occupies the exact tree position the original unsplit node used to.</p><p>Default scope is <code>segment</code>: one Exchange-bounded slice of the plan, resolved via <code>computeSegments</code>/<code>zipSegmentsToStages</code> (<code>packages/core/src/plan-duration-attribution.ts</code>, shared with <code>attributeStageDurationToPlan</code>). <code>computeSegments</code> gives every connected component a stable pre-order identity plus separate parent/depth/traversal metadata. The numeric identity does not encode Exchange-ancestor depth: <code>zipSegmentsToStages</code> orders by the explicit depth metadata (deepest first, plan traversal order for ties), and the display-only fallback uses component-tree edge distance to find the nearest strictly paired component.</p><p>The view has an opt-in "Expand to full plan" toggle gated by a 300-node confirmation dialog (<code>ExpandConfirmDialog.tsx</code>). A segment-lookup failure that would otherwise render an unguarded full plan is routed through the same guardrail rather than bypassing it. That same failure can also resolve to a full-scope model <em>below</em> the guardrail threshold, rendering immediately with no dialog and no <code>requestedScope</code> change. Since it's a permanent property of that stage's plan (<code>buildPlanGraphModel</code> is deterministic per <code>(planTree, stageId, appModel)</code>), there is no segment view left for that stage to switch back to. So the toggle, still correctly labeled "Back to segment view" per <code>model?.scope</code>, renders <code>disabled</code> rather than silently no-opping on click. Only the explicit-expand path (<code>requestedScope === 'full'</code>) leaves it enabled.</p><p>The four Plan Advisor detectors (<code>duplicatePlanSubtree</code>, <code>smallFiles</code>, <code>overBroadcast</code>, <code>underBroadcast</code>, in <code>packages/core/src/detectors.ts</code>) set <code>Finding.planNodeIds</code>, an unambiguous pointer to the specific plan-tree node(s) each finding is about (a whole subtree's root(s) for <code>duplicatePlanSubtree</code>, the flagged scan/write node for <code>smallFiles</code>, the join/broadcast node(s) for the broadcast pair). <code>buildPlanGraphModel</code> indexes <code>findings</code> by <code>planNodeIds</code> and attaches each node's matches to its <code>PlanGraphNodeData.findings</code>, scoped to the current SQL execution (see below), and <code>PlanGraphNode.tsx</code> renders a corner badge from that per-node list. The badge follows the same dot+tag problem-flagging vocabulary as the rest of the dashboard: an <code>ImpactDot</code> plus the ALL-CAPS <code>typeTag</code> (<code>PLAN</code> for every current plan-node finding), colored by the worst band across the node's findings (critical > warning > info, via the local <code>worstFinding</code> helper), plus a count when the node carries more than one finding. Hovering or focusing the badge opens a tooltip listing each finding by its <code>findingActionLabel</code> (e.g. "Dedupe repeated subtree", "Compact small files"), so the node discloses which findings hit it without leaving the graph. Every other finding type still has no plan-node pointer and renders at the stage level only, via <code>Finding.stageId</code>/<code>Finding.stageIds</code>.</p><p>Plan-node ids are only unique within one SQL execution's tree, since <code>resolvePlanTree</code> resets its <code>n0, n1, ...</code> id counter on every call (once per SQL execution), so <code>buildPlanGraphModel</code> filters <code>findings</code> to <code>finding.executionId === sqlExecutionId</code> (the execution the stage being graphed belongs to) before indexing by node id. Without that filter, two unrelated executions' trees can both contain a node named e.g. <code>n1</code>, and a finding from one would badge onto the other's same-named node.</p><p>Two other per-node UI features render independently of findings:</p><ul><li>A traffic-light <strong>duration heat bar</strong> on each node (<code>plan-graph-heat.ts</code>'s <code>heatBand</code>, consumed by <code>PlanGraphNode.tsx</code>) bands the node's duration share into critical/warning/info, reusing the impact-band color tokens (see the Plan Violet exception noted in <code>DESIGN.md</code>).</li><li>A <strong>duration-attribution mode toggle</strong> (<code>PlanGraphDurationModeControl.tsx</code>) switches every node's displayed share between "Node only" (exclusive) and "Node + descendants" (inclusive), threaded into the model-build/cache key as <code>durationMode</code>.</li></ul><p>One more identity subtlety: a split <code>Exchange</code>/<code>BroadcastExchange</code> pair (the write/read halves <code>resolvePlanTree</code> synthesizes, see above) counts as <strong>one</strong> node for <code>duplicatePlanSubtree</code>'s subtree-size and occurrence counting. <code>computePlanShapes</code> in <code>detectors.ts</code> walks straight through the read half to the write half's real children, so every real Exchange in a matched subtree is counted once instead of twice.</p><p>The segment-level group box (<code>PlanGraphSegmentGroupNode.tsx</code>) always renders, even in the default single-stage view where it's the only box on screen. It is headered with its stage id (via <code>segmentStageIds</code>) and a duration chip, or an em-dash placeholder when the segment has no attributed duration. It also carries one finding chip (<code>PlanGraphFindingChip</code>) per finding on this stage, but only when there's no outer stage box to carry them instead (i.e. only in the default single-stage view). Each chip is a <code>TagBadge</code> (the ALL-CAPS tag) followed by the finding's compact magnitude and recoverable-time detail from <code>formatFindingChipDetail</code> (e.g. "SPILL 4.2 GB · ~38s": the finding's own <code>value</code>/<code>metric</code> and its <code>impactEstimate.wallClock</code>), or the bare tag when the finding carries neither.</p><p>The expanded full-plan view draws two nested layers of background group boxes (Dagre <code>compound: true</code> for spacing, <code>computeGroupBounds</code> for both): the same inner segment box described above, plus an outer solid tinted container per distinct stage id (<code>PlanGraphStageGroupNode.tsx</code>, layered under the inner segment box, which is in turn under the plan nodes; all three sit above the edge layer so a routed edge can't paint over a box's finding chips) merging every segment zipped to that stage, so a stage split across several Exchange-bounded segments still reads as one unit of work. In this expanded view the outer stage box, not the nested segment box, carries the stage's finding chips, since findings are stage-scoped rather than segment-scoped. Each stage box shows only findings matched to its own stage through <code>Finding.stageId</code> or <code>Finding.stageIds</code>; the default segment scope uses the same matching for its displayed stage.</p><p>The outer layer's own corner tag is positioned opposite the segment box's top-left header, because a stage with just one segment (the common case) would otherwise show the same "Stage N" header twice, stacked. <code>STAGE_GROUP_PADDING_Y</code> (64, no reserved header height) is sized to strictly exceed the segment layer's own reserved offset (<code>GROUP_PADDING + GROUP_HEADER_HEIGHT</code> = 56 at the top), while <code>STAGE_GROUP_PADDING_X</code> (32) is kept close to <code>GROUP_PADDING</code>'s own 24px floor so the outer box doesn't visibly balloon sideways. Both still strictly contain the outer box around its nested segment box(es).</p><p>Every global control lives on one <strong>vertical control rail</strong> down the left edge (<code>PlanGraphControlRail.tsx</code>), grouped View / Navigate / Display, so nothing floats in its own corner. View is zoom in/out and fit (replacing React Flow's <code>Controls</code>); Navigate is "Next worst duration" and "Next problem" (cycling to the worst-share node and the worst-finding stage, via <code>setCenter</code>); Display is the node-filter/duration Settings popover (<code>PlanGraphSettingsControl</code> with its <code>iconOnly</code> rail variant, moved off the topbar), plus the legend and minimap toggles. The rail renders inside <code>ReactFlowProvider</code> alongside <code><ReactFlow></code>, so its <code>useReactFlow</code> zoom/center calls drive the same instance.</p><p>Clicking a node opens the <strong>detail inspector</strong> (<code>PlanGraphNodeDetail.tsx</code>), a right-docked panel (not a floating card) that reflows the graph rather than covering it. Each node box is a fixed size and truncates every field to one line, showing a single <code>primaryMetric</code>; the inspector is where the whole operator is legible: category and segment, duration share, the node's findings (dot + tag + <code>findingActionLabel</code>), the operator's <strong>full metric set</strong> (<code>PlanGraphNodeData.metrics</code>, every metric formatted through <code>formatPlanMetricValue</code>), and the <strong>complete plan text</strong> (<code>PlanGraphNodeData.detailText</code>, the untruncated <code>PlanNode.detail</code>). For a split Exchange half the inspector adds an <strong>Exchange section</strong>: which half is in view, the shuffle volume across the boundary (<code>PlanGraphNodeData.exchangeShuffleBytes</code>, the same producing-stage bytes the pairing edge is weighted by, mirrored onto both halves so it shows whichever half is open, or "Broadcast, no shuffle"), and a <strong>jump to the paired half</strong> (<code>PlanGraphNodeData.pairedNodeId</code>). Because the two halves always sit in different segments, the jump selects the partner directly when it is already on screen (full plan) and otherwise expands to the full plan first, then selects it (<code>handleJumpToPaired</code> in <code>PlanGraphRoute.tsx</code>, carried past the selection-reset effect by a pending-jump state); either way the canvas recenters on it via a <code>centerRequest</code> token. The selected node id lives in <code>PlanGraphRoute.tsx</code>, not the canvas, so the route's one Escape handler closes the inspector first and the whole route only on a second press. Nodes are <code>draggable: false</code> (a read-only, auto-laid-out graph); they click to inspect but don't move.</p><p>The remaining legibility aids sit on the canvas itself:</p><ul><li>The <strong>MiniMap</strong> (bottom-right, toggled from the rail) colors each node by the worst finding band on it (<code>planGraphMiniMapNodeColor</code>, <code>plan-graph-minimap.ts</code>), so the overview shows where the problems are; a node with no finding keeps the neutral plan color and the large group boxes recede into a muted fill.</li><li>The rail's legend toggle reveals a <strong>legend</strong> panel (<code>PlanGraphLegend.tsx</code>) keying the operator icons, the heat-bar colors, the shuffle-weighted edge thickness, and the segment-vs-stage box layers.</li><li>When the category filter hides every operator in view, a <strong>status hint</strong> (top-center) names the count hidden and points at Settings, instead of leaving only empty group boxes on screen.</li></ul><p>The view is reached via a "View plan graph" button in <code>PlanView.tsx</code>'s toolbar, gated by the same <code>if (dot)</code> check described in "Plan DOT serialization" above. It opens a <code>planGraph: { active, stageId }</code> Zustand slice (<code>openPlanGraph</code>/<code>closePlanGraph</code>, <code>src/store/store.ts</code>) driving a top-level <code>AppRoutes</code> branch in <code>src/App.tsx</code>, modeled directly on the existing <code>comparison</code>/<code>RunComparisonRoute</code> full-takeover pattern.</p><p><code>buildPlanGraphModel</code>'s output is memoized per <code>(activeFileId, stageId, scope)</code> in <code>PlanGraphRoute.tsx</code>, since <code>stageId</code> alone isn't unique across loaded runs and <code>applySnapshot</code> mutates <code>appModel</code> in place rather than replacing it (see <a href="./state-and-history.html#state-model">State model</a>). The memo cache is a module-level <code>Map</code>, so it survives across route open/close: re-opening the same stage in the same run reuses the cached model instead of rebuilding it. It must still be evicted on a fresh parse or reload. <code>resetModel()</code> (<code>store.ts</code>) bumps a <code>modelResetCount</code> counter for exactly this purpose, and <code>PlanGraphRoute.tsx</code> subscribes to it to clear the cache. A counter rather than a direct call, since <code>store.ts</code> has no view-layer imports anywhere else and importing a <code>.tsx</code> module there would invert that dependency direction.</p><h2 id="disclosure-hierarchy" tabindex="-1">Disclosure hierarchy <a class="header-anchor" href="#disclosure-hierarchy" aria-label="Permalink to "Disclosure hierarchy""></a></h2><p>The Summary/Context/Details 3-tier framing (collapsed lead metric → expanded widget body → per-stage <code>StageDetailDialog</code>) does not apply uniformly across the 28 registry widgets (<code>src/view/detector-registry.tsx</code>). 14 have a stage-anchored Details tier reachable via <code>StagePill</code>/<code>StagePillGroup</code>: Skew, StageShape, TinyTask (all split from TaskSkew), ShuffleIO, PartitionSizing (split from ShuffleIO), Spill, GcPressure, StageFailed, TaskFailures, RetryWaste (split from Failures), SlowHost, StageSlowness, Straggler, and SpeculationWaste (split from ExecutorTimeline). The other 14 are app/sql-scope with no stage to drill into, by design, so they stop at Summary/Context: MemoryUtilization, ExecutorUtilization (split from the same widget as MemoryUtilization; <code>utilization</code> is an app-wide average, no stage), JobFailures, ConfigAudit, CacheUtilization, CoreUsageArea, AutoscalingChurn, CachingOpportunity (<code>scope: 'app'</code>, <code>stageId: null</code> on both its finding constructions, so it has no stage to anchor to despite reading like a per-stage widget), IncompleteRun, DuplicatePlanSubtree, SmallFiles, UnderBroadcast, OverBroadcast (all split from PlanFindings, sql-scope, spanning multiple stages via <code>stageIds</code> rather than one <code>stageId</code>), and ColdStart (split from ExecutorTimeline, but unlike its four siblings above, app-scoped with no <code>stageId</code>).</p><h2 id="reference-panel" tabindex="-1">Reference panel <a class="header-anchor" href="#reference-panel" aria-label="Permalink to "Reference panel""></a></h2><p>The topbar's "Reference" button and <code>DocsLink</code> (<code>src/view/DocsContext.tsx</code>) open a shadcn <code>Sheet</code> (<code>src/view/DocsSheet.tsx</code>, mounted once inside <code>DocsProvider</code>/<code>Dashboard.tsx</code>) that iframes a docs-site (VitePress) page, built from the tuning reference committed under <code>packages/core/src/docs-content/</code> and published as static HTML at <code>docs/tuning-reference/<page>.html</code> (<code>docs-config.ts</code>'s <code>docsUrl()</code> resolves an anchor to that path plus a <code>#<anchor></code> fragment). <code>useDocs().open(anchor)</code> sets React state (<code>isOpen</code>, <code>target</code>); Radix/Base UI's <code>Sheet</code> owns the slide-in animation, focus trap, and outside-click/Escape dismissal. There is a single <code>DocsTarget</code> shape (<code>{ kind: 'site', path }</code>): no vendor HTML and no <code>'vendor'</code> target kind, so <code>DocsSheet</code> always drives the iframe the same way, reassigning <code>src</code> on any path or theme change.</p><p>Most anchors the app links to are the page of the same name; a handful are in-page fragments on another page instead (config-audit sub-findings and the metric glossary live on the <code>config</code>/<code>metrics</code> pages; the two bottleneck "stage-*" sub-anchors live on the page of the bottleneck that owns them): <code>docs-config.ts</code>'s <code>pageForAnchor()</code> is the one place that resolves an anchor to its owning page. <code>npm run docs:build</code> (run automatically by <code>npm run build</code>) renders <code>packages/core/src/docs-content/</code> into <code>docs-site/.vitepress/dist</code>, and <code>vite.config.ts</code>'s <code>copyDocsSite</code> plugin copies that output to <code>dist/docs</code>; Vite's relative asset base keeps the app and docs usable when <code>dist/</code> is deployed under a URL subpath. <code>tests/doc-anchor-coverage.test.js</code> intersects <code>packages/core/src/docs-content/chapters/nav-index.json</code> against every detector's <code>docAnchor</code> and warns (never fails) on dead links (detector points at an anchor the nav index doesn't have) or orphaned Detector Catalog anchors (no detector points at them); see <code>scripts/doc-anchor-coverage.js</code>. The ported landing page (<code>docs-site/tuning-reference/index.md</code>, the symptom-picker entry page) is hand-authored, committed markdown like the rest of the corpus, not a build-time copy.</p><p>A docs-site page has no channel back to this app: it's a plain static page with no <code>postMessage</code> listener. <code>DocsSheet.tsx</code> reassigns the iframe's <code>src</code> outright on any change to the resolved path or the theme, forcing a full reload. Since re-assigning the exact same <code>src</code> string wouldn't make the browser reload it, a <code>t=<theme></code> marker is threaded into the query string ahead of the <code>#anchor</code> hash purely to change the string and force a real reload; the page never reads that param itself: it reads its light/dark preference once, from the <code>vitepress-theme-appearance</code> localStorage key <code>ThemeProvider</code> keeps current, the moment it boots.</p>',33)])])}const u=o(n,[["render",s]]);export{g as __pageData,u as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as o,o as t,c as a,a5 as d}from"./chunks/framework.DSg0KOwT.js";const g=JSON.parse('{"title":"Drill-down","description":"","frontmatter":{},"headers":[],"relativePath":"contributor-guide/architecture/drill-down.md","filePath":"contributor-guide/architecture/drill-down.md"}'),n={name:"contributor-guide/architecture/drill-down.md"};function s(c,e,i,r,l,h){return t(),a("div",null,[...e[0]||(e[0]=[d("",33)])])}const u=o(n,[["render",s]]);export{g as __pageData,u as default};
|