sparkforensics-server 0.5.2 → 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 (445) 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/instrument-sans-latin-ext-wdth-normal-C5E2Gvlv.woff2 +0 -0
  63. package/public/assets/instrument-sans-latin-wdth-normal-BVScPF0l.woff2 +0 -0
  64. package/public/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  65. package/public/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  66. package/public/assets/layout.worker-CLcBHxVp.js +3 -0
  67. package/public/assets/{parser-worker-CA5RxqKc.js → parser-worker-CRIOPMOW.js} +8 -8
  68. package/public/assets/plan-graph-vendor-Ce6d4HU6.js +7 -0
  69. package/public/assets/react-vendor-C6KcUD36.js +9 -0
  70. package/public/assets/select-BHqOqun5.js +1 -0
  71. package/public/assets/toggle-DPeN6aNa.js +1 -0
  72. package/public/assets/toggle-group-Kl3uyHSr.js +1 -0
  73. package/public/assets/usePagedRows-BafD6bEN.js +1 -0
  74. package/public/assets/useRecentFiles-DzJZAoOT.js +3 -0
  75. package/public/assets/useRegisterFieldControl-pQv-ZDoU.js +1 -0
  76. package/public/assets/useScrollLock-BCxn_dMp.js +1 -0
  77. package/public/assets/useSortMode-DjrAN-z4.js +1 -0
  78. package/public/docs/404.html +3 -7
  79. package/public/docs/assets/{app.eYTPqTeq.js → app.1gvzd5Zb.js} +1 -1
  80. package/public/docs/assets/chunks/@localSearchIndexroot.DS2TQP5_.js +1 -0
  81. package/public/docs/assets/chunks/{VPLocalSearchBox.DsZrQKQR.js → VPLocalSearchBox.CNxT3s0I.js} +1 -1
  82. package/public/docs/assets/chunks/{theme.DsTNE2pE.js → theme.C5lLqUv7.js} +2 -2
  83. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.js +1 -0
  84. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.lean.js +1 -0
  85. package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.js} +1 -1
  86. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.js +1 -0
  87. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.js +1 -0
  88. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.lean.js +1 -0
  89. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CHHgywoP.js +1 -0
  90. package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.wj6Et6UD.lean.js → contributor-guide_architecture_detector-contract.md.CHHgywoP.lean.js} +1 -1
  91. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.js +1 -0
  92. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.lean.js +1 -0
  93. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.js +1 -0
  94. package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.CKjWG56Y.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.lean.js} +1 -1
  95. package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.js} +1 -1
  96. 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
  97. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.js +1 -0
  98. 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
  99. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.K71YB-Rr.js +1 -0
  100. package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.lean.js → contributor-guide_architecture_impact-estimation.md.K71YB-Rr.lean.js} +1 -1
  101. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.js +1 -0
  102. 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.DLCAffwn.lean.js} +1 -1
  103. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.js +1 -0
  104. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.lean.js +1 -0
  105. package/public/docs/assets/contributor-guide_architecture_overview.md.nxkpOhgf.js +1 -0
  106. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.js +1 -0
  107. 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
  108. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.js +1 -0
  109. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.js +1 -0
  110. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.lean.js +1 -0
  111. 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
  112. 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
  113. package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js → contributor-guide_architecture_worker-protocol.md.Br6OE7RW.js} +1 -1
  114. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.Br6OE7RW.lean.js +1 -0
  115. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.js +1 -0
  116. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.DC1YqXBp.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.lean.js} +1 -1
  117. 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
  118. 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
  119. 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
  120. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.js +1 -0
  121. package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.lean.js} +1 -1
  122. package/public/docs/assets/contributor-guide_contributing.md.DBW4drtZ.js +1 -0
  123. package/public/docs/assets/{contributor-guide_development-setup.md.D9bu4j_c.js → contributor-guide_development-setup.md.B21ZTDhn.js} +3 -3
  124. package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.lean.js +1 -0
  125. package/public/docs/assets/contributor-guide_testing.md.DpkozNoG.js +4 -0
  126. package/public/docs/assets/{contributor-guide_testing.md.By6XGR9a.lean.js → contributor-guide_testing.md.DpkozNoG.lean.js} +1 -1
  127. package/public/docs/assets/{index.md.CHJVslga.js → index.md.lMm7SUMz.js} +1 -1
  128. package/public/docs/assets/{index.md.CHJVslga.lean.js → index.md.lMm7SUMz.lean.js} +1 -1
  129. package/public/docs/assets/instrument-sans-latin-ext-wdth-normal.C5E2Gvlv.woff2 +0 -0
  130. package/public/docs/assets/instrument-sans-latin-wdth-normal.BVScPF0l.woff2 +0 -0
  131. package/public/docs/assets/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2 +0 -0
  132. package/public/docs/assets/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2 +0 -0
  133. package/public/docs/assets/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2 +0 -0
  134. package/public/docs/assets/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2 +0 -0
  135. package/public/docs/assets/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2 +0 -0
  136. package/public/docs/assets/style.Cfxqlj0y.css +1 -0
  137. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.C0pcQoNc.js +29 -0
  138. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.C0pcQoNc.lean.js +1 -0
  139. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.js +1 -0
  140. 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
  141. 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
  142. package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.js → user-guide_mcp-tools.md.49r3L2YE.js} +1 -1
  143. 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
  144. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.BQ_kIptC.lean.js +1 -0
  145. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.azCZXkH8.js +44 -0
  146. 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
  147. package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.js} +2 -2
  148. 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
  149. 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
  150. 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
  151. 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
  152. package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.js +1 -0
  153. package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.lean.js +1 -0
  154. package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.js +1 -0
  155. package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.lean.js +1 -0
  156. package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +6 -10
  157. package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +6 -10
  158. package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -10
  159. package/public/docs/contributor-guide/architecture/board-widgets.html +6 -10
  160. package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +6 -10
  161. package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -10
  162. package/public/docs/contributor-guide/architecture/detector-contract.html +6 -10
  163. package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +6 -10
  164. package/public/docs/contributor-guide/architecture/drill-down.html +6 -10
  165. package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -10
  166. package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +6 -10
  167. package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -10
  168. package/public/docs/contributor-guide/architecture/index.html +5 -9
  169. package/public/docs/contributor-guide/architecture/overview.html +6 -10
  170. package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +5 -9
  171. package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -10
  172. package/public/docs/contributor-guide/architecture/state-and-history.html +5 -9
  173. package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +6 -10
  174. package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +5 -9
  175. package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +6 -10
  176. package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +5 -9
  177. package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +6 -10
  178. package/public/docs/contributor-guide/architecture/widget-rendering.html +6 -10
  179. package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -10
  180. package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +6 -10
  181. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +7 -11
  182. package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -10
  183. package/public/docs/contributor-guide/architecture/worker-protocol.html +6 -10
  184. package/public/docs/contributor-guide/contributing.html +6 -10
  185. package/public/docs/contributor-guide/development-setup.html +8 -12
  186. package/public/docs/contributor-guide/testing.html +9 -10
  187. package/public/docs/favicon.svg +2 -2
  188. package/public/docs/hashmap.json +1 -1
  189. package/public/docs/index.html +6 -10
  190. package/public/docs/logo-dark.svg +1 -0
  191. package/public/docs/logo-light.svg +1 -0
  192. package/public/docs/tuning-reference/anti-patterns.html +5 -9
  193. package/public/docs/tuning-reference/aqe.html +5 -9
  194. package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -9
  195. package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -9
  196. package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -9
  197. package/public/docs/tuning-reference/bottleneck-failures.html +5 -9
  198. package/public/docs/tuning-reference/bottleneck-gc.html +5 -9
  199. package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -9
  200. package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -9
  201. package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -9
  202. package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -9
  203. package/public/docs/tuning-reference/bottleneck-skew.html +5 -9
  204. package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -9
  205. package/public/docs/tuning-reference/bottleneck-small-files.html +5 -9
  206. package/public/docs/tuning-reference/bottleneck-spill.html +5 -9
  207. package/public/docs/tuning-reference/bottleneck-straggler.html +5 -9
  208. package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -9
  209. package/public/docs/tuning-reference/bottleneck-utilization.html +5 -9
  210. package/public/docs/tuning-reference/caching.html +5 -9
  211. package/public/docs/tuning-reference/cluster-config.html +5 -9
  212. package/public/docs/tuning-reference/config.html +5 -9
  213. package/public/docs/tuning-reference/data-formats.html +5 -9
  214. package/public/docs/tuning-reference/index.html +5 -9
  215. package/public/docs/tuning-reference/intro.html +5 -9
  216. package/public/docs/tuning-reference/joins.html +5 -9
  217. package/public/docs/tuning-reference/memory-model.html +5 -9
  218. package/public/docs/tuning-reference/metrics.html +5 -9
  219. package/public/docs/tuning-reference/partitioning.html +5 -9
  220. package/public/docs/tuning-reference/pyspark.html +5 -9
  221. package/public/docs/tuning-reference/shuffle.html +5 -9
  222. package/public/docs/tuning-reference/spark-architecture.html +5 -9
  223. package/public/docs/tuning-reference/table-formats.html +5 -9
  224. package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +5 -9
  225. package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +5 -9
  226. package/public/docs/user-guide/alternative-log-retrieval.html +5 -9
  227. package/public/docs/user-guide/getting-started/ci-and-automation.html +17 -15
  228. package/public/docs/user-guide/getting-started/reading-the-dashboard.html +6 -10
  229. package/public/docs/user-guide/getting-started/regression-metric-keys.html +5 -9
  230. package/public/docs/user-guide/getting-started/tuning-thresholds.html +7 -11
  231. package/public/docs/user-guide/getting-started.html +5 -9
  232. package/public/docs/user-guide/mcp-tools/compare-runs.html +16 -15
  233. package/public/docs/user-guide/mcp-tools/diagnose-run.html +28 -11
  234. package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -10
  235. package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +5 -9
  236. package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +7 -10
  237. package/public/docs/user-guide/mcp-tools/get-reference-doc.html +5 -9
  238. package/public/docs/user-guide/mcp-tools/get-run-summary.html +6 -10
  239. package/public/docs/user-guide/mcp-tools/list-runs.html +5 -9
  240. package/public/docs/user-guide/mcp-tools.html +6 -10
  241. package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +5 -9
  242. package/public/docs/user-guide/run-comparison.html +6 -10
  243. package/public/docs/user-guide/understanding-findings.html +6 -10
  244. package/public/export-template.html +46 -46
  245. package/public/favicon.svg +2 -2
  246. package/public/index.html +8 -10
  247. package/public/sample-runs/sample-run.ndjson.gz +0 -0
  248. package/vendor-core/allocation.js +126 -39
  249. package/vendor-core/analyzer.js +85 -58
  250. package/vendor-core/api-merge-writes.js +3 -0
  251. package/vendor-core/aqe-skew.js +347 -0
  252. package/vendor-core/cli/budgets.js +6 -4
  253. package/vendor-core/cli/collect-run-pair.js +89 -0
  254. package/vendor-core/cli/collect-run-worker.js +16 -0
  255. package/vendor-core/cli/collect-run.js +1 -2
  256. package/vendor-core/cli/native-zstd.js +12 -16
  257. package/vendor-core/comparability.js +234 -0
  258. package/vendor-core/comparison-output.js +15 -7
  259. package/vendor-core/comparison-verdict.js +105 -25
  260. package/vendor-core/core-source-hash.txt +1 -1
  261. package/vendor-core/core-time-series.js +0 -1
  262. package/vendor-core/core-usage-locality.js +1 -1
  263. package/vendor-core/delta-log-paths.js +10 -1
  264. package/vendor-core/detectors.js +1078 -416
  265. package/vendor-core/docs-config.js +0 -4
  266. package/vendor-core/docs-content/detection/cache.md +2 -1
  267. package/vendor-core/docs-content/detection/cfg.md +19 -11
  268. package/vendor-core/docs-content/detection/cold.md +2 -1
  269. package/vendor-core/docs-content/detection/gc.md +7 -3
  270. package/vendor-core/docs-content/detection/host.md +11 -1
  271. package/vendor-core/docs-content/detection/mem.md +17 -6
  272. package/vendor-core/docs-content/detection/part.md +3 -1
  273. package/vendor-core/docs-content/detection/plan.md +55 -13
  274. package/vendor-core/docs-content/detection/shfl.md +11 -5
  275. package/vendor-core/docs-content/detection/skew.md +48 -1
  276. package/vendor-core/docs-content/detection/slow.md +4 -2
  277. package/vendor-core/docs-content/detection/spec.md +6 -1
  278. package/vendor-core/docs-content/detection/spill.md +6 -3
  279. package/vendor-core/docs-content/detection/strag.md +19 -6
  280. package/vendor-core/docs-content/detection/tiny.md +9 -3
  281. package/vendor-core/docs-content/detection/util.md +6 -2
  282. package/vendor-core/effective-conf.js +7 -10
  283. package/vendor-core/efficiency-model.js +24 -16
  284. package/vendor-core/etl-phases.js +1 -2
  285. package/vendor-core/event-handlers.js +175 -57
  286. package/vendor-core/event-schemas.js +21 -16
  287. package/vendor-core/evidence-availability.js +9 -2
  288. package/vendor-core/evidence-report.js +118 -35
  289. package/vendor-core/executor-peaks.js +41 -0
  290. package/vendor-core/export-data.js +5 -5
  291. package/vendor-core/finding-identity.js +78 -0
  292. package/vendor-core/finding-presentation.js +129 -39
  293. package/vendor-core/finding-types.js +108 -30
  294. package/vendor-core/format-utils.js +14 -15
  295. package/vendor-core/html-export.js +1 -1
  296. package/vendor-core/impact-band.js +2 -2
  297. package/vendor-core/impact-format.js +47 -19
  298. package/vendor-core/impact-model.js +28 -17
  299. package/vendor-core/job-groups.js +4 -4
  300. package/vendor-core/mcp-server-factory.js +48 -12
  301. package/vendor-core/mcp-tools.js +179 -24
  302. package/vendor-core/median.js +2 -2
  303. package/vendor-core/nested-loop-join.js +73 -0
  304. package/vendor-core/occupancy.js +1 -2
  305. package/vendor-core/parser-worker.js +1 -1
  306. package/vendor-core/plan-graph-model.js +14 -22
  307. package/vendor-core/plan-node-detail.js +2 -3
  308. package/vendor-core/plan-summary.js +24 -19
  309. package/vendor-core/plan-tree-walk.js +6 -8
  310. package/vendor-core/python-stage.js +12 -1
  311. package/vendor-core/recommendation-rollup.js +13 -12
  312. package/vendor-core/redact.js +52 -46
  313. package/vendor-core/remediation.js +10 -5
  314. package/vendor-core/run-aggregates.js +6 -8
  315. package/vendor-core/run-comparison.js +226 -66
  316. package/vendor-core/run-identity.js +12 -0
  317. package/vendor-core/run-interpretation.js +21 -0
  318. package/vendor-core/run-metrics.js +2 -2
  319. package/vendor-core/run-output.js +44 -11
  320. package/vendor-core/run-totals.js +12 -0
  321. package/vendor-core/run-verdict.js +76 -3
  322. package/vendor-core/scaling-sim.js +3 -4
  323. package/vendor-core/snappy-block.js +2 -2
  324. package/vendor-core/spark-conf.js +168 -0
  325. package/vendor-core/sql-stages.js +2 -2
  326. package/vendor-core/stage-alignment.js +2 -12
  327. package/vendor-core/stage-identity.js +58 -3
  328. package/vendor-core/stage-label.js +14 -0
  329. package/vendor-core/stage-plan-nodes.js +17 -3
  330. package/vendor-core/stage-quantiles.js +141 -9
  331. package/vendor-core/stage-structure.js +17 -3
  332. package/vendor-core/threshold-overrides.js +9 -3
  333. package/vendor-core/types.js +70 -23
  334. package/vendor-core/wall-clock.js +8 -0
  335. package/vendor-core/wasted-core-hours.js +12 -12
  336. package/vendor-core/write-targets.js +146 -14
  337. package/vendor-core/zstd-worker-client.js +1 -1
  338. package/vendor-core/zstd-worker.js +1 -1
  339. package/public/assets/AutoscalingChurn-8t3U3isq.js +0 -1
  340. package/public/assets/CacheUtilization-CJs9tTum.js +0 -1
  341. package/public/assets/CachingOpportunity-D1NcHs0V.js +0 -1
  342. package/public/assets/ColdStart-CdRJ9gD8.js +0 -1
  343. package/public/assets/ConfigAudit-B5Gbe5js.js +0 -1
  344. package/public/assets/CoreUsageArea-a--NTRGY.js +0 -1
  345. package/public/assets/Dashboard-C1hNeX49.js +0 -2
  346. package/public/assets/DisclosureContext-ZpE6CkHx.js +0 -1
  347. package/public/assets/DuplicatePlanSubtree-dobXdz7E.js +0 -1
  348. package/public/assets/ExecutorUtilization-D9grwi7t.js +0 -1
  349. package/public/assets/GcPressure-8-G2ri10.js +0 -1
  350. package/public/assets/IncompleteRun-B91lky7a.js +0 -1
  351. package/public/assets/JobFailures-BsWvBMyJ.js +0 -1
  352. package/public/assets/MemoryUtilization-BM5schZI.js +0 -1
  353. package/public/assets/OverBroadcast-DdN8JIJH.js +0 -1
  354. package/public/assets/PartitionHint-CUvbnnxy.js +0 -1
  355. package/public/assets/PartitionSizing-CY5HDYtp.js +0 -1
  356. package/public/assets/PlanExplorer-DgHCDBD_.js +0 -1
  357. package/public/assets/PlanGraphRoute-DH3KJb8j.js +0 -1
  358. package/public/assets/RetryWaste-DOnE9vGF.js +0 -1
  359. package/public/assets/ShuffleIO-MMnDESkF.js +0 -1
  360. package/public/assets/Skew-BmZZJ5ms.js +0 -1
  361. package/public/assets/SlowHost-CsJzfEqo.js +0 -1
  362. package/public/assets/SmallFiles-BoK3R1Ex.js +0 -1
  363. package/public/assets/SpeculationWaste-CnVxB5AJ.js +0 -1
  364. package/public/assets/Spill-BhNicyBy.js +0 -1
  365. package/public/assets/StageFailed-mAhHU4l_.js +0 -1
  366. package/public/assets/StageFindingGroup-BEtPWcT0.js +0 -1
  367. package/public/assets/StageHeader-DJkYkrsU.js +0 -1
  368. package/public/assets/StageShape-BDxRknLp.js +0 -1
  369. package/public/assets/StageSlowness-BPRw-otN.js +0 -1
  370. package/public/assets/Straggler-CZCbnOWP.js +0 -1
  371. package/public/assets/TaskFailures-C57YcLFQ.js +0 -1
  372. package/public/assets/TinyTask-Bi84qU7n.js +0 -1
  373. package/public/assets/UnderBroadcast-DdQV7htE.js +0 -1
  374. package/public/assets/charts-vendor-0VMaLVLN.js +0 -59
  375. package/public/assets/finding-anchor-Cl6_5hfV.js +0 -1
  376. package/public/assets/impact-sort-CssZ6MT2.js +0 -1
  377. package/public/assets/index-2h_TgUAb.css +0 -2
  378. package/public/assets/index-B-03mG7K.js +0 -14
  379. package/public/assets/plan-graph-vendor-BYl-AzN5.js +0 -7
  380. package/public/assets/react-vendor-DEdfrRTJ.js +0 -9
  381. package/public/assets/select-CQjNhprm.js +0 -1
  382. package/public/assets/usePagedRows-Dyl6T8Ok.js +0 -1
  383. package/public/assets/useRecentFiles-XZEnLlal.js +0 -11
  384. package/public/assets/useSortMode-Dhvit8dt.js +0 -1
  385. package/public/docs/assets/chunks/@localSearchIndexroot.u_e5bQz4.js +0 -1
  386. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +0 -1
  387. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js +0 -1
  388. package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +0 -1
  389. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.js +0 -1
  390. package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.lean.js +0 -1
  391. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.wj6Et6UD.js +0 -1
  392. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +0 -1
  393. package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +0 -1
  394. package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.CKjWG56Y.js +0 -1
  395. package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +0 -1
  396. package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.js +0 -1
  397. package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.js +0 -1
  398. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +0 -1
  399. package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +0 -1
  400. package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +0 -1
  401. package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +0 -1
  402. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +0 -1
  403. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +0 -1
  404. package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +0 -1
  405. package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +0 -1
  406. package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.DC1YqXBp.js +0 -1
  407. package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.js +0 -1
  408. package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +0 -1
  409. package/public/docs/assets/contributor-guide_development-setup.md.D9bu4j_c.lean.js +0 -1
  410. package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +0 -1
  411. package/public/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  412. package/public/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  413. package/public/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  414. package/public/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  415. package/public/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  416. package/public/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  417. package/public/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  418. package/public/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  419. package/public/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  420. package/public/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  421. package/public/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  422. package/public/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  423. package/public/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  424. package/public/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  425. package/public/docs/assets/style.tmDhD3c8.css +0 -1
  426. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.OUQqw3mI.js +0 -23
  427. package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.OUQqw3mI.lean.js +0 -1
  428. package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +0 -1
  429. package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js +0 -1
  430. package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +0 -23
  431. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +0 -1
  432. package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +0 -1
  433. package/public/docs/assets/user-guide_understanding-findings.md.C9YRruMT.js +0 -1
  434. package/public/docs/assets/user-guide_understanding-findings.md.C9YRruMT.lean.js +0 -1
  435. /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.BYDo3ClR.lean.js} +0 -0
  436. /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
  437. /package/public/docs/assets/{contributor-guide_architecture_overview.md.C1sSvm9i.lean.js → contributor-guide_architecture_overview.md.nxkpOhgf.lean.js} +0 -0
  438. /package/public/docs/assets/{contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js → contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.lean.js} +0 -0
  439. /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
  440. /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
  441. /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
  442. /package/public/docs/assets/{contributor-guide_contributing.md.CQMvGwFt.lean.js → contributor-guide_contributing.md.DBW4drtZ.lean.js} +0 -0
  443. /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
  444. /package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.lean.js → user-guide_mcp-tools.md.49r3L2YE.lean.js} +0 -0
  445. /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
@@ -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,15 +365,29 @@
332
365
 
333
366
 
334
367
 
368
+
369
+
370
+
371
+
335
372
 
336
373
 
337
374
 
338
375
 
339
376
 
340
377
 
341
-
342
-
378
+
379
+
380
+
381
+
382
+
343
383
 
384
+
385
+
386
+
387
+
388
+
389
+
390
+
344
391
 
345
392
 
346
393
  // `Finding` is a union discriminated on `type`, one member per emitted finding type: see
@@ -353,7 +400,7 @@
353
400
 
354
401
 
355
402
 
356
-
403
+
357
404
 
358
405
 
359
406
 
@@ -1,5 +1,13 @@
1
1
  import { mergeIntervals } from './intervals.js';
2
2
 
3
+ /** The union of the stages' [submittedAt, completedAt) windows, sorted: where stages were active. */
4
+ export function stageWindows(stages ) {
5
+ const intervals = [...stages.values()]
6
+ .filter((s) => s.submittedAt != null && s.completedAt != null)
7
+ .map((s) => [s.submittedAt , s.completedAt ]);
8
+ return mergeIntervals(intervals);
9
+ }
10
+
3
11
  export function computeWallClock(app , stages ) {
4
12
  const start = app?.startTime ?? 0;
5
13
  const stageList = [...stages.values()].filter(s => s.submittedAt != null && s.completedAt != null);
@@ -1,7 +1,8 @@
1
1
  // Wasted core-hours summary. Pure, worker-agnostic reducer over the same inputs
2
- // the memoryUtilization detector's idle-cores math uses: capacity core-time vs.
2
+ // the memoryUtilization detector's idle-cores math uses: allocated core-time vs.
3
3
  // core-time that actually ran tasks. Feeds a report widget only, no detector.
4
4
 
5
+ import { allocatedCoreMs } from './allocation.js';
5
6
  import { computePeakConcurrentCores } from './core-count.js';
6
7
  import { MS_PER_CORE_HOUR } from './format-utils.js';
7
8
 
@@ -32,29 +33,28 @@ const EMPTY = {
32
33
  // SparkAppInfo | null before parsing completes), which the guard below already
33
34
  // tolerates. `resources` is on app's shape so the computePeakConcurrentCores
34
35
  // pass-through type-checks; real app objects always carry it. An omitted
35
- // `executorsRemoved` means no executor left, where peak cores equal the sum.
36
+ // `executorsRemoved` means no executor left.
36
37
  export function computeWastedCoreHours(
37
- app ,
38
+ app ,
38
39
  executorsAdded = [],
39
40
  runAggregates ,
40
41
  executorsRemoved = [],
41
42
  ) {
42
- // Nullish (not falsy) guard on times: a literal startTime:0 is valid.
43
43
  if (!runAggregates || app?.startTime == null || app?.endTime == null) return EMPTY;
44
44
  const appDurationMs = app.endTime - app.startTime;
45
45
  if (appDurationMs <= 0) return EMPTY;
46
46
 
47
- // Total cores: peak concurrent Executor-Added Total Cores, else peak executors ×
48
- // configured cores. Same capacity as the memoryUtilization detector, so an
49
- // executor replaced mid-run is not counted twice.
47
+ // Capacity is the run's allocation (cores x time alive), the same as the idle findings. The
48
+ // application end is set here, so the allocation never needs the stages to find where a log stops.
49
+ const allocatedMs = allocatedCoreMs({ app, stages: new Map(), executors: { added: executorsAdded, removed: executorsRemoved } });
50
+ if (allocatedMs == null) return EMPTY;
51
+ // Peak concurrent cores, reported as the cluster size: an executor replaced mid-run counts once.
50
52
  const totalCores = computePeakConcurrentCores(app, executorsAdded, executorsRemoved);
51
- if (totalCores <= 0) return EMPTY;
52
53
 
53
- const totalCoreHours = (totalCores * appDurationMs) / MS_PER_CORE_HOUR;
54
+ const totalCoreHours = allocatedMs / MS_PER_CORE_HOUR;
54
55
  const usefulCoreHours = (runAggregates.busyCoreMs ?? 0) / MS_PER_CORE_HOUR;
55
- // Clamp: totalCores can be a fallback estimate while busyCoreMs is measured
56
- // from task events, so the difference can dip below zero (same guard as
57
- // src/efficiency-model.js).
56
+ // Clamp: busyCoreMs is measured from task events while the allocation comes from executor
57
+ // events, so the difference can dip below zero (same guard as efficiency-model.ts).
58
58
  const wastedCoreHours = Math.max(0, totalCoreHours - usefulCoreHours);
59
59
 
60
60
  const topStages = Object.entries(runAggregates.perStage ?? {})