sparkforensics-server 0.6.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +16 -7
- package/package.json +1 -1
- package/public/assets/AutoscalingChurn-DenWKTv-.js +1 -0
- package/public/assets/CacheUtilization-DLz7NdjV.js +1 -0
- package/public/assets/CachingOpportunity-zpQIqhzm.js +1 -0
- package/public/assets/ChartTheme-DAiARbLP.js +8 -0
- package/public/assets/ColdStart-C-9HjxWl.js +1 -0
- package/public/assets/ConfigAudit-D0V9tcrJ.js +1 -0
- package/public/assets/CoreUsageArea-yeerrljd.js +1 -0
- package/public/assets/Dashboard-DkUPrHKV.js +1 -0
- package/public/assets/DisclosureContext-DK1_iz6J.js +1 -0
- package/public/assets/DuplicatePlanSubtree-xDNDbCd3.js +1 -0
- package/public/assets/DurationHistogram-B6jsE0Ov.js +1 -0
- package/public/assets/EmptyState-C1uL40VU.js +1 -0
- package/public/assets/ExecutorUtilization-DXDZKmSc.js +1 -0
- package/public/assets/GcPressure-MRf7I4IG.js +1 -0
- package/public/assets/ImpactEstimate-Dm3xWbzV.js +1 -0
- package/public/assets/IncompleteRun-CaLOhMtf.js +1 -0
- package/public/assets/JobFailures-CzmzlvhO.js +1 -0
- package/public/assets/MemoryUtilization-AWUMUjCH.js +1 -0
- package/public/assets/NestedLoopJoin-C9sNr6Ss.js +1 -0
- package/public/assets/OverBroadcast-BOmRE6ca.js +1 -0
- package/public/assets/PartitionHint-BL2EmJfE.js +1 -0
- package/public/assets/PartitionSizing-BzBySvUq.js +1 -0
- package/public/assets/PlanExplorer-DdiQRcJU.js +1 -0
- package/public/assets/PlanGraphRoute-CIH9gBuR.js +1 -0
- package/public/assets/PlanView-B1hxzFRs.js +2 -0
- package/public/assets/PythonUdf-DvgCCueD.js +1 -0
- package/public/assets/RetryWaste-uXXu_CyM.js +1 -0
- package/public/assets/RowPagination-C-LzWkI5.js +1 -0
- package/public/assets/RowStatusCluster-Dbtmte7l.js +1 -0
- package/public/assets/ShuffleIO-nALwzC1_.js +1 -0
- package/public/assets/Skew-CHMEKgvk.js +1 -0
- package/public/assets/SlowHost-yyvjpHk6.js +1 -0
- package/public/assets/SmallFiles-y6odJ97n.js +1 -0
- package/public/assets/SpeculationWaste-C-SD2LOt.js +1 -0
- package/public/assets/Spill-DHTMmOup.js +1 -0
- package/public/assets/StageFailed-CQ3DxC9R.js +1 -0
- package/public/assets/StageFindingGroup-BgYLyj5q.js +1 -0
- package/public/assets/StageHeader-CIETkjVZ.js +1 -0
- package/public/assets/StagePill--P7PDd3s.js +1 -0
- package/public/assets/StageShape-BIv8vx_o.js +1 -0
- package/public/assets/StageSlowness-MMqzERxN.js +1 -0
- package/public/assets/Straggler-BgrY7AyW.js +1 -0
- package/public/assets/TaskFailures-BHHyKYTs.js +1 -0
- package/public/assets/TinyTask-DDScNhCl.js +1 -0
- package/public/assets/UnderBroadcast-CGIC04TI.js +1 -0
- package/public/assets/WidgetLeadSummary-CXFZP00C.js +1 -0
- package/public/assets/button-DbpTz2yk.js +1 -0
- package/public/assets/charts-vendor-CtTxwTeu.js +59 -0
- package/public/assets/composite-C9Nu2EI9.js +1 -0
- package/public/assets/d3-vendor-DNlD2-cz.js +1 -0
- package/public/assets/detector-registry-Bp8hrLua.js +2 -0
- package/public/assets/downsample-WmNM1qc9.js +1 -0
- package/public/assets/finding-anchor-Dr40LkS9.js +1 -0
- package/public/assets/findings-of-type-BL2u3RGZ.js +1 -0
- package/public/assets/impact-sort-mC5ISgX8.js +1 -0
- package/public/assets/inbox-KPU1Gppm.js +1 -0
- package/public/assets/index-CaRVPvAT.css +2 -0
- package/public/assets/index-fDiwfuoO.js +14 -0
- package/public/assets/inertValue-L9VxV0VE.js +1 -0
- package/public/assets/layout.worker-CLcBHxVp.js +3 -0
- package/public/assets/{parser-worker-CA5RxqKc.js → parser-worker-CRIOPMOW.js} +8 -8
- package/public/assets/plan-graph-vendor-Ce6d4HU6.js +7 -0
- package/public/assets/react-vendor-C6KcUD36.js +9 -0
- package/public/assets/select-BHqOqun5.js +1 -0
- package/public/assets/toggle-DPeN6aNa.js +1 -0
- package/public/assets/toggle-group-Kl3uyHSr.js +1 -0
- package/public/assets/usePagedRows-BafD6bEN.js +1 -0
- package/public/assets/useRecentFiles-DzJZAoOT.js +3 -0
- package/public/assets/useRegisterFieldControl-pQv-ZDoU.js +1 -0
- package/public/assets/useScrollLock-BCxn_dMp.js +1 -0
- package/public/assets/useSortMode-DjrAN-z4.js +1 -0
- package/public/docs/404.html +3 -3
- package/public/docs/assets/{app.CnWL6bKV.js → app.1gvzd5Zb.js} +1 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.DS2TQP5_.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.C0IBKeRQ.js → VPLocalSearchBox.CNxT3s0I.js} +1 -1
- package/public/docs/assets/chunks/{theme.BH-2HuRf.js → theme.C5lLqUv7.js} +2 -2
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.js → contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CHHgywoP.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.N4hhenrA.lean.js → contributor-guide_architecture_detector-contract.md.CHHgywoP.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.lean.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.lean.js → contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.K71YB-Rr.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.DfvbqEUu.lean.js → contributor-guide_architecture_impact-estimation.md.K71YB-Rr.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.lean.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_overview.md.nxkpOhgf.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js → contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js → contributor-guide_architecture_worker-protocol.md.Br6OE7RW.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.Br6OE7RW.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.js} +2 -2
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.lean.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_contributing.md.DBW4drtZ.js +1 -0
- package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.js +15 -0
- package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.lean.js +1 -0
- package/public/docs/assets/contributor-guide_testing.md.DpkozNoG.js +4 -0
- package/public/docs/assets/{contributor-guide_testing.md.By6XGR9a.lean.js → contributor-guide_testing.md.DpkozNoG.lean.js} +1 -1
- package/public/docs/assets/{index.md.CHJVslga.js → index.md.lMm7SUMz.js} +1 -1
- package/public/docs/assets/{index.md.CHJVslga.lean.js → index.md.lMm7SUMz.lean.js} +1 -1
- package/public/docs/assets/{style.CyK9Ss73.css → style.Cfxqlj0y.css} +1 -1
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.C0pcQoNc.js +29 -0
- package/public/docs/assets/{user-guide_getting-started_ci-and-automation.md.BEepbDDL.lean.js → user-guide_getting-started_ci-and-automation.md.C0pcQoNc.lean.js} +1 -1
- package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.js +1 -0
- package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.js} +2 -2
- package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.lean.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.js → user-guide_mcp-tools.md.49r3L2YE.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js → user-guide_mcp-tools_compare-runs.md.BQ_kIptC.js} +11 -6
- package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.BQ_kIptC.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.azCZXkH8.js +44 -0
- package/public/docs/assets/{user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.lean.js → user-guide_mcp-tools_diagnose-run.md.azCZXkH8.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.js} +2 -2
- package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.lean.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.js} +2 -1
- package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.lean.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.lean.js} +1 -1
- package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.js +1 -0
- package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.lean.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.lean.js +1 -0
- package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +6 -6
- package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +6 -6
- package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -6
- package/public/docs/contributor-guide/architecture/board-widgets.html +6 -6
- package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +6 -6
- package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -6
- package/public/docs/contributor-guide/architecture/detector-contract.html +6 -6
- package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +6 -6
- package/public/docs/contributor-guide/architecture/drill-down.html +6 -6
- package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -6
- package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +6 -6
- package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -6
- package/public/docs/contributor-guide/architecture/index.html +5 -5
- package/public/docs/contributor-guide/architecture/overview.html +6 -6
- package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +5 -5
- package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -6
- package/public/docs/contributor-guide/architecture/state-and-history.html +5 -5
- package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +6 -6
- package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +5 -5
- package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +6 -6
- package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +5 -5
- package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +6 -6
- package/public/docs/contributor-guide/architecture/widget-rendering.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +7 -7
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol.html +6 -6
- package/public/docs/contributor-guide/contributing.html +6 -6
- package/public/docs/contributor-guide/development-setup.html +8 -8
- package/public/docs/contributor-guide/testing.html +9 -6
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +6 -6
- package/public/docs/logo-dark.svg +1 -0
- package/public/docs/logo-light.svg +1 -0
- package/public/docs/tuning-reference/anti-patterns.html +5 -5
- package/public/docs/tuning-reference/aqe.html +5 -5
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -5
- package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -5
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -5
- package/public/docs/tuning-reference/bottleneck-failures.html +5 -5
- package/public/docs/tuning-reference/bottleneck-gc.html +5 -5
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -5
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -5
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -5
- package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -5
- package/public/docs/tuning-reference/bottleneck-skew.html +5 -5
- package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -5
- package/public/docs/tuning-reference/bottleneck-small-files.html +5 -5
- package/public/docs/tuning-reference/bottleneck-spill.html +5 -5
- package/public/docs/tuning-reference/bottleneck-straggler.html +5 -5
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -5
- package/public/docs/tuning-reference/bottleneck-utilization.html +5 -5
- package/public/docs/tuning-reference/caching.html +5 -5
- package/public/docs/tuning-reference/cluster-config.html +5 -5
- package/public/docs/tuning-reference/config.html +5 -5
- package/public/docs/tuning-reference/data-formats.html +5 -5
- package/public/docs/tuning-reference/index.html +5 -5
- package/public/docs/tuning-reference/intro.html +5 -5
- package/public/docs/tuning-reference/joins.html +5 -5
- package/public/docs/tuning-reference/memory-model.html +5 -5
- package/public/docs/tuning-reference/metrics.html +5 -5
- package/public/docs/tuning-reference/partitioning.html +5 -5
- package/public/docs/tuning-reference/pyspark.html +5 -5
- package/public/docs/tuning-reference/shuffle.html +5 -5
- package/public/docs/tuning-reference/spark-architecture.html +5 -5
- package/public/docs/tuning-reference/table-formats.html +5 -5
- package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +5 -5
- package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +5 -5
- package/public/docs/user-guide/alternative-log-retrieval.html +5 -5
- package/public/docs/user-guide/getting-started/ci-and-automation.html +16 -11
- package/public/docs/user-guide/getting-started/reading-the-dashboard.html +6 -6
- package/public/docs/user-guide/getting-started/regression-metric-keys.html +5 -5
- package/public/docs/user-guide/getting-started/tuning-thresholds.html +7 -7
- package/public/docs/user-guide/getting-started.html +5 -5
- package/public/docs/user-guide/mcp-tools/compare-runs.html +16 -11
- package/public/docs/user-guide/mcp-tools/diagnose-run.html +28 -7
- package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -6
- package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +5 -5
- package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +7 -6
- package/public/docs/user-guide/mcp-tools/get-reference-doc.html +5 -5
- package/public/docs/user-guide/mcp-tools/get-run-summary.html +5 -5
- package/public/docs/user-guide/mcp-tools/list-runs.html +5 -5
- package/public/docs/user-guide/mcp-tools.html +6 -6
- package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +5 -5
- package/public/docs/user-guide/run-comparison.html +6 -6
- package/public/docs/user-guide/understanding-findings.html +6 -6
- package/public/export-template.html +45 -45
- package/public/index.html +8 -7
- package/public/sample-runs/sample-run.ndjson.gz +0 -0
- package/vendor-core/allocation.js +25 -23
- package/vendor-core/analyzer.js +85 -58
- package/vendor-core/api-merge-writes.js +2 -0
- package/vendor-core/aqe-skew.js +347 -0
- package/vendor-core/cli/budgets.js +6 -4
- package/vendor-core/cli/collect-run-pair.js +89 -0
- package/vendor-core/cli/collect-run-worker.js +16 -0
- package/vendor-core/cli/collect-run.js +1 -2
- package/vendor-core/cli/native-zstd.js +12 -16
- package/vendor-core/comparability.js +234 -0
- package/vendor-core/comparison-output.js +15 -7
- package/vendor-core/comparison-verdict.js +105 -25
- package/vendor-core/core-source-hash.txt +1 -1
- package/vendor-core/core-time-series.js +0 -1
- package/vendor-core/core-usage-locality.js +1 -1
- package/vendor-core/detectors.js +1007 -380
- package/vendor-core/docs-config.js +0 -4
- package/vendor-core/docs-content/detection/cache.md +2 -1
- package/vendor-core/docs-content/detection/cfg.md +19 -11
- package/vendor-core/docs-content/detection/cold.md +2 -1
- package/vendor-core/docs-content/detection/gc.md +7 -3
- package/vendor-core/docs-content/detection/host.md +11 -1
- package/vendor-core/docs-content/detection/mem.md +17 -6
- package/vendor-core/docs-content/detection/part.md +3 -1
- package/vendor-core/docs-content/detection/plan.md +55 -13
- package/vendor-core/docs-content/detection/shfl.md +11 -5
- package/vendor-core/docs-content/detection/skew.md +47 -1
- package/vendor-core/docs-content/detection/slow.md +4 -2
- package/vendor-core/docs-content/detection/spec.md +6 -1
- package/vendor-core/docs-content/detection/spill.md +6 -3
- package/vendor-core/docs-content/detection/strag.md +19 -6
- package/vendor-core/docs-content/detection/tiny.md +9 -3
- package/vendor-core/effective-conf.js +7 -10
- package/vendor-core/efficiency-model.js +2 -2
- package/vendor-core/etl-phases.js +1 -2
- package/vendor-core/event-handlers.js +175 -57
- package/vendor-core/event-schemas.js +21 -16
- package/vendor-core/evidence-availability.js +9 -2
- package/vendor-core/evidence-report.js +118 -35
- package/vendor-core/executor-peaks.js +41 -0
- package/vendor-core/export-data.js +5 -5
- package/vendor-core/finding-identity.js +78 -0
- package/vendor-core/finding-presentation.js +116 -33
- package/vendor-core/finding-types.js +88 -21
- package/vendor-core/format-utils.js +14 -15
- package/vendor-core/html-export.js +1 -1
- package/vendor-core/impact-band.js +2 -2
- package/vendor-core/impact-format.js +47 -19
- package/vendor-core/impact-model.js +26 -15
- package/vendor-core/job-groups.js +4 -4
- package/vendor-core/mcp-server-factory.js +48 -12
- package/vendor-core/mcp-tools.js +179 -24
- package/vendor-core/median.js +2 -2
- package/vendor-core/nested-loop-join.js +73 -0
- package/vendor-core/occupancy.js +1 -2
- package/vendor-core/parser-worker.js +1 -1
- package/vendor-core/plan-graph-model.js +14 -22
- package/vendor-core/plan-node-detail.js +2 -3
- package/vendor-core/plan-summary.js +24 -19
- package/vendor-core/plan-tree-walk.js +6 -8
- package/vendor-core/python-stage.js +12 -1
- package/vendor-core/recommendation-rollup.js +13 -12
- package/vendor-core/redact.js +52 -46
- package/vendor-core/run-aggregates.js +6 -8
- package/vendor-core/run-comparison.js +226 -66
- package/vendor-core/run-identity.js +12 -0
- package/vendor-core/run-interpretation.js +21 -0
- package/vendor-core/run-metrics.js +2 -2
- package/vendor-core/run-output.js +32 -10
- package/vendor-core/run-totals.js +12 -0
- package/vendor-core/run-verdict.js +76 -3
- package/vendor-core/scaling-sim.js +3 -4
- package/vendor-core/snappy-block.js +2 -2
- package/vendor-core/spark-conf.js +168 -0
- package/vendor-core/sql-stages.js +2 -2
- package/vendor-core/stage-alignment.js +2 -12
- package/vendor-core/stage-identity.js +58 -3
- package/vendor-core/stage-label.js +14 -0
- package/vendor-core/stage-plan-nodes.js +17 -3
- package/vendor-core/stage-quantiles.js +141 -9
- package/vendor-core/stage-structure.js +17 -3
- package/vendor-core/threshold-overrides.js +9 -3
- package/vendor-core/types.js +58 -21
- package/vendor-core/wasted-core-hours.js +0 -1
- package/vendor-core/write-targets.js +114 -14
- package/vendor-core/zstd-worker-client.js +1 -1
- package/vendor-core/zstd-worker.js +1 -1
- package/public/assets/AutoscalingChurn-CRA2Mb7F.js +0 -1
- package/public/assets/CacheUtilization-Bh9OY_V9.js +0 -1
- package/public/assets/CachingOpportunity-oCiQwmTJ.js +0 -1
- package/public/assets/ColdStart-BJrtjfmn.js +0 -1
- package/public/assets/ConfigAudit-DMvKxySP.js +0 -1
- package/public/assets/CoreUsageArea-REC2zsKw.js +0 -1
- package/public/assets/Dashboard-BHZ76JCu.js +0 -2
- package/public/assets/DisclosureContext-OO7X1HAx.js +0 -1
- package/public/assets/DuplicatePlanSubtree-BgG-L-cK.js +0 -1
- package/public/assets/ExecutorUtilization-D2KhSVbY.js +0 -1
- package/public/assets/GcPressure-D1BTE8qh.js +0 -1
- package/public/assets/IncompleteRun-DD40xUpy.js +0 -1
- package/public/assets/JobFailures-YwNzCF19.js +0 -1
- package/public/assets/MemoryUtilization-9b8ee_3_.js +0 -1
- package/public/assets/OverBroadcast-CfctS8a8.js +0 -1
- package/public/assets/PartitionHint-CUvbnnxy.js +0 -1
- package/public/assets/PartitionSizing-VG8kBgG5.js +0 -1
- package/public/assets/PlanExplorer-DmubTAOP.js +0 -1
- package/public/assets/PlanGraphRoute-DctPZjS0.js +0 -1
- package/public/assets/RetryWaste-CnpM8bB7.js +0 -1
- package/public/assets/ShuffleIO-D13vGmas.js +0 -1
- package/public/assets/Skew-DlagstYP.js +0 -1
- package/public/assets/SlowHost-cSkLRZ_e.js +0 -1
- package/public/assets/SmallFiles-B-kERMcq.js +0 -1
- package/public/assets/SpeculationWaste-DaysAzXO.js +0 -1
- package/public/assets/Spill-H44Gz4US.js +0 -1
- package/public/assets/StageFailed-DCfii9h_.js +0 -1
- package/public/assets/StageFindingGroup-BXJ0Mj5n.js +0 -1
- package/public/assets/StageHeader--oN2J-oD.js +0 -1
- package/public/assets/StageShape-IZ80CZiJ.js +0 -1
- package/public/assets/StageSlowness--BRIaBc1.js +0 -1
- package/public/assets/Straggler-Ctq0Df_o.js +0 -1
- package/public/assets/TaskFailures-Bks_gqhL.js +0 -1
- package/public/assets/TinyTask-BssfzGFB.js +0 -1
- package/public/assets/UnderBroadcast-DsScrxc-.js +0 -1
- package/public/assets/charts-vendor-0VMaLVLN.js +0 -59
- package/public/assets/finding-anchor-BYW1oz5R.js +0 -1
- package/public/assets/impact-sort-xzy0g-Vh.js +0 -1
- package/public/assets/index-DhpRGMZc.css +0 -2
- package/public/assets/index-DwqkFUFk.js +0 -14
- package/public/assets/plan-graph-vendor-BYl-AzN5.js +0 -7
- package/public/assets/react-vendor-DfpuPQZf.js +0 -9
- package/public/assets/select-C8edjYTq.js +0 -1
- package/public/assets/usePagedRows-CsuUFeYx.js +0 -1
- package/public/assets/useRecentFiles-BvkKo3JN.js +0 -11
- package/public/assets/useSortMode-joX71BZN.js +0 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.tE5pz54z.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.N4hhenrA.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DfvbqEUu.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.js +0 -1
- package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +0 -1
- package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.js +0 -15
- package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.lean.js +0 -1
- package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +0 -1
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.js +0 -24
- package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +0 -1
- package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js +0 -1
- package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +0 -23
- package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.lean.js +0 -1
- /package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.lean.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.lean.js → contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_overview.md.C1sSvm9i.lean.js → contributor-guide_architecture_overview.md.nxkpOhgf.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js → contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.lean.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.lean.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.lean.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_contributing.md.CQMvGwFt.lean.js → contributor-guide_contributing.md.DBW4drtZ.lean.js} +0 -0
- /package/public/docs/assets/{user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.lean.js → user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.lean.js} +0 -0
- /package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.lean.js → user-guide_mcp-tools.md.49r3L2YE.lean.js} +0 -0
|
@@ -6,17 +6,21 @@ import { hasFinishedStage, isCleanRun } from './check-coverage.js';
|
|
|
6
6
|
import { findingActionLabel } from './finding-action-label.js';
|
|
7
7
|
import { singleStageId } from './finding-filter-predicate.js';
|
|
8
8
|
import { recommendationText } from './finding-names.js';
|
|
9
|
-
import { formatDuration, IMPACT_BAND_ORDER } from './format-utils.js';
|
|
9
|
+
import { formatDuration, formatRawWaste, IMPACT_BAND_ORDER, readsAsZero } from './format-utils.js';
|
|
10
10
|
import { impactFigure, savingsMeaning } from './impact-format.js';
|
|
11
11
|
import { isEligible } from './recommendation-rollup.js';
|
|
12
12
|
import { FAILURE_TYPES, quotesReasonOf, summarizeRunOutcome, } from './run-outcome.js';
|
|
13
13
|
import { getScorecardEstimates, hasCompleteApplicationInterval } from './scorecard-estimates.js';
|
|
14
14
|
import { computeWallClock } from './wall-clock.js';
|
|
15
|
+
import { computeWastedCoreHours } from './wasted-core-hours.js';
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
/** How many next steps the verdict lists before pointing at the full list. */
|
|
18
19
|
export const NEXT_STEP_LIMIT = 3;
|
|
19
20
|
|
|
21
|
+
/** How many cost-lane entries the verdict lists. */
|
|
22
|
+
export const COST_LANE_LIMIT = 3;
|
|
23
|
+
|
|
20
24
|
/** Finding types whose widget sits in the board's reference region (listed after every action
|
|
21
25
|
* widget). The dashboard's registry must agree: tests/view/detector-registry.test.tsx checks it. */
|
|
22
26
|
export const REFERENCE_DISPLAY_TYPES = new Set([
|
|
@@ -69,6 +73,56 @@ export function rankBySavings(findings ) {
|
|
|
69
73
|
return candidates.map(({ finding }) => finding);
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
/** One entry of the cost lane: a finding and the measured busy core time its fix removes. */
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
/** The cost lane: rankable findings ordered by measured busy core time (`coreTimeMs.high`), the
|
|
86
|
+
* one unit all of them share. An idle capacity figure, a modeled figure and a bytes-only figure
|
|
87
|
+
* never carry `coreTimeMs`, and a finding without one is in neither lane. Ties fall back to impact
|
|
88
|
+
* band, widget display order, then input order, like `rankBySavings`. */
|
|
89
|
+
export function buildCostLane(findings ) {
|
|
90
|
+
const entries = findings
|
|
91
|
+
.map((finding, index) => ({ finding, index, coreTimeMs: finding.impactEstimate?.coreTimeMs?.high }))
|
|
92
|
+
.filter((entry) =>
|
|
93
|
+
isRankable(entry.finding) && typeof entry.coreTimeMs === 'number' && Number.isFinite(entry.coreTimeMs) && entry.coreTimeMs > 0);
|
|
94
|
+
entries.sort((left, right) => (
|
|
95
|
+
right.coreTimeMs - left.coreTimeMs
|
|
96
|
+
|| (IMPACT_BAND_ORDER[left.finding.impactBand] ?? 9) - (IMPACT_BAND_ORDER[right.finding.impactBand] ?? 9)
|
|
97
|
+
|| (DISPLAY_INDEX.get(left.finding.type) ?? Number.MAX_SAFE_INTEGER) - (DISPLAY_INDEX.get(right.finding.type) ?? Number.MAX_SAFE_INTEGER)
|
|
98
|
+
|| left.index - right.index
|
|
99
|
+
));
|
|
100
|
+
return entries
|
|
101
|
+
.map(({ finding, coreTimeMs }) => ({ finding, coreTimeMs, figure: formatRawWaste({ value: coreTimeMs, unit: 'coreMs' }) }))
|
|
102
|
+
.filter((entry) => !readsAsZero(entry.figure));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The run's idle capacity, bounded by the run: allocated core time that ran no task. */
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
export function buildRunIdleLine(appModel ) {
|
|
116
|
+
const { wastedCoreHours, totalCoreHours } = computeWastedCoreHours(
|
|
117
|
+
appModel.app, appModel.executors.added, appModel.runAggregates, appModel.executors.removed,
|
|
118
|
+
);
|
|
119
|
+
if (wastedCoreHours == null || totalCoreHours == null || totalCoreHours <= 0) return null;
|
|
120
|
+
const figure = formatRawWaste({ value: wastedCoreHours, unit: 'coreHours' });
|
|
121
|
+
if (readsAsZero(figure)) return null;
|
|
122
|
+
const pct = Math.round((wastedCoreHours / totalCoreHours) * 100);
|
|
123
|
+
return { coreHours: wastedCoreHours, allocatedCoreHours: totalCoreHours, pct, text: `${figure} (${pct}%) of the allocation ran no task.` };
|
|
124
|
+
}
|
|
125
|
+
|
|
72
126
|
/** One place worth a look: the highest-ranked finding at a location, plus the
|
|
73
127
|
* other finding types flagged at that same location. Findings that share a
|
|
74
128
|
* stage usually share one root cause, so they read as one step, not several. */
|
|
@@ -179,6 +233,9 @@ function plural(count , noun ) {
|
|
|
179
233
|
|
|
180
234
|
|
|
181
235
|
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
182
239
|
|
|
183
240
|
|
|
184
241
|
function isFailedRun(facts ) {
|
|
@@ -215,6 +272,13 @@ export function verdictTitle(eligible , steps , facts
|
|
|
215
272
|
return `Start here: ${lowerFirst(findingActionLabel(lead.lead))}`;
|
|
216
273
|
}
|
|
217
274
|
|
|
275
|
+
/** What the finding count leaves out, as a sentence tail: notes about an incomplete log or data the
|
|
276
|
+
* log lacked are not fixes, so the count does not include them. */
|
|
277
|
+
function uncountedNote(uncounted ) {
|
|
278
|
+
if (!uncounted) return '';
|
|
279
|
+
return ` The count leaves out ${plural(uncounted, 'note')} on incomplete or missing log data.`;
|
|
280
|
+
}
|
|
281
|
+
|
|
218
282
|
/** The run-level summary under the title: how much was found and where, what
|
|
219
283
|
* the first fix is worth, and the run's idle capacity when that is large
|
|
220
284
|
* enough to matter but is not the first step (whose title already says it). */
|
|
@@ -234,7 +298,7 @@ export function verdictSummary(eligible , steps , facts
|
|
|
234
298
|
} else if (steps.length === 0) {
|
|
235
299
|
sentences.push('They are listed by impact under Findings.');
|
|
236
300
|
} else {
|
|
237
|
-
sentences.push(`${plural(eligible.length, 'finding')} in ${plural(steps.length, 'place')}
|
|
301
|
+
sentences.push(`${plural(eligible.length, 'finding')} in ${plural(steps.length, 'place')}.${uncountedNote(facts.uncounted)}`);
|
|
238
302
|
const wallClock = steps[0].lead.impactEstimate?.wallClock;
|
|
239
303
|
// An idle-capacity lead needs no sentence here: the title gives the idle share and step 1 the fix.
|
|
240
304
|
if (!isIdleCapacityStep(steps[0]) && wallClock && facts.runMs != null) {
|
|
@@ -323,6 +387,10 @@ export function stepCopyRecommendation(step , outcome )
|
|
|
323
387
|
|
|
324
388
|
|
|
325
389
|
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
|
|
326
394
|
|
|
327
395
|
|
|
328
396
|
export function buildRunVerdict(appModel , allFindings ) {
|
|
@@ -336,6 +404,7 @@ export function buildRunVerdict(appModel , allFindings )
|
|
|
336
404
|
clean: isCleanRun(appModel, allFindings),
|
|
337
405
|
outcome,
|
|
338
406
|
noFinishedStages: !hasFinishedStage(appModel.stages),
|
|
407
|
+
uncounted: allFindings.length - eligible.length,
|
|
339
408
|
};
|
|
340
409
|
const title = verdictTitle(eligible, steps, facts);
|
|
341
410
|
const shown = steps.slice(0, NEXT_STEP_LIMIT);
|
|
@@ -348,5 +417,9 @@ export function buildRunVerdict(appModel , allFindings )
|
|
|
348
417
|
remaining,
|
|
349
418
|
})
|
|
350
419
|
: null;
|
|
351
|
-
return {
|
|
420
|
+
return {
|
|
421
|
+
eligible, outcome, steps, facts, title, summary: verdictSummary(eligible, steps, facts), shown, remaining, copyText,
|
|
422
|
+
costLane: buildCostLane(eligible).slice(0, COST_LANE_LIMIT),
|
|
423
|
+
idle: buildRunIdleLine(appModel),
|
|
424
|
+
};
|
|
352
425
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// scale). Every prediction ships with a Model Error confidence indicator.
|
|
1
|
+
// What-if executor-scaling simulator. The makespan model is a first-cut approximation and is NOT
|
|
2
|
+
// validated against ground truth (a single event log only ever observes one scale). Every
|
|
3
|
+
// prediction ships with a Model Error confidence indicator.
|
|
5
4
|
//
|
|
6
5
|
// Builds on computeCoreTimeSeries' clamp conceptually, but operates on the
|
|
7
6
|
// worker-posted per-stage aggregates (runAggregates.perStage) so raw task data
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Each raw block is the standard Snappy format (varint uncompressed length +
|
|
6
6
|
// a sequence of literal/copy tagged elements): see
|
|
7
7
|
// https://github.com/google/snappy/blob/main/format_description.txt. This is
|
|
8
|
-
// a different framing from Spark's own LZ4Block format (
|
|
8
|
+
// a different framing from Spark's own LZ4Block format (lz4-block.ts).
|
|
9
9
|
|
|
10
10
|
const MAGIC = [0x82, 0x53, 0x4e, 0x41, 0x50, 0x50, 0x59, 0x00]; // 0x82 'S' 'N' 'A' 'P' 'P' 'Y' 0x00
|
|
11
11
|
const HEADER_SIZE = 16; // 8-byte magic + 4-byte version + 4-byte compatible version
|
|
@@ -102,7 +102,7 @@ export function decodeSnappyBlock(bytes ) {
|
|
|
102
102
|
|
|
103
103
|
// Streaming counterpart: push arbitrary byte slices, each fully-received
|
|
104
104
|
// block is decompressed and handed to `onChunk` as it completes. Mirrors
|
|
105
|
-
// createLz4BlockDecoder's shape/contract (see
|
|
105
|
+
// createLz4BlockDecoder's shape/contract (see lz4-block.ts).
|
|
106
106
|
export function createSnappyBlockDecoder(
|
|
107
107
|
onChunk ,
|
|
108
108
|
) {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// The Spark configuration a run actually executed under, resolved key by key from three layers:
|
|
2
|
+
// 1. a per-query value: `SparkListenerSQLExecutionStart.modifiedConfigs`, the session settings of
|
|
3
|
+
// one SQL execution that differ from the SparkContext's (what `spark.conf.set` leaves),
|
|
4
|
+
// 2. the app's Spark Properties from `SparkListenerEnvironmentUpdate`,
|
|
5
|
+
// 3. Spark's own default for the run's Spark version (SPARK_DEFAULTS below).
|
|
6
|
+
// A detector scoped to one SQL execution passes that execution's modifiedConfigs; any other caller
|
|
7
|
+
// leaves it out and gets the run-wide answer.
|
|
8
|
+
//
|
|
9
|
+
// Properties with no entry in SPARK_DEFAULTS have no fixed default: spark.executor.instances and
|
|
10
|
+
// spark.default.parallelism depend on the cluster manager and the cluster, spark.executor.memoryOverhead
|
|
11
|
+
// is derived from spark.executor.memory (executorOverheadSettings gives the pair it is derived from),
|
|
12
|
+
// spark.dynamicAllocation.initialExecutors from minExecutors,
|
|
13
|
+
// and spark.sql.adaptive.autoBroadcastJoinThreshold falls back to spark.sql.autoBroadcastJoinThreshold
|
|
14
|
+
// when unset. The lookup returns undefined for them rather than guess.
|
|
15
|
+
|
|
16
|
+
import { parseSparkMemoryMB } from './spark-memory.js';
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const ALWAYS = 'always';
|
|
38
|
+
|
|
39
|
+
// The per-query (`modifiedConfigs`) keys a detector reads: every key with a default above, and the
|
|
40
|
+
// two SQL settings it reads with none. A per-query value under any other key is the job's own text
|
|
41
|
+
// (a path, a bucket, a host) that no detector needs.
|
|
42
|
+
const PER_QUERY_KEYS_WITHOUT_DEFAULT = [
|
|
43
|
+
'spark.sql.adaptive.autoBroadcastJoinThreshold',
|
|
44
|
+
'spark.sql.adaptive.coalescePartitions.initialPartitionNum',
|
|
45
|
+
] ;
|
|
46
|
+
export function isDetectorConfKey(key ) {
|
|
47
|
+
return key in SPARK_DEFAULTS || (PER_QUERY_KEYS_WITHOUT_DEFAULT ).includes(key);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The text a per-query value is replaced with once it leaves the run: Spark's own placeholder for a
|
|
51
|
+
* hidden value, which the effective-conf lookup already ignores. */
|
|
52
|
+
export const REDACTED_CONF_VALUE = '*********(redacted)';
|
|
53
|
+
|
|
54
|
+
// Defaults by the Spark release that introduced them (table: detector-contract/threshold-tables.md,
|
|
55
|
+
// Spark defaults by release), each key's steps in ascending order. A key whose first step is
|
|
56
|
+
// 'always' has that default on every release, including a run whose version the log did not
|
|
57
|
+
// record; any other key is only answered for a known version at or after its first step (before
|
|
58
|
+
// it the property does not exist). Values are the strings Spark's ConfigBuilder declares. Each was read from the SQLConf.scala / config/package.scala sources at the release tags
|
|
59
|
+
// from v3.0.0 through v4.2.0, e.g. https://github.com/apache/spark/blob/v4.0.0/core/src/main/scala/org/apache/spark/internal/config/package.scala
|
|
60
|
+
const SPARK_DEFAULTS = {
|
|
61
|
+
'spark.speculation': [[ALWAYS, 'false']],
|
|
62
|
+
'spark.speculation.multiplier': [[ALWAYS, '1.5'], [[4, 0], '3']],
|
|
63
|
+
'spark.speculation.quantile': [[ALWAYS, '0.75'], [[4, 0], '0.9']],
|
|
64
|
+
|
|
65
|
+
'spark.executor.memory': [[ALWAYS, '1g']],
|
|
66
|
+
'spark.serializer': [[ALWAYS, 'org.apache.spark.serializer.JavaSerializer']],
|
|
67
|
+
'spark.shuffle.service.enabled': [[ALWAYS, 'false']],
|
|
68
|
+
'spark.eventLog.logBlockUpdates.enabled': [[ALWAYS, 'false']],
|
|
69
|
+
'spark.eventLog.logStageExecutorMetrics': [[ALWAYS, 'false']],
|
|
70
|
+
|
|
71
|
+
'spark.dynamicAllocation.enabled': [[ALWAYS, 'false']],
|
|
72
|
+
'spark.dynamicAllocation.minExecutors': [[ALWAYS, '0']],
|
|
73
|
+
'spark.dynamicAllocation.maxExecutors': [[ALWAYS, '2147483647']],
|
|
74
|
+
'spark.dynamicAllocation.executorIdleTimeout': [[ALWAYS, '60s']],
|
|
75
|
+
|
|
76
|
+
'spark.sql.shuffle.partitions': [[ALWAYS, '200']],
|
|
77
|
+
'spark.sql.autoBroadcastJoinThreshold': [[ALWAYS, '10MB']],
|
|
78
|
+
'spark.sql.files.maxPartitionBytes': [[ALWAYS, '128MB']],
|
|
79
|
+
'spark.sql.adaptive.enabled': [[ALWAYS, 'false'], [[3, 2], 'true']],
|
|
80
|
+
'spark.sql.adaptive.skewJoin.enabled': [[[3, 0], 'true']],
|
|
81
|
+
'spark.sql.adaptive.coalescePartitions.enabled': [[[3, 0], 'true']],
|
|
82
|
+
'spark.sql.adaptive.skewJoin.skewedPartitionFactor': [[[3, 0], '5']],
|
|
83
|
+
'spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes': [[[3, 0], '256MB']],
|
|
84
|
+
'spark.sql.adaptive.forceOptimizeSkewedJoin': [[[3, 3], 'false']],
|
|
85
|
+
// Falls back to spark.sql.adaptive.shuffle.targetPostShuffleInputSize, itself 64MB.
|
|
86
|
+
'spark.sql.adaptive.advisoryPartitionSizeInBytes': [[[3, 0], '64MB']],
|
|
87
|
+
'spark.sql.adaptive.coalescePartitions.parallelismFirst': [[[3, 2], 'true']],
|
|
88
|
+
// The factor is 0.4 instead on Kubernetes non-JVM jobs, which the log does not tell apart.
|
|
89
|
+
'spark.executor.memoryOverheadFactor': [[[3, 3], '0.1']],
|
|
90
|
+
'spark.executor.minMemoryOverhead': [[[4, 0], '384m']],
|
|
91
|
+
'spark.sql.execution.pythonUDF.arrow.enabled': [[[3, 4], 'false'], [[4, 2], 'true']],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// The text Spark substitutes for a sensitive value in modifiedConfigs (a fixed constant; only the
|
|
95
|
+
// pattern that selects values is configurable).
|
|
96
|
+
const DEFAULT_REDACTION = REDACTED_CONF_VALUE;
|
|
97
|
+
|
|
98
|
+
function parseVersion(sparkVersion ) {
|
|
99
|
+
const m = /^(\d+)\.(\d+)/.exec(sparkVersion ?? '');
|
|
100
|
+
return m == null ? null : [Number(m[1]), Number(m[2])];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Spark's default for `key` on `sparkVersion`; undefined when the property has no fixed default,
|
|
104
|
+
* does not exist on that version, or the default depends on a version the log did not record. */
|
|
105
|
+
export function sparkConfDefault(sparkVersion , key ) {
|
|
106
|
+
const steps = SPARK_DEFAULTS[key];
|
|
107
|
+
if (steps == null) return undefined;
|
|
108
|
+
const version = parseVersion(sparkVersion);
|
|
109
|
+
if (version == null) return steps.length === 1 && steps[0][0] === ALWAYS ? steps[0][1] : undefined;
|
|
110
|
+
let value ;
|
|
111
|
+
for (const [since, stepValue] of steps) {
|
|
112
|
+
if (since === ALWAYS || version[0] > since[0] || (version[0] === since[0] && version[1] >= since[1])) value = stepValue;
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** A per-query setting that can override: not Spark's redaction placeholder for a hidden value. */
|
|
118
|
+
function isUsableModified(value ) {
|
|
119
|
+
return value !== DEFAULT_REDACTION;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The Spark Properties with one execution's modifiedConfigs applied over them. Returns
|
|
123
|
+
* `properties` itself when there is nothing to apply. */
|
|
124
|
+
export function overlayModifiedConfigs(
|
|
125
|
+
properties ,
|
|
126
|
+
modified ,
|
|
127
|
+
) {
|
|
128
|
+
if (modified == null) return properties;
|
|
129
|
+
const usable = Object.entries(modified).filter(([, value]) => isUsableModified(value));
|
|
130
|
+
return usable.length === 0 ? properties : { ...properties, ...Object.fromEntries(usable) };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The run's effective value of `key` and the layer it came from, or undefined when no layer has it. */
|
|
134
|
+
export function effectiveSparkConf(layers , key ) {
|
|
135
|
+
const modified = layers.modified?.[key];
|
|
136
|
+
if (modified !== undefined && isUsableModified(modified)) return { value: modified.trim(), source: 'query' };
|
|
137
|
+
const logged = layers.properties?.[key];
|
|
138
|
+
// A log can carry a real boolean or number where Spark wrote a string.
|
|
139
|
+
if (logged !== undefined) return { value: String(logged).trim(), source: 'app' };
|
|
140
|
+
const fallback = sparkConfDefault(layers.sparkVersion, key);
|
|
141
|
+
return fallback === undefined ? undefined : { value: fallback, source: 'default' };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** A Spark byte-size value as bytes (a plain number, or with a k/m/g/t suffix, optionally 'b'), or
|
|
145
|
+
* null when it is not one. Negative numbers pass through: -1 turns auto-broadcast off. */
|
|
146
|
+
export function parseSparkBytes(value ) {
|
|
147
|
+
const m = /^(-?\d+(?:\.\d+)?)\s*([kmgt]?)b?$/i.exec(value?.trim() ?? '');
|
|
148
|
+
if (m == null) return null;
|
|
149
|
+
return m[2] === '' ? Number(m[1]) : Number(m[1]) * 1024 ** ('kmgt'.indexOf(m[2].toLowerCase()) + 1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** The factor and minimum (MiB) Spark sizes the executor overhead from when
|
|
153
|
+
* spark.executor.memoryOverhead is unset: max(factor x executor memory, minimum). The effective
|
|
154
|
+
* settings win, each only on a Spark version that reads it (the factor from 3.3.0, the minimum from
|
|
155
|
+
* 4.0.0); an unrecorded version is read as current. `fallback` is the pair used where the effective
|
|
156
|
+
* conf has none (a version before the key, or an unreadable value). */
|
|
157
|
+
export function executorOverheadSettings(
|
|
158
|
+
layers , fallback ,
|
|
159
|
+
) {
|
|
160
|
+
const version = parseVersion(layers.sparkVersion);
|
|
161
|
+
const [major, minor] = version ?? [Infinity, 0];
|
|
162
|
+
const factor = Number.parseFloat(effectiveSparkConf(layers, 'spark.executor.memoryOverheadFactor')?.value ?? '');
|
|
163
|
+
const minMB = parseSparkMemoryMB(effectiveSparkConf(layers, 'spark.executor.minMemoryOverhead')?.value);
|
|
164
|
+
return {
|
|
165
|
+
factor: (major > 3 || (major === 3 && minor >= 3)) && Number.isFinite(factor) && factor > 0 ? factor : fallback.floorPct,
|
|
166
|
+
minMB: major >= 4 && minMB != null && minMB >= 0 ? minMB : fallback.floorMB,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Shared by every scope:'sql' detector and the plan views. sql.get(id).stageIds is always empty
|
|
2
|
-
//
|
|
1
|
+
// Shared by every scope:'sql' detector and the plan views. sql.get(id).stageIds is always empty
|
|
2
|
+
// (see detector-contract/plan-attribution.md), so stages are linked by their own sqlExecutionId.
|
|
3
3
|
// Minimal param shape (not DetectorStage): external callers pass Map<StageId, Stage>.
|
|
4
4
|
export function stageIdsForSqlExec(
|
|
5
5
|
executionId ,
|
|
@@ -16,7 +16,7 @@ import { withEarlierAttempts, totalExecutorCpuMs } from './run-totals.js';
|
|
|
16
16
|
import { planNodesOfStage } from './stage-plan-nodes.js';
|
|
17
17
|
import { normalizeStageName, stageIdentityWith, } from './stage-identity.js';
|
|
18
18
|
import { alignSequences } from './sequence-alignment.js';
|
|
19
|
-
import {
|
|
19
|
+
import { attributeNamesOf, detailTokens, jaccard, structuralKey } from './stage-structure.js';
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
@@ -24,8 +24,6 @@ import { attributeNames, detailTokens, jaccard, structuralKey } from './stage-st
|
|
|
24
24
|
* `runtimeCoverage`). Additive changes keep it; a consumer refuses a version it does not know. */
|
|
25
25
|
export const COMPARISON_SCHEMA_VERSION = 1;
|
|
26
26
|
|
|
27
|
-
// ---- Comparison normalizer ------------------------------------------------------------------
|
|
28
|
-
|
|
29
27
|
// Targeted patches for the volatile tokens that differ between two runs of one job. Each applies
|
|
30
28
|
// on top of `normalizeDetail`, in this order. No blanket numeric or path stripping: those fuse
|
|
31
29
|
// distinct stages of one run.
|
|
@@ -95,8 +93,6 @@ export function comparisonDetailNormalizer(callerPatterns = [
|
|
|
95
93
|
};
|
|
96
94
|
}
|
|
97
95
|
|
|
98
|
-
// ---- Identity and bookkeeping ---------------------------------------------------------------
|
|
99
|
-
|
|
100
96
|
/** `stageIdentity`'s recipe (normalized stage name plus the attributed nodes' fingerprint) with the
|
|
101
97
|
* comparison normalizer. Internal to the aligner: consumers key on a pair's `pairId`. */
|
|
102
98
|
export function comparisonIdentity(
|
|
@@ -125,8 +121,6 @@ export function isDeltaBookkeepingStage(
|
|
|
125
121
|
return scans > 0;
|
|
126
122
|
}
|
|
127
123
|
|
|
128
|
-
// ---- Alignment ------------------------------------------------------------------------------
|
|
129
|
-
|
|
130
124
|
export const PAIR_DELTA_METRICS = [
|
|
131
125
|
'executorRunTime', 'executorCpuTime', 'memoryBytesSpilled', 'diskBytesSpilled',
|
|
132
126
|
'inputBytes', 'outputBytes', 'shuffleReadBytes', 'shuffleWriteBytes',
|
|
@@ -268,8 +262,6 @@ function sumFigures(rows ) {
|
|
|
268
262
|
|
|
269
263
|
const ascending = (a , b ) => a - b;
|
|
270
264
|
|
|
271
|
-
// ---- Per-run index --------------------------------------------------------------------------
|
|
272
|
-
|
|
273
265
|
// What the aligner knows about one SQL execution (or the pseudo-execution of stages outside any):
|
|
274
266
|
// the non-bookkeeping stages it ran and the three things its similarity reads.
|
|
275
267
|
|
|
@@ -325,7 +317,7 @@ function planStructure(root ) {
|
|
|
325
317
|
(function walk(node ) {
|
|
326
318
|
if (!/^(WholeStageCodegen|InputAdapter|ColumnarToRow)\b/.test(node.name ?? '')) {
|
|
327
319
|
tokens.push(normalizeStageName(node.name ?? ''));
|
|
328
|
-
for (const name of
|
|
320
|
+
for (const name of attributeNamesOf(node)) attributes.add(`@${name}`);
|
|
329
321
|
}
|
|
330
322
|
for (const child of node.children ?? []) walk(child);
|
|
331
323
|
})(root);
|
|
@@ -395,8 +387,6 @@ function executionSimilarity(a , b ) {
|
|
|
395
387
|
return Math.round((1000 * sum) / weight);
|
|
396
388
|
}
|
|
397
389
|
|
|
398
|
-
// ---- Stage alignment inside one execution pair -----------------------------------------------
|
|
399
|
-
|
|
400
390
|
|
|
401
391
|
|
|
402
392
|
function alignWithin(
|
|
@@ -13,6 +13,24 @@ import { planNodesOfStage } from './stage-plan-nodes.js';
|
|
|
13
13
|
// Replace run-varying tokens (digit runs, long hex ids) with a stable marker so
|
|
14
14
|
// the same logical stage across two runs normalizes to one identity.
|
|
15
15
|
export function normalizeStageName(name ) {
|
|
16
|
+
if (name.length > STAGE_NAME_CACHE_MAX_LENGTH) return computeStageName(name);
|
|
17
|
+
let normalized = stageNameCache.get(name);
|
|
18
|
+
if (normalized === undefined) {
|
|
19
|
+
if (stageNameCache.size >= STAGE_NAME_CACHE_MAX_ENTRIES) stageNameCache.clear();
|
|
20
|
+
normalized = computeStageName(name);
|
|
21
|
+
stageNameCache.set(name, normalized);
|
|
22
|
+
}
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// A comparison normalizes the same few operator and stage names once per stage, identity and key.
|
|
27
|
+
// Only short names are kept (long inputs are call-site text, rarely repeated), and the whole table
|
|
28
|
+
// resets when full, so it stays small however many runs a long-lived process analyzes.
|
|
29
|
+
const STAGE_NAME_CACHE_MAX_LENGTH = 256;
|
|
30
|
+
const STAGE_NAME_CACHE_MAX_ENTRIES = 4096;
|
|
31
|
+
const stageNameCache = new Map ();
|
|
32
|
+
|
|
33
|
+
function computeStageName(name ) {
|
|
16
34
|
return String(name)
|
|
17
35
|
.toLowerCase()
|
|
18
36
|
.replace(/\b[0-9a-f]{8,}\b/g, '#') // hex ids/uuids first (they contain digits)
|
|
@@ -21,6 +39,28 @@ export function normalizeStageName(name ) {
|
|
|
21
39
|
.trim();
|
|
22
40
|
}
|
|
23
41
|
|
|
42
|
+
// A plan node belongs to every stage that ran part of it, and each of those stages reads its
|
|
43
|
+
// normalized detail again (exact key, structural key, similarity tokens), on both runs of a
|
|
44
|
+
// comparison. The normalizer is a pure function of the detail and a node is never mutated after the
|
|
45
|
+
// parser posts it, so the result is memoized per node. Like the per-tree identity below, each node
|
|
46
|
+
// keeps at most MAX_NORMALIZERS_PER_TREE normalizers and drops the oldest first.
|
|
47
|
+
const nodeDetailCache = new WeakMap ();
|
|
48
|
+
|
|
49
|
+
export function normalizedDetailOf(node , normalizeDetail ) {
|
|
50
|
+
let byNormalizer = nodeDetailCache.get(node);
|
|
51
|
+
if (!byNormalizer) {
|
|
52
|
+
byNormalizer = new Map();
|
|
53
|
+
nodeDetailCache.set(node, byNormalizer);
|
|
54
|
+
}
|
|
55
|
+
let normalized = byNormalizer.get(normalizeDetail);
|
|
56
|
+
if (normalized === undefined) {
|
|
57
|
+
normalized = normalizeDetail(node.detail ?? '');
|
|
58
|
+
if (byNormalizer.size >= MAX_NORMALIZERS_PER_TREE) byNormalizer.delete(byNormalizer.keys().next().value );
|
|
59
|
+
byNormalizer.set(normalizeDetail, normalized);
|
|
60
|
+
}
|
|
61
|
+
return normalized;
|
|
62
|
+
}
|
|
63
|
+
|
|
24
64
|
// Bottom-up, order-independent structural identity of a resolved plan tree:
|
|
25
65
|
// each node folds its normalized name/detail with its children's digests
|
|
26
66
|
// (children sorted, so AQE picking a different broadcast side still matches),
|
|
@@ -32,13 +72,28 @@ export function normalizeStageName(name ) {
|
|
|
32
72
|
// instead of embedding full child identity strings, which would re-escape
|
|
33
73
|
// every level below and blow identity size up to ~2^depth on the 20-60+
|
|
34
74
|
// operator-deep plans real Spark produces.
|
|
75
|
+
//
|
|
76
|
+
// Memoized per resolved tree (a tree is never mutated after the parser posts it), because every
|
|
77
|
+
// stage of an execution that has no attributed node falls back to the same whole-tree identity.
|
|
78
|
+
// The aligner builds a fresh normalizer closure per call, so each tree keeps at most
|
|
79
|
+
// MAX_NORMALIZERS_PER_TREE entries and drops the oldest first.
|
|
80
|
+
const MAX_NORMALIZERS_PER_TREE = 4;
|
|
81
|
+
const planTreeIdentityCache = new WeakMap ();
|
|
82
|
+
|
|
35
83
|
function planTreeIdentity(root , normalizeDetail ) {
|
|
36
84
|
if (!root) return null;
|
|
85
|
+
const byNormalizer = planTreeIdentityCache.get(root) ?? new Map ();
|
|
86
|
+
const cached = byNormalizer.get(normalizeDetail);
|
|
87
|
+
if (cached !== undefined) return cached;
|
|
37
88
|
function visit(node ) {
|
|
38
89
|
const childDigests = (node.children ?? []).map(visit).sort();
|
|
39
|
-
return cyrb53(JSON.stringify([normalizeStageName(node.name ?? ''),
|
|
90
|
+
return cyrb53(JSON.stringify([normalizeStageName(node.name ?? ''), normalizedDetailOf(node, normalizeDetail), childDigests]));
|
|
40
91
|
}
|
|
41
|
-
|
|
92
|
+
const identity = visit(root);
|
|
93
|
+
if (byNormalizer.size >= MAX_NORMALIZERS_PER_TREE) byNormalizer.delete(byNormalizer.keys().next().value );
|
|
94
|
+
byNormalizer.set(normalizeDetail, identity);
|
|
95
|
+
planTreeIdentityCache.set(root, byNormalizer);
|
|
96
|
+
return identity;
|
|
42
97
|
}
|
|
43
98
|
|
|
44
99
|
// Plan identity for the stage's SQL execution, scoped to only the plan nodes
|
|
@@ -56,7 +111,7 @@ function sqlNodeIdentity(
|
|
|
56
111
|
const root = snapshot.sql.get(execId)?.planTree ?? null;
|
|
57
112
|
if (!root) return '';
|
|
58
113
|
const fingerprints = (nodes ?? planNodesOfStage(stage, snapshot.sql))
|
|
59
|
-
.map((node) => JSON.stringify([normalizeStageName(node.name ?? ''),
|
|
114
|
+
.map((node) => JSON.stringify([normalizeStageName(node.name ?? ''), normalizedDetailOf(node, normalizeDetail)]));
|
|
60
115
|
if (fingerprints.length === 0) return planTreeIdentity(root, normalizeDetail) ?? '';
|
|
61
116
|
return cyrb53(JSON.stringify(fingerprints.sort()));
|
|
62
117
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// A path inside a stage name: an absolute path, optionally behind a URI scheme and authority
|
|
2
|
+
// (`hdfs://nn/...`). It starts at the beginning of the name or after a space, quote, bracket, `=`
|
|
3
|
+
// or `,`, so a `/` inside a word ("a/b") is never taken for one. A `:` ends the path, which leaves
|
|
4
|
+
// a trailing `:<line>` in place.
|
|
5
|
+
const PATH_IN_STAGE_NAME = /(?<![^\s"'(\[=,])(?:[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s/]*)?(?:\/[^\s/:"')\]]+)+/g;
|
|
6
|
+
|
|
7
|
+
/** A stage name for display and output: every path in it reduced to its last segment, so the
|
|
8
|
+
* directories of the job's call site (a YARN container directory such as
|
|
9
|
+
* `.../usercache/<user>/appcache/<app>/container_<id>/`, a home directory) and the user name in
|
|
10
|
+
* them never reach the output. `collect at /home/jdoe/jobs/etl.py:42` reads `collect at etl.py:42`.
|
|
11
|
+
* Display only: `normalizeStageName` stays the stage identity, so stage pairing does not move. */
|
|
12
|
+
export function stageDisplayLabel(name ) {
|
|
13
|
+
return name.replace(PATH_IN_STAGE_NAME, (path) => path.slice(path.lastIndexOf('/') + 1));
|
|
14
|
+
}
|
|
@@ -9,10 +9,24 @@ export function planNodesOfStage(stage , sq
|
|
|
9
9
|
if (execId == null) return [];
|
|
10
10
|
const root = sql.get(execId)?.planTree ?? null;
|
|
11
11
|
if (!root) return [];
|
|
12
|
-
|
|
12
|
+
return [...(nodesByStage(root).get(stage.id) ?? [])];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// One preorder walk per resolved plan tree serves every stage of its execution. A resolved tree is
|
|
16
|
+
// never mutated after the parser posts it, so keying by root identity is safe.
|
|
17
|
+
const nodesByStageCache = new WeakMap ();
|
|
18
|
+
|
|
19
|
+
function nodesByStage(root ) {
|
|
20
|
+
const cached = nodesByStageCache.get(root);
|
|
21
|
+
if (cached) return cached;
|
|
22
|
+
const index = new Map ();
|
|
13
23
|
(function collect(node ) {
|
|
14
|
-
|
|
24
|
+
for (const id of new Set(node.stageIds ?? [])) {
|
|
25
|
+
const list = index.get(id);
|
|
26
|
+
if (list) list.push(node); else index.set(id, [node]);
|
|
27
|
+
}
|
|
15
28
|
for (const child of node.children ?? []) collect(child);
|
|
16
29
|
})(root);
|
|
17
|
-
|
|
30
|
+
nodesByStageCache.set(root, index);
|
|
31
|
+
return index;
|
|
18
32
|
}
|