sparkforensics-server 0.5.1 → 0.6.0

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.
Files changed (275) hide show
  1. package/package.json +1 -1
  2. package/public/assets/AutoscalingChurn-CRA2Mb7F.js +1 -0
  3. package/public/assets/CacheUtilization-Bh9OY_V9.js +1 -0
  4. package/public/assets/CachingOpportunity-oCiQwmTJ.js +1 -0
  5. package/public/assets/{ColdStart-B38OiAOp.js → ColdStart-BJrtjfmn.js} +1 -1
  6. package/public/assets/ConfigAudit-DMvKxySP.js +1 -0
  7. package/public/assets/CoreUsageArea-REC2zsKw.js +1 -0
  8. package/public/assets/Dashboard-BHZ76JCu.js +2 -0
  9. package/public/assets/DisclosureContext-OO7X1HAx.js +1 -0
  10. package/public/assets/{DuplicatePlanSubtree-BqS2A2j4.js → DuplicatePlanSubtree-BgG-L-cK.js} +1 -1
  11. package/public/assets/ExecutorUtilization-D2KhSVbY.js +1 -0
  12. package/public/assets/{GcPressure-CT1yzR_s.js → GcPressure-D1BTE8qh.js} +1 -1
  13. package/public/assets/{IncompleteRun-C7Qkjtjn.js → IncompleteRun-DD40xUpy.js} +1 -1
  14. package/public/assets/{JobFailures-WHl3ELfh.js → JobFailures-YwNzCF19.js} +1 -1
  15. package/public/assets/MemoryUtilization-9b8ee_3_.js +1 -0
  16. package/public/assets/{OverBroadcast-6oSSAQ_R.js → OverBroadcast-CfctS8a8.js} +1 -1
  17. package/public/assets/PartitionSizing-VG8kBgG5.js +1 -0
  18. package/public/assets/{PlanExplorer-DcxmiQWL.js → PlanExplorer-DmubTAOP.js} +1 -1
  19. package/public/assets/PlanGraphRoute-DctPZjS0.js +1 -0
  20. package/public/assets/RetryWaste-CnpM8bB7.js +1 -0
  21. package/public/assets/{ShuffleIO-CRhrYNE0.js → ShuffleIO-D13vGmas.js} +1 -1
  22. package/public/assets/Skew-DlagstYP.js +1 -0
  23. package/public/assets/SlowHost-cSkLRZ_e.js +1 -0
  24. package/public/assets/{SmallFiles-C082c2rd.js → SmallFiles-B-kERMcq.js} +1 -1
  25. package/public/assets/{SpeculationWaste-BS2vpadr.js → SpeculationWaste-DaysAzXO.js} +1 -1
  26. package/public/assets/Spill-H44Gz4US.js +1 -0
  27. package/public/assets/{StageFailed-D3ac9Sou.js → StageFailed-DCfii9h_.js} +1 -1
  28. package/public/assets/StageFindingGroup-BXJ0Mj5n.js +1 -0
  29. package/public/assets/{StageHeader-pI1nTwVf.js → StageHeader--oN2J-oD.js} +1 -1
  30. package/public/assets/StageShape-IZ80CZiJ.js +1 -0
  31. package/public/assets/{StageSlowness-BaNyfNvL.js → StageSlowness--BRIaBc1.js} +1 -1
  32. package/public/assets/Straggler-Ctq0Df_o.js +1 -0
  33. package/public/assets/TaskFailures-Bks_gqhL.js +1 -0
  34. package/public/assets/{TinyTask-8_5lpq_2.js → TinyTask-BssfzGFB.js} +1 -1
  35. package/public/assets/{UnderBroadcast-DJ0XSwth.js → UnderBroadcast-DsScrxc-.js} +1 -1
  36. package/public/assets/{finding-anchor-Cys8Z6Yn.js → finding-anchor-BYW1oz5R.js} +1 -1
  37. package/public/assets/{impact-sort-BTQndbof.js → impact-sort-xzy0g-Vh.js} +1 -1
  38. package/public/assets/index-DhpRGMZc.css +2 -0
  39. package/public/assets/index-DwqkFUFk.js +14 -0
  40. package/public/assets/instrument-sans-latin-ext-wdth-normal-C5E2Gvlv.woff2 +0 -0
  41. package/public/assets/instrument-sans-latin-wdth-normal-BVScPF0l.woff2 +0 -0
  42. package/public/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  43. package/public/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  44. package/public/assets/{parser-worker-DmeCnLWU.js → parser-worker-CA5RxqKc.js} +8 -8
  45. package/public/assets/react-vendor-DfpuPQZf.js +9 -0
  46. package/public/assets/{select-DIYN6D3y.js → select-C8edjYTq.js} +1 -1
  47. package/public/assets/{usePagedRows-BxvdFnPk.js → usePagedRows-CsuUFeYx.js} +1 -1
  48. package/public/assets/useRecentFiles-BvkKo3JN.js +11 -0
  49. package/public/assets/{useSortMode-B8SRfAoG.js → useSortMode-joX71BZN.js} +1 -1
  50. package/public/docs/404.html +3 -7
  51. package/public/docs/assets/{app.D2GqAkhy.js → app.CnWL6bKV.js} +1 -1
  52. package/public/docs/assets/chunks/@localSearchIndexroot.tE5pz54z.js +1 -0
  53. package/public/docs/assets/chunks/{VPLocalSearchBox.BS4cSmtW.js → VPLocalSearchBox.C0IBKeRQ.js} +1 -1
  54. package/public/docs/assets/chunks/{theme.BuxVpb0H.js → theme.BH-2HuRf.js} +2 -2
  55. package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.js} +1 -1
  56. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.js +1 -0
  57. package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.wj6Et6UD.js → contributor-guide_architecture_detector-contract.md.N4hhenrA.js} +1 -1
  58. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.js +1 -0
  59. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DfvbqEUu.js +1 -0
  60. package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.lean.js → contributor-guide_architecture_impact-estimation.md.DfvbqEUu.lean.js} +1 -1
  61. package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.js} +1 -1
  62. package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.lean.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.lean.js} +1 -1
  63. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +1 -0
  64. package/public/docs/assets/{contributor-guide_architecture_state-and-history_run-comparison-internals.md.uuAKpY0Y.lean.js → contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js} +1 -1
  65. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.js +1 -0
  66. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.js} +1 -1
  67. package/public/docs/assets/{contributor-guide_development-setup.md.D9bu4j_c.js → contributor-guide_development-setup.md.DQdzera7.js} +1 -1
  68. package/public/docs/assets/instrument-sans-latin-ext-wdth-normal.C5E2Gvlv.woff2 +0 -0
  69. package/public/docs/assets/instrument-sans-latin-wdth-normal.BVScPF0l.woff2 +0 -0
  70. package/public/docs/assets/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2 +0 -0
  71. package/public/docs/assets/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2 +0 -0
  72. package/public/docs/assets/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2 +0 -0
  73. package/public/docs/assets/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2 +0 -0
  74. package/public/docs/assets/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2 +0 -0
  75. package/public/docs/assets/style.CyK9Ss73.css +1 -0
  76. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.js +24 -0
  77. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.lean.js +1 -0
  78. package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.CPowlb2q.js → user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js} +3 -2
  79. package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.CPowlb2q.lean.js → user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js} +1 -1
  80. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.Dq5Azk8G.js → user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js} +2 -2
  81. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.Dq5Azk8G.lean.js → user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.lean.js} +1 -1
  82. package/public/docs/assets/{user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.js → user-guide_mcp-tools_get-run-summary.md.EhaIsele.js} +1 -1
  83. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +1 -0
  84. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +1 -0
  85. package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.DSXGu2tA.js +1 -0
  86. package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.DSXGu2tA.lean.js +1 -0
  87. package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.js +1 -0
  88. package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.lean.js +1 -0
  89. package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +6 -10
  90. package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +4 -8
  91. package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -10
  92. package/public/docs/contributor-guide/architecture/board-widgets.html +4 -8
  93. package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +4 -8
  94. package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -10
  95. package/public/docs/contributor-guide/architecture/detector-contract.html +6 -10
  96. package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +4 -8
  97. package/public/docs/contributor-guide/architecture/drill-down.html +4 -8
  98. package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -10
  99. package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +4 -8
  100. package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -10
  101. package/public/docs/contributor-guide/architecture/index.html +4 -8
  102. package/public/docs/contributor-guide/architecture/overview.html +4 -8
  103. package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +4 -8
  104. package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -10
  105. package/public/docs/contributor-guide/architecture/state-and-history.html +4 -8
  106. package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +4 -8
  107. package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +4 -8
  108. package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +4 -8
  109. package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +4 -8
  110. package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +4 -8
  111. package/public/docs/contributor-guide/architecture/widget-rendering.html +4 -8
  112. package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -10
  113. package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +4 -8
  114. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +4 -8
  115. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -10
  116. package/public/docs/contributor-guide/architecture/worker-protocol.html +4 -8
  117. package/public/docs/contributor-guide/contributing.html +4 -8
  118. package/public/docs/contributor-guide/development-setup.html +6 -10
  119. package/public/docs/contributor-guide/testing.html +4 -8
  120. package/public/docs/favicon.svg +2 -2
  121. package/public/docs/hashmap.json +1 -1
  122. package/public/docs/index.html +4 -8
  123. package/public/docs/tuning-reference/anti-patterns.html +4 -8
  124. package/public/docs/tuning-reference/aqe.html +4 -8
  125. package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +4 -8
  126. package/public/docs/tuning-reference/bottleneck-cold-start.html +4 -8
  127. package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +4 -8
  128. package/public/docs/tuning-reference/bottleneck-failures.html +4 -8
  129. package/public/docs/tuning-reference/bottleneck-gc.html +4 -8
  130. package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +4 -8
  131. package/public/docs/tuning-reference/bottleneck-memory-utilization.html +4 -8
  132. package/public/docs/tuning-reference/bottleneck-retry-waste.html +4 -8
  133. package/public/docs/tuning-reference/bottleneck-shuffle.html +4 -8
  134. package/public/docs/tuning-reference/bottleneck-skew.html +4 -8
  135. package/public/docs/tuning-reference/bottleneck-slow-host.html +4 -8
  136. package/public/docs/tuning-reference/bottleneck-small-files.html +4 -8
  137. package/public/docs/tuning-reference/bottleneck-spill.html +4 -8
  138. package/public/docs/tuning-reference/bottleneck-straggler.html +4 -8
  139. package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +4 -8
  140. package/public/docs/tuning-reference/bottleneck-utilization.html +4 -8
  141. package/public/docs/tuning-reference/caching.html +4 -8
  142. package/public/docs/tuning-reference/cluster-config.html +4 -8
  143. package/public/docs/tuning-reference/config.html +4 -8
  144. package/public/docs/tuning-reference/data-formats.html +4 -8
  145. package/public/docs/tuning-reference/index.html +5 -9
  146. package/public/docs/tuning-reference/intro.html +4 -8
  147. package/public/docs/tuning-reference/joins.html +4 -8
  148. package/public/docs/tuning-reference/memory-model.html +4 -8
  149. package/public/docs/tuning-reference/metrics.html +4 -8
  150. package/public/docs/tuning-reference/partitioning.html +4 -8
  151. package/public/docs/tuning-reference/pyspark.html +4 -8
  152. package/public/docs/tuning-reference/shuffle.html +4 -8
  153. package/public/docs/tuning-reference/spark-architecture.html +4 -8
  154. package/public/docs/tuning-reference/table-formats.html +4 -8
  155. package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +4 -8
  156. package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +4 -8
  157. package/public/docs/user-guide/alternative-log-retrieval.html +4 -8
  158. package/public/docs/user-guide/getting-started/ci-and-automation.html +11 -13
  159. package/public/docs/user-guide/getting-started/reading-the-dashboard.html +4 -8
  160. package/public/docs/user-guide/getting-started/regression-metric-keys.html +4 -8
  161. package/public/docs/user-guide/getting-started/tuning-thresholds.html +4 -8
  162. package/public/docs/user-guide/getting-started.html +4 -8
  163. package/public/docs/user-guide/mcp-tools/compare-runs.html +8 -11
  164. package/public/docs/user-guide/mcp-tools/diagnose-run.html +4 -8
  165. package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -10
  166. package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +4 -8
  167. package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +4 -8
  168. package/public/docs/user-guide/mcp-tools/get-reference-doc.html +4 -8
  169. package/public/docs/user-guide/mcp-tools/get-run-summary.html +6 -10
  170. package/public/docs/user-guide/mcp-tools/list-runs.html +4 -8
  171. package/public/docs/user-guide/mcp-tools.html +4 -8
  172. package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +6 -10
  173. package/public/docs/user-guide/run-comparison.html +6 -10
  174. package/public/docs/user-guide/understanding-findings.html +6 -10
  175. package/public/export-template.html +46 -46
  176. package/public/favicon.svg +2 -2
  177. package/public/index.html +4 -7
  178. package/vendor-core/allocation.js +105 -20
  179. package/vendor-core/api-merge-writes.js +1 -0
  180. package/vendor-core/cli/budgets.js +62 -2
  181. package/vendor-core/cli/regression-budgets.js +35 -0
  182. package/vendor-core/comparison-output.js +2 -1
  183. package/vendor-core/core-source-hash.txt +1 -1
  184. package/vendor-core/delta-log-paths.js +10 -1
  185. package/vendor-core/detectors.js +290 -92
  186. package/vendor-core/docs-content/detection/chrn.md +3 -1
  187. package/vendor-core/docs-content/detection/cold.md +2 -1
  188. package/vendor-core/docs-content/detection/mem.md +1 -1
  189. package/vendor-core/docs-content/detection/plan.md +9 -2
  190. package/vendor-core/docs-content/detection/shfl.md +8 -2
  191. package/vendor-core/docs-content/detection/skew.md +12 -5
  192. package/vendor-core/docs-content/detection/slow.md +6 -2
  193. package/vendor-core/docs-content/detection/spill.md +4 -1
  194. package/vendor-core/docs-content/detection/strag.md +5 -3
  195. package/vendor-core/docs-content/detection/tiny.md +3 -2
  196. package/vendor-core/docs-content/detection/util.md +6 -2
  197. package/vendor-core/efficiency-model.js +22 -14
  198. package/vendor-core/evidence-report.js +11 -11
  199. package/vendor-core/finding-presentation.js +52 -19
  200. package/vendor-core/finding-types.js +71 -16
  201. package/vendor-core/impact-model.js +2 -2
  202. package/vendor-core/mcp-server-factory.js +6 -2
  203. package/vendor-core/mcp-tools.js +10 -1
  204. package/vendor-core/median.js +8 -0
  205. package/vendor-core/remediation.js +10 -5
  206. package/vendor-core/run-comparison.js +2 -0
  207. package/vendor-core/run-output.js +14 -3
  208. package/vendor-core/sequence-alignment.js +83 -0
  209. package/vendor-core/stage-alignment.js +387 -63
  210. package/vendor-core/stage-quantiles.js +3 -1
  211. package/vendor-core/stage-structure.js +83 -0
  212. package/vendor-core/types.js +12 -2
  213. package/vendor-core/wall-clock.js +8 -0
  214. package/vendor-core/wasted-core-hours.js +12 -11
  215. package/vendor-core/write-targets.js +32 -0
  216. package/public/assets/AutoscalingChurn-5hDGIXi4.js +0 -1
  217. package/public/assets/CacheUtilization-CT2SCnJJ.js +0 -1
  218. package/public/assets/CachingOpportunity-D_O_WkbH.js +0 -1
  219. package/public/assets/ConfigAudit-BoplryCH.js +0 -1
  220. package/public/assets/CoreUsageArea-csgwNxkl.js +0 -1
  221. package/public/assets/Dashboard-BgKodJl2.js +0 -2
  222. package/public/assets/DisclosureContext-B0siMVdg.js +0 -1
  223. package/public/assets/ExecutorUtilization-Dp52QGWo.js +0 -1
  224. package/public/assets/MemoryUtilization-D8IUDHJI.js +0 -1
  225. package/public/assets/PartitionSizing-BwdFL-mw.js +0 -1
  226. package/public/assets/PlanGraphRoute-pqnHTt4i.js +0 -1
  227. package/public/assets/RetryWaste-BTbeHkJc.js +0 -1
  228. package/public/assets/Skew-DSNXRG9V.js +0 -1
  229. package/public/assets/SlowHost-B7Lwu2EU.js +0 -1
  230. package/public/assets/Spill-B4DVKHXx.js +0 -1
  231. package/public/assets/StageFindingGroup-D9naK109.js +0 -1
  232. package/public/assets/StageShape-eO5le8iG.js +0 -1
  233. package/public/assets/Straggler-D5Fnok_y.js +0 -1
  234. package/public/assets/TaskFailures-BWnC-K_G.js +0 -1
  235. package/public/assets/index-BcqTLKFp.css +0 -2
  236. package/public/assets/index-DCeWge12.js +0 -13
  237. package/public/assets/react-vendor-DEdfrRTJ.js +0 -9
  238. package/public/assets/useRecentFiles-C3Ov47U7.js +0 -11
  239. package/public/docs/assets/chunks/@localSearchIndexroot.D4OdkQjy.js +0 -1
  240. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.js +0 -1
  241. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.fkPBXsU2.js +0 -1
  242. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.js +0 -1
  243. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.uuAKpY0Y.js +0 -1
  244. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.Dk08VS7n.js +0 -1
  245. package/public/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  246. package/public/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  247. package/public/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  248. package/public/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  249. package/public/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  250. package/public/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  251. package/public/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  252. package/public/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  253. package/public/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  254. package/public/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  255. package/public/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  256. package/public/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  257. package/public/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  258. package/public/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  259. package/public/docs/assets/style.tmDhD3c8.css +0 -1
  260. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.DpnfKz3x.js +0 -22
  261. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.DpnfKz3x.lean.js +0 -1
  262. package/public/docs/assets/user-guide_run-comparison.md.B6aNtDYF.js +0 -1
  263. package/public/docs/assets/user-guide_run-comparison.md.B6aNtDYF.lean.js +0 -1
  264. package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.XBzQ1oEU.js +0 -1
  265. package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.XBzQ1oEU.lean.js +0 -1
  266. package/public/docs/assets/user-guide_understanding-findings.md.Dqt2H_C1.js +0 -1
  267. package/public/docs/assets/user-guide_understanding-findings.md.Dqt2H_C1.lean.js +0 -1
  268. /package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.lean.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.lean.js} +0 -0
  269. /package/public/docs/assets/{contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.lean.js → contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.lean.js} +0 -0
  270. /package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.wj6Et6UD.lean.js → contributor-guide_architecture_detector-contract.md.N4hhenrA.lean.js} +0 -0
  271. /package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.fkPBXsU2.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.lean.js} +0 -0
  272. /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.Dk08VS7n.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.lean.js} +0 -0
  273. /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.lean.js} +0 -0
  274. /package/public/docs/assets/{contributor-guide_development-setup.md.D9bu4j_c.lean.js → contributor-guide_development-setup.md.DQdzera7.lean.js} +0 -0
  275. /package/public/docs/assets/{user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.lean.js → user-guide_mcp-tools_get-run-summary.md.EhaIsele.lean.js} +0 -0
@@ -3,6 +3,8 @@
3
3
  Executors are stood up and torn down again before they can do useful work:
4
4
  re-provisioning churn rather than normal scale-down. Raise
5
5
  `spark.dynamicAllocation.executorIdleTimeout`, or widen the
6
- `minExecutors`/`maxExecutors` bounds to reduce flapping. Self-flags a
6
+ `minExecutors`/`maxExecutors` bounds to reduce flapping. With dynamic
7
+ allocation off (`evidence.dynamicAllocation` is `off`) none of those applies:
8
+ the churn is executor loss or preemption, and `remediation` is empty. Self-flags a
7
9
  confidence that scales with how far the short-lived-executor share sits
8
10
  past the threshold.
@@ -3,4 +3,5 @@
3
3
  The first stage waited more than 30 s for an executor. Keep a warm pool of
4
4
  executors, or, with dynamic allocation, raise
5
5
  `spark.dynamicAllocation.minExecutors`/`initialExecutors` so the app doesn't
6
- scale up from zero.
6
+ scale up from zero. With dynamic allocation off (`evidence.dynamicAllocation`
7
+ is `off`) no executor-count property applies and `remediation` is empty.
@@ -1,7 +1,7 @@
1
1
  ### `MEM`: Memory utilization {#mem}
2
2
 
3
3
  Executor memory or core capacity may be over- or under-provisioned: more
4
- than 50% of available core time ran no task, an executor's heap peaked above
4
+ than 50% of allocated core time ran no task, an executor's heap peaked above
5
5
  95% of its allocation, or it stayed below 70%. Some
6
6
  detail here needs `spark.eventLog.logStageExecutorMetrics=true` on the run
7
7
  being analyzed; without it, per-executor memory usage can't be broken down.
@@ -11,7 +11,14 @@ this tag:
11
11
  under 3 MB. Compact upstream output, or coalesce before writing.
12
12
  - Under-broadcast: the smaller side of a Sort Merge Join looks well under
13
13
  the broadcast threshold; consider a `broadcast()` hint or raising
14
- `spark.sql.autoBroadcastJoinThreshold`.
14
+ `spark.sql.autoBroadcastJoinThreshold`. When the effective threshold
15
+ (logged, else Spark's 10 MiB) already admits the smaller side
16
+ (`evidence.broadcastThreshold` is `notLimiting`), the threshold is not what
17
+ stopped the broadcast, so `remediation` is empty and the advice is a hint or
18
+ table statistics.
15
19
  - Over-broadcast: a broadcast exceeds the 1 GB threshold; check for a
16
20
  misapplied broadcast hint or a misconfigured
17
- `spark.sql.autoBroadcastJoinThreshold`.
21
+ `spark.sql.autoBroadcastJoinThreshold`. When the effective threshold is
22
+ below the broadcast or auto-broadcast is disabled (`evidence.broadcastThreshold`
23
+ is `notLimiting` or `disabled`), a hint forced it: remove the hint, and
24
+ `remediation` is empty.
@@ -1,5 +1,11 @@
1
1
  ### `SHFL`: Shuffle I/O {#shfl}
2
2
 
3
3
  Tasks move a large amount of intermediate data between stages. Raise
4
- `spark.sql.shuffle.partitions`, or add a broadcast join. Only flagged on
5
- stages that take at least 0.5% of the run.
4
+ `spark.sql.shuffle.partitions`, or add a broadcast join. The partition-count
5
+ advice follows the effective conf (`evidence.partitions`): `raise` when the
6
+ property limits the stage, `sufficient` when the tasks already read a good
7
+ size each, `aqeCoalesced` when AQE merged the partitions (lower
8
+ `spark.sql.adaptive.advisoryPartitionSizeInBytes`), and `ownPartitioning` when
9
+ the property is already high enough (the stage's own `repartition(n)` or RDD
10
+ parallelism limits it). Only flagged on stages that take at least 0.5% of the
11
+ run.
@@ -1,8 +1,15 @@
1
1
  ### `SKEW`: Task skew {#skew}
2
2
 
3
3
  A small number of tasks take much longer than their peers in the same
4
- stage. For join-driven skew, enable AQE skew-join handling
5
- (`spark.sql.adaptive.skewJoin.enabled`); otherwise salt the key or
6
- repartition on a better key. Flagged when P95 task time (the longest task,
7
- on a stage with fewer than 20 tasks) exceeds 3x the median and the
8
- recoverable tail is at least 0.5% of the run.
4
+ stage. The fix depends on what the stage reads, which the finding's
5
+ `evidence.origin` records. A stage that reads a shuffle feeding a join
6
+ (`shuffleJoin`) gets AQE skew-join handling
7
+ (`spark.sql.adaptive.skewJoin.enabled`), unless the run's effective conf
8
+ already has it; otherwise salt the key or repartition on a better key. A
9
+ stage that reads files with uneven sizes (`inputScan`) gets compaction of
10
+ small files or a lower `spark.sql.files.maxPartitionBytes`. Any other stage
11
+ (`other`) gets the salting advice and no conf, as a `code` entry in
12
+ `remediation`; so does a join stage whose skew-join handling is already on. Flagged when P95 task time
13
+ (the longest task, on a stage with fewer than 20 tasks) exceeds 3x the median
14
+ and the recoverable tail is at least 0.5% of the run. The median is the
15
+ textbook one: on an even task count, the mean of the two middle values.
@@ -1,6 +1,10 @@
1
1
  ### `SLOW`: Stage slowness {#slow}
2
2
 
3
3
  A stage ran for 15 minutes or more and no slow host was flagged on it. It
4
- can appear alongside other findings on the same stage. Often a partition-count problem: raise parallelism via
4
+ can appear alongside other findings on the same stage. On a stage that reads a
5
+ shuffle, often a partition-count problem: raise parallelism via
5
6
  `spark.sql.shuffle.partitions` or `spark.default.parallelism`, or check for a
6
- large per-task data volume driving heavy shuffle and spill.
7
+ large per-task data volume driving heavy shuffle and spill. On a stage that
8
+ reads input files and no shuffle, check input file sizes and lower
9
+ `spark.sql.files.maxPartitionBytes`. `evidence.reads` says which case
10
+ applied (`shuffle`, `input` or `other`).
@@ -4,4 +4,7 @@ Tasks are writing data out of memory, which slows execution. Two spill
4
4
  patterns get flagged differently: skew spill, where a few heavy tasks spill
5
5
  while most don't (rebalance partitioning), and volume spill, where most
6
6
  tasks spill because the data genuinely exceeds available memory (add
7
- partitions or executor memory). Only flagged on stages that take at least 0.5% of the run.
7
+ partitions or executor memory). `spark.sql.shuffle.partitions` is suggested
8
+ only for a stage that reads a shuffle; `evidence.reads` says what the stage
9
+ reads (`shuffle`, `input` or `other`). Only flagged on stages that take at
10
+ least 0.5% of the run.
@@ -1,6 +1,8 @@
1
1
  ### `STRAG`: Straggler tasks {#strag}
2
2
 
3
3
  A few tasks run much slower than the rest of their stage. Rule out a GC
4
- pause or a slow shuffle fetch before assuming a hardware issue; if a skewed
5
- key is the real cause, that's a candidate for AQE's skew-join handling. Only
6
- flagged on stages that take at least 0.5% of the run.
4
+ pause or a slow shuffle fetch before assuming a hardware issue. If uneven
5
+ data is the cause, the advice is the one a skew finding gives for the same
6
+ stage (`evidence.origin`, see `SKEW`): AQE skew-join handling for a shuffle
7
+ feeding a join, file sizes for a scan, salting otherwise. Only flagged on
8
+ stages that take at least 0.5% of the run.
@@ -1,5 +1,6 @@
1
1
  ### `TINY`: Tiny tasks {#tiny}
2
2
 
3
3
  Many very short tasks add scheduling overhead out of proportion to the work
4
- each one does. Repartition to fewer, larger tasks. Only flagged on stages that
5
- take at least 0.5% of the run.
4
+ each one does. Repartition to fewer, larger tasks; a stage that reads a shuffle
5
+ (`evidence.reads` is `shuffle`) can also lower `spark.sql.shuffle.partitions`.
6
+ Only flagged on stages that take at least 0.5% of the run.
@@ -1,4 +1,8 @@
1
1
  ### `UTIL`: Low utilization {#util}
2
2
 
3
- Allocated executors sit idle for a large share of the application run.
4
- Consider a smaller cluster, or enable dynamic allocation.
3
+ Allocated executors sit idle for a large share of the application run: under
4
+ 60% of the core time the run allocated (cores times the time each executor was
5
+ alive) ran a task. Consider a smaller cluster. With dynamic allocation off,
6
+ either lower `spark.executor.instances` or enable dynamic allocation; the two are
7
+ alternatives. With it on, lower `spark.dynamicAllocation.maxExecutors`, and
8
+ `spark.dynamicAllocation.minExecutors` too when the run sets it above 0.
@@ -1,12 +1,13 @@
1
1
  // §5 Efficiency/wastage model. DESIGN SPIKE:
2
2
  // decomposes available compute-hours into driver-bound vs executor-bound waste, plus two floors.
3
- import { computeWallClock } from './wall-clock.js';
3
+ import { stageWindows } from './wall-clock.js';
4
+ import { allocatedCoreMs, allocatedCoreMsWithin } from './allocation.js';
4
5
  import { computePeakConcurrentCores } from './core-count.js';
5
6
 
6
7
 
7
8
  export function computeEfficiencyModel({ app, stages, executorsAdded, executorsRemoved, runAggregates }
8
9
 
9
-
10
+
10
11
 
11
12
 
12
13
 
@@ -18,23 +19,25 @@ export function computeEfficiencyModel({ app, stages, executorsAdded, executorsR
18
19
 
19
20
 
20
21
  {
21
- // Capacity is the peak concurrent core count, the one the utilization and idle-cores detectors
22
- // use, so the Unused core time tile matches the verdict's idle figure. Summing every addition
23
- // (computeTotalCores) counts a replaced executor's cores alongside its replacement's.
24
- // `app ?? {}`: callers tolerate a null app (malformed logs); `app!` would crash on app.resources.
25
- const totalCores = computePeakConcurrentCores(app ?? {}, executorsAdded, executorsRemoved);
26
- const appDurationMs = (app?.endTime ?? 0) - (app?.startTime ?? 0);
27
- const wc = computeWallClock(app, stages );
22
+ // Capacity is the run's allocation (cores x time alive, metrics.allocation.coreHours), the one the
23
+ // utilization and idle-cores detectors use, so the Unused core time tile matches the verdict's
24
+ // idle figure. Peak concurrent cores times the whole run would count cores the run never held
25
+ // under dynamic allocation. Null without executor cores: no capacity, so no figure.
26
+ const input = { app, stages, executors: { added: executorsAdded, removed: executorsRemoved } };
27
+ const allocatedMs = allocatedCoreMs(input);
28
+ // Split by where the allocated cores were held, from the same alive intervals: inside the union
29
+ // of the stages' windows, or outside it (startup, gaps between stages, after the last stage).
30
+ // Executors that join after startup hold no cores there, so spreading the allocation evenly
31
+ // over the run would bill the driver for cores that never existed.
32
+ const allocatedActiveCoreMs = allocatedMs == null ? 0 : allocatedCoreMsWithin(input, stageWindows(stages)) ?? 0;
28
33
 
29
- const availableComputeHours = totalCores * (appDurationMs / 3600000);
34
+ const availableComputeHours = (allocatedMs ?? 0) / 3600000;
30
35
 
31
- // Driver-bound: cores idle during startup + gaps + idle (no active stage).
32
- const driverIdleMs = wc.startup + wc.gaps + wc.idle;
33
- const driverWasteHours = totalCores * (driverIdleMs / 3600000);
36
+ // Driver-bound: cores held while no stage was active (startup, gaps, idle).
37
+ const driverWasteHours = Math.max(0, (allocatedMs ?? 0) - allocatedActiveCoreMs) / 3600000;
34
38
 
35
39
  // Executor-bound: allocated capacity beyond busy cores during stagesActive. Tasks only run in
36
40
  // active windows, so whole-run busyCoreMs already lives inside stagesActive.
37
- const allocatedActiveCoreMs = totalCores * wc.stagesActive;
38
41
  const busyCoreMs = runAggregates?.busyCoreMs ?? 0;
39
42
  const executorWasteHours = Math.max(0, allocatedActiveCoreMs - busyCoreMs) / 3600000;
40
43
 
@@ -44,6 +47,11 @@ export function computeEfficiencyModel({ app, stages, executorsAdded, executorsR
44
47
  let totalTaskMs = 0;
45
48
  const perStage = runAggregates?.perStage ?? {};
46
49
  for (const k of Object.keys(perStage)) totalTaskMs += perStage[k].totalTaskDurationSum;
50
+ // The floor is the least time the work takes on the widest the cluster ever was: peak concurrent
51
+ // cores (not the allocation), summing every addition (computeTotalCores) would count a replaced
52
+ // executor's cores alongside its replacement's. `app ?? {}`: callers tolerate a null app
53
+ // (malformed logs); `app!` would crash on app.resources.
54
+ const totalCores = computePeakConcurrentCores(app ?? {}, executorsAdded, executorsRemoved);
47
55
  const floorZeroSkewMs = totalCores > 0 ? totalTaskMs / totalCores : 0;
48
56
 
49
57
  let dominantWaste = null;
@@ -180,29 +180,29 @@ export const EVIDENCE_SCHEMA_VERSION = 5;
180
180
  // module (stageShape's totalCores, utilization's unrounded fraction) is left off both, so it never
181
181
  // reaches the report; adding, renaming or dropping a key here changes the report contract.
182
182
  const EVIDENCE_KEYS = {
183
- skew: [],
183
+ skew: ['origin'],
184
184
  stageShape: ['rule'],
185
- shuffle: [],
186
- partitionSizing: ['rule'],
187
- spill: ['spillMagnitude'],
185
+ shuffle: ['partitions'],
186
+ partitionSizing: ['rule', 'origin', 'partitions'],
187
+ spill: ['spillMagnitude', 'reads'],
188
188
  gc: ['direction'],
189
189
  slowHost: ['variant', 'host', 'hostTaskShare', 'hostMeanMs', 'dimension', 'executorId', 'execMaxValue'],
190
- stageSlowness: [],
190
+ stageSlowness: ['reads'],
191
191
  stageFailed: ['variant', 'numTasks', 'memoryBytesSpilled', 'failedTaskDetails'],
192
192
  failures: ['failedTasks', 'dominantReason', 'dominantError', 'failureGroups', 'otherFailedTasks'],
193
- straggler: ['unit', 'speculativeTasks', 'stragglerCount'],
193
+ straggler: ['unit', 'speculativeTasks', 'stragglerCount', 'origin'],
194
194
  speculationWaste: [],
195
195
  retryWaste: ['numTasks', 'memoryBytesSpilled', 'retriedTaskDetails'],
196
- tinyTask: [],
196
+ tinyTask: ['reads'],
197
197
  incompleteRun: [],
198
- coldStart: [],
198
+ coldStart: ['dynamicAllocation'],
199
199
  utilization: ['cpuUtilizationPct'],
200
200
  memoryUtilization: ['variant', 'rule', 'executorId', 'heap', 'dataUnavailable'],
201
201
  cacheUtilization: [
202
202
  'variant', 'rddId', 'rddName', 'memorySize', 'diskSize', 'numCachedPartitions', 'numPartitions', 'dataUnavailable',
203
203
  ],
204
204
  coreLocality: ['nonLocalTaskCount'],
205
- autoscalingChurn: ['shortLivedExecutorCount'],
205
+ autoscalingChurn: ['shortLivedExecutorCount', 'dynamicAllocation'],
206
206
  cachingOpportunity: ['variant', 'relation', 'format', 'relations', 'operator', 'executionIds', 'totalReadBytes'],
207
207
  jobFailureRate: ['failedJobs', 'totalJobs', 'failedTasks', 'totalTasks', 'avgJobDurationMs', 'taskFailureRate'],
208
208
  configAudit: ['property'],
@@ -211,8 +211,8 @@ const EVIDENCE_KEYS = {
211
211
  'groupIndex',
212
212
  ],
213
213
  smallFiles: ['executionId', 'stageIds', 'fileCount', 'direction', 'nodeName'],
214
- underBroadcast: ['executionId', 'stageIds', 'largerSideBytes'],
215
- overBroadcast: ['executionId', 'stageIds'],
214
+ underBroadcast: ['executionId', 'stageIds', 'largerSideBytes', 'broadcastThreshold'],
215
+ overBroadcast: ['executionId', 'stageIds', 'broadcastThreshold'],
216
216
  } ;
217
217
 
218
218
  // The other direction: an evidence field EVIDENCE_KEYS doesn't list fails here.
@@ -8,7 +8,7 @@
8
8
  // detectors.ts is type-only, so it is erased at build time. A detector's scope, order and emits
9
9
  // list stay on its DETECTORS entry; renderers get them through detectorInfoByType().
10
10
 
11
-
11
+
12
12
 
13
13
 
14
14
 
@@ -35,7 +35,14 @@ export const shareLabel = (share ) => `${Math.round(share * 1e6)
35
35
  // a generic line reads the same decision and never recommends a switch the row says is on.
36
36
  // A finding with no remediation (older or hand-built data) keeps the property wording.
37
37
  function switchAlreadyOn(finding , key ) {
38
- return finding.remediation != null && !finding.remediation.some((r) => r.key === key);
38
+ return finding.remediation != null && !finding.remediation.some((r) => r.kind === 'conf' && r.key === key);
39
+ }
40
+
41
+ // The properties an idle-capacity finding lowers once dynamic allocation is on, worded as its row
42
+ // does: the executor cap, and the floor when the row's remediation lowers it too.
43
+ function idleCapacityLowering(f ) {
44
+ const lowersFloor = f.remediation?.some((r) => r.kind === 'conf' && r.key === 'spark.dynamicAllocation.minExecutors');
45
+ return ` by lowering spark.dynamicAllocation.maxExecutors${lowersFloor ? ' and spark.dynamicAllocation.minExecutors' : ''}`;
39
46
  }
40
47
 
41
48
  const SKEW_JOIN_KEY = 'spark.sql.adaptive.skewJoin.enabled';
@@ -43,8 +50,10 @@ const SKEW_JOIN_ALREADY_ON = 'AQE skew-join handling is already on, so salt the
43
50
  const SKEW_JOIN_AQE_OFF = 'AQE is off, so enable it (spark.sql.adaptive.enabled) for skew-join handling to apply; otherwise salt the key or repartition on a better key.';
44
51
 
45
52
  // The skew-join generic line, worded per the row's remediation: AQE logged off, switch already on, or neither.
46
- function skewJoinGeneric(f , unset ) {
47
- if (f.remediation?.some((r) => r.key === 'spark.sql.adaptive.enabled')) return SKEW_JOIN_AQE_OFF;
53
+ function skewJoinGeneric(f , unset ) {
54
+ if (f.origin === 'inputScan') return 'Uneven input files: compact small files or split large ones (lower spark.sql.files.maxPartitionBytes).';
55
+ if (f.origin === 'other') return 'Work is uneven across tasks: salt the key or repartition on a better key.';
56
+ if (f.remediation?.some((r) => r.kind === 'conf' && r.key === 'spark.sql.adaptive.enabled')) return SKEW_JOIN_AQE_OFF;
48
57
  return switchAlreadyOn(f, SKEW_JOIN_KEY) ? SKEW_JOIN_ALREADY_ON : unset;
49
58
  }
50
59
  const DYNAMIC_ALLOCATION_KEY = 'spark.dynamicAllocation.enabled';
@@ -120,8 +129,9 @@ export const FINDING_PRESENTATION
120
129
  tag: 'TINY',
121
130
  thresholdSummary: (t) => `${t.minTasks}+ tasks with a median of ${t.maxP50}ms or less and a P95 of ${t.maxP95}ms or less`,
122
131
  actionLabel: () => 'Coalesce small tasks',
123
- // The shuffle-vs-no-shuffle fix isn't a Finding field, so one sentence covers both.
124
- genericRecommendation: () => 'Scheduler overhead may dominate: lower spark.sql.shuffle.partitions, or coalesce down to fewer, larger tasks.',
132
+ genericRecommendation: (f) => (f.reads != null && f.reads !== 'shuffle'
133
+ ? 'Scheduler overhead may dominate: coalesce down to fewer, larger tasks.'
134
+ : 'Scheduler overhead may dominate: lower spark.sql.shuffle.partitions, or coalesce down to fewer, larger tasks.'),
125
135
  },
126
136
 
127
137
  shuffle: {
@@ -129,7 +139,14 @@ export const FINDING_PRESENTATION
129
139
  tag: 'SHFL',
130
140
  thresholdSummary: (t) => `stage shuffle read above ${t.minBytes / 1048576} MiB`,
131
141
  actionLabel: () => 'Reduce shuffle size',
132
- genericRecommendation: () => 'Raise spark.sql.shuffle.partitions, or use a broadcast join for the smaller side.',
142
+ genericRecommendation: (f) => {
143
+ switch (f.partitions) {
144
+ case 'sufficient': return 'The tasks are already a good size, so more partitions will not help: use a broadcast join for the smaller side.';
145
+ case 'aqeCoalesced': return 'AQE coalesced the shuffle: lower spark.sql.adaptive.advisoryPartitionSizeInBytes, or use a broadcast join for the smaller side.';
146
+ case 'ownPartitioning': return "spark.sql.shuffle.partitions is already high enough, so raise this stage's own partition count (its repartition(n) or RDD parallelism), or use a broadcast join for the smaller side.";
147
+ default: return 'Raise spark.sql.shuffle.partitions, or use a broadcast join for the smaller side.';
148
+ }
149
+ },
133
150
  },
134
151
  partitionSizing: {
135
152
  name: 'partition sizing',
@@ -146,7 +163,9 @@ export const FINDING_PRESENTATION
146
163
  genericRecommendation(f) {
147
164
  switch (f.rule) {
148
165
  case 'shufflePartitionSkew': return skewJoinGeneric(f, 'For join skew, enable AQE skew-join handling (spark.sql.adaptive.skewJoin.enabled); otherwise salt the key or repartition on a better key.');
149
- case 'lowShuffleParallelism': return switchAlreadyOn(f, 'spark.sql.shuffle.partitions')
166
+ case 'lowShuffleParallelism':
167
+ if (f.partitions === 'aqeCoalesced') return 'AQE coalesced the shuffle into few tasks: lower spark.sql.adaptive.advisoryPartitionSizeInBytes so each partition is smaller.';
168
+ return switchAlreadyOn(f, 'spark.sql.shuffle.partitions')
150
169
  ? "spark.sql.shuffle.partitions is already high enough, so raise this stage's own partition count (its repartition(n) or RDD parallelism) so each partition is smaller."
151
170
  : 'Raise spark.sql.shuffle.partitions so each partition is smaller.';
152
171
  case 'maxPartitionTooBig': return 'Repartition to break up the oversized partition before this stage.';
@@ -161,7 +180,9 @@ export const FINDING_PRESENTATION
161
180
  thresholdSummary: (t) => `single-task disk spill above ${t.singleTaskDiskGiB} GiB`,
162
181
  actionLabel: () => 'Reduce spill',
163
182
  // The skew/volume classification isn't a Finding field, so one sentence covers both.
164
- genericRecommendation: () => 'If the spill is skew-driven, fix task skew first: adding memory will not help. Otherwise raise spark.sql.shuffle.partitions or increase executor memory.',
183
+ genericRecommendation: (f) => (f.reads != null && f.reads !== 'shuffle'
184
+ ? 'If the spill is skew-driven, fix task skew first: adding memory will not help. Otherwise increase executor memory or process less data per task.'
185
+ : 'If the spill is skew-driven, fix task skew first: adding memory will not help. Otherwise raise spark.sql.shuffle.partitions or increase executor memory.'),
165
186
  },
166
187
 
167
188
  gc: {
@@ -219,14 +240,18 @@ export const FINDING_PRESENTATION
219
240
  tag: 'SLOW',
220
241
  thresholdSummary: () => 'a stage running far longer than its peers, not attributable to a single slow host',
221
242
  actionLabel: () => 'Profile slow stage',
222
- genericRecommendation: () => 'Often a partition-count problem: raise parallelism via spark.sql.shuffle.partitions or spark.default.parallelism, or check for a large per-task data volume driving heavy shuffle and spill.',
243
+ genericRecommendation: (f) => f.reads === 'input'
244
+ ? 'Often too few or too uneven input partitions: check input file sizes and lower spark.sql.files.maxPartitionBytes, or look for a large per-task data volume driving heavy spill.'
245
+ : f.reads === 'other'
246
+ ? 'Check what the stage computes and for a large per-task data volume driving heavy spill.'
247
+ : 'Often a partition-count problem: raise parallelism via spark.sql.shuffle.partitions or spark.default.parallelism, or check for a large per-task data volume driving heavy shuffle and spill.',
223
248
  },
224
249
  straggler: {
225
250
  name: 'straggling task',
226
251
  tag: 'STRAG',
227
252
  thresholdSummary: () => 'one or more tasks finishing far after the rest of their stage',
228
253
  actionLabel: () => 'Fix stragglers',
229
- genericRecommendation: () => 'Rule out a GC pause or a slow shuffle fetch before assuming a hardware issue. If a skewed key is the real cause, that is a candidate for AQE\'s skew-join handling.',
254
+ genericRecommendation: (f) => `Rule out a GC pause or a slow shuffle fetch before assuming a hardware issue. If uneven data is the cause: ${skewJoinGeneric(f, 'for join-driven skew, enable AQE skew-join handling (spark.sql.adaptive.skewJoin.enabled); otherwise salt the key or repartition on a better key.')}`,
230
255
  },
231
256
  speculationWaste: {
232
257
  name: 'speculation waste',
@@ -240,7 +265,9 @@ export const FINDING_PRESENTATION
240
265
  tag: 'COLD',
241
266
  thresholdSummary: (t) => `the first stage waiting over ${t.gapSeconds}s for an executor`,
242
267
  actionLabel: () => 'Pre-warm cluster',
243
- genericRecommendation: () => 'Keep a warm pool of idle executors, or if using dynamic allocation, raise the minimum/initial executor count so it does not scale up from zero.',
268
+ genericRecommendation: (f) => (f.dynamicAllocation === 'off'
269
+ ? 'Keep a warm pool of idle executors: dynamic allocation is off, so no executor-count property applies.'
270
+ : 'Keep a warm pool of idle executors, or if using dynamic allocation, raise the minimum/initial executor count so it does not scale up from zero.'),
244
271
  },
245
272
 
246
273
  memoryUtilization: {
@@ -260,8 +287,8 @@ export const FINDING_PRESENTATION
260
287
  genericRecommendation(f) {
261
288
  switch (f.variant) {
262
289
  case 'idleCores': return switchAlreadyOn(f, DYNAMIC_ALLOCATION_KEY)
263
- ? 'Dynamic allocation is already on, so reduce cluster size.'
264
- : 'Reduce cluster size or enable dynamic allocation.';
290
+ ? `Dynamic allocation is already on, so reduce cluster size${idleCapacityLowering(f)}.`
291
+ : 'Either reduce cluster size (spark.executor.instances) or enable dynamic allocation.';
265
292
  case 'wasteModel': return 'Review spark.executor.memory and executor count.';
266
293
  case 'memoryBand':
267
294
  if (f.dataUnavailable) return undefined;
@@ -278,8 +305,8 @@ export const FINDING_PRESENTATION
278
305
  thresholdSummary: (t) => `average executor utilization below ${shareLabel(t.minUtil)}`,
279
306
  actionLabel: () => 'Reduce cluster size',
280
307
  genericRecommendation: (f) => (switchAlreadyOn(f, DYNAMIC_ALLOCATION_KEY)
281
- ? 'Dynamic allocation is already on, so consider reducing cluster size.'
282
- : 'Consider reducing cluster size or enabling dynamic allocation.'),
308
+ ? `Dynamic allocation is already on, so consider reducing cluster size${idleCapacityLowering(f)}.`
309
+ : 'Consider either reducing cluster size (spark.executor.instances) or enabling dynamic allocation.'),
283
310
  },
284
311
  coreLocality: {
285
312
  name: 'core locality',
@@ -322,7 +349,9 @@ export const FINDING_PRESENTATION
322
349
  tag: 'CHRN',
323
350
  thresholdSummary: (t) => `over ${shareLabel(t.warningPct)} of executors living under ${t.shortLivedMs / 60000} minutes`,
324
351
  actionLabel: () => 'Reduce autoscaling churn',
325
- genericRecommendation: () => 'This looks like wasteful re-provisioning rather than normal scale-down: consider raising spark.dynamicAllocation.executorIdleTimeout or widening the minExecutors/maxExecutors bounds to reduce flapping.',
352
+ genericRecommendation: (f) => (f.dynamicAllocation === 'off'
353
+ ? 'Dynamic allocation is off, so the churn comes from executors lost or preempted: check the cluster manager\'s preemption and executor-loss logs.'
354
+ : 'This looks like wasteful re-provisioning rather than normal scale-down: consider raising spark.dynamicAllocation.executorIdleTimeout or widening the minExecutors/maxExecutors bounds to reduce flapping.'),
326
355
  },
327
356
 
328
357
  configAudit: CONFIG_AUDIT_PRESENTATION,
@@ -349,14 +378,18 @@ export const FINDING_PRESENTATION
349
378
  tag: 'PLAN',
350
379
  thresholdSummary: () => 'a join below the configured size floor that skipped broadcast',
351
380
  actionLabel: () => 'Use broadcast join',
352
- genericRecommendation: () => 'This could have been a broadcast join: consider a broadcast() hint or raising spark.sql.autoBroadcastJoinThreshold.',
381
+ genericRecommendation: (f) => (f.broadcastThreshold === 'notLimiting'
382
+ ? 'The threshold already admits the smaller side, so it is not what stopped the broadcast: consider a broadcast() hint or collecting table statistics.'
383
+ : 'This could have been a broadcast join: consider a broadcast() hint or raising spark.sql.autoBroadcastJoinThreshold.'),
353
384
  },
354
385
  overBroadcast: {
355
386
  name: 'oversized broadcast join',
356
387
  tag: 'PLAN',
357
388
  thresholdSummary: (t) => `a broadcast over ${t.overBroadcastBytes / 1073741824} GiB`,
358
389
  actionLabel: () => 'Fix oversized broadcast',
359
- genericRecommendation: (f) => (switchAlreadyOn(f, 'spark.sql.autoBroadcastJoinThreshold')
390
+ genericRecommendation: (f) => (f.broadcastThreshold === 'notLimiting'
391
+ ? 'The configured threshold is below this broadcast, so remove the broadcast() hint that forced it.'
392
+ : switchAlreadyOn(f, 'spark.sql.autoBroadcastJoinThreshold')
360
393
  ? 'Automatic broadcast is already disabled, so remove the broadcast() hint that forced it.'
361
394
  : 'Check for a misapplied broadcast hint or a misconfigured spark.sql.autoBroadcastJoinThreshold.'),
362
395
  },
@@ -10,16 +10,27 @@
10
10
 
11
11
 
12
12
 
13
- /** One concrete change a finding's fix makes, alongside its prose `recommendation`. Only a Spark
14
- * property the detector already names; `suggested` is null when it computes no value. Sizes carry a
15
- * Spark unit suffix ("384m"), counts are plain numbers, switches are booleans. */
16
-
13
+ /** One concrete change a finding's fix makes, alongside its prose `recommendation`. Entries are
14
+ * applied together unless the recommendation words them as alternatives ("either ... or"), as an
15
+ * idle-capacity finding does with dynamic allocation off: then a consumer applies one. */
16
+
17
+
18
+ /** A Spark property the detector already names; `suggested` is null when it computes no value.
19
+ * Sizes carry a Spark unit suffix ("384m"), counts are plain numbers, switches are booleans. */
20
+
17
21
 
18
22
 
19
23
 
20
24
 
21
25
 
22
26
 
27
+ /** A fix no Spark property makes: the job's code or data has to change. `hint` is the same
28
+ * wording the finding's `recommendation` gives. */
29
+
30
+
31
+
32
+
33
+
23
34
  // The columns every finding carries, whatever its detector.
24
35
 
25
36
 
@@ -79,7 +90,22 @@
79
90
 
80
91
  // ── Per-stage detectors ─────────────────────────────────────────────────────
81
92
 
82
-
93
+ /** What a skewed stage reads, which decides the fix: 'shuffleJoin' (a shuffle feeding a join, where
94
+ * AQE skew-join handling applies), 'inputScan' (uneven input files) or 'other'. */
95
+
96
+
97
+ /** What a stage reads, by the dominant side: a shuffle, input files, or neither. */
98
+
99
+
100
+ /** Why a shuffle finding's partition-count advice is or is not given: 'raise' (the property limits
101
+ * the stage), 'sufficient' (tasks are already a good size), 'aqeCoalesced' (AQE merged the
102
+ * partitions, so the advisory size is the lever) or 'ownPartitioning' (the property is already high
103
+ * enough, so the stage's own repartition(n) or RDD parallelism is). */
104
+
105
+
106
+
107
+
108
+
83
109
 
84
110
 
85
111
 
@@ -90,16 +116,24 @@
90
116
 
91
117
 
92
118
 
93
-
119
+
120
+
121
+
94
122
 
95
123
 
96
124
 
97
125
 
126
+
127
+
128
+
129
+
98
130
 
99
131
 
100
132
 
101
133
 
102
134
 
135
+
136
+
103
137
 
104
138
 
105
139
 
@@ -124,7 +158,10 @@
124
158
 
125
159
 
126
160
 
127
-
161
+
162
+
163
+
164
+
128
165
 
129
166
 
130
167
 
@@ -151,6 +188,8 @@
151
188
 
152
189
 
153
190
 
191
+
192
+
154
193
 
155
194
 
156
195
 
@@ -167,7 +206,10 @@
167
206
 
168
207
 
169
208
 
170
-
209
+
210
+
211
+
212
+
171
213
 
172
214
 
173
215
  // ── App-level detectors ─────────────────────────────────────────────────────
@@ -176,7 +218,10 @@
176
218
  // valueText is always 'missing': the ApplicationEnd event the log lacks.
177
219
 
178
220
 
179
-
221
+
222
+
223
+
224
+
180
225
 
181
226
 
182
227
 
@@ -184,11 +229,12 @@
184
229
 
185
230
 
186
231
 
187
-
188
-
232
+
233
+
189
234
 
190
235
 
191
236
 
237
+
192
238
 
193
239
 
194
240
 
@@ -202,11 +248,10 @@
202
248
 
203
249
 
204
250
 
205
-
206
-
251
+
252
+
207
253
 
208
-
209
-
254
+
210
255
 
211
256
 
212
257
 
@@ -231,6 +276,8 @@
231
276
 
232
277
 
233
278
 
279
+
280
+
234
281
 
235
282
 
236
283
 
@@ -296,13 +343,21 @@
296
343
 
297
344
 
298
345
 
346
+
347
+ /** How the effective spark.sql.autoBroadcastJoinThreshold relates to the finding: 'limits' (the
348
+ * property decided it, or is unknown), 'notLimiting' (already admits, or already below, the
349
+ * broadcast, so something else decided it) or 'disabled' (-1). */
350
+
299
351
 
300
352
 
301
353
 
354
+
302
355
 
303
356
 
304
357
 
305
-
358
+
359
+
360
+
306
361
 
307
362
 
308
363
  // ── Unions ──────────────────────────────────────────────────────────────────