sparkforensics-server 0.6.0 → 0.8.1

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 (410) hide show
  1. package/index.js +16 -7
  2. package/package.json +1 -1
  3. package/public/assets/AutoscalingChurn-DenWKTv-.js +1 -0
  4. package/public/assets/CacheUtilization-DLz7NdjV.js +1 -0
  5. package/public/assets/CachingOpportunity-zpQIqhzm.js +1 -0
  6. package/public/assets/ChartTheme-DAiARbLP.js +8 -0
  7. package/public/assets/ColdStart-C-9HjxWl.js +1 -0
  8. package/public/assets/ConfigAudit-D0V9tcrJ.js +1 -0
  9. package/public/assets/CoreUsageArea-yeerrljd.js +1 -0
  10. package/public/assets/Dashboard-DkUPrHKV.js +1 -0
  11. package/public/assets/DisclosureContext-DK1_iz6J.js +1 -0
  12. package/public/assets/DuplicatePlanSubtree-xDNDbCd3.js +1 -0
  13. package/public/assets/DurationHistogram-B6jsE0Ov.js +1 -0
  14. package/public/assets/EmptyState-C1uL40VU.js +1 -0
  15. package/public/assets/ExecutorUtilization-DXDZKmSc.js +1 -0
  16. package/public/assets/GcPressure-MRf7I4IG.js +1 -0
  17. package/public/assets/ImpactEstimate-Dm3xWbzV.js +1 -0
  18. package/public/assets/IncompleteRun-CaLOhMtf.js +1 -0
  19. package/public/assets/JobFailures-CzmzlvhO.js +1 -0
  20. package/public/assets/MemoryUtilization-AWUMUjCH.js +1 -0
  21. package/public/assets/NestedLoopJoin-C9sNr6Ss.js +1 -0
  22. package/public/assets/OverBroadcast-BOmRE6ca.js +1 -0
  23. package/public/assets/PartitionHint-BL2EmJfE.js +1 -0
  24. package/public/assets/PartitionSizing-BzBySvUq.js +1 -0
  25. package/public/assets/PlanExplorer-DdiQRcJU.js +1 -0
  26. package/public/assets/PlanGraphRoute-CIH9gBuR.js +1 -0
  27. package/public/assets/PlanView-B1hxzFRs.js +2 -0
  28. package/public/assets/PythonUdf-DvgCCueD.js +1 -0
  29. package/public/assets/RetryWaste-uXXu_CyM.js +1 -0
  30. package/public/assets/RowPagination-C-LzWkI5.js +1 -0
  31. package/public/assets/RowStatusCluster-Dbtmte7l.js +1 -0
  32. package/public/assets/ShuffleIO-nALwzC1_.js +1 -0
  33. package/public/assets/Skew-CHMEKgvk.js +1 -0
  34. package/public/assets/SlowHost-yyvjpHk6.js +1 -0
  35. package/public/assets/SmallFiles-y6odJ97n.js +1 -0
  36. package/public/assets/SpeculationWaste-C-SD2LOt.js +1 -0
  37. package/public/assets/Spill-DHTMmOup.js +1 -0
  38. package/public/assets/StageFailed-CQ3DxC9R.js +1 -0
  39. package/public/assets/StageFindingGroup-BgYLyj5q.js +1 -0
  40. package/public/assets/StageHeader-CIETkjVZ.js +1 -0
  41. package/public/assets/StagePill--P7PDd3s.js +1 -0
  42. package/public/assets/StageShape-BIv8vx_o.js +1 -0
  43. package/public/assets/StageSlowness-MMqzERxN.js +1 -0
  44. package/public/assets/Straggler-BgrY7AyW.js +1 -0
  45. package/public/assets/TaskFailures-BHHyKYTs.js +1 -0
  46. package/public/assets/TinyTask-DDScNhCl.js +1 -0
  47. package/public/assets/UnderBroadcast-CGIC04TI.js +1 -0
  48. package/public/assets/WidgetLeadSummary-CXFZP00C.js +1 -0
  49. package/public/assets/button-DbpTz2yk.js +1 -0
  50. package/public/assets/charts-vendor-CtTxwTeu.js +59 -0
  51. package/public/assets/composite-C9Nu2EI9.js +1 -0
  52. package/public/assets/d3-vendor-DNlD2-cz.js +1 -0
  53. package/public/assets/detector-registry-Bp8hrLua.js +2 -0
  54. package/public/assets/downsample-WmNM1qc9.js +1 -0
  55. package/public/assets/finding-anchor-Dr40LkS9.js +1 -0
  56. package/public/assets/findings-of-type-BL2u3RGZ.js +1 -0
  57. package/public/assets/impact-sort-mC5ISgX8.js +1 -0
  58. package/public/assets/inbox-KPU1Gppm.js +1 -0
  59. package/public/assets/index-CaRVPvAT.css +2 -0
  60. package/public/assets/index-fDiwfuoO.js +14 -0
  61. package/public/assets/inertValue-L9VxV0VE.js +1 -0
  62. package/public/assets/layout.worker-CLcBHxVp.js +3 -0
  63. package/public/assets/{parser-worker-CA5RxqKc.js → parser-worker-CRIOPMOW.js} +8 -8
  64. package/public/assets/plan-graph-vendor-Ce6d4HU6.js +7 -0
  65. package/public/assets/react-vendor-C6KcUD36.js +9 -0
  66. package/public/assets/select-BHqOqun5.js +1 -0
  67. package/public/assets/toggle-DPeN6aNa.js +1 -0
  68. package/public/assets/toggle-group-Kl3uyHSr.js +1 -0
  69. package/public/assets/usePagedRows-BafD6bEN.js +1 -0
  70. package/public/assets/useRecentFiles-DzJZAoOT.js +3 -0
  71. package/public/assets/useRegisterFieldControl-pQv-ZDoU.js +1 -0
  72. package/public/assets/useScrollLock-BCxn_dMp.js +1 -0
  73. package/public/assets/useSortMode-DjrAN-z4.js +1 -0
  74. package/public/docs/404.html +3 -3
  75. package/public/docs/assets/{app.CnWL6bKV.js → app.1gvzd5Zb.js} +1 -1
  76. package/public/docs/assets/chunks/@localSearchIndexroot.DS2TQP5_.js +1 -0
  77. package/public/docs/assets/chunks/{VPLocalSearchBox.C0IBKeRQ.js → VPLocalSearchBox.CNxT3s0I.js} +1 -1
  78. package/public/docs/assets/chunks/{theme.BH-2HuRf.js → theme.C5lLqUv7.js} +2 -2
  79. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.js +1 -0
  80. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.lean.js +1 -0
  81. package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.js} +1 -1
  82. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.js +1 -0
  83. package/public/docs/assets/{contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.js → contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.js} +1 -1
  84. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.lean.js +1 -0
  85. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CHHgywoP.js +1 -0
  86. package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.N4hhenrA.lean.js → contributor-guide_architecture_detector-contract.md.CHHgywoP.lean.js} +1 -1
  87. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.js +1 -0
  88. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.lean.js +1 -0
  89. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.js +1 -0
  90. package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.lean.js} +1 -1
  91. package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.js} +1 -1
  92. package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.lean.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.lean.js} +1 -1
  93. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.js +1 -0
  94. package/public/docs/assets/{contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.lean.js → contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.lean.js} +1 -1
  95. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.K71YB-Rr.js +1 -0
  96. package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.DfvbqEUu.lean.js → contributor-guide_architecture_impact-estimation.md.K71YB-Rr.lean.js} +1 -1
  97. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.js +1 -0
  98. package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.lean.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.lean.js} +1 -1
  99. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.js +1 -0
  100. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.lean.js +1 -0
  101. package/public/docs/assets/contributor-guide_architecture_overview.md.nxkpOhgf.js +1 -0
  102. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.js +1 -0
  103. package/public/docs/assets/{contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js → contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.lean.js} +1 -1
  104. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.js +1 -0
  105. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.js +1 -0
  106. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.lean.js +1 -0
  107. package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.js} +1 -1
  108. package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.js} +1 -1
  109. package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js → contributor-guide_architecture_worker-protocol.md.Br6OE7RW.js} +1 -1
  110. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.Br6OE7RW.lean.js +1 -0
  111. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.js +1 -0
  112. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.lean.js} +1 -1
  113. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.js} +1 -1
  114. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.js} +2 -2
  115. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.lean.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.lean.js} +1 -1
  116. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.js +1 -0
  117. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.lean.js} +1 -1
  118. package/public/docs/assets/contributor-guide_contributing.md.DBW4drtZ.js +1 -0
  119. package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.js +15 -0
  120. package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.lean.js +1 -0
  121. package/public/docs/assets/contributor-guide_testing.md.DpkozNoG.js +4 -0
  122. package/public/docs/assets/{contributor-guide_testing.md.By6XGR9a.lean.js → contributor-guide_testing.md.DpkozNoG.lean.js} +1 -1
  123. package/public/docs/assets/{index.md.CHJVslga.js → index.md.lMm7SUMz.js} +1 -1
  124. package/public/docs/assets/{index.md.CHJVslga.lean.js → index.md.lMm7SUMz.lean.js} +1 -1
  125. package/public/docs/assets/{style.CyK9Ss73.css → style.Cfxqlj0y.css} +1 -1
  126. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.C0pcQoNc.js +29 -0
  127. package/public/docs/assets/{user-guide_getting-started_ci-and-automation.md.BEepbDDL.lean.js → user-guide_getting-started_ci-and-automation.md.C0pcQoNc.lean.js} +1 -1
  128. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.js +1 -0
  129. package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.js} +2 -2
  130. package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.lean.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.lean.js} +1 -1
  131. package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.js → user-guide_mcp-tools.md.49r3L2YE.js} +1 -1
  132. package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js → user-guide_mcp-tools_compare-runs.md.BQ_kIptC.js} +11 -6
  133. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.BQ_kIptC.lean.js +1 -0
  134. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.azCZXkH8.js +44 -0
  135. package/public/docs/assets/{user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.lean.js → user-guide_mcp-tools_diagnose-run.md.azCZXkH8.lean.js} +1 -1
  136. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.js} +2 -2
  137. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.lean.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.lean.js} +1 -1
  138. package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.js} +2 -1
  139. package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.lean.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.lean.js} +1 -1
  140. package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.js +1 -0
  141. package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.lean.js +1 -0
  142. package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.js +1 -0
  143. package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.lean.js +1 -0
  144. package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +6 -6
  145. package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +6 -6
  146. package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -6
  147. package/public/docs/contributor-guide/architecture/board-widgets.html +6 -6
  148. package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +6 -6
  149. package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -6
  150. package/public/docs/contributor-guide/architecture/detector-contract.html +6 -6
  151. package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +6 -6
  152. package/public/docs/contributor-guide/architecture/drill-down.html +6 -6
  153. package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -6
  154. package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +6 -6
  155. package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -6
  156. package/public/docs/contributor-guide/architecture/index.html +5 -5
  157. package/public/docs/contributor-guide/architecture/overview.html +6 -6
  158. package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +5 -5
  159. package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -6
  160. package/public/docs/contributor-guide/architecture/state-and-history.html +5 -5
  161. package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +6 -6
  162. package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +5 -5
  163. package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +6 -6
  164. package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +5 -5
  165. package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +6 -6
  166. package/public/docs/contributor-guide/architecture/widget-rendering.html +6 -6
  167. package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -6
  168. package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +6 -6
  169. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +7 -7
  170. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -6
  171. package/public/docs/contributor-guide/architecture/worker-protocol.html +6 -6
  172. package/public/docs/contributor-guide/contributing.html +6 -6
  173. package/public/docs/contributor-guide/development-setup.html +8 -8
  174. package/public/docs/contributor-guide/testing.html +9 -6
  175. package/public/docs/hashmap.json +1 -1
  176. package/public/docs/index.html +6 -6
  177. package/public/docs/logo-dark.svg +1 -0
  178. package/public/docs/logo-light.svg +1 -0
  179. package/public/docs/tuning-reference/anti-patterns.html +5 -5
  180. package/public/docs/tuning-reference/aqe.html +5 -5
  181. package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -5
  182. package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -5
  183. package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -5
  184. package/public/docs/tuning-reference/bottleneck-failures.html +5 -5
  185. package/public/docs/tuning-reference/bottleneck-gc.html +5 -5
  186. package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -5
  187. package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -5
  188. package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -5
  189. package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -5
  190. package/public/docs/tuning-reference/bottleneck-skew.html +5 -5
  191. package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -5
  192. package/public/docs/tuning-reference/bottleneck-small-files.html +5 -5
  193. package/public/docs/tuning-reference/bottleneck-spill.html +5 -5
  194. package/public/docs/tuning-reference/bottleneck-straggler.html +5 -5
  195. package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -5
  196. package/public/docs/tuning-reference/bottleneck-utilization.html +5 -5
  197. package/public/docs/tuning-reference/caching.html +5 -5
  198. package/public/docs/tuning-reference/cluster-config.html +5 -5
  199. package/public/docs/tuning-reference/config.html +5 -5
  200. package/public/docs/tuning-reference/data-formats.html +5 -5
  201. package/public/docs/tuning-reference/index.html +5 -5
  202. package/public/docs/tuning-reference/intro.html +5 -5
  203. package/public/docs/tuning-reference/joins.html +5 -5
  204. package/public/docs/tuning-reference/memory-model.html +5 -5
  205. package/public/docs/tuning-reference/metrics.html +5 -5
  206. package/public/docs/tuning-reference/partitioning.html +5 -5
  207. package/public/docs/tuning-reference/pyspark.html +5 -5
  208. package/public/docs/tuning-reference/shuffle.html +5 -5
  209. package/public/docs/tuning-reference/spark-architecture.html +5 -5
  210. package/public/docs/tuning-reference/table-formats.html +5 -5
  211. package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +5 -5
  212. package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +5 -5
  213. package/public/docs/user-guide/alternative-log-retrieval.html +5 -5
  214. package/public/docs/user-guide/getting-started/ci-and-automation.html +16 -11
  215. package/public/docs/user-guide/getting-started/reading-the-dashboard.html +6 -6
  216. package/public/docs/user-guide/getting-started/regression-metric-keys.html +5 -5
  217. package/public/docs/user-guide/getting-started/tuning-thresholds.html +7 -7
  218. package/public/docs/user-guide/getting-started.html +5 -5
  219. package/public/docs/user-guide/mcp-tools/compare-runs.html +16 -11
  220. package/public/docs/user-guide/mcp-tools/diagnose-run.html +28 -7
  221. package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -6
  222. package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +5 -5
  223. package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +7 -6
  224. package/public/docs/user-guide/mcp-tools/get-reference-doc.html +5 -5
  225. package/public/docs/user-guide/mcp-tools/get-run-summary.html +5 -5
  226. package/public/docs/user-guide/mcp-tools/list-runs.html +5 -5
  227. package/public/docs/user-guide/mcp-tools.html +6 -6
  228. package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +5 -5
  229. package/public/docs/user-guide/run-comparison.html +6 -6
  230. package/public/docs/user-guide/understanding-findings.html +6 -6
  231. package/public/export-template.html +45 -45
  232. package/public/index.html +8 -7
  233. package/public/sample-runs/sample-run.ndjson.gz +0 -0
  234. package/vendor-core/allocation.js +25 -23
  235. package/vendor-core/analyzer.js +85 -58
  236. package/vendor-core/api-merge-writes.js +2 -0
  237. package/vendor-core/aqe-skew.js +347 -0
  238. package/vendor-core/cli/budgets.js +6 -4
  239. package/vendor-core/cli/collect-run-pair.js +89 -0
  240. package/vendor-core/cli/collect-run-worker.js +16 -0
  241. package/vendor-core/cli/collect-run.js +1 -2
  242. package/vendor-core/cli/native-zstd.js +12 -16
  243. package/vendor-core/comparability.js +234 -0
  244. package/vendor-core/comparison-output.js +15 -7
  245. package/vendor-core/comparison-verdict.js +105 -25
  246. package/vendor-core/core-source-hash.txt +1 -1
  247. package/vendor-core/core-time-series.js +0 -1
  248. package/vendor-core/core-usage-locality.js +1 -1
  249. package/vendor-core/detectors.js +1007 -380
  250. package/vendor-core/docs-config.js +0 -4
  251. package/vendor-core/docs-content/detection/cache.md +2 -1
  252. package/vendor-core/docs-content/detection/cfg.md +19 -11
  253. package/vendor-core/docs-content/detection/cold.md +2 -1
  254. package/vendor-core/docs-content/detection/gc.md +7 -3
  255. package/vendor-core/docs-content/detection/host.md +11 -1
  256. package/vendor-core/docs-content/detection/mem.md +17 -6
  257. package/vendor-core/docs-content/detection/part.md +3 -1
  258. package/vendor-core/docs-content/detection/plan.md +55 -13
  259. package/vendor-core/docs-content/detection/shfl.md +11 -5
  260. package/vendor-core/docs-content/detection/skew.md +47 -1
  261. package/vendor-core/docs-content/detection/slow.md +4 -2
  262. package/vendor-core/docs-content/detection/spec.md +6 -1
  263. package/vendor-core/docs-content/detection/spill.md +6 -3
  264. package/vendor-core/docs-content/detection/strag.md +19 -6
  265. package/vendor-core/docs-content/detection/tiny.md +9 -3
  266. package/vendor-core/effective-conf.js +7 -10
  267. package/vendor-core/efficiency-model.js +2 -2
  268. package/vendor-core/etl-phases.js +1 -2
  269. package/vendor-core/event-handlers.js +175 -57
  270. package/vendor-core/event-schemas.js +21 -16
  271. package/vendor-core/evidence-availability.js +9 -2
  272. package/vendor-core/evidence-report.js +118 -35
  273. package/vendor-core/executor-peaks.js +41 -0
  274. package/vendor-core/export-data.js +5 -5
  275. package/vendor-core/finding-identity.js +78 -0
  276. package/vendor-core/finding-presentation.js +116 -33
  277. package/vendor-core/finding-types.js +88 -21
  278. package/vendor-core/format-utils.js +14 -15
  279. package/vendor-core/html-export.js +1 -1
  280. package/vendor-core/impact-band.js +2 -2
  281. package/vendor-core/impact-format.js +47 -19
  282. package/vendor-core/impact-model.js +26 -15
  283. package/vendor-core/job-groups.js +4 -4
  284. package/vendor-core/mcp-server-factory.js +48 -12
  285. package/vendor-core/mcp-tools.js +179 -24
  286. package/vendor-core/median.js +2 -2
  287. package/vendor-core/nested-loop-join.js +73 -0
  288. package/vendor-core/occupancy.js +1 -2
  289. package/vendor-core/parser-worker.js +1 -1
  290. package/vendor-core/plan-graph-model.js +14 -22
  291. package/vendor-core/plan-node-detail.js +2 -3
  292. package/vendor-core/plan-summary.js +24 -19
  293. package/vendor-core/plan-tree-walk.js +6 -8
  294. package/vendor-core/python-stage.js +12 -1
  295. package/vendor-core/recommendation-rollup.js +13 -12
  296. package/vendor-core/redact.js +52 -46
  297. package/vendor-core/run-aggregates.js +6 -8
  298. package/vendor-core/run-comparison.js +226 -66
  299. package/vendor-core/run-identity.js +12 -0
  300. package/vendor-core/run-interpretation.js +21 -0
  301. package/vendor-core/run-metrics.js +2 -2
  302. package/vendor-core/run-output.js +32 -10
  303. package/vendor-core/run-totals.js +12 -0
  304. package/vendor-core/run-verdict.js +76 -3
  305. package/vendor-core/scaling-sim.js +3 -4
  306. package/vendor-core/snappy-block.js +2 -2
  307. package/vendor-core/spark-conf.js +168 -0
  308. package/vendor-core/sql-stages.js +2 -2
  309. package/vendor-core/stage-alignment.js +2 -12
  310. package/vendor-core/stage-identity.js +58 -3
  311. package/vendor-core/stage-label.js +14 -0
  312. package/vendor-core/stage-plan-nodes.js +17 -3
  313. package/vendor-core/stage-quantiles.js +141 -9
  314. package/vendor-core/stage-structure.js +17 -3
  315. package/vendor-core/threshold-overrides.js +9 -3
  316. package/vendor-core/types.js +58 -21
  317. package/vendor-core/wasted-core-hours.js +0 -1
  318. package/vendor-core/write-targets.js +114 -14
  319. package/vendor-core/zstd-worker-client.js +1 -1
  320. package/vendor-core/zstd-worker.js +1 -1
  321. package/public/assets/AutoscalingChurn-CRA2Mb7F.js +0 -1
  322. package/public/assets/CacheUtilization-Bh9OY_V9.js +0 -1
  323. package/public/assets/CachingOpportunity-oCiQwmTJ.js +0 -1
  324. package/public/assets/ColdStart-BJrtjfmn.js +0 -1
  325. package/public/assets/ConfigAudit-DMvKxySP.js +0 -1
  326. package/public/assets/CoreUsageArea-REC2zsKw.js +0 -1
  327. package/public/assets/Dashboard-BHZ76JCu.js +0 -2
  328. package/public/assets/DisclosureContext-OO7X1HAx.js +0 -1
  329. package/public/assets/DuplicatePlanSubtree-BgG-L-cK.js +0 -1
  330. package/public/assets/ExecutorUtilization-D2KhSVbY.js +0 -1
  331. package/public/assets/GcPressure-D1BTE8qh.js +0 -1
  332. package/public/assets/IncompleteRun-DD40xUpy.js +0 -1
  333. package/public/assets/JobFailures-YwNzCF19.js +0 -1
  334. package/public/assets/MemoryUtilization-9b8ee_3_.js +0 -1
  335. package/public/assets/OverBroadcast-CfctS8a8.js +0 -1
  336. package/public/assets/PartitionHint-CUvbnnxy.js +0 -1
  337. package/public/assets/PartitionSizing-VG8kBgG5.js +0 -1
  338. package/public/assets/PlanExplorer-DmubTAOP.js +0 -1
  339. package/public/assets/PlanGraphRoute-DctPZjS0.js +0 -1
  340. package/public/assets/RetryWaste-CnpM8bB7.js +0 -1
  341. package/public/assets/ShuffleIO-D13vGmas.js +0 -1
  342. package/public/assets/Skew-DlagstYP.js +0 -1
  343. package/public/assets/SlowHost-cSkLRZ_e.js +0 -1
  344. package/public/assets/SmallFiles-B-kERMcq.js +0 -1
  345. package/public/assets/SpeculationWaste-DaysAzXO.js +0 -1
  346. package/public/assets/Spill-H44Gz4US.js +0 -1
  347. package/public/assets/StageFailed-DCfii9h_.js +0 -1
  348. package/public/assets/StageFindingGroup-BXJ0Mj5n.js +0 -1
  349. package/public/assets/StageHeader--oN2J-oD.js +0 -1
  350. package/public/assets/StageShape-IZ80CZiJ.js +0 -1
  351. package/public/assets/StageSlowness--BRIaBc1.js +0 -1
  352. package/public/assets/Straggler-Ctq0Df_o.js +0 -1
  353. package/public/assets/TaskFailures-Bks_gqhL.js +0 -1
  354. package/public/assets/TinyTask-BssfzGFB.js +0 -1
  355. package/public/assets/UnderBroadcast-DsScrxc-.js +0 -1
  356. package/public/assets/charts-vendor-0VMaLVLN.js +0 -59
  357. package/public/assets/finding-anchor-BYW1oz5R.js +0 -1
  358. package/public/assets/impact-sort-xzy0g-Vh.js +0 -1
  359. package/public/assets/index-DhpRGMZc.css +0 -2
  360. package/public/assets/index-DwqkFUFk.js +0 -14
  361. package/public/assets/plan-graph-vendor-BYl-AzN5.js +0 -7
  362. package/public/assets/react-vendor-DfpuPQZf.js +0 -9
  363. package/public/assets/select-C8edjYTq.js +0 -1
  364. package/public/assets/usePagedRows-CsuUFeYx.js +0 -1
  365. package/public/assets/useRecentFiles-BvkKo3JN.js +0 -11
  366. package/public/assets/useSortMode-joX71BZN.js +0 -1
  367. package/public/docs/assets/chunks/@localSearchIndexroot.tE5pz54z.js +0 -1
  368. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +0 -1
  369. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js +0 -1
  370. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +0 -1
  371. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.lean.js +0 -1
  372. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.N4hhenrA.js +0 -1
  373. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +0 -1
  374. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +0 -1
  375. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.js +0 -1
  376. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +0 -1
  377. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DfvbqEUu.js +0 -1
  378. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.js +0 -1
  379. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +0 -1
  380. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +0 -1
  381. package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +0 -1
  382. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +0 -1
  383. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +0 -1
  384. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +0 -1
  385. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +0 -1
  386. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +0 -1
  387. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.js +0 -1
  388. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.js +0 -1
  389. package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +0 -1
  390. package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.js +0 -15
  391. package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.lean.js +0 -1
  392. package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +0 -1
  393. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.js +0 -24
  394. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +0 -1
  395. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js +0 -1
  396. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +0 -23
  397. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +0 -1
  398. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +0 -1
  399. package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.js +0 -1
  400. package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.lean.js +0 -1
  401. /package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.lean.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.lean.js} +0 -0
  402. /package/public/docs/assets/{contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.lean.js → contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.lean.js} +0 -0
  403. /package/public/docs/assets/{contributor-guide_architecture_overview.md.C1sSvm9i.lean.js → contributor-guide_architecture_overview.md.nxkpOhgf.lean.js} +0 -0
  404. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js → contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.lean.js} +0 -0
  405. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.lean.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.lean.js} +0 -0
  406. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.lean.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.lean.js} +0 -0
  407. /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.lean.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.lean.js} +0 -0
  408. /package/public/docs/assets/{contributor-guide_contributing.md.CQMvGwFt.lean.js → contributor-guide_contributing.md.DBW4drtZ.lean.js} +0 -0
  409. /package/public/docs/assets/{user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.lean.js → user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.lean.js} +0 -0
  410. /package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.lean.js → user-guide_mcp-tools.md.49r3L2YE.lean.js} +0 -0
@@ -1,6 +1,7 @@
1
1
 
2
2
  import { medianOfSorted } from './median.js';
3
3
 
4
+
4
5
 
5
6
  // Single source of truth for the packed per-task numeric array: FIELDS (offset
6
7
  // constants), TASK_FIELD_NAMES (display labels), and the finalizeStage hot-loop
@@ -43,9 +44,8 @@ export const TASK_FIELD_NAMES = TASK_FIELD_DESCRIPTORS.map((d) => d.na
43
44
  const TASK_FIELD_PROPS = TASK_FIELD_DESCRIPTORS.map((d) => d.prop);
44
45
 
45
46
  // Numeric accumulator fields on `stage` that this function reads/increments.
46
- // `stage`'s public parameter type stays the loose Record shape per this
47
- // module's Produces contract; this narrow view is only for the arithmetic
48
- // below, never a restatement of the object's real shape.
47
+ // `stage`'s public parameter type stays the loose Record shape; this narrow view is only for the
48
+ // arithmetic below, never a restatement of the object's real shape.
49
49
 
50
50
 
51
51
 
@@ -82,7 +82,8 @@ export function finalizeStage(
82
82
  const localityStats = new Map();
83
83
  let peakExecutionMemoryMax = 0;
84
84
 
85
- for (const t of stage.taskAttempts.values()) {
85
+ const attempts = stage.taskAttempts;
86
+ for (const t of attempts.values()) {
86
87
  taskCount++;
87
88
  if (t.failed) {
88
89
  failedTasks++;
@@ -105,7 +106,15 @@ export function finalizeStage(
105
106
  }
106
107
  if (t.executorId) {
107
108
  let es = executorStats.get(t.executorId);
108
- if (!es) { es = { taskCount: 0, totalDuration: 0, inputBytes: 0, shuffleReadBytes: 0, shuffleWriteBytes: 0 }; executorStats.set(t.executorId, es); }
109
+ if (!es) { es = { taskCount: 0, totalDuration: 0, logDurationSum: 0, logDurationM2: 0, inputBytes: 0, shuffleReadBytes: 0, shuffleWriteBytes: 0 }; executorStats.set(t.executorId, es); }
110
+ // Task durations scatter multiplicatively, so the spread slowHost tests an executor against is
111
+ // kept on the log scale: a running sum and Welford's M2 of ln(1 + duration). A non-finite
112
+ // duration makes both NaN, which the detector reads as "no spread known".
113
+ const logDuration = Math.log1p(t.duration);
114
+ const logMeanBefore = es.taskCount > 0 ? es.logDurationSum / es.taskCount : 0;
115
+ const logMeanAfter = (es.logDurationSum + logDuration) / (es.taskCount + 1);
116
+ es.logDurationM2 += (logDuration - logMeanBefore) * (logDuration - logMeanAfter);
117
+ es.logDurationSum += logDuration;
109
118
  es.taskCount++;
110
119
  es.totalDuration += t.duration;
111
120
  es.inputBytes += t.inputBytes;
@@ -145,9 +154,9 @@ export function finalizeStage(
145
154
  const { p50: spillMemP50, p95: spillMemP95, max: spillMemMax } = computeFieldQuantiles(arr, FIELDS.MEM_SPILLED);
146
155
  const { p50: spillDiskP50, p95: spillDiskP95, max: spillDiskMax } = computeFieldQuantiles(arr, FIELDS.DISK_SPILLED);
147
156
 
148
- // Straggler count: tasks with duration > 4 * P50, their summed excess over P50, and the longest
149
- // task that isn't one.
150
- const stragglerThreshold = 4 * p50;
157
+ // Straggler count: tasks with duration > STRAGGLER_FACTOR * P50, their summed excess over P50, and
158
+ // the longest task that isn't one.
159
+ const stragglerThreshold = STRAGGLER_FACTOR * p50;
151
160
  let stragglerCount = 0;
152
161
  let stragglerExcessMs = 0;
153
162
  let longestNonStragglerMs = 0;
@@ -160,6 +169,7 @@ export function finalizeStage(
160
169
  }
161
170
  }
162
171
 
172
+ const tailAttribution = p50 > 0 ? attributeTail(attempts.values() , p50) : null;
163
173
  const peakConcurrentTasks = computePeakConcurrentTasks(arr);
164
174
  const tailReplayRecoveryMs = stragglerCount > 0
165
175
  ? computeTailReplayRecoveryMs(arr, p50, peakConcurrentTasks)
@@ -169,7 +179,7 @@ export function finalizeStage(
169
179
  ([host, s]) => ({ host, taskCount: s.taskCount, totalDuration: s.totalDuration })
170
180
  );
171
181
  const executorStatsArr = [...executorStats.entries()].map(
172
- ([executorId, s]) => ({ executorId, taskCount: s.taskCount, totalDuration: s.totalDuration, inputBytes: s.inputBytes, shuffleReadBytes: s.shuffleReadBytes, shuffleWriteBytes: s.shuffleWriteBytes })
182
+ ([executorId, s]) => ({ executorId, taskCount: s.taskCount, totalDuration: s.totalDuration, logDurationSum: s.logDurationSum, logDurationM2: s.logDurationM2, inputBytes: s.inputBytes, shuffleReadBytes: s.shuffleReadBytes, shuffleWriteBytes: s.shuffleWriteBytes })
173
183
  );
174
184
  const failureReasonsArr = [...failureReasons.entries()].map(
175
185
  ([reason, count]) => ({ reason, count })
@@ -191,6 +201,7 @@ export function finalizeStage(
191
201
  peakExecutionMemoryMax,
192
202
  taskActiveMs: computeTaskActiveMs(arr),
193
203
  peakConcurrentTasks,
204
+ ...(tailAttribution ? { tailAttribution } : {}),
194
205
  taskDurationP50: p50,
195
206
  taskDurationP95: p95,
196
207
  taskDurationMax: max,
@@ -210,6 +221,127 @@ export function finalizeStage(
210
221
  return { type: 'stage', data };
211
222
  }
212
223
 
224
+ // A task is in the tail when it runs over this many times the stage's median task: skew's own
225
+ // P95/median ratio threshold, so every stage either detector flags on duration has a tail here.
226
+ export const TAIL_FACTOR = 3;
227
+
228
+ // A task is a straggler when it runs over this many times the stage's median task.
229
+ export const STRAGGLER_FACTOR = 4;
230
+
231
+ // The per-task fields attributeTail reads; a TaskRecord in event-handlers.ts has all of them.
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+ // A hosts-concentration reading needs this many tail tasks on the host, at least this share of the
247
+ // tail, and at least this multiple of the host's share of all tasks, before the host explains the
248
+ // excess the other causes leave. One slow task on a host that ran a tenth of the stage is chance.
249
+ const TAIL_HOST_MIN_TASKS = 3;
250
+ const TAIL_HOST_MIN_TAIL_SHARE = 0.5;
251
+ const TAIL_HOST_MIN_OVERREPRESENTATION = 2;
252
+
253
+ // A median task that read next to nothing makes every ratio against it huge (a median of 4 bytes
254
+ // against a 1 MB task reads as 250000x). Below these floors the median task is read as having
255
+ // read almost nothing: there is no ratio to take, and a task counts as having read data only from
256
+ // the floors up.
257
+ const MIN_VOLUME_BYTES = 1024 * 1024;
258
+ const MIN_VOLUME_RECORDS = 1000;
259
+
260
+ // Why the stage's slow tasks (non-failed, over TAIL_FACTOR x P50) were slow, from what each task
261
+ // logged. A task's excess is its duration over P50. Four causes claim it, in this order so no
262
+ // millisecond counts twice:
263
+ // 1. Data: had run time scaled with data volume, the task would have taken P50 x (volume ratio)
264
+ // against the median task, with volume the task's input plus shuffle-read, in bytes or in
265
+ // records, whichever is further above the median. This is the definition Spark's UI and AQE
266
+ // use for skew (a partition far larger than the median one), applied to run time.
267
+ // 2. GC time over the median task's.
268
+ // 3. Shuffle fetch wait over the median task's.
269
+ // 4. Host: what is left of the tail tasks on the one host that holds most of the tail.
270
+ // With a median task that read almost nothing there is no ratio to take, so GC and fetch wait claim their
271
+ // excess first and a task that read data owns what they leave: the time was GC or waiting unless
272
+ // the data is all that is left to explain it.
273
+ // Null when the stage has no tail. `tasks` is consumed once.
274
+ export function attributeTail(tasks , p50 ) {
275
+ const all = [];
276
+ const tail = [];
277
+ for (const t of tasks) {
278
+ if (t.failed) continue; // a failed attempt's metrics stop where it died
279
+ all.push(t);
280
+ if (t.duration > TAIL_FACTOR * p50) tail.push(t);
281
+ }
282
+ if (tail.length === 0) return null;
283
+
284
+ const bytesOf = (t ) => t.inputBytes + t.shuffleRead;
285
+ const recordsOf = (t ) => t.inputRecords + t.shuffleReadRecords;
286
+ const medianBytes = medianOf(all, bytesOf);
287
+ const medianRecords = medianOf(all, recordsOf);
288
+ const medianGc = medianOf(all, (t) => t.gcTime);
289
+ const medianFetch = medianOf(all, (t) => t.fetchWaitTime);
290
+ const bytesComparable = medianBytes >= MIN_VOLUME_BYTES;
291
+ const recordsComparable = medianRecords >= MIN_VOLUME_RECORDS;
292
+ const hasVolume = bytesComparable || recordsComparable;
293
+ const volumeRatio = (t ) => Math.max(
294
+ bytesComparable ? bytesOf(t) / medianBytes : 0,
295
+ recordsComparable ? recordsOf(t) / medianRecords : 0,
296
+ );
297
+ const readsData = (t ) => bytesOf(t) >= MIN_VOLUME_BYTES || recordsOf(t) >= MIN_VOLUME_RECORDS;
298
+
299
+ const tailHosts = new Map ();
300
+ const allHosts = new Map ();
301
+ for (const t of all) if (t.host) allHosts.set(t.host, (allHosts.get(t.host) ?? 0) + 1);
302
+ for (const t of tail) if (t.host) tailHosts.set(t.host, (tailHosts.get(t.host) ?? 0) + 1);
303
+ let topHost = null;
304
+ for (const [host, n] of tailHosts) if (topHost === null || n > tailHosts.get(topHost) ) topHost = host;
305
+ const hostConcentrated = topHost !== null
306
+ && tailHosts.get(topHost) >= TAIL_HOST_MIN_TASKS
307
+ && tailHosts.get(topHost) / tail.length >= TAIL_HOST_MIN_TAIL_SHARE
308
+ && (tailHosts.get(topHost) / tail.length) / (allHosts.get(topHost) / all.length) >= TAIL_HOST_MIN_OVERREPRESENTATION;
309
+
310
+ let excessMs = 0, dataMs = 0, gcMs = 0, fetchWaitMs = 0, hostMs = 0, cpuNs = 0, runMs = 0;
311
+ const ratios = [];
312
+ for (const t of tail) {
313
+ const excess = t.duration - p50;
314
+ excessMs += excess;
315
+ let left = excess;
316
+ if (hasVolume) {
317
+ const ratio = volumeRatio(t);
318
+ ratios.push(ratio);
319
+ const data = Math.min(left, Math.max(0, p50 * (ratio - 1)));
320
+ dataMs += data; left -= data;
321
+ }
322
+ const gc = Math.min(left, Math.max(0, t.gcTime - medianGc));
323
+ gcMs += gc; left -= gc;
324
+ const fetch = Math.min(left, Math.max(0, t.fetchWaitTime - medianFetch));
325
+ fetchWaitMs += fetch; left -= fetch;
326
+ if (!hasVolume && readsData(t)) { dataMs += left; left = 0; }
327
+ if (hostConcentrated && t.host === topHost) { hostMs += left; }
328
+ cpuNs += t.executorCpuTime;
329
+ runMs += t.executorRunTime;
330
+ }
331
+ ratios.sort((a, b) => a - b);
332
+ return {
333
+ tasks: tail.length, excessMs, dataMs, gcMs, fetchWaitMs, hostMs,
334
+ host: hostConcentrated ? topHost : null,
335
+ hostTasks: hostConcentrated ? tailHosts.get(topHost ) : 0,
336
+ dataRatio: hasVolume ? medianOfSorted(Float64Array.from(ratios)) : null,
337
+ cpuPct: cpuNs > 0 && runMs > 0 ? Math.min(100, (cpuNs / 1e6 / runMs) * 100) : null,
338
+ };
339
+ }
340
+
341
+ function medianOf(tasks , valueOf ) {
342
+ return medianOfSorted(Float64Array.from(tasks, valueOf).sort());
343
+ }
344
+
213
345
  // Wall-clock time during which at least one of the stage's tasks was running: the union of its
214
346
  // [launch, finish) intervals. A stage's submittedAt..completedAt window also covers time it sat
215
347
  // open with no task running (waiting for a free slot, or between retried tasks), which no
@@ -3,7 +3,7 @@
3
3
  // removed) and the token sets its similarity scores read. Pure.
4
4
 
5
5
  import { cyrb53 } from './string-hash.js';
6
- import { normalizeStageName, } from './stage-identity.js';
6
+ import { normalizeStageName, normalizedDetailOf, } from './stage-identity.js';
7
7
 
8
8
 
9
9
  // Plumbing nodes: codegen boundaries and input adapters change with how Spark fuses a stage, not with
@@ -43,7 +43,7 @@ export function structuralKey(nodes ) {
43
43
  const digestOf = (node ) => {
44
44
  let d = digests.get(node);
45
45
  if (d === undefined) {
46
- d = cyrb53(JSON.stringify([normalizeStageName(node.name ?? ''), attributeNames(node.detail ?? ''), below(node).sort()]));
46
+ d = cyrb53(JSON.stringify([normalizeStageName(node.name ?? ''), attributeNamesOf(node), below(node).sort()]));
47
47
  digests.set(node, d);
48
48
  }
49
49
  return d;
@@ -53,6 +53,20 @@ export function structuralKey(nodes ) {
53
53
  return cyrb53(JSON.stringify(roots));
54
54
  }
55
55
 
56
+ // A node's attribute names are read by the structural key of every stage it ran in and by the
57
+ // execution's plan structure; a node is never mutated after the parser posts it.
58
+ const attributeNamesCache = new WeakMap ();
59
+
60
+ /** `attributeNames` of a plan node's detail, memoized per node. The array is shared: do not change it. */
61
+ export function attributeNamesOf(node ) {
62
+ let names = attributeNamesCache.get(node);
63
+ if (names === undefined) {
64
+ names = attributeNames(node.detail ?? '');
65
+ attributeNamesCache.set(node, names);
66
+ }
67
+ return names;
68
+ }
69
+
56
70
  const TOKEN_SPLIT = /[^A-Za-z0-9_.$]+/;
57
71
 
58
72
  /** Sorted distinct tokens of a stage's attributed nodes (name and normalized detail) and its name.
@@ -64,7 +78,7 @@ export function detailTokens(stageName , nodes
64
78
  for (const node of nodes) {
65
79
  if (isPlumbing(node)) continue;
66
80
  add(normalizeStageName(node.name ?? ''));
67
- add(normalize(node.detail ?? ''));
81
+ add(normalizedDetailOf(node, normalize));
68
82
  }
69
83
  return [...tokens].sort();
70
84
  }
@@ -88,11 +88,17 @@ export function tunedThresholdsOf(entry , overrides
88
88
  return Object.keys(tuned).length > 0 ? tuned : null;
89
89
  }
90
90
 
91
- /** The tuned thresholds an entry's findings carry: its own, plus its `suppressedBy` entry's
92
- * (named `<suppressor>.<threshold>`), since tuning the suppressor changes which of them survive. */
91
+ // straggler judges a tail with skew's resolved thresholds (DetectorCtx.skewThresholds), so tuning skew
92
+ // changes which tails it reports without any `suppressedBy` link.
93
+ const THRESHOLD_DEPENDENCY = { straggler: 'skew' };
94
+
95
+ /** The tuned thresholds an entry's findings carry: its own, plus its `suppressedBy` entry's or the
96
+ * entry it reads thresholds from (named `<entry>.<threshold>`), since tuning either changes which
97
+ * of them survive. */
93
98
  export function findingTunedThresholds(entry , overrides ) {
94
99
  const own = tunedThresholdsOf(entry, overrides);
95
- const suppressor = entry.suppressedBy ? entries.find((d) => d.type === entry.suppressedBy) : undefined;
100
+ const suppressorType = entry.suppressedBy ?? THRESHOLD_DEPENDENCY[entry.type];
101
+ const suppressor = suppressorType ? entries.find((d) => d.type === suppressorType) : undefined;
96
102
  const bySuppressor = suppressor ? tunedThresholdsOf(suppressor, overrides) : null;
97
103
  if (!suppressor || !bySuppressor) return own;
98
104
  const prefixed = Object.fromEntries(Object.entries(bySuppressor).map(([name, t]) => [`${suppressor.type}.${name}`, t]));
@@ -43,8 +43,7 @@
43
43
 
44
44
  // Contract-pinned evidence `eventType`: the same fixed set as the parser's
45
45
  // `evidenceInputs` counter keys, so provenance stays addressable against the
46
- // documented `{ eventType, count }` shape (review: values must not diverge
47
- // from the counters they summarize).
46
+ // documented `{ eventType, count }` shape.
48
47
 
49
48
 
50
49
 
@@ -88,10 +87,10 @@
88
87
 
89
88
 
90
89
 
91
-
90
+
92
91
 
93
92
 
94
-
93
+
95
94
 
96
95
 
97
96
 
@@ -120,7 +119,7 @@
120
119
 
121
120
 
122
121
 
123
-
122
+
124
123
 
125
124
 
126
125
 
@@ -131,20 +130,51 @@
131
130
 
132
131
 
133
132
 
133
+
134
+
135
+
134
136
 
135
137
 
136
138
 
139
+
140
+
141
+ // The tail of a stage: its non-failed tasks over TAIL_FACTOR x P50. Each cause is the summed time
142
+ // (ms) of the tail's excess over P50 that the cause accounts for, taken in the order data, GC, fetch
143
+ // wait, host so that no millisecond counts twice; what none of them accounts for is
144
+ // excessMs - dataMs - gcMs - fetchWaitMs - hostMs.
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
137
165
 
138
166
 
139
167
 
140
168
 
141
-
142
-
143
-
169
+
170
+
144
171
 
145
172
 
146
173
 
147
-
174
+
175
+
176
+
177
+
148
178
 
149
179
 
150
180
 
@@ -161,6 +191,8 @@
161
191
 
162
192
 
163
193
 
194
+
195
+
164
196
 
165
197
 
166
198
 
@@ -179,19 +211,14 @@
179
211
 
180
212
 
181
213
 
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
214
+
215
+
216
+
217
+
218
+
192
219
 
193
220
 
194
-
221
+
195
222
 
196
223
 
197
224
 
@@ -289,6 +316,9 @@
289
316
 
290
317
 
291
318
 
319
+
320
+
321
+
292
322
 
293
323
 
294
324
 
@@ -316,6 +346,9 @@
316
346
 
317
347
 
318
348
 
349
+
350
+
351
+
319
352
 
320
353
 
321
354
 
@@ -332,6 +365,10 @@
332
365
 
333
366
 
334
367
 
368
+
369
+
370
+
371
+
335
372
 
336
373
 
337
374
 
@@ -363,7 +400,7 @@
363
400
 
364
401
 
365
402
 
366
-
403
+
367
404
 
368
405
 
369
406
 
@@ -40,7 +40,6 @@ export function computeWastedCoreHours(
40
40
  runAggregates ,
41
41
  executorsRemoved = [],
42
42
  ) {
43
- // Nullish (not falsy) guard on times: a literal startTime:0 is valid.
44
43
  if (!runAggregates || app?.startTime == null || app?.endTime == null) return EMPTY;
45
44
  const appDurationMs = app.endTime - app.startTime;
46
45
  if (appDurationMs <= 0) return EMPTY;
@@ -1,21 +1,14 @@
1
1
  // Write targets: every SQL write command in a run's plans, with the path or table it writes to.
2
2
  // Read by automation that halts a session when a target leaves its sandbox, so every rule here
3
- // fails toward "target unknown" (null), never toward a guessed or partial target:
4
- // - a write node whose target cannot be parsed is still reported, with target null and its raw
5
- // simpleString;
6
- // - a target Spark cut short (no delimiter after it, or a "..." marker in it) is unparseable;
7
- // - a write-like node outside the known list is reported as an unrecognized write;
8
- // - an execution whose plan is not in the model (or whose start event could not be read) is
9
- // listed, never skipped, and the count of unreadable log lines is reported;
10
- // - a Delta target is set only when exactly one table or path is named (delta-targets.ts,
11
- // api-merge-writes.ts).
12
- // Targets are verbatim from the simpleString: nothing is resolved (relative paths, ${var}
13
- // placeholders, catalog-relative table names all pass through as the log states them).
3
+ // fails toward "target unknown" (null), never toward a guessed or partial target, and targets are
4
+ // verbatim from the simpleString. Rules and fields: docs-site/user-guide/getting-started/ci-and-automation.md,
5
+ // "Write targets" (a node whose target cannot be parsed, a cut-short target, an unrecognized write,
6
+ // an unreadable execution, a Delta target named by exactly one table or path).
14
7
  import { walkPlanTree } from './plan-tree-walk.js';
15
8
  import { apiMergeWrites } from './api-merge-writes.js';
16
9
  import { indexByRoot, isStagedDeltaWrite, parseDeltaTableV2, resolveDeltaCommandTarget, } from './delta-targets.js';
17
10
  import { IDENT_PARTS, isCut, splitArgs, tableName, } from './write-target-args.js';
18
-
11
+
19
12
 
20
13
  /** The row counts a MergeIntoCommand node reports in its SQL metrics. */
21
14
 
@@ -42,8 +35,25 @@ import { IDENT_PARTS, isCut, splitArgs, tableName, } from './write-targ
42
35
 
43
36
 
44
37
 
45
-
38
+
39
+
40
+
41
+
42
+
43
+
46
44
 
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
47
57
 
48
58
 
49
59
 
@@ -216,6 +226,14 @@ const DELTA_COMMANDS = new Set([
216
226
  'CreateDeltaTableCommand', 'OptimizeTableCommand', 'RestoreTableCommand', 'DeltaReorgTableCommand',
217
227
  ]);
218
228
 
229
+ // Row-level and maintenance commands: a stage sum would misstate their output (their figures come
230
+ // from the command node's own counters, see commandOutputOf). A plain write is any other recognized
231
+ // write command.
232
+ const NOT_PLAIN_WRITES = new Set([
233
+ 'MergeIntoCommand', 'UpdateCommand', 'DeleteCommand', 'DeltaMerge', 'ReplaceData', 'WriteDelta',
234
+ 'CreateDeltaTableCommand', 'OptimizeTableCommand', 'RestoreTableCommand', 'DeltaReorgTableCommand',
235
+ ]);
236
+
219
237
  const ARG_PARSERS = {
220
238
  InsertIntoHadoopFsRelationCommand: parseHadoopFsPath,
221
239
  InsertIntoHiveTable: parseTableArg,
@@ -225,6 +243,11 @@ const ARG_PARSERS = {
225
243
  SaveIntoDataSourceCommand: parseSaveIntoDataSource,
226
244
  };
227
245
 
246
+ /** True for a recognized command that writes its output rows (not MERGE, UPDATE, DELETE or table maintenance). */
247
+ export function isPlainWrite(write ) {
248
+ return write.recognized && !NOT_PLAIN_WRITES.has(write.command);
249
+ }
250
+
228
251
  function isKnownWrite(command ) {
229
252
  return command in ARG_PARSERS || V2_WRITES.has(command) || V2_TABLE_AS_SELECT.has(command) || DELTA_COMMANDS.has(command);
230
253
  }
@@ -254,6 +277,35 @@ function metricValue(node , ...names ) {
254
277
  return metric !== undefined && Number.isFinite(metric.value) ? metric.value : null;
255
278
  }
256
279
 
280
+ // Delta 4.x labels the DELETE copied-rows counter with a trailing period ("number of rows copied.");
281
+ // metricValue matches names exactly, so the counter lists both spellings.
282
+ const COPIED_ROWS_LABELS = ['number of rows copied.', 'number of rows copied'];
283
+
284
+ // Rows and bytes a MERGE, UPDATE or DELETE wrote to new files, from the command node's counters.
285
+ // A MERGE writes its inserted, updated and copied rows; an UPDATE writes the rows it touched
286
+ // (updated plus copied); a DELETE writes the target rows it copied. Rows removed by a DELETE or
287
+ // MERGE delete clause are not written, so they are not output rows.
288
+ function commandOutputOf(command , node ) {
289
+ if (command === 'MergeIntoCommand') {
290
+ const parts = [
291
+ metricValue(node, 'number of inserted rows'),
292
+ metricValue(node, 'number of updated rows'),
293
+ metricValue(node, 'number of target rows rewritten unmodified', 'number of rows copied'),
294
+ ];
295
+ return {
296
+ rows: parts.some((v) => v === null) ? null : parts.reduce((sum, v) => sum + v , 0),
297
+ bytes: metricValue(node, 'number of target bytes added'),
298
+ };
299
+ }
300
+ if (command === 'UpdateCommand') {
301
+ return { rows: metricValue(node, 'number of rows touched (copied + updated)'), bytes: metricValue(node, 'number of bytes added') };
302
+ }
303
+ if (command === 'DeleteCommand') {
304
+ return { rows: metricValue(node, ...COPIED_ROWS_LABELS), bytes: metricValue(node, 'number of bytes added') };
305
+ }
306
+ return { rows: null, bytes: null };
307
+ }
308
+
257
309
  function mergeRowsOf(command , node ) {
258
310
  if (command !== 'MergeIntoCommand') return null;
259
311
  const rows = {
@@ -273,6 +325,9 @@ function collectWrites(exec , root , byRoot
273
325
  const [recognized, parsedFromDetail] = parseWrite(command, detail);
274
326
  // A Delta command's simpleString is bare on real logs: its target is found elsewhere.
275
327
  const parsed = parsedFromDetail ?? resolveDeltaCommandTarget(command, exec, node, byRoot);
328
+ const nodeRows = outputRowsOf(node);
329
+ const commandOutput = commandOutputOf(command, node);
330
+ const outputRows = nodeRows ?? commandOutput.rows;
276
331
  out.push({
277
332
  sqlExecutionId: exec.id,
278
333
  nodeId: node.id ?? null,
@@ -280,7 +335,9 @@ function collectWrites(exec , root , byRoot
280
335
  recognized,
281
336
  kind: parsed?.kind ?? null,
282
337
  target: parsed?.target ?? null,
283
- outputRows: outputRowsOf(node),
338
+ outputRows,
339
+ outputRowsSource: nodeRows !== null ? 'nodeMetric' : outputRows !== null ? 'commandMetric' : null,
340
+ outputBytes: commandOutput.bytes,
284
341
  mergeRows: mergeRowsOf(command, node),
285
342
  raw: detail !== '' ? detail : node.name,
286
343
  });
@@ -301,7 +358,49 @@ function attributeStagedWrites(writes , sql
301
358
  }
302
359
  }
303
360
 
361
+ // The rows and bytes a plain write stored are reported by the stages of the executions under its
362
+ // root: a Delta DataFrame write's node sits in the root execution while its stages run in a child.
363
+ // Applied only when the root holds one logical write, so the stages cannot belong to another write.
364
+ function attachStageOutput(writes , sql , stages ) {
365
+ const byRoot = indexByRoot(sql);
366
+ const stagesByExecution = new Map ();
367
+ for (const stage of stages.values()) {
368
+ if (stage.sqlExecutionId == null) continue;
369
+ const list = stagesByExecution.get(stage.sqlExecutionId);
370
+ if (list) list.push(stage);
371
+ else stagesByExecution.set(stage.sqlExecutionId, [stage]);
372
+ }
373
+ const rootOf = (write ) => sql.get(write.sqlExecutionId)?.rootExecutionId ?? write.sqlExecutionId;
374
+ // One logical write per root: the plain writes that name the same table or path count once (a
375
+ // CTAS or RTAS node and the append it runs, Iceberg's replace and its overwrite). A write with no
376
+ // target, a MERGE, UPDATE or DELETE, or a second target in the root makes the stages ambiguous.
377
+ const identitiesPerRoot = new Map ();
378
+ writes.forEach((write, i) => {
379
+ const key = isPlainWrite(write) && write.target !== null ? `${write.kind}:${write.target}` : `#${i}`;
380
+ const root = rootOf(write);
381
+ const keys = identitiesPerRoot.get(root);
382
+ if (keys) keys.add(key);
383
+ else identitiesPerRoot.set(root, new Set([key]));
384
+ });
385
+ for (const write of writes) {
386
+ const root = rootOf(write);
387
+ if (!isPlainWrite(write) || identitiesPerRoot.get(root)?.size !== 1) continue;
388
+ const group = [root, ...(byRoot.get(root) ?? []).map((exec) => exec.id)];
389
+ const grouped = group.flatMap((id) => stagesByExecution.get(id) ?? []);
390
+ if (grouped.length === 0) continue;
391
+ const withBytes = grouped.filter((stage) => stage.outputBytes != null);
392
+ if (withBytes.length > 0) write.outputBytes = withBytes.reduce((sum, stage) => sum + stage.outputBytes , 0);
393
+ if (write.outputRows !== null) continue;
394
+ const reported = grouped.filter((stage) => stage.outputRecords != null);
395
+ if (reported.length === 0) continue;
396
+ write.outputRows = reported.reduce((sum, stage) => sum + stage.outputRecords , 0);
397
+ write.outputRowsSource = 'stageSum';
398
+ }
399
+ }
400
+
304
401
 
402
+
403
+
305
404
 
306
405
 
307
406
 
@@ -320,6 +419,7 @@ export function extractWriteTargets(sql , input
320
419
  // DeltaTable API merges run no command node: they are found by their executions' descriptions.
321
420
  attributeStagedWrites(writes, sql);
322
421
  writes.push(...apiMergeWrites(sql));
422
+ if (input.stages) attachStageOutput(writes, sql, input.stages);
323
423
  writes.sort((a, b) => a.sqlExecutionId - b.sqlExecutionId);
324
424
  const executionsWithoutPlan = [...withoutPlan].sort((a, b) => a[0] - b[0])
325
425
  .map(([sqlExecutionId, reason]) => ({ sqlExecutionId, reason }));
@@ -41,7 +41,7 @@ export const MAX_IN_FLIGHT_SLICES = 3;
41
41
  // `spawn` starts the decompress worker; it runs at most once, on the first zstd stream, and the
42
42
  // worker then serves every later stream (the files of a rolling log) one at a time. When it
43
43
  // cannot start (no nested workers, a blocked script, a crash), each stream is decoded by
44
- // `fallback` on the calling thread instead, the path used before this worker existed.
44
+ // `fallback` on the calling thread instead.
45
45
  export function createWorkerZstdDecoders(
46
46
  spawn ,
47
47
  fallback ,