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
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Constructors for the structured `remediation` a detector attaches next to its prose
|
|
2
|
+
// `recommendation`: only where the detector already names the Spark property, never an invented one.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/** The property should be raised; `suggested` is the value when the detector computed one. */
|
|
8
|
+
export function increaseConf(key , suggested = null) {
|
|
9
|
+
return { kind: 'conf', key, direction: 'increase', suggested };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** The property should be lowered; `suggested` is the value when the detector computed one. */
|
|
13
|
+
export function decreaseConf(key , suggested = null) {
|
|
14
|
+
return { kind: 'conf', key, direction: 'decrease', suggested };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The property should take a specific value (a switch or a class name), not move up or down. */
|
|
18
|
+
export function setConf(key , suggested = null) {
|
|
19
|
+
return { kind: 'conf', key, direction: 'set', suggested };
|
|
20
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { computeWallClock } from './wall-clock.js';
|
|
2
2
|
import { normalizeDetail } from './detectors.js';
|
|
3
|
-
import {
|
|
3
|
+
import { normalizeStageName, stageIdentityWith, identityIndexWith, pairEqualCounts } from './stage-identity.js';
|
|
4
|
+
import { computeAllocation } from './allocation.js';
|
|
5
|
+
import { totalExecutorCpuMs, withEarlierAttempts } from './run-totals.js';
|
|
4
6
|
import { captureSnapshot } from './session-snapshot.js';
|
|
7
|
+
import { alignStages, compileNormalizePatterns, COMPARISON_SCHEMA_VERSION, } from './stage-alignment.js';
|
|
5
8
|
import { tunedRunNote } from './threshold-overrides.js';
|
|
6
|
-
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
import { isIncompleteRun } from './check-coverage.js';
|
|
@@ -18,13 +21,26 @@ import { summarizeRunOutcome } from './run-outcome.js';
|
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
21
28
|
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
24
34
|
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
25
41
|
|
|
26
42
|
|
|
27
|
-
|
|
43
|
+
|
|
28
44
|
|
|
29
45
|
|
|
30
46
|
|
|
@@ -38,73 +54,10 @@ function jobOutcome(snapshot ) {
|
|
|
38
54
|
return { failedJobs, totalJobs, incomplete: isIncompleteRun(snapshot.catalog) };
|
|
39
55
|
}
|
|
40
56
|
|
|
41
|
-
|
|
42
|
-
// the same logical stage across two runs normalizes to one identity.
|
|
43
|
-
export function normalizeStageName(name ) {
|
|
44
|
-
return String(name)
|
|
45
|
-
.toLowerCase()
|
|
46
|
-
.replace(/\b[0-9a-f]{8,}\b/g, '#') // hex ids/uuids first (they contain digits)
|
|
47
|
-
.replace(/\d+/g, '#')
|
|
48
|
-
.replace(/\s+/g, ' ')
|
|
49
|
-
.trim();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Bottom-up, order-independent structural identity of a resolved plan tree:
|
|
53
|
-
// each node folds its normalized name/detail with its children's digests
|
|
54
|
-
// (children sorted, so AQE picking a different broadcast side still matches),
|
|
55
|
-
// so two plans collide only when their whole shape and every node's detail
|
|
56
|
-
// agree. `normalizeDetail` strips the run-to-run noise (expr ids, `plan_id=`,
|
|
57
|
-
// codegen numbers, AQE build-side choice, commutative-operand order). Each
|
|
58
|
-
// node folds to a fixed-length cyrb53 digest (JSON.stringify-encoded, so
|
|
59
|
-
// detail text containing `<`/`>`/`{`/`,` can't collide two different plans)
|
|
60
|
-
// instead of embedding full child identity strings, which would re-escape
|
|
61
|
-
// every level below and blow identity size up to ~2^depth on the 20-60+
|
|
62
|
-
// operator-deep plans real Spark produces.
|
|
63
|
-
function planTreeIdentity(root ) {
|
|
64
|
-
if (!root) return null;
|
|
65
|
-
function visit(node ) {
|
|
66
|
-
const childDigests = (node.children ?? []).map(visit).sort();
|
|
67
|
-
return cyrb53(JSON.stringify([normalizeStageName(node.name ?? ''), normalizeDetail(node.detail ?? ''), childDigests]));
|
|
68
|
-
}
|
|
69
|
-
return visit(root);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Plan identity for the stage's SQL execution, scoped to only the plan nodes
|
|
73
|
-
// this stage actually ran (`node.stageIds`), not the whole tree: two stages
|
|
74
|
-
// sharing one SQL execution (e.g. a self-join's two Exchange stages) otherwise
|
|
75
|
-
// collapse onto one identity regardless of which part of the plan each ran.
|
|
76
|
-
// Falls back to the coarser whole-tree identity when the stage has no
|
|
77
|
-
// attributed nodes (hand-built snapshots without `stageIds`, or unmatched
|
|
78
|
-
// accumulables).
|
|
79
|
-
function sqlNodeIdentity(stage , snapshot ) {
|
|
80
|
-
const execId = stage.sqlExecutionId;
|
|
81
|
-
if (execId == null) return '';
|
|
82
|
-
const root = snapshot.sql.get(execId)?.planTree ?? null;
|
|
83
|
-
if (!root) return '';
|
|
84
|
-
const fingerprints = [];
|
|
85
|
-
(function collect(node ) {
|
|
86
|
-
if (node.stageIds?.includes(stage.id)) {
|
|
87
|
-
fingerprints.push(JSON.stringify([normalizeStageName(node.name ?? ''), normalizeDetail(node.detail ?? '')]));
|
|
88
|
-
}
|
|
89
|
-
for (const child of node.children ?? []) collect(child);
|
|
90
|
-
})(root);
|
|
91
|
-
if (fingerprints.length === 0) return planTreeIdentity(root) ?? '';
|
|
92
|
-
return cyrb53(JSON.stringify(fingerprints.sort()));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function stageIdentity(stage , snapshot ) {
|
|
96
|
-
return normalizeStageName(stage.name ?? '') + '§' + sqlNodeIdentity(stage, snapshot);
|
|
97
|
-
}
|
|
57
|
+
export { normalizeStageName };
|
|
98
58
|
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
for (const [id, stage] of snapshot.stages) {
|
|
102
|
-
const key = stageIdentity(stage, snapshot);
|
|
103
|
-
const ids = byIdentity.get(key);
|
|
104
|
-
if (ids) ids.push(id);
|
|
105
|
-
else byIdentity.set(key, [id]);
|
|
106
|
-
}
|
|
107
|
-
return byIdentity;
|
|
59
|
+
export function stageIdentity(stage , snapshot ) {
|
|
60
|
+
return stageIdentityWith(stage, snapshot, normalizeDetail);
|
|
108
61
|
}
|
|
109
62
|
|
|
110
63
|
export function matchStages(baseSnap , candSnap )
|
|
@@ -113,10 +66,8 @@ export function matchStages(baseSnap , candSnap
|
|
|
113
66
|
|
|
114
67
|
|
|
115
68
|
{
|
|
116
|
-
const baseIdx =
|
|
117
|
-
const candIdx =
|
|
118
|
-
const pairs = [];
|
|
119
|
-
const matchedIdentities = new Set ();
|
|
69
|
+
const baseIdx = identityIndexWith(baseSnap, (stage) => stageIdentity(stage, baseSnap));
|
|
70
|
+
const candIdx = identityIndexWith(candSnap, (stage) => stageIdentity(stage, candSnap));
|
|
120
71
|
const collisionIdentities = new Set ();
|
|
121
72
|
// A collision is a single-run property: more than one stage in the SAME run
|
|
122
73
|
// shares an identity. Recorded per-run regardless of whether the other run
|
|
@@ -124,26 +75,7 @@ export function matchStages(baseSnap , candSnap
|
|
|
124
75
|
// don't, so this set means "was ambiguous", not "stayed unpaired".
|
|
125
76
|
for (const idx of [baseIdx, candIdx])
|
|
126
77
|
for (const [identity, ids] of idx) if (ids.length > 1) collisionIdentities.add(identity);
|
|
127
|
-
|
|
128
|
-
// *count*, so pair its stages off positionally by sorted id rather than
|
|
129
|
-
// dropping them. This is exact when the identity actually distinguishes
|
|
130
|
-
// stages (e.g. self-comparing a run: every stage matches itself). It's a
|
|
131
|
-
// best-effort guess when the identity is coarse (no SQL/attribution) and
|
|
132
|
-
// the two sides are genuinely different runs -- two unrelated same-named
|
|
133
|
-
// stages could get cross-paired. Accepted tradeoff: dropping them instead
|
|
134
|
-
// would also sacrifice the exact-self-comparison case, which matters more.
|
|
135
|
-
for (const identity of [...baseIdx.keys()].sort()) {
|
|
136
|
-
const candIds = candIdx.get(identity);
|
|
137
|
-
if (!candIds) continue;
|
|
138
|
-
const baseIds = baseIdx.get(identity) ;
|
|
139
|
-
if (baseIds.length !== candIds.length) continue;
|
|
140
|
-
const sortedBaseIds = [...baseIds].sort((a, b) => a - b);
|
|
141
|
-
const sortedCandIds = [...candIds].sort((a, b) => a - b);
|
|
142
|
-
for (let i = 0; i < sortedBaseIds.length; i++) {
|
|
143
|
-
pairs.push({ identity, baseId: sortedBaseIds[i], candId: sortedCandIds[i] });
|
|
144
|
-
}
|
|
145
|
-
matchedIdentities.add(identity);
|
|
146
|
-
}
|
|
78
|
+
const { pairs, matchedIdentities } = pairEqualCounts(baseIdx, candIdx);
|
|
147
79
|
const total = baseSnap.stages.size + candSnap.stages.size;
|
|
148
80
|
// Both runs stage-less: nothing to compare, not "nothing matched".
|
|
149
81
|
const coverage = total === 0 ? 1 : (2 * pairs.length) / total;
|
|
@@ -199,6 +131,7 @@ function skewRatios(stages ) {
|
|
|
199
131
|
export const COMPARISON_METRIC_KEYS = [
|
|
200
132
|
'wallClock', 'shuffleSpill', 'taskSkew', 'failedTaskRate', 'diskSpill', 'gcTime',
|
|
201
133
|
'inputBytes', 'outputBytes', 'executorRunTime', 'taskCount', 'executorsAdded',
|
|
134
|
+
'executorCpuTime', 'allocatedCoreHours',
|
|
202
135
|
];
|
|
203
136
|
|
|
204
137
|
// Volume/count metrics, not cost metrics: more or less input/output data, or
|
|
@@ -228,20 +161,24 @@ function metric(
|
|
|
228
161
|
|
|
229
162
|
export function metricDeltas(baseSnap , candSnap ) {
|
|
230
163
|
const out = [];
|
|
164
|
+
// Run totals count every task attempt, failed and speculative ones too (run-totals.ts), the
|
|
165
|
+
// same sums the CLI metrics block reports; skew stays on each stage's latest attempt.
|
|
166
|
+
const attemptsOf = (snap ) => withEarlierAttempts([...snap.stages.values()]);
|
|
231
167
|
|
|
232
168
|
// Wall-clock: always computable (computeWallClock tolerates a null app).
|
|
233
169
|
out.push(metric('wallClock', 'Wall-clock duration',
|
|
234
170
|
computeWallClock(baseSnap.app, baseSnap.stages).total,
|
|
235
171
|
computeWallClock(candSnap.app, candSnap.stages).total));
|
|
236
172
|
|
|
237
|
-
//
|
|
238
|
-
// matching is unreliable on real logs
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
173
|
+
// Memory spill (Spark's memoryBytesSpilled) over the whole run: a sum needs no
|
|
174
|
+
// stage matching, and matching is unreliable on real logs, so scope it to all
|
|
175
|
+
// stages exactly like task-skew and failed-rate below. The key stays
|
|
176
|
+
// `shuffleSpill` so existing --regression-metric callers keep working.
|
|
177
|
+
const bSpill = sumField(attemptsOf(baseSnap), 'memoryBytesSpilled');
|
|
178
|
+
const cSpill = sumField(attemptsOf(candSnap), 'memoryBytesSpilled');
|
|
179
|
+
out.push(metric('shuffleSpill', 'Memory spill',
|
|
243
180
|
bSpill.present ? bSpill.sum : null, cSpill.present ? cSpill.sum : null,
|
|
244
|
-
{ unavailableReason: bSpill.present && cSpill.present ? undefined : 'No
|
|
181
|
+
{ unavailableReason: bSpill.present && cSpill.present ? undefined : 'No memory-spill data recorded for a run' }));
|
|
245
182
|
|
|
246
183
|
// Task skew: p95 of per-stage ratio across the whole run.
|
|
247
184
|
const bSkew = p95(skewRatios([...baseSnap.stages.values()]));
|
|
@@ -250,10 +187,10 @@ export function metricDeltas(baseSnap , candSnap
|
|
|
250
187
|
{ unavailableReason: bSkew != null && cSkew != null ? undefined : 'No stage had measurable duration for a run' }));
|
|
251
188
|
|
|
252
189
|
// Failed-task rate: Σ failedTasks / Σ taskCount.
|
|
253
|
-
const bTasks = sumField(
|
|
254
|
-
const cTasks = sumField(
|
|
255
|
-
const bFailed = sumField(
|
|
256
|
-
const cFailed = sumField(
|
|
190
|
+
const bTasks = sumField(attemptsOf(baseSnap), 'taskCount');
|
|
191
|
+
const cTasks = sumField(attemptsOf(candSnap), 'taskCount');
|
|
192
|
+
const bFailed = sumField(attemptsOf(baseSnap), 'failedTasks');
|
|
193
|
+
const cFailed = sumField(attemptsOf(candSnap), 'failedTasks');
|
|
257
194
|
// Guard on BOTH inputs: a missing `failedTasks` field must render Unavailable,
|
|
258
195
|
// not a false 0% rate (dividing an absent-and-therefore-0 numerator).
|
|
259
196
|
const bRate = bTasks.present && bTasks.sum > 0 && bFailed.present ? bFailed.sum / bTasks.sum : null;
|
|
@@ -265,8 +202,8 @@ export function metricDeltas(baseSnap , candSnap
|
|
|
265
202
|
// carries (set in finalizeStage). Correct at any match coverage, like the
|
|
266
203
|
// sums above; no parser or detector change.
|
|
267
204
|
const sumMetric = (key , label , field , reason ) => {
|
|
268
|
-
const b = sumField(
|
|
269
|
-
const c = sumField(
|
|
205
|
+
const b = sumField(attemptsOf(baseSnap), field);
|
|
206
|
+
const c = sumField(attemptsOf(candSnap), field);
|
|
270
207
|
out.push(metric(key, label, b.present ? b.sum : null, c.present ? c.sum : null,
|
|
271
208
|
{ unavailableReason: b.present && c.present ? undefined : reason }));
|
|
272
209
|
};
|
|
@@ -285,6 +222,18 @@ export function metricDeltas(baseSnap , candSnap
|
|
|
285
222
|
out.push(metric('executorsAdded', 'Executors added', bExec, cExec,
|
|
286
223
|
{ unavailableReason: bExec != null && cExec != null ? undefined : 'No executor events recorded for a run' }));
|
|
287
224
|
|
|
225
|
+
// Executor CPU time (ms) and allocated core-hours cost resources, so less is better. CPU time is
|
|
226
|
+
// null, not 0, on a run whose log never recorded it (older Spark).
|
|
227
|
+
const cpuMs = (snap ) => totalExecutorCpuMs(attemptsOf(snap));
|
|
228
|
+
const bCpu = cpuMs(baseSnap), cCpu = cpuMs(candSnap);
|
|
229
|
+
out.push(metric('executorCpuTime', 'Executor CPU time', bCpu, cCpu,
|
|
230
|
+
{ unavailableReason: bCpu != null && cCpu != null ? undefined : 'No executor CPU time recorded for a run' }));
|
|
231
|
+
const coreHours = (snap ) =>
|
|
232
|
+
(snap.executors ? computeAllocation(snap).coreHours : null);
|
|
233
|
+
const bCore = coreHours(baseSnap), cCore = coreHours(candSnap);
|
|
234
|
+
out.push(metric('allocatedCoreHours', 'Allocated core-hours', bCore, cCore,
|
|
235
|
+
{ unavailableReason: bCore != null && cCore != null ? undefined : 'No executor lifecycle or core count recorded for a run' }));
|
|
236
|
+
|
|
288
237
|
return out;
|
|
289
238
|
}
|
|
290
239
|
|
|
@@ -333,19 +282,19 @@ function namesConflict(a , b )
|
|
|
333
282
|
}
|
|
334
283
|
|
|
335
284
|
// A pair's skew ratio is null when the stage's duration wasn't measurable (see
|
|
336
|
-
// `stageSkewRatio`).
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}).sort((x, y) => x.
|
|
285
|
+
// `stageSkewRatio`). Rows come from the aligner's `stagePairs`, so the table and the coverage
|
|
286
|
+
// banner agree. A pair covers one stage per side here; `pairId` is the unique row key.
|
|
287
|
+
function stageSkewDeltas(baseSnap , candSnap , stagePairs ) {
|
|
288
|
+
return stagePairs.map((p) => {
|
|
289
|
+
const baseId = p.baseStageIds[0], candId = p.candStageIds[0];
|
|
290
|
+
const baseStage = baseSnap.stages.get(baseId) ;
|
|
291
|
+
const b = stageSkewRatio(baseStage);
|
|
292
|
+
const c = stageSkewRatio(candSnap.stages.get(candId) );
|
|
293
|
+
return {
|
|
294
|
+
pairId: p.pairId, name: normalizeStageName(baseStage.name ?? ''), baseId, candId,
|
|
295
|
+
baseline: b, candidate: c, delta: b != null && c != null ? c - b : null,
|
|
296
|
+
};
|
|
297
|
+
}).sort((x, y) => x.name < y.name ? -1 : x.name > y.name ? 1 : x.baseId - y.baseId);
|
|
349
298
|
}
|
|
350
299
|
|
|
351
300
|
// Compact, view-friendly per-stage record for the manual stage-pinning panel.
|
|
@@ -387,6 +336,14 @@ function stageList(snap )
|
|
|
387
336
|
// caller's reference, so one shared empty Map is safe here.
|
|
388
337
|
const EMPTY_TASK_DATA = new Map ();
|
|
389
338
|
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
390
347
|
// Wraps the analyze()-to-compareRuns() snapshot-building sequence shared by
|
|
391
348
|
// the CLI's --baseline path and mcp-tools.ts's compareRuns tool: both need
|
|
392
349
|
// captureSnapshot (with an empty taskDataCache, the interactive drill-down
|
|
@@ -395,50 +352,65 @@ const EMPTY_TASK_DATA = new Map ();
|
|
|
395
352
|
export function buildComparison(
|
|
396
353
|
baseline ,
|
|
397
354
|
candidate ,
|
|
355
|
+
options ,
|
|
398
356
|
) {
|
|
399
357
|
return compareRuns(
|
|
400
358
|
{ label: baseline.label, snapshot: captureSnapshot(baseline.appModel, baseline.catalog, EMPTY_TASK_DATA) },
|
|
401
359
|
{ label: candidate.label, snapshot: captureSnapshot(candidate.appModel, candidate.catalog, EMPTY_TASK_DATA) },
|
|
360
|
+
options,
|
|
402
361
|
);
|
|
403
362
|
}
|
|
404
363
|
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
|
|
409
|
-
// comparison is dominated by guesswork, not genuine pairing. 0.5 is thus the
|
|
410
|
-
// natural midpoint for "more matched than not," not an arbitrary tuning knob.
|
|
411
|
-
const LOW_COVERAGE_THRESHOLD = 0.5;
|
|
364
|
+
// `ok` needs this share of both runs' executor run time to sit in paired stages. Run time, not
|
|
365
|
+
// stage count: the stages a run spends its time in decide whether a per-stage delta compares the
|
|
366
|
+
// same work, and a count lets many small matched stages hide an unmatched heavy one.
|
|
367
|
+
const RUNTIME_COVERAGE_THRESHOLD = 0.9;
|
|
412
368
|
|
|
413
369
|
export function compareRuns(
|
|
414
370
|
baseline ,
|
|
415
371
|
candidate ,
|
|
372
|
+
options = {},
|
|
416
373
|
) {
|
|
417
374
|
const baseSnap = baseline.snapshot, candSnap = candidate.snapshot;
|
|
375
|
+
// The old exact matcher stays the source of `matchedCoverage`; pairs and the gate come from the aligner.
|
|
418
376
|
const match = matchStages(baseSnap, candSnap);
|
|
377
|
+
const alignment = alignStages(baseSnap, candSnap, { normalizePath: compileNormalizePatterns(options.normalizePath) });
|
|
378
|
+
const runtimeCoverage = alignment.runtimeCoverage;
|
|
419
379
|
// Name equality is a weak confidence signal, not a hard gate: renaming a job
|
|
420
380
|
// is the normal way to label an A/B experiment, so a mismatch must not block
|
|
421
381
|
// the (matching-free, name-independent) deltas. Surface it as `low` instead.
|
|
422
382
|
const namesDiffer = namesConflict(baseSnap.app, candSnap.app);
|
|
423
|
-
//
|
|
424
|
-
//
|
|
425
|
-
//
|
|
426
|
-
const
|
|
427
|
-
const
|
|
428
|
-
|
|
383
|
+
// Runtime coverage is the other half of the signal: identical names on two runs whose heavy
|
|
384
|
+
// stages do not pair are just as misleading as differing names on two runs that pair well, so
|
|
385
|
+
// either condition alone drops confidence to `low`. No run time at all is `insufficient`.
|
|
386
|
+
const insufficient = runtimeCoverage === null;
|
|
387
|
+
const lowCoverage = runtimeCoverage !== null && runtimeCoverage < RUNTIME_COVERAGE_THRESHOLD;
|
|
388
|
+
const confidence = insufficient ? 'insufficient' : namesDiffer || lowCoverage ? 'low' : 'ok';
|
|
389
|
+
// Rounded down, so a share just under the gate never reads as 90%.
|
|
390
|
+
const share = runtimeCoverage === null ? '' : `${Math.floor(runtimeCoverage * 100)}%`;
|
|
391
|
+
const reason = insufficient
|
|
392
|
+
? `${namesDiffer ? 'Run names differ and no' : 'No'} executor run time was recorded in either run, so there is no work to compare.`
|
|
393
|
+
: namesDiffer && lowCoverage
|
|
394
|
+
? `Run names differ and only ${share} of executor run time is in matched stages, so deltas may compare different work.`
|
|
429
395
|
: namesDiffer
|
|
430
396
|
? 'Run names differ, so deltas may compare different work.'
|
|
431
397
|
: lowCoverage
|
|
432
|
-
? `Only ${
|
|
398
|
+
? `Only ${share} of executor run time is in matched stages, so per-stage rows mostly compare unrelated work.`
|
|
433
399
|
: null;
|
|
434
400
|
return {
|
|
435
401
|
baselineLabel: baseline.label, candidateLabel: candidate.label,
|
|
436
|
-
confidence
|
|
402
|
+
confidence,
|
|
437
403
|
reason,
|
|
438
404
|
matchedCoverage: match.coverage,
|
|
405
|
+
comparisonSchemaVersion: COMPARISON_SCHEMA_VERSION,
|
|
406
|
+
runtimeCoverage,
|
|
407
|
+
stagePairs: alignment.pairs,
|
|
408
|
+
unmatched: alignment.unmatched,
|
|
409
|
+
replanned: alignment.replanned,
|
|
410
|
+
bookkeepingStageIds: alignment.bookkeepingStageIds,
|
|
439
411
|
metrics: metricDeltas(baseSnap, candSnap),
|
|
440
412
|
findings: findingsDelta(baseSnap, candSnap),
|
|
441
|
-
stageSkew: stageSkewDeltas(baseSnap, candSnap,
|
|
413
|
+
stageSkew: stageSkewDeltas(baseSnap, candSnap, alignment.pairs),
|
|
442
414
|
baseStages: stageList(baseSnap),
|
|
443
415
|
candStages: stageList(candSnap),
|
|
444
416
|
jobOutcomes: { baseline: jobOutcome(baseSnap), candidate: jobOutcome(candSnap) },
|
|
@@ -467,16 +439,22 @@ export function renderComparisonMarkdown(
|
|
|
467
439
|
const lines = ['', '## Comparison to baseline', ''];
|
|
468
440
|
if (tuned) lines.push(`- Tuned thresholds (both runs): ${tunedRunNote(tuned)}`, '');
|
|
469
441
|
if (verdict) {
|
|
470
|
-
//
|
|
471
|
-
|
|
442
|
+
// Names each run by its label (MCP's run IDs), unless the labels are just the role names the
|
|
443
|
+
// CLI passes, where the verdict below already says baseline and candidate.
|
|
444
|
+
if (comparison.baselineLabel !== 'baseline' || comparison.candidateLabel !== 'candidate') {
|
|
445
|
+
lines.push(`Baseline: ${comparison.baselineLabel} · Candidate: ${comparison.candidateLabel}`, '');
|
|
446
|
+
}
|
|
447
|
+
lines.push(verdict.title);
|
|
472
448
|
if (verdict.sentences.length > 0) lines.push('', verdict.sentences.join(' '));
|
|
473
449
|
lines.push('');
|
|
474
450
|
}
|
|
475
|
-
if (comparison.confidence
|
|
476
|
-
lines.push(`- confidence:
|
|
451
|
+
if (comparison.confidence !== 'ok') {
|
|
452
|
+
lines.push(`- confidence: ${comparison.confidence}, ${comparison.reason}`);
|
|
477
453
|
lines.push('');
|
|
478
454
|
}
|
|
479
455
|
lines.push(`- matched stage coverage: ${(comparison.matchedCoverage * 100).toFixed(1)}%`);
|
|
456
|
+
lines.push(`- runtime coverage: ${comparison.runtimeCoverage === null ? 'n/a' : `${(comparison.runtimeCoverage * 100).toFixed(1)}%`}`);
|
|
457
|
+
lines.push(`- stage pairs: ${comparison.stagePairs.length}, unmatched: ${comparison.unmatched.baseStageIds.length} baseline / ${comparison.unmatched.candStageIds.length} candidate, Delta bookkeeping: ${comparison.bookkeepingStageIds.baseStageIds.length} baseline / ${comparison.bookkeepingStageIds.candStageIds.length} candidate`);
|
|
480
458
|
lines.push('');
|
|
481
459
|
lines.push('### Metric deltas');
|
|
482
460
|
lines.push('');
|
|
@@ -192,6 +192,7 @@ function interpretEfficiency(appModel ) {
|
|
|
192
192
|
app: appModel.app,
|
|
193
193
|
stages: appModel.stages,
|
|
194
194
|
executorsAdded: appModel.executors.added,
|
|
195
|
+
executorsRemoved: appModel.executors.removed,
|
|
195
196
|
runAggregates: appModel.runAggregates,
|
|
196
197
|
});
|
|
197
198
|
}
|
|
@@ -270,7 +271,7 @@ export function interpretRun(appModel , catalog , configFindi
|
|
|
270
271
|
coverage: interpretCoverage(appModel, allFindings),
|
|
271
272
|
runShape: interpretRunShape(appModel),
|
|
272
273
|
wallClock: computeWallClock(appModel.app, appModel.stages),
|
|
273
|
-
wastedCoreHours: computeWastedCoreHours(appModel.app, appModel.executors.added, appModel.runAggregates),
|
|
274
|
+
wastedCoreHours: computeWastedCoreHours(appModel.app, appModel.executors.added, appModel.runAggregates, appModel.executors.removed),
|
|
274
275
|
efficiency: interpretEfficiency(appModel),
|
|
275
276
|
wallClockReliable: checkConcurrentJobGroups(appModel.jobs).wallClockReliable,
|
|
276
277
|
etlPhases: attributeEtlPhases(appModel.stages),
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// The machine-readable metrics block of the CLI's JSON output: run-level totals and per-stage rows
|
|
2
|
+
// an automated tuning loop can compare across runs without reading a report. Its schemaVersion
|
|
3
|
+
// moves independently of the evidence report's. A figure the log cannot provide is null, never 0.
|
|
4
|
+
import { computeAllocation, } from './allocation.js';
|
|
5
|
+
import { computeSkewRatio, ENTRY_BY_TYPE, } from './detectors.js';
|
|
6
|
+
import { totalExecutorCpuMs, withEarlierAttempts } from './run-totals.js';
|
|
7
|
+
import { isPythonStage } from './python-stage.js';
|
|
8
|
+
import { stageIdentity } from './run-comparison.js';
|
|
9
|
+
import { hasCompleteApplicationInterval } from './scorecard-estimates.js';
|
|
10
|
+
import { effectiveThresholds } from './threshold-overrides.js';
|
|
11
|
+
import { computeWallClock } from './wall-clock.js';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export const METRICS_SCHEMA_VERSION = 1;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const finite = (v ) => typeof v === 'number' && Number.isFinite(v);
|
|
73
|
+
|
|
74
|
+
// Σ of a field over the stages that carry a finite value; null when none does.
|
|
75
|
+
function sumOf(stages , pick ) {
|
|
76
|
+
let sum = 0, present = false;
|
|
77
|
+
for (const s of stages) {
|
|
78
|
+
const v = pick(s);
|
|
79
|
+
if (finite(v)) { sum += v; present = true; }
|
|
80
|
+
}
|
|
81
|
+
return present ? sum : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 0 means either no execution memory used or not recorded; only a positive peak is reported.
|
|
85
|
+
function peakExecutionMemory(stages ) {
|
|
86
|
+
const peaks = stages.map((s) => s.peakExecutionMemoryMax).filter((v) => finite(v) && v > 0);
|
|
87
|
+
return peaks.length > 0 ? Math.max(...peaks) : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function maxSkew(stages , minTasksForP95 ) {
|
|
91
|
+
const ratios = stages
|
|
92
|
+
.map((s) => computeSkewRatio(s, minTasksForP95)?.ratio)
|
|
93
|
+
.filter((r) => finite(r));
|
|
94
|
+
return ratios.length > 0 ? Math.max(...ratios) : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Failed stage attempts and stages submitted more than once, from the parser's per-stage attempt
|
|
98
|
+
// counts; null when any stage record lacks them.
|
|
99
|
+
function stageAttempts(stages ) {
|
|
100
|
+
let failed = 0, retried = 0;
|
|
101
|
+
for (const s of stages) {
|
|
102
|
+
if (!finite(s.stageAttempts) || !finite(s.failedStageAttempts)) return null;
|
|
103
|
+
failed += s.failedStageAttempts;
|
|
104
|
+
if (s.stageAttempts > 1) retried++;
|
|
105
|
+
}
|
|
106
|
+
return { failed, retried };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Metrics of a set of stages, every attempt included; a row folds the stages sharing one
|
|
110
|
+
// fingerprint. Skew describes the latest attempt's task durations.
|
|
111
|
+
function stageMetrics(stages , minTasksForP95 ) {
|
|
112
|
+
const attempts = withEarlierAttempts(stages);
|
|
113
|
+
// Task-derived figures are null for stages that never finished (no task records).
|
|
114
|
+
// Superseded attempts carry work but no task count of their own, so they stay in once any
|
|
115
|
+
// attempt finished a task.
|
|
116
|
+
const finished = attempts.some((s) => (s.taskCount ?? 0) > 0) ? attempts : [];
|
|
117
|
+
const tasks = sumOf(attempts, (s) => s.taskCount);
|
|
118
|
+
const fromTasks = (pick ) => (finished.length > 0 ? sumOf(finished, pick) : null);
|
|
119
|
+
const durations = attempts.filter((s) => finite(s.submittedAt) && finite(s.completedAt) && (s.completedAt ) >= (s.submittedAt ));
|
|
120
|
+
return {
|
|
121
|
+
durationMs: durations.length > 0 ? sumOf(durations, (s) => (s.completedAt ) - (s.submittedAt )) : null,
|
|
122
|
+
executorCpuTimeMs: totalExecutorCpuMs(finished),
|
|
123
|
+
executorRunTimeMs: fromTasks((s) => s.executorRunTime),
|
|
124
|
+
gcTimeMs: fromTasks((s) => s.jvmGCTime),
|
|
125
|
+
memorySpillBytes: fromTasks((s) => s.memoryBytesSpilled),
|
|
126
|
+
diskSpillBytes: fromTasks((s) => s.diskBytesSpilled),
|
|
127
|
+
shuffleReadBytes: fromTasks((s) => s.shuffleReadBytes),
|
|
128
|
+
shuffleWriteBytes: fromTasks((s) => s.shuffleWriteBytes),
|
|
129
|
+
inputBytes: fromTasks((s) => s.inputBytes),
|
|
130
|
+
outputBytes: fromTasks((s) => s.outputBytes),
|
|
131
|
+
outputRows: sumOf(finished, (s) => s.outputRecords),
|
|
132
|
+
peakExecutionMemoryBytes: peakExecutionMemory(finished),
|
|
133
|
+
taskCount: tasks != null && tasks > 0 ? tasks : null,
|
|
134
|
+
failedTasks: fromTasks((s) => s.failedTasks),
|
|
135
|
+
retriedTasks: fromTasks((s) => (s.wastedAttempts ) ?? 0),
|
|
136
|
+
skew: maxSkew(stages.filter((s) => (s.taskCount ?? 0) > 0), minTasksForP95),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The run's metrics block. `thresholds` only moves the skew ratio's P95 cutoff, as for the
|
|
141
|
+
* --max-skew budget. */
|
|
142
|
+
export function computeRunMetrics(appModel , thresholds ) {
|
|
143
|
+
const stageList = [...appModel.stages.values()];
|
|
144
|
+
const minTasksForP95 = effectiveThresholds(ENTRY_BY_TYPE.get('skew') , thresholds).minTasksForP95 ;
|
|
145
|
+
const all = stageMetrics(stageList, minTasksForP95);
|
|
146
|
+
const python = stageList.filter((s) => isPythonStage(s, appModel.sql));
|
|
147
|
+
const pythonRunTimeMs = sumOf(withEarlierAttempts(python), (s) => s.executorRunTime);
|
|
148
|
+
|
|
149
|
+
const byFingerprint = new Map ();
|
|
150
|
+
for (const s of stageList) {
|
|
151
|
+
const key = stageIdentity(s, appModel);
|
|
152
|
+
const group = byFingerprint.get(key);
|
|
153
|
+
if (group) group.push(s); else byFingerprint.set(key, [s]);
|
|
154
|
+
}
|
|
155
|
+
const stages = {};
|
|
156
|
+
for (const [key, group] of byFingerprint) {
|
|
157
|
+
const attempts = stageAttempts(group);
|
|
158
|
+
stages[key] = {
|
|
159
|
+
stageIds: group.map((s) => s.id).sort((a, b) => a - b),
|
|
160
|
+
failed: attempts ? attempts.failed > 0 : null,
|
|
161
|
+
retried: attempts ? attempts.retried > 0 : null,
|
|
162
|
+
python: group.some((s) => isPythonStage(s, appModel.sql)),
|
|
163
|
+
...stageMetrics(group, minTasksForP95),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const attempts = stageAttempts(stageList);
|
|
168
|
+
return {
|
|
169
|
+
schemaVersion: METRICS_SCHEMA_VERSION,
|
|
170
|
+
runComplete: appModel.app?.endTime != null,
|
|
171
|
+
time: {
|
|
172
|
+
wallClockMs: hasCompleteApplicationInterval(appModel.app) ? computeWallClock(appModel.app, appModel.stages).total : null,
|
|
173
|
+
executorCpuTimeMs: all.executorCpuTimeMs,
|
|
174
|
+
executorRunTimeMs: all.executorRunTimeMs,
|
|
175
|
+
gcTimeMs: all.gcTimeMs,
|
|
176
|
+
},
|
|
177
|
+
data: {
|
|
178
|
+
memorySpillBytes: all.memorySpillBytes, diskSpillBytes: all.diskSpillBytes,
|
|
179
|
+
shuffleReadBytes: all.shuffleReadBytes, shuffleWriteBytes: all.shuffleWriteBytes,
|
|
180
|
+
inputBytes: all.inputBytes, outputBytes: all.outputBytes, outputRows: all.outputRows,
|
|
181
|
+
peakExecutionMemoryBytes: all.peakExecutionMemoryBytes,
|
|
182
|
+
},
|
|
183
|
+
shape: {
|
|
184
|
+
taskCount: all.taskCount,
|
|
185
|
+
stageCount: stageList.length,
|
|
186
|
+
failedStageAttempts: attempts?.failed ?? null,
|
|
187
|
+
retriedStages: attempts?.retried ?? null,
|
|
188
|
+
failedTasks: all.failedTasks,
|
|
189
|
+
retriedTasks: all.retriedTasks,
|
|
190
|
+
maxSkew: all.skew,
|
|
191
|
+
},
|
|
192
|
+
allocation: computeAllocation(appModel),
|
|
193
|
+
python: {
|
|
194
|
+
shareOfTaskRunTime: all.executorRunTimeMs != null && all.executorRunTimeMs > 0 ? (pythonRunTimeMs ?? 0) / all.executorRunTimeMs : null,
|
|
195
|
+
},
|
|
196
|
+
stages,
|
|
197
|
+
};
|
|
198
|
+
}
|