sparkforensics-server 0.4.0 → 0.5.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.
- package/index.js +3 -0
- package/package.json +3 -3
- package/public/assets/AutoscalingChurn-5hDGIXi4.js +1 -0
- package/public/assets/CacheUtilization-CT2SCnJJ.js +1 -0
- package/public/assets/CachingOpportunity-D_O_WkbH.js +1 -0
- package/public/assets/ColdStart-B38OiAOp.js +1 -0
- package/public/assets/ConfigAudit-BoplryCH.js +1 -0
- package/public/assets/CoreUsageArea-csgwNxkl.js +1 -0
- package/public/assets/Dashboard-BgKodJl2.js +2 -0
- package/public/assets/DisclosureContext-B0siMVdg.js +1 -0
- package/public/assets/DuplicatePlanSubtree-BqS2A2j4.js +1 -0
- package/public/assets/ExecutorUtilization-Dp52QGWo.js +1 -0
- package/public/assets/GcPressure-CT1yzR_s.js +1 -0
- package/public/assets/IncompleteRun-C7Qkjtjn.js +1 -0
- package/public/assets/JobFailures-WHl3ELfh.js +1 -0
- package/public/assets/MemoryUtilization-D8IUDHJI.js +1 -0
- package/public/assets/OverBroadcast-6oSSAQ_R.js +1 -0
- package/public/assets/PartitionHint-CUvbnnxy.js +1 -0
- package/public/assets/PartitionSizing-BwdFL-mw.js +1 -0
- package/public/assets/{PlanExplorer-DdmPkKO1.js → PlanExplorer-DcxmiQWL.js} +1 -1
- package/public/assets/PlanGraphRoute-pqnHTt4i.js +1 -0
- package/public/assets/RetryWaste-BTbeHkJc.js +1 -0
- package/public/assets/ShuffleIO-CRhrYNE0.js +1 -0
- package/public/assets/Skew-DSNXRG9V.js +1 -0
- package/public/assets/SlowHost-B7Lwu2EU.js +1 -0
- package/public/assets/SmallFiles-C082c2rd.js +1 -0
- package/public/assets/SpeculationWaste-BS2vpadr.js +1 -0
- package/public/assets/Spill-B4DVKHXx.js +1 -0
- package/public/assets/StageFailed-D3ac9Sou.js +1 -0
- package/public/assets/StageFindingGroup-D9naK109.js +1 -0
- package/public/assets/{StageHeader-BSV8ALhT.js → StageHeader-pI1nTwVf.js} +1 -1
- package/public/assets/StageShape-eO5le8iG.js +1 -0
- package/public/assets/StageSlowness-BaNyfNvL.js +1 -0
- package/public/assets/Straggler-D5Fnok_y.js +1 -0
- package/public/assets/TaskFailures-BWnC-K_G.js +1 -0
- package/public/assets/{TinyTask-CIPRxWfT.js → TinyTask-8_5lpq_2.js} +1 -1
- package/public/assets/UnderBroadcast-DJ0XSwth.js +1 -0
- package/public/assets/{finding-anchor-DF1cSHO1.js → finding-anchor-Cys8Z6Yn.js} +1 -1
- package/public/assets/{impact-sort-7z0ZcBZf.js → impact-sort-BTQndbof.js} +1 -1
- package/public/assets/index-DCeWge12.js +13 -0
- package/public/assets/parser-worker-DmeCnLWU.js +122 -0
- package/public/assets/{plan-graph-vendor-B0sEms9Z.js → plan-graph-vendor-BYl-AzN5.js} +1 -1
- package/public/assets/{react-vendor-BAKt3vny.js → react-vendor-DEdfrRTJ.js} +1 -1
- package/public/assets/{select-Cy5pmbrh.js → select-DIYN6D3y.js} +1 -1
- package/public/assets/{usePagedRows-BjPOpUU8.js → usePagedRows-BxvdFnPk.js} +1 -1
- package/public/assets/useRecentFiles-C3Ov47U7.js +11 -0
- package/public/assets/{useSortMode-CU0vzThu.js → useSortMode-B8SRfAoG.js} +1 -1
- package/public/docs/404.html +4 -3
- package/public/docs/assets/{app.C6w47a9W.js → app.D2GqAkhy.js} +1 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.D4OdkQjy.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.BcTNvT55.js → VPLocalSearchBox.BS4cSmtW.js} +1 -1
- package/public/docs/assets/chunks/{theme.D-mE6j02.js → theme.BuxVpb0H.js} +2 -2
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.wj6Et6UD.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.wj6Et6UD.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.fkPBXsU2.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.fkPBXsU2.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.CqZPrdo9.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.CqZPrdo9.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_index.md.tymxf1Fh.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_index.md.tymxf1Fh.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DyCBdjuM.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_state-and-history.md.DOTVHjTV.lean.js → contributor-guide_architecture_state-and-history.md.DyCBdjuM.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history_history-server-intake.md.C298hrHZ.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history_history-server-intake.md.C298hrHZ.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.uuAKpY0Y.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.uuAKpY0Y.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_investigation-routing.md.BGdiyElu.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_investigation-routing.md.BGdiyElu.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_reference-section.md.DWqC80bN.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_reference-section.md.DWqC80bN.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.Dk08VS7n.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.Dk08VS7n.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.js +6 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.lean.js +1 -0
- package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +1 -0
- package/public/docs/assets/{contributor-guide_contributing.md.B2E9ni79.lean.js → contributor-guide_contributing.md.CQMvGwFt.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_development-setup.md.D9bu4j_c.js +15 -0
- package/public/docs/assets/contributor-guide_development-setup.md.D9bu4j_c.lean.js +1 -0
- package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +1 -0
- package/public/docs/assets/{contributor-guide_testing.md.DTwhpoN2.lean.js → contributor-guide_testing.md.By6XGR9a.lean.js} +1 -1
- package/public/docs/assets/{style.DSixAiZE.css → style.tmDhD3c8.css} +1 -1
- package/public/docs/assets/user-guide_alternative-log-retrieval.md.CJpYvHGo.js +4 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval.md.CJpYvHGo.lean.js +1 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval_managed-platforms.md.DvDwHNtC.js +4 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval_managed-platforms.md.DvDwHNtC.lean.js +1 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval_ssh-bastion.md.Gszzlplv.js +1 -0
- package/public/docs/assets/user-guide_alternative-log-retrieval_ssh-bastion.md.Gszzlplv.lean.js +1 -0
- package/public/docs/assets/user-guide_getting-started.md.DYHHHkQR.js +1 -0
- package/public/docs/assets/user-guide_getting-started.md.DYHHHkQR.lean.js +1 -0
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.DpnfKz3x.js +22 -0
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.DpnfKz3x.lean.js +1 -0
- package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +1 -0
- package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.lean.js +1 -0
- package/public/docs/assets/user-guide_getting-started_regression-metric-keys.md.B_gzr2Mw.js +1 -0
- package/public/docs/assets/user-guide_getting-started_regression-metric-keys.md.B_gzr2Mw.lean.js +1 -0
- package/public/docs/assets/user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.js +4 -0
- package/public/docs/assets/user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools.md.C65SjWUL.js +16 -0
- package/public/docs/assets/user-guide_mcp-tools.md.C65SjWUL.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.CPowlb2q.js +55 -0
- package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.CPowlb2q.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +23 -0
- package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_evaluate-budgets.md.Dq5Azk8G.js +26 -0
- package/public/docs/assets/user-guide_mcp-tools_evaluate-budgets.md.Dq5Azk8G.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_get-finding-documentation.md.BtTTHNV6.js +14 -0
- package/public/docs/assets/user-guide_mcp-tools_get-finding-documentation.md.BtTTHNV6.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.js +20 -0
- package/public/docs/assets/user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_get-reference-doc.md.Yac4tH3u.js +5 -0
- package/public/docs/assets/user-guide_mcp-tools_get-reference-doc.md.Yac4tH3u.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.js +15 -0
- package/public/docs/assets/user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_list-runs.md.DpQPeCM3.js +12 -0
- package/public/docs/assets/user-guide_mcp-tools_list-runs.md.DpQPeCM3.lean.js +1 -0
- package/public/docs/assets/user-guide_run-comparison.md.B6aNtDYF.js +1 -0
- package/public/docs/assets/user-guide_run-comparison.md.B6aNtDYF.lean.js +1 -0
- package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.XBzQ1oEU.js +1 -0
- package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.XBzQ1oEU.lean.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.Dqt2H_C1.js +1 -0
- package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +30 -0
- package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +30 -0
- package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +30 -0
- package/public/docs/contributor-guide/architecture/board-widgets.html +7 -6
- package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +30 -0
- package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +30 -0
- package/public/docs/contributor-guide/architecture/detector-contract.html +7 -6
- package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +30 -0
- package/public/docs/contributor-guide/architecture/drill-down.html +7 -6
- package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +30 -0
- package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +30 -0
- package/public/docs/contributor-guide/architecture/impact-estimation.html +7 -6
- package/public/docs/contributor-guide/architecture/index.html +7 -6
- package/public/docs/contributor-guide/architecture/overview.html +7 -6
- package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +30 -0
- package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +30 -0
- package/public/docs/contributor-guide/architecture/state-and-history.html +7 -6
- package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +30 -0
- package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +30 -0
- package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +30 -0
- package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +30 -0
- package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +30 -0
- package/public/docs/contributor-guide/architecture/widget-rendering.html +7 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +30 -0
- package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +30 -0
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +35 -0
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +30 -0
- package/public/docs/contributor-guide/architecture/worker-protocol.html +7 -11
- package/public/docs/contributor-guide/contributing.html +7 -6
- package/public/docs/contributor-guide/development-setup.html +11 -10
- package/public/docs/contributor-guide/testing.html +7 -6
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +5 -4
- package/public/docs/tuning-reference/anti-patterns.html +5 -4
- package/public/docs/tuning-reference/aqe.html +5 -4
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -4
- package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -4
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -4
- package/public/docs/tuning-reference/bottleneck-failures.html +5 -4
- package/public/docs/tuning-reference/bottleneck-gc.html +5 -4
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -4
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -4
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -4
- package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -4
- package/public/docs/tuning-reference/bottleneck-skew.html +5 -4
- package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -4
- package/public/docs/tuning-reference/bottleneck-small-files.html +5 -4
- package/public/docs/tuning-reference/bottleneck-spill.html +5 -4
- package/public/docs/tuning-reference/bottleneck-straggler.html +5 -4
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -4
- package/public/docs/tuning-reference/bottleneck-utilization.html +5 -4
- package/public/docs/tuning-reference/caching.html +5 -4
- package/public/docs/tuning-reference/cluster-config.html +5 -4
- package/public/docs/tuning-reference/config.html +5 -4
- package/public/docs/tuning-reference/data-formats.html +5 -4
- package/public/docs/tuning-reference/index.html +5 -4
- package/public/docs/tuning-reference/intro.html +5 -4
- package/public/docs/tuning-reference/joins.html +5 -4
- package/public/docs/tuning-reference/memory-model.html +5 -4
- package/public/docs/tuning-reference/metrics.html +5 -4
- package/public/docs/tuning-reference/partitioning.html +5 -4
- package/public/docs/tuning-reference/pyspark.html +5 -4
- package/public/docs/tuning-reference/shuffle.html +5 -4
- package/public/docs/tuning-reference/spark-architecture.html +5 -4
- package/public/docs/tuning-reference/table-formats.html +5 -4
- package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +33 -0
- package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +30 -0
- package/public/docs/user-guide/alternative-log-retrieval.html +11 -7
- package/public/docs/user-guide/getting-started/ci-and-automation.html +51 -0
- package/public/docs/user-guide/getting-started/reading-the-dashboard.html +30 -0
- package/public/docs/user-guide/getting-started/regression-metric-keys.html +30 -0
- package/public/docs/user-guide/getting-started/tuning-thresholds.html +33 -0
- package/public/docs/user-guide/getting-started.html +7 -11
- package/public/docs/user-guide/mcp-tools/compare-runs.html +84 -0
- package/public/docs/user-guide/mcp-tools/diagnose-run.html +52 -0
- package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +55 -0
- package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +43 -0
- package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +49 -0
- package/public/docs/user-guide/mcp-tools/get-reference-doc.html +34 -0
- package/public/docs/user-guide/mcp-tools/get-run-summary.html +44 -0
- package/public/docs/user-guide/mcp-tools/list-runs.html +41 -0
- package/public/docs/user-guide/mcp-tools.html +12 -126
- package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +30 -0
- package/public/docs/user-guide/run-comparison.html +7 -6
- package/public/docs/user-guide/understanding-findings.html +7 -6
- package/public/export-template.html +44 -44
- package/public/index.html +4 -4
- package/vendor-core/allocation.js +106 -0
- package/vendor-core/analyzer.js +13 -13
- package/vendor-core/api-merge-writes.js +80 -0
- package/vendor-core/cli/budgets.js +23 -9
- package/vendor-core/cli/collect-run.js +11 -4
- package/vendor-core/cli/regression-budgets.js +83 -0
- package/vendor-core/comparison-output.js +49 -0
- package/vendor-core/comparison-verdict.js +22 -22
- package/vendor-core/core-source-hash.txt +1 -1
- package/vendor-core/delta-commands.js +13 -0
- package/vendor-core/delta-log-paths.js +42 -0
- package/vendor-core/delta-targets.js +127 -0
- package/vendor-core/detectors.js +202 -68
- package/vendor-core/docs-content/detection/cache.md +3 -2
- package/vendor-core/docs-content/detection/cfg.md +9 -8
- package/vendor-core/docs-content/detection/chrn.md +1 -2
- package/vendor-core/docs-content/detection/cold.md +4 -2
- package/vendor-core/docs-content/detection/fail.md +3 -2
- package/vendor-core/docs-content/detection/gc.md +3 -2
- package/vendor-core/docs-content/detection/host.md +2 -1
- package/vendor-core/docs-content/detection/local.md +1 -1
- package/vendor-core/docs-content/detection/mem.md +5 -2
- package/vendor-core/docs-content/detection/plan.md +2 -1
- package/vendor-core/docs-content/detection/sfail.md +2 -1
- package/vendor-core/docs-content/detection/shape.md +5 -4
- package/vendor-core/docs-content/detection/skew.md +3 -1
- package/vendor-core/docs-content/detection/slow.md +2 -2
- package/vendor-core/docs-content/detection/spec.md +2 -3
- package/vendor-core/docs-content/detection/spill.md +1 -1
- package/vendor-core/docs-site-config.js +1 -1
- package/vendor-core/effective-conf.js +107 -0
- package/vendor-core/efficiency-model.js +8 -6
- package/vendor-core/event-handlers.js +255 -56
- package/vendor-core/event-schemas.js +5 -1
- package/vendor-core/evidence-report.js +31 -14
- package/vendor-core/finding-generic-recommendation.js +20 -1
- package/vendor-core/finding-names.js +7 -0
- package/vendor-core/finding-presentation.js +61 -26
- package/vendor-core/finding-tag-help.js +1 -1
- package/vendor-core/finding-types.js +12 -0
- package/vendor-core/format-utils.js +4 -3
- package/vendor-core/impact-estimator.js +20 -2
- package/vendor-core/impact-format.js +14 -13
- package/vendor-core/impact-model.js +27 -5
- package/vendor-core/ingest.js +4 -2
- package/vendor-core/list-runs.js +5 -2
- package/vendor-core/mcp-server-factory.js +13 -6
- package/vendor-core/mcp-tools.js +43 -34
- package/vendor-core/model-assembler.js +23 -1
- package/vendor-core/parser-worker.js +1 -1
- package/vendor-core/plan-graph-model.js +1 -1
- package/vendor-core/plan-summary.js +13 -10
- package/vendor-core/proxy.js +3 -1
- package/vendor-core/python-stage.js +25 -0
- package/vendor-core/recommendation-rollup.js +16 -9
- package/vendor-core/redact.js +51 -10
- package/vendor-core/remediation.js +20 -0
- package/vendor-core/run-comparison.js +118 -140
- package/vendor-core/run-interpretation.js +2 -1
- package/vendor-core/run-metrics.js +198 -0
- package/vendor-core/run-output.js +26 -0
- package/vendor-core/run-totals.js +24 -0
- package/vendor-core/run-verdict.js +3 -4
- package/vendor-core/scorecard-estimates.js +1 -0
- package/vendor-core/session-snapshot.js +7 -0
- package/vendor-core/shs-schemas.js +2 -2
- package/vendor-core/spark-memory.js +17 -0
- package/vendor-core/stage-alignment.js +262 -0
- package/vendor-core/stage-identity.js +112 -0
- package/vendor-core/stage-plan-nodes.js +18 -0
- package/vendor-core/stage-quantiles.js +4 -0
- package/vendor-core/types.js +54 -1
- package/vendor-core/wasted-core-hours.js +10 -7
- package/vendor-core/write-target-args.js +48 -0
- package/vendor-core/write-targets.js +295 -0
- package/public/assets/AutoscalingChurn-CUSIvvab.js +0 -1
- package/public/assets/CacheUtilization-Ce0K6_X2.js +0 -1
- package/public/assets/CachingOpportunity-Cn-LmRA4.js +0 -1
- package/public/assets/ColdStart-BoiYzEyV.js +0 -1
- package/public/assets/ConfigAudit-BoJc6PAw.js +0 -1
- package/public/assets/CoreUsageArea-BFfXKu7K.js +0 -1
- package/public/assets/Dashboard-Bo_1jGqH.js +0 -2
- package/public/assets/DisclosureContext-LYzHroHO.js +0 -1
- package/public/assets/DuplicatePlanSubtree-BaAuGAoo.js +0 -1
- package/public/assets/ExecutorUtilization-OE6VMFkG.js +0 -1
- package/public/assets/GcPressure-AoL_ZNrV.js +0 -1
- package/public/assets/IncompleteRun-Bv6IzgsQ.js +0 -1
- package/public/assets/JobFailures-DF3U2z1f.js +0 -1
- package/public/assets/MemoryUtilization-B65OVZlq.js +0 -1
- package/public/assets/OverBroadcast-DXbJREDl.js +0 -1
- package/public/assets/PartitionSizing-DZLnjy0d.js +0 -1
- package/public/assets/PlanGraphRoute-C-BGmC6j.js +0 -1
- package/public/assets/RetryWaste-BbEPsdMw.js +0 -1
- package/public/assets/ShuffleIO-wZB94LS8.js +0 -1
- package/public/assets/Skew-SpQE7zeJ.js +0 -1
- package/public/assets/SlowHost-DwDBCTvr.js +0 -1
- package/public/assets/SmallFiles-yjY_6sT1.js +0 -1
- package/public/assets/SpeculationWaste-73PDmj0c.js +0 -1
- package/public/assets/Spill-DEw0Kd1i.js +0 -1
- package/public/assets/StageFailed-BEfXdEJc.js +0 -1
- package/public/assets/StageFindingGroup-Bzm2TY03.js +0 -1
- package/public/assets/StageShape-CzR4jHOt.js +0 -1
- package/public/assets/StageSlowness-vMPsnAaa.js +0 -1
- package/public/assets/Straggler-Cf__OGl5.js +0 -1
- package/public/assets/TaskFailures-BZ8kLZiO.js +0 -1
- package/public/assets/UnderBroadcast-CBm8ZPoS.js +0 -1
- package/public/assets/index-CEM9bz_x.js +0 -13
- package/public/assets/parser-worker-Q5KCPdKT.js +0 -122
- package/public/assets/useRecentFiles-BrBl-AP1.js +0 -11
- package/public/docs/assets/chunks/@localSearchIndexroot.DbZy5EtM.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.VTcYTH4d.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.VTcYTH4d.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CsqZHz_T.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CsqZHz_T.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.CFNCE8JB.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down.md.CFNCE8JB.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.HT9IJGIl.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.HT9IJGIl.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_index.md.3TO9ic6w.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_index.md.3TO9ic6w.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.CTEqVeV4.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.CTEqVeV4.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DOTVHjTV.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BmpARZve.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BmpARZve.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.21pTYhFF.js +0 -6
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.21pTYhFF.lean.js +0 -1
- package/public/docs/assets/contributor-guide_contributing.md.B2E9ni79.js +0 -1
- package/public/docs/assets/contributor-guide_development-setup.md.CjgZJwjK.js +0 -15
- package/public/docs/assets/contributor-guide_development-setup.md.CjgZJwjK.lean.js +0 -1
- package/public/docs/assets/contributor-guide_testing.md.DTwhpoN2.js +0 -1
- package/public/docs/assets/user-guide_alternative-log-retrieval.md.ZHPSbBkF.js +0 -1
- package/public/docs/assets/user-guide_alternative-log-retrieval.md.ZHPSbBkF.lean.js +0 -1
- package/public/docs/assets/user-guide_getting-started.md.CEa2ZX-l.js +0 -6
- package/public/docs/assets/user-guide_getting-started.md.CEa2ZX-l.lean.js +0 -1
- package/public/docs/assets/user-guide_mcp-tools.md.dQwIvdTF.js +0 -131
- package/public/docs/assets/user-guide_mcp-tools.md.dQwIvdTF.lean.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.ChLI3MK0.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.ChLI3MK0.lean.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.D1ccs60C.js +0 -1
- /package/public/docs/assets/{user-guide_understanding-findings.md.D1ccs60C.lean.js → user-guide_understanding-findings.md.Dqt2H_C1.lean.js} +0 -0
|
@@ -18,12 +18,14 @@ import {
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
} from './event-schemas.js';
|
|
21
|
+
import { DELTA_COMMANDS } from './delta-commands.js';
|
|
21
22
|
import { assertNever } from './assert-never.js';
|
|
22
23
|
import { finalizeStage } from './stage-quantiles.js';
|
|
23
24
|
import { MAX_FAILURE_DETAILS_PER_STAGE, extractTaskFailureDetail, taskFailureKey, } from './task-failure.js';
|
|
24
25
|
import { computeRunAggregates } from './run-aggregates.js';
|
|
26
|
+
import { parseSparkMemoryMB } from './spark-memory.js';
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
// Internal parser-state shapes: the real runtime objects the handlers build and mutate, not the
|
|
@@ -129,6 +131,8 @@ const MAX_TASK_SAMPLES = 20;
|
|
|
129
131
|
|
|
130
132
|
|
|
131
133
|
|
|
134
|
+
|
|
135
|
+
|
|
132
136
|
|
|
133
137
|
|
|
134
138
|
|
|
@@ -149,12 +153,29 @@ const MAX_TASK_SAMPLES = 20;
|
|
|
149
153
|
|
|
150
154
|
|
|
151
155
|
|
|
156
|
+
|
|
157
|
+
|
|
152
158
|
|
|
153
159
|
|
|
154
160
|
|
|
155
161
|
|
|
156
162
|
|
|
157
163
|
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
158
179
|
|
|
159
180
|
|
|
160
181
|
|
|
@@ -178,6 +199,9 @@ const MAX_TASK_SAMPLES = 20;
|
|
|
178
199
|
|
|
179
200
|
|
|
180
201
|
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
181
205
|
|
|
182
206
|
|
|
183
207
|
|
|
@@ -198,6 +222,9 @@ const MAX_TASK_SAMPLES = 20;
|
|
|
198
222
|
|
|
199
223
|
|
|
200
224
|
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
201
228
|
|
|
202
229
|
|
|
203
230
|
|
|
@@ -256,6 +283,10 @@ export function buildChunkDecoder() {
|
|
|
256
283
|
// line again after every slice scanned about 10 GB on a 100 MB line.
|
|
257
284
|
let pendingIsSqlEvent = false;
|
|
258
285
|
let keyTail = '';
|
|
286
|
+
// Where the pending line's plan-description value starts, once its key is found but too little
|
|
287
|
+
// of the value has arrived to tell whether it is a small Delta command description (see
|
|
288
|
+
// descriptionDisposition); -1 otherwise.
|
|
289
|
+
let undecidedValueStart = -1;
|
|
259
290
|
let skippingPlanDescription = false;
|
|
260
291
|
// Length of the backslash run the skipped bytes ended with, which escapes a quote at the start
|
|
261
292
|
// of the next chunk when odd.
|
|
@@ -289,11 +320,24 @@ export function buildChunkDecoder() {
|
|
|
289
320
|
keyTail = '';
|
|
290
321
|
appended = pending; // nothing of it was searched while it was shorter than the prefix
|
|
291
322
|
}
|
|
292
|
-
|
|
293
|
-
if (
|
|
323
|
+
let valueStart = undecidedValueStart;
|
|
324
|
+
if (valueStart === -1) {
|
|
325
|
+
const keyAt = findPlanKey(appended);
|
|
326
|
+
if (keyAt === -1) return; // the key may still arrive in a later chunk
|
|
327
|
+
valueStart = keyAt + PLAN_DESCRIPTION_KEY.length;
|
|
328
|
+
}
|
|
329
|
+
if (closingQuoteIndex(pending, valueStart) !== -1) { // complete: stripPlanDescription handles it
|
|
330
|
+
undecidedValueStart = -1;
|
|
331
|
+
pendingSettled = true;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
// A small kept description is not skipped: the line completes in a later chunk and
|
|
335
|
+
// stripPlanDescription keeps its arguments line.
|
|
336
|
+
const disposition = descriptionDisposition(pending, valueStart);
|
|
337
|
+
if (disposition === 'wait') { undecidedValueStart = valueStart; return; }
|
|
338
|
+
undecidedValueStart = -1;
|
|
294
339
|
pendingSettled = true;
|
|
295
|
-
|
|
296
|
-
if (closingQuoteIndex(pending, valueStart) !== -1) return; // complete: stripPlanDescription empties it
|
|
340
|
+
if (disposition === 'keep') return;
|
|
297
341
|
// Only a chunk whose last byte is a backslash carries a run over; any other last byte (such
|
|
298
342
|
// as part of a split multibyte char) ends it.
|
|
299
343
|
let run = 0;
|
|
@@ -355,6 +399,7 @@ export function buildChunkDecoder() {
|
|
|
355
399
|
pending = pendingHead = appended = start < text.length ? text.substring(start) : '';
|
|
356
400
|
pendingSettled = false;
|
|
357
401
|
pendingIsSqlEvent = false;
|
|
402
|
+
undecidedValueStart = -1;
|
|
358
403
|
}
|
|
359
404
|
if (!pendingSettled && pending !== '') settlePending(buffer[buffer.length - 1], appended);
|
|
360
405
|
}
|
|
@@ -391,6 +436,7 @@ export function createState() {
|
|
|
391
436
|
jobs: new Map(),
|
|
392
437
|
executors: { added: [], removed: [] },
|
|
393
438
|
skippedLines: 0,
|
|
439
|
+
unreadableSqlStarts: new Set(),
|
|
394
440
|
accumState: new Map(),
|
|
395
441
|
rddInfo: new Map(),
|
|
396
442
|
rddBlocks: new Map(),
|
|
@@ -432,23 +478,7 @@ export function normalizeSparkProperties(
|
|
|
432
478
|
return map;
|
|
433
479
|
}
|
|
434
480
|
|
|
435
|
-
|
|
436
|
-
// so a bare number means MiB. A k/m/g/t suffix sets the unit (trailing "b" redundant); a lone "b"
|
|
437
|
-
// ("10b") means bytes.
|
|
438
|
-
export function parseSparkMemoryMB(value ) {
|
|
439
|
-
if (value == null) return null;
|
|
440
|
-
const m = String(value).trim().toLowerCase().match(/^([\d.]+)\s*([kmgt]?)(b?)$/);
|
|
441
|
-
if (!m) return null;
|
|
442
|
-
const n = parseFloat(m[1]);
|
|
443
|
-
if (!Number.isFinite(n)) return null;
|
|
444
|
-
switch (m[2]) {
|
|
445
|
-
case 'k': return Math.round(n / 1024);
|
|
446
|
-
case 'g': return Math.round(n * 1024);
|
|
447
|
-
case 't': return Math.round(n * 1024 * 1024);
|
|
448
|
-
case 'm': return Math.round(n);
|
|
449
|
-
default: return m[3] === 'b' ? Math.round(n / (1024 * 1024)) : Math.round(n);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
481
|
+
export { parseSparkMemoryMB };
|
|
452
482
|
|
|
453
483
|
// Derive an allocated-resource summary from the Spark config map. Absent keys degrade to null,
|
|
454
484
|
// not guessed defaults.
|
|
@@ -533,29 +563,11 @@ function internTaskFailure(stage , endReason
|
|
|
533
563
|
return detail;
|
|
534
564
|
}
|
|
535
565
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (!stage) return null;
|
|
540
|
-
// Late TaskEnd for a stage whose StageCompleted already freed taskAttempts (finalizeStage): its
|
|
541
|
-
// stats are already baked into the finalized stage, don't re-add. The one exception is a losing
|
|
542
|
-
// speculative attempt, whose wasted time the finalized stage never saw.
|
|
543
|
-
if (stage.taskAttempts === null) {
|
|
544
|
-
accountLateSpeculativeLoser(event, stage);
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
state.evidenceInputs.taskRecords++;
|
|
549
|
-
|
|
550
|
-
const accumulables = event['Task Info']?.Accumulables ?? [];
|
|
551
|
-
for (const acc of accumulables) {
|
|
552
|
-
if (!state.taskAccumStages.has(acc.ID)) state.taskAccumStages.set(acc.ID, new Set());
|
|
553
|
-
state.taskAccumStages.get(acc.ID) .add(stageId);
|
|
554
|
-
}
|
|
566
|
+
// 'Task Info' and its Failed/Killed/Speculative fields are optional in the schema; Partial<>
|
|
567
|
+
// lets the {} fallback type-check while reads below default via ??/||.
|
|
568
|
+
|
|
555
569
|
|
|
556
|
-
|
|
557
|
-
// lets the {} fallback type-check while reads below default via ??/||.
|
|
558
|
-
|
|
570
|
+
function taskRecordOf(event , failure ) {
|
|
559
571
|
const info = event['Task Info'] ?? {};
|
|
560
572
|
const m = event['Task Metrics'] ?? {};
|
|
561
573
|
const sr = m['Shuffle Read Metrics'] ?? {};
|
|
@@ -566,14 +578,14 @@ export function accumulateTask(event , state
|
|
|
566
578
|
const duration = (info['Finish Time'] ?? 0) - (info['Launch Time'] ?? 0);
|
|
567
579
|
const failed = !!(info['Failed'] || info['Killed']);
|
|
568
580
|
|
|
569
|
-
|
|
581
|
+
return {
|
|
570
582
|
duration, failed,
|
|
571
583
|
taskId: info['Task ID'] ?? null,
|
|
572
584
|
attemptNumber: info['Attempt Number'] ?? 0,
|
|
573
585
|
launchTime: info['Launch Time'] ?? 0,
|
|
574
586
|
finishTime: info['Finish Time'] ?? 0,
|
|
575
587
|
reason: event['Task End Reason']?.['Reason'] ?? null,
|
|
576
|
-
failure
|
|
588
|
+
failure,
|
|
577
589
|
speculative: info['Speculative'] === true,
|
|
578
590
|
host: info['Host'] ?? '',
|
|
579
591
|
executorId: info['Executor ID'] ?? '',
|
|
@@ -589,7 +601,37 @@ export function accumulateTask(event , state
|
|
|
589
601
|
executorCpuTime: m['Executor CPU Time'] ?? 0,
|
|
590
602
|
inputBytes: inp['Bytes Read'] ?? 0,
|
|
591
603
|
outputBytes: out['Bytes Written'] ?? 0,
|
|
604
|
+
outputRecords: out['Records Written'] ?? null,
|
|
592
605
|
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export function accumulateTask(event , state ) {
|
|
609
|
+
const stageId = event['Stage ID'];
|
|
610
|
+
const stage = state.stages.get(stageId);
|
|
611
|
+
if (!stage) return null;
|
|
612
|
+
// Late TaskEnd for a stage whose StageCompleted already freed taskAttempts (finalizeStage): the
|
|
613
|
+
// finalized stage's figures stay as posted. A losing speculative attempt adds the wasted time the
|
|
614
|
+
// finalized stage never saw; any other task of a failed or earlier attempt is work only the
|
|
615
|
+
// metrics block reads, from lateAttemptWork.
|
|
616
|
+
if (stage.taskAttempts === null) {
|
|
617
|
+
const earlierAttempt = (event['Stage Attempt ID'] ?? 0) !== stage.stageAttemptId;
|
|
618
|
+
if (!accountLateSpeculativeLoser(event, stage) && (stage.stageFailureReason != null || earlierAttempt)) {
|
|
619
|
+
stage.lateAttemptWork = mergeAttemptTotals(taskAttemptTotals(taskRecordOf(event, null)), stage.lateAttemptWork);
|
|
620
|
+
}
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
state.evidenceInputs.taskRecords++;
|
|
625
|
+
|
|
626
|
+
const accumulables = event['Task Info']?.Accumulables ?? [];
|
|
627
|
+
for (const acc of accumulables) {
|
|
628
|
+
if (!state.taskAccumStages.has(acc.ID)) state.taskAccumStages.set(acc.ID, new Set());
|
|
629
|
+
state.taskAccumStages.get(acc.ID) .add(stageId);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const info = event['Task Info'] ?? {};
|
|
633
|
+
const failed = !!(info['Failed'] || info['Killed']);
|
|
634
|
+
const record = taskRecordOf(event, failed ? internTaskFailure(stage, event['Task End Reason']) : null);
|
|
593
635
|
|
|
594
636
|
// Dedupe only when Index is present (always true for real logs). Without it every event is a
|
|
595
637
|
// distinct task, preserving behavior for fixtures that omit Index.
|
|
@@ -614,9 +656,11 @@ export function accumulateTask(event , state
|
|
|
614
656
|
stage.retryTaskSamples.push(taskRecordToSample(existing));
|
|
615
657
|
}
|
|
616
658
|
}
|
|
659
|
+
stage.lateAttemptWork = mergeAttemptTotals(discardedAttemptTotals(existing), stage.lateAttemptWork);
|
|
617
660
|
stage.taskAttempts.set(key, record);
|
|
618
661
|
if (record.speculative) stage.speculativeWinners.add(key);
|
|
619
662
|
} else {
|
|
663
|
+
stage.lateAttemptWork = mergeAttemptTotals(discardedAttemptTotals(record), stage.lateAttemptWork);
|
|
620
664
|
// Non-winning duplicate (both failed, or a race where a winner is
|
|
621
665
|
// already recorded): its time is waste, its metrics are discarded.
|
|
622
666
|
if (existing.speculative || record.speculative) {
|
|
@@ -639,14 +683,16 @@ export function accumulateTask(event , state
|
|
|
639
683
|
// its time as speculation waste, pairing it the same way accumulateTask does: the late attempt is
|
|
640
684
|
// the speculative copy itself, or the original that a speculative winner beat. Every other stat
|
|
641
685
|
// of a late attempt stays excluded, as the finalized stage already posted them.
|
|
642
|
-
function accountLateSpeculativeLoser(event , stage )
|
|
686
|
+
function accountLateSpeculativeLoser(event , stage ) {
|
|
643
687
|
const info = event['Task Info'];
|
|
644
|
-
if (info?.['Index'] == null) return;
|
|
688
|
+
if (info?.['Index'] == null) return false;
|
|
645
689
|
const key = `${event['Stage Attempt ID'] ?? 0}:${info['Index']}`;
|
|
646
|
-
if (info['Speculative'] !== true && !stage.speculativeWinners.has(key)) return;
|
|
690
|
+
if (info['Speculative'] !== true && !stage.speculativeWinners.has(key)) return false;
|
|
647
691
|
stage.speculationWasteMs += (info['Finish Time'] ?? 0) - (info['Launch Time'] ?? 0);
|
|
648
692
|
stage.speculationWastedAttempts++;
|
|
649
693
|
stage.lateSpeculationWaste = true;
|
|
694
|
+
stage.lateAttemptWork = mergeAttemptTotals(discardedAttemptTotals(taskRecordOf(event, null)), stage.lateAttemptWork);
|
|
695
|
+
return true;
|
|
650
696
|
}
|
|
651
697
|
|
|
652
698
|
export function resolvePlanTree(
|
|
@@ -853,10 +899,63 @@ export function endJob(event , state
|
|
|
853
899
|
return { type: 'job', data: { ...job } };
|
|
854
900
|
}
|
|
855
901
|
|
|
902
|
+
const SUMMED_ATTEMPT_FIELDS = [
|
|
903
|
+
'taskCount', 'failedTasks', 'wastedAttempts', 'executorRunTime', 'executorCpuTime', 'jvmGCTime',
|
|
904
|
+
'memoryBytesSpilled', 'diskBytesSpilled', 'shuffleReadBytes', 'shuffleWriteBytes', 'inputBytes', 'outputBytes',
|
|
905
|
+
] ;
|
|
906
|
+
|
|
907
|
+
const addNullable = (a , b ) => (a == null ? b : b == null ? a : a + b);
|
|
908
|
+
|
|
909
|
+
function mergeAttemptTotals(a , b ) {
|
|
910
|
+
if (b == null) return a;
|
|
911
|
+
const totals = {
|
|
912
|
+
outputRecords: addNullable(a.outputRecords, b.outputRecords),
|
|
913
|
+
peakExecutionMemoryMax: Math.max(a.peakExecutionMemoryMax, b.peakExecutionMemoryMax),
|
|
914
|
+
durationMs: addNullable(a.durationMs, b.durationMs),
|
|
915
|
+
} ;
|
|
916
|
+
for (const field of SUMMED_ATTEMPT_FIELDS) totals[field] = a[field] + b[field];
|
|
917
|
+
return totals;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// One task's work; a late task adds no stage duration.
|
|
921
|
+
function taskAttemptTotals(t ) {
|
|
922
|
+
return {
|
|
923
|
+
taskCount: 1, failedTasks: t.failed ? 1 : 0, wastedAttempts: 0,
|
|
924
|
+
executorRunTime: t.executorRunTime, executorCpuTime: t.executorCpuTime, jvmGCTime: t.gcTime,
|
|
925
|
+
memoryBytesSpilled: t.memSpilled, diskBytesSpilled: t.diskSpilled,
|
|
926
|
+
shuffleReadBytes: t.shuffleRead, shuffleWriteBytes: t.shuffleWrite,
|
|
927
|
+
inputBytes: t.inputBytes, outputBytes: t.outputBytes, outputRecords: t.outputRecords,
|
|
928
|
+
peakExecutionMemoryMax: t.peakExecMem, durationMs: null,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// A task attempt the stage's figures drop because another attempt of the same task won (a retry
|
|
933
|
+
// after a failure, a speculative twin): its CPU, run time and I/O were still spent, so the
|
|
934
|
+
// metrics block counts them, but the task itself is already counted once.
|
|
935
|
+
function discardedAttemptTotals(t ) {
|
|
936
|
+
return { ...taskAttemptTotals(t), taskCount: 0, failedTasks: 0 };
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// The replaced record's finalized attempt added to the attempts it had already folded. An attempt resubmitted before its StageCompleted was never finalized, so its
|
|
940
|
+
// tasks are not counted.
|
|
941
|
+
function foldEarlierAttempts(replaced ) {
|
|
942
|
+
if (!replaced) return null;
|
|
943
|
+
if (replaced.taskAttempts !== null) return replaced.earlierAttempts;
|
|
944
|
+
const attempt = {
|
|
945
|
+
...Object.fromEntries(SUMMED_ATTEMPT_FIELDS.map((field) => [field, replaced[field]])) ,
|
|
946
|
+
outputRecords: replaced.outputRecords,
|
|
947
|
+
peakExecutionMemoryMax: replaced.peakExecutionMemoryMax ?? 0,
|
|
948
|
+
durationMs: replaced.submittedAt > 0 && replaced.completedAt >= replaced.submittedAt
|
|
949
|
+
? replaced.completedAt - replaced.submittedAt : null,
|
|
950
|
+
};
|
|
951
|
+
return mergeAttemptTotals(attempt, replaced.earlierAttempts);
|
|
952
|
+
}
|
|
953
|
+
|
|
856
954
|
export function submitStage(event , state ) {
|
|
857
955
|
state.evidenceInputs.stageSubmissions++;
|
|
858
956
|
const info = event['Stage Info'];
|
|
859
957
|
const id = info['Stage ID'];
|
|
958
|
+
const replaced = state.stages.get(id);
|
|
860
959
|
state.stages.set(id, {
|
|
861
960
|
id, name: info['Stage Name'] ?? '', details: info['Details'] ?? '',
|
|
862
961
|
submittedAt: info['Submission Time'] ?? 0, completedAt: 0,
|
|
@@ -864,13 +963,18 @@ export function submitStage(event , st
|
|
|
864
963
|
shuffleReadBytes: 0, shuffleWriteBytes: 0, fetchWaitTime: 0,
|
|
865
964
|
memoryBytesSpilled: 0, diskBytesSpilled: 0,
|
|
866
965
|
jvmGCTime: 0, executorRunTime: 0, executorCpuTime: 0,
|
|
867
|
-
inputBytes: 0, outputBytes: 0,
|
|
966
|
+
inputBytes: 0, outputBytes: 0, outputRecords: null,
|
|
868
967
|
sqlExecutionId: state.stageToSqlExec.get(id) ?? null,
|
|
869
968
|
parentIds: info['Parent IDs'] ?? [],
|
|
870
969
|
hostStats: new Map(),
|
|
871
970
|
speculativeTasks: 0,
|
|
872
971
|
failureReasons: new Map(),
|
|
873
972
|
stageFailureReason: null,
|
|
973
|
+
stageAttemptId: info['Stage Attempt ID'] ?? 0,
|
|
974
|
+
stageAttempts: (replaced?.stageAttempts ?? 0) + 1,
|
|
975
|
+
failedStageAttempts: replaced?.failedStageAttempts ?? 0,
|
|
976
|
+
earlierAttempts: foldEarlierAttempts(replaced),
|
|
977
|
+
lateAttemptWork: replaced?.lateAttemptWork ?? null,
|
|
874
978
|
taskAttempts: new Map(),
|
|
875
979
|
failureDetails: new Map(),
|
|
876
980
|
retryTaskSamples: [],
|
|
@@ -1027,6 +1131,10 @@ export function startSqlExecution(event
|
|
|
1027
1131
|
sparkPlanInfo,
|
|
1028
1132
|
hadAdaptiveUpdate: false,
|
|
1029
1133
|
};
|
|
1134
|
+
if (event.rootExecutionId !== undefined) exec.rootExecutionId = event.rootExecutionId;
|
|
1135
|
+
if (event.physicalPlanDescription && KEPT_ARGUMENTS.test(event.physicalPlanDescription)) {
|
|
1136
|
+
exec.commandArguments = event.physicalPlanDescription;
|
|
1137
|
+
}
|
|
1030
1138
|
state.sqlExecutions.set(exec.id, exec);
|
|
1031
1139
|
// A restarted execution carries a new plan: its next end must resolve it again.
|
|
1032
1140
|
state.resolvedPlanExecutions.delete(exec.id);
|
|
@@ -1164,6 +1272,8 @@ export function processEvent(event , state ) {
|
|
|
1164
1272
|
// every stage-duration figure becomes the epoch timestamp itself (a "47-year" stage).
|
|
1165
1273
|
if (!stage.submittedAt && info['Submission Time'] != null) stage.submittedAt = info['Submission Time'];
|
|
1166
1274
|
stage.stageFailureReason = info['Failure Reason'] ?? null;
|
|
1275
|
+
// A duplicate StageCompleted of an already-finalized attempt is not another failed attempt.
|
|
1276
|
+
if (stage.stageFailureReason != null && stage.taskAttempts !== null) stage.failedStageAttempts++;
|
|
1167
1277
|
// finalizeStage keeps its `stage` parameter typed as a loose Record (see that module); bridge
|
|
1168
1278
|
// StageRecord's more precise shape across that boundary with an explicit cast.
|
|
1169
1279
|
return finalizeStage(
|
|
@@ -1211,13 +1321,32 @@ const KNOWN_EVENT_TYPES = new Set(
|
|
|
1211
1321
|
);
|
|
1212
1322
|
|
|
1213
1323
|
// SQLExecutionStart and SQLAdaptiveExecutionUpdate carry `physicalPlanDescription`, Spark's text
|
|
1214
|
-
// rendering of the plan.
|
|
1215
|
-
//
|
|
1324
|
+
// rendering of the plan. The plan tree comes from sparkPlanInfo, yet on a real 3.5 GB log the
|
|
1325
|
+
// description was 72% of the AQE-update bytes, which were themselves 73% of the log. Cutting its
|
|
1216
1326
|
// string value out before JSON.parse halves the parse cost of those lines.
|
|
1327
|
+
//
|
|
1328
|
+
// One exception: a Delta write command's node prints only its bare name in sparkPlanInfo, and its
|
|
1329
|
+
// target table appears only on its `Arguments:` line of the description. When the description is
|
|
1330
|
+
// that of one of KEPT_ARGUMENTS_COMMANDS (checked on its first bytes, so no other line pays for it),
|
|
1331
|
+
// the value is cut down to that one line, never kept whole. A value that spans decoder chunks is
|
|
1332
|
+
// still dropped unread (buildChunkDecoder), so the write target then falls back to its child paths.
|
|
1217
1333
|
const SQL_UI_EVENT_PREFIX = '{"Event":"org.apache.spark.sql.execution.ui.SparkListenerSQL';
|
|
1218
1334
|
const PLAN_DESCRIPTION_KEY = '"physicalPlanDescription":"';
|
|
1219
1335
|
|
|
1220
|
-
const QUOTE = 0x22, BACKSLASH = 0x5c, NEWLINE = 0x0a;
|
|
1336
|
+
const QUOTE = 0x22, BACKSLASH = 0x5c, NEWLINE = 0x0a, LOWER_N = 0x6e;
|
|
1337
|
+
|
|
1338
|
+
// Delta commands whose root node carries no target in sparkPlanInfo. The description of a
|
|
1339
|
+
// command execution starts `== Physical Plan ==\nExecute <Command> (1)` (JSON-escaped newline).
|
|
1340
|
+
const KEPT_ARGUMENTS_COMMANDS = new Set(DELTA_COMMANDS);
|
|
1341
|
+
// Those whose description is just the command (no child plan printed under it), so it is small
|
|
1342
|
+
// enough to decode whole when it spans decoder chunks. SaveIntoDataSourceCommand prints the whole
|
|
1343
|
+
// query under it, which can be as large as any other plan description.
|
|
1344
|
+
const SMALL_DESCRIPTION_COMMANDS = new Set(
|
|
1345
|
+
[...KEPT_ARGUMENTS_COMMANDS].filter((command) => command !== 'SaveIntoDataSourceCommand'),
|
|
1346
|
+
);
|
|
1347
|
+
const PLAN_HEADER = '== Physical Plan ==\\nExecute ';
|
|
1348
|
+
// The kept form once JSON-parsed: `<Command>\nArguments: <line>`.
|
|
1349
|
+
const KEPT_ARGUMENTS = /^[A-Za-z]+\nArguments: /;
|
|
1221
1350
|
|
|
1222
1351
|
// Index of the closing quote of the JSON string whose content starts at `valueStart`: the first
|
|
1223
1352
|
// quote preceded by an even number of backslashes. -1 when the string is unterminated.
|
|
@@ -1242,7 +1371,48 @@ function closingQuoteAt(buf , limit , carried )
|
|
|
1242
1371
|
return -1;
|
|
1243
1372
|
}
|
|
1244
1373
|
|
|
1245
|
-
//
|
|
1374
|
+
// The command named by a description that starts at text[valueStart] with PLAN_HEADER, when text
|
|
1375
|
+
// holds the whole name; null otherwise.
|
|
1376
|
+
function describedCommand(text , valueStart ) {
|
|
1377
|
+
if (!text.startsWith(PLAN_HEADER, valueStart)) return null;
|
|
1378
|
+
const nameStart = valueStart + PLAN_HEADER.length;
|
|
1379
|
+
let nameEnd = nameStart;
|
|
1380
|
+
while (nameEnd < text.length && /[A-Za-z]/.test(text[nameEnd])) nameEnd++;
|
|
1381
|
+
return nameEnd < text.length ? text.slice(nameStart, nameEnd) : null;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// What to do with an open description value whose first bytes are text[valueStart...]: 'keep' it
|
|
1385
|
+
// (a small Delta command description), 'skip' it, or 'wait' when too little has arrived to tell.
|
|
1386
|
+
function descriptionDisposition(text , valueStart ) {
|
|
1387
|
+
const seen = text.slice(valueStart, valueStart + PLAN_HEADER.length);
|
|
1388
|
+
if (!PLAN_HEADER.startsWith(seen)) return 'skip';
|
|
1389
|
+
if (seen.length < PLAN_HEADER.length) return 'wait';
|
|
1390
|
+
const command = describedCommand(text, valueStart);
|
|
1391
|
+
if (command === null) return 'wait'; // the command name is still arriving
|
|
1392
|
+
return SMALL_DESCRIPTION_COMMANDS.has(command) ? 'keep' : 'skip';
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// The `<Command>\nArguments: <line>` part (still JSON-escaped) of the root command of a plan
|
|
1396
|
+
// description whose value spans line[valueStart, quote), or null when it is not one of
|
|
1397
|
+
// KEPT_ARGUMENTS_COMMANDS. SaveIntoDataSourceCommand is kept only for the Delta source: its other
|
|
1398
|
+
// providers (JDBC) print option maps that this tool has no use for.
|
|
1399
|
+
function keptCommandArguments(line , valueStart , quote ) {
|
|
1400
|
+
const command = describedCommand(line, valueStart);
|
|
1401
|
+
if (command === null || !KEPT_ARGUMENTS_COMMANDS.has(command)) return null;
|
|
1402
|
+
const key = `${command}\\nArguments: `;
|
|
1403
|
+
const at = line.indexOf(key, valueStart + PLAN_HEADER.length + command.length);
|
|
1404
|
+
if (at === -1 || at >= quote) return null;
|
|
1405
|
+
// The line ends at the first escaped newline: a backslash starts a two-char escape.
|
|
1406
|
+
let end = at + key.length;
|
|
1407
|
+
while (end < quote && !(line.charCodeAt(end) === BACKSLASH && line.charCodeAt(end + 1) === LOWER_N)) {
|
|
1408
|
+
end += line.charCodeAt(end) === BACKSLASH ? 2 : 1;
|
|
1409
|
+
}
|
|
1410
|
+
const kept = line.slice(at, Math.min(end, quote));
|
|
1411
|
+
return command === 'SaveIntoDataSourceCommand' && !kept.includes('DeltaDataSource') ? null : kept;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// Returns `line` with the physicalPlanDescription string value emptied (or cut to the kept
|
|
1415
|
+
// arguments line, see keptCommandArguments), or `line` unchanged when
|
|
1246
1416
|
// the key isn't found in Spark's compact form. The key pattern can't match inside another JSON
|
|
1247
1417
|
// string: there its quotes would be backslash-escaped. buildChunkDecoder already empties a value
|
|
1248
1418
|
// that spans chunks, so here the value is empty or within one chunk.
|
|
@@ -1255,7 +1425,7 @@ export function stripPlanDescription(line ) {
|
|
|
1255
1425
|
const quote = closingQuoteIndex(line, valueStart);
|
|
1256
1426
|
// Unterminated string (a truncated line): leave it for JSON.parse to reject.
|
|
1257
1427
|
if (quote === -1) return line;
|
|
1258
|
-
return line.slice(0, valueStart) + line.slice(quote);
|
|
1428
|
+
return line.slice(0, valueStart) + (keptCommandArguments(line, valueStart, quote) ?? '') + line.slice(quote);
|
|
1259
1429
|
}
|
|
1260
1430
|
|
|
1261
1431
|
const TASK_END_PREFIX = '{"Event":"SparkListenerTaskEnd",';
|
|
@@ -1363,6 +1533,18 @@ export function dispatchLine(
|
|
|
1363
1533
|
const BLOCK_UPDATED_PREFIX = '{"Event":"SparkListenerBlockUpdated",';
|
|
1364
1534
|
const RDD_BLOCK_ID_FRAGMENT = '"Block ID":"rdd_';
|
|
1365
1535
|
|
|
1536
|
+
const SQL_START_EVENT = 'org.apache.spark.sql.execution.ui.SparkListenerSQLExecutionStart';
|
|
1537
|
+
const SQL_START_ID = /"executionId":(\d+)/;
|
|
1538
|
+
|
|
1539
|
+
// Records the execution id of a skipped SQL start line, when the line's head still names it: a
|
|
1540
|
+
// line cut off mid-plan fails JSON.parse but keeps its leading fields.
|
|
1541
|
+
function noteUnreadableSqlStart(line , state ) {
|
|
1542
|
+
const head = line.slice(0, 300);
|
|
1543
|
+
if (!head.includes(`${SQL_START_EVENT}"`)) return;
|
|
1544
|
+
const id = SQL_START_ID.exec(head);
|
|
1545
|
+
if (id) state.unreadableSqlStarts.add(Number(id[1]));
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1366
1548
|
function parseAndDispatch(line , state , emit ) {
|
|
1367
1549
|
if (line.startsWith(BLOCK_UPDATED_PREFIX) && !line.includes(RDD_BLOCK_ID_FRAGMENT)) return;
|
|
1368
1550
|
let parsed ;
|
|
@@ -1370,6 +1552,7 @@ function parseAndDispatch(line , state , emit
|
|
|
1370
1552
|
parsed = parseTaskEnd(line) ?? JSON.parse(stripPlanDescription(line));
|
|
1371
1553
|
} catch {
|
|
1372
1554
|
state.skippedLines++;
|
|
1555
|
+
noteUnreadableSqlStart(line, state);
|
|
1373
1556
|
return;
|
|
1374
1557
|
}
|
|
1375
1558
|
// A real Spark event log carries many event types this tool never modeled (BlockManagerAdded,
|
|
@@ -1382,6 +1565,7 @@ function parseAndDispatch(line , state , emit
|
|
|
1382
1565
|
const result = SparkEventSchema.safeParse(parsed);
|
|
1383
1566
|
if (!result.success) {
|
|
1384
1567
|
state.skippedLines++;
|
|
1568
|
+
if (eventType === SQL_START_EVENT) noteUnreadableSqlStart(line, state);
|
|
1385
1569
|
return;
|
|
1386
1570
|
}
|
|
1387
1571
|
if (result.data.Event === 'org.apache.spark.sql.execution.ui.SparkListenerSQLExecutionEnd') {
|
|
@@ -1428,14 +1612,29 @@ export function collectLateSpeculationWaste(
|
|
|
1428
1612
|
return out;
|
|
1429
1613
|
}
|
|
1430
1614
|
|
|
1615
|
+
// Late work of every stage a failed attempt's late TaskEnd added to, re-posted once before `done`:
|
|
1616
|
+
// the stage message posted at completion predates it.
|
|
1617
|
+
export function collectLateAttemptWork(state ) {
|
|
1618
|
+
const out = new Map ();
|
|
1619
|
+
for (const [id, stage] of state.stages) {
|
|
1620
|
+
if (stage.lateAttemptWork != null) out.set(id, stage.lateAttemptWork);
|
|
1621
|
+
}
|
|
1622
|
+
return out;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1431
1625
|
export function emitParseCompletion(state , emit , linesProcessed ) {
|
|
1432
1626
|
// Executions that never ended keep their latest AQE update, as they did before it was deferred.
|
|
1433
1627
|
for (const executionId of [...state.pendingAdaptiveUpdates.keys()]) flushAdaptiveUpdate(executionId, state, emit);
|
|
1434
1628
|
emit({ type: 'progress', pct: 1, linesProcessed });
|
|
1629
|
+
emit({ type: 'stageLateAttemptWork', data: collectLateAttemptWork(state) });
|
|
1435
1630
|
emit({ type: 'runAggregates', data: computeRunAggregates(state.taskStore) });
|
|
1436
1631
|
emit({ type: 'stageSpeculationWaste', data: collectLateSpeculationWaste(state) });
|
|
1437
1632
|
emit({ type: 'stageExecutorMetrics', data: collectStageExecutorMetrics(state) });
|
|
1438
1633
|
emit(appMessage(state));
|
|
1439
|
-
emit({
|
|
1634
|
+
emit({
|
|
1635
|
+
type: 'done',
|
|
1636
|
+
skippedLines: state.skippedLines,
|
|
1637
|
+
...(state.unreadableSqlStarts.size > 0 ? { unreadableSqlExecutions: [...state.unreadableSqlStarts].sort((a, b) => a - b) } : {}),
|
|
1638
|
+
});
|
|
1440
1639
|
state.accumState.clear();
|
|
1441
1640
|
}
|
|
@@ -201,6 +201,7 @@ export const StageSubmittedEventSchema = z.object({
|
|
|
201
201
|
Event: z.literal('SparkListenerStageSubmitted'),
|
|
202
202
|
'Stage Info': z.object({
|
|
203
203
|
'Stage ID': z.number(),
|
|
204
|
+
'Stage Attempt ID': z.number().optional(),
|
|
204
205
|
'Stage Name': z.string().optional(),
|
|
205
206
|
Details: z.string().optional(),
|
|
206
207
|
'Submission Time': z.number().optional(),
|
|
@@ -311,6 +312,7 @@ export const TaskEndEventSchema = z.object({
|
|
|
311
312
|
}).optional(),
|
|
312
313
|
'Output Metrics': z.object({
|
|
313
314
|
'Bytes Written': z.number().optional(),
|
|
315
|
+
'Records Written': z.number().optional(),
|
|
314
316
|
}).optional(),
|
|
315
317
|
}).optional(),
|
|
316
318
|
});
|
|
@@ -319,13 +321,15 @@ export const TaskEndEventSchema = z.object({
|
|
|
319
321
|
export const SqlExecutionStartEventSchema = z.object({
|
|
320
322
|
Event: z.literal('org.apache.spark.sql.execution.ui.SparkListenerSQLExecutionStart'),
|
|
321
323
|
executionId: z.number(),
|
|
324
|
+
rootExecutionId: z.number().optional(),
|
|
322
325
|
description: z.string().optional(),
|
|
326
|
+
physicalPlanDescription: z.string().optional(),
|
|
323
327
|
time: z.number(),
|
|
324
328
|
sparkPlanInfo: SparkPlanInfoFieldSchema,
|
|
325
329
|
});
|
|
326
330
|
|
|
327
331
|
// applyAdaptiveExecutionUpdate: AQE re-plans mid-query and re-emits sparkPlanInfo for the same
|
|
328
|
-
// executionId; last write wins.
|
|
332
|
+
// executionId; last write wins. This schema does not declare physicalPlanDescription: nothing reads
|
|
329
333
|
// it, so zod strips it (dispatchLine already empties it before JSON.parse, see stripPlanDescription).
|
|
330
334
|
export const SqlAdaptiveExecutionUpdateEventSchema = z.object({
|
|
331
335
|
Event: z.literal('org.apache.spark.sql.execution.ui.SparkListenerSQLAdaptiveExecutionUpdate'),
|