sparkforensics-server 0.5.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/instrument-sans-latin-ext-wdth-normal-C5E2Gvlv.woff2 +0 -0
- package/public/assets/instrument-sans-latin-wdth-normal-BVScPF0l.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/public/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -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 -7
- package/public/docs/assets/{app.eYTPqTeq.js → app.1gvzd5Zb.js} +1 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.DS2TQP5_.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.DsZrQKQR.js → VPLocalSearchBox.CNxT3s0I.js} +1 -1
- package/public/docs/assets/chunks/{theme.DsTNE2pE.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.8bH1HnZp.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.WqY74T0T.js +1 -0
- 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.wj6Et6UD.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.CKjWG56Y.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.CZv_ZGOp.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.BUucRc0A.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.DC1YqXBp.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.DVGZOWvW.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.D9bu4j_c.js → contributor-guide_development-setup.md.B21ZTDhn.js} +3 -3
- 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/instrument-sans-latin-ext-wdth-normal.C5E2Gvlv.woff2 +0 -0
- package/public/docs/assets/instrument-sans-latin-wdth-normal.BVScPF0l.woff2 +0 -0
- package/public/docs/assets/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2 +0 -0
- package/public/docs/assets/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2 +0 -0
- package/public/docs/assets/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2 +0 -0
- package/public/docs/assets/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2 +0 -0
- package/public/docs/assets/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2 +0 -0
- package/public/docs/assets/style.Cfxqlj0y.css +1 -0
- 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.C0pcQoNc.lean.js +1 -0
- 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_mcp-tools_get-run-summary.md.DpxZ4ZNp.js → user-guide_mcp-tools_get-run-summary.md.EhaIsele.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 -10
- package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +6 -10
- package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -10
- package/public/docs/contributor-guide/architecture/board-widgets.html +6 -10
- package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +6 -10
- package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -10
- package/public/docs/contributor-guide/architecture/detector-contract.html +6 -10
- package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +6 -10
- package/public/docs/contributor-guide/architecture/drill-down.html +6 -10
- package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -10
- package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +6 -10
- package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -10
- package/public/docs/contributor-guide/architecture/index.html +5 -9
- package/public/docs/contributor-guide/architecture/overview.html +6 -10
- package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +5 -9
- package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -10
- package/public/docs/contributor-guide/architecture/state-and-history.html +5 -9
- package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +6 -10
- package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +5 -9
- package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +6 -10
- package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +5 -9
- package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +6 -10
- package/public/docs/contributor-guide/architecture/widget-rendering.html +6 -10
- package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -10
- package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +6 -10
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +7 -11
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -10
- package/public/docs/contributor-guide/architecture/worker-protocol.html +6 -10
- package/public/docs/contributor-guide/contributing.html +6 -10
- package/public/docs/contributor-guide/development-setup.html +8 -12
- package/public/docs/contributor-guide/testing.html +9 -10
- package/public/docs/favicon.svg +2 -2
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +6 -10
- 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 -9
- package/public/docs/tuning-reference/aqe.html +5 -9
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -9
- package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -9
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -9
- package/public/docs/tuning-reference/bottleneck-failures.html +5 -9
- package/public/docs/tuning-reference/bottleneck-gc.html +5 -9
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -9
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -9
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -9
- package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -9
- package/public/docs/tuning-reference/bottleneck-skew.html +5 -9
- package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -9
- package/public/docs/tuning-reference/bottleneck-small-files.html +5 -9
- package/public/docs/tuning-reference/bottleneck-spill.html +5 -9
- package/public/docs/tuning-reference/bottleneck-straggler.html +5 -9
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -9
- package/public/docs/tuning-reference/bottleneck-utilization.html +5 -9
- package/public/docs/tuning-reference/caching.html +5 -9
- package/public/docs/tuning-reference/cluster-config.html +5 -9
- package/public/docs/tuning-reference/config.html +5 -9
- package/public/docs/tuning-reference/data-formats.html +5 -9
- package/public/docs/tuning-reference/index.html +5 -9
- package/public/docs/tuning-reference/intro.html +5 -9
- package/public/docs/tuning-reference/joins.html +5 -9
- package/public/docs/tuning-reference/memory-model.html +5 -9
- package/public/docs/tuning-reference/metrics.html +5 -9
- package/public/docs/tuning-reference/partitioning.html +5 -9
- package/public/docs/tuning-reference/pyspark.html +5 -9
- package/public/docs/tuning-reference/shuffle.html +5 -9
- package/public/docs/tuning-reference/spark-architecture.html +5 -9
- package/public/docs/tuning-reference/table-formats.html +5 -9
- package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +5 -9
- package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +5 -9
- package/public/docs/user-guide/alternative-log-retrieval.html +5 -9
- package/public/docs/user-guide/getting-started/ci-and-automation.html +17 -15
- package/public/docs/user-guide/getting-started/reading-the-dashboard.html +6 -10
- package/public/docs/user-guide/getting-started/regression-metric-keys.html +5 -9
- package/public/docs/user-guide/getting-started/tuning-thresholds.html +7 -11
- package/public/docs/user-guide/getting-started.html +5 -9
- package/public/docs/user-guide/mcp-tools/compare-runs.html +16 -15
- package/public/docs/user-guide/mcp-tools/diagnose-run.html +28 -11
- package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -10
- package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +5 -9
- package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +7 -10
- package/public/docs/user-guide/mcp-tools/get-reference-doc.html +5 -9
- package/public/docs/user-guide/mcp-tools/get-run-summary.html +6 -10
- package/public/docs/user-guide/mcp-tools/list-runs.html +5 -9
- package/public/docs/user-guide/mcp-tools.html +6 -10
- package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +5 -9
- package/public/docs/user-guide/run-comparison.html +6 -10
- package/public/docs/user-guide/understanding-findings.html +6 -10
- package/public/export-template.html +46 -46
- package/public/favicon.svg +2 -2
- package/public/index.html +8 -10
- package/public/sample-runs/sample-run.ndjson.gz +0 -0
- package/vendor-core/allocation.js +126 -39
- package/vendor-core/analyzer.js +85 -58
- package/vendor-core/api-merge-writes.js +3 -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/delta-log-paths.js +10 -1
- package/vendor-core/detectors.js +1078 -416
- 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 +48 -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/docs-content/detection/util.md +6 -2
- package/vendor-core/effective-conf.js +7 -10
- package/vendor-core/efficiency-model.js +24 -16
- 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 +129 -39
- package/vendor-core/finding-types.js +108 -30
- 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 +28 -17
- 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/remediation.js +10 -5
- 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 +44 -11
- 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 +70 -23
- package/vendor-core/wall-clock.js +8 -0
- package/vendor-core/wasted-core-hours.js +12 -12
- package/vendor-core/write-targets.js +146 -14
- package/vendor-core/zstd-worker-client.js +1 -1
- package/vendor-core/zstd-worker.js +1 -1
- package/public/assets/AutoscalingChurn-8t3U3isq.js +0 -1
- package/public/assets/CacheUtilization-CJs9tTum.js +0 -1
- package/public/assets/CachingOpportunity-D1NcHs0V.js +0 -1
- package/public/assets/ColdStart-CdRJ9gD8.js +0 -1
- package/public/assets/ConfigAudit-B5Gbe5js.js +0 -1
- package/public/assets/CoreUsageArea-a--NTRGY.js +0 -1
- package/public/assets/Dashboard-C1hNeX49.js +0 -2
- package/public/assets/DisclosureContext-ZpE6CkHx.js +0 -1
- package/public/assets/DuplicatePlanSubtree-dobXdz7E.js +0 -1
- package/public/assets/ExecutorUtilization-D9grwi7t.js +0 -1
- package/public/assets/GcPressure-8-G2ri10.js +0 -1
- package/public/assets/IncompleteRun-B91lky7a.js +0 -1
- package/public/assets/JobFailures-BsWvBMyJ.js +0 -1
- package/public/assets/MemoryUtilization-BM5schZI.js +0 -1
- package/public/assets/OverBroadcast-DdN8JIJH.js +0 -1
- package/public/assets/PartitionHint-CUvbnnxy.js +0 -1
- package/public/assets/PartitionSizing-CY5HDYtp.js +0 -1
- package/public/assets/PlanExplorer-DgHCDBD_.js +0 -1
- package/public/assets/PlanGraphRoute-DH3KJb8j.js +0 -1
- package/public/assets/RetryWaste-DOnE9vGF.js +0 -1
- package/public/assets/ShuffleIO-MMnDESkF.js +0 -1
- package/public/assets/Skew-BmZZJ5ms.js +0 -1
- package/public/assets/SlowHost-CsJzfEqo.js +0 -1
- package/public/assets/SmallFiles-BoK3R1Ex.js +0 -1
- package/public/assets/SpeculationWaste-CnVxB5AJ.js +0 -1
- package/public/assets/Spill-BhNicyBy.js +0 -1
- package/public/assets/StageFailed-mAhHU4l_.js +0 -1
- package/public/assets/StageFindingGroup-BEtPWcT0.js +0 -1
- package/public/assets/StageHeader-DJkYkrsU.js +0 -1
- package/public/assets/StageShape-BDxRknLp.js +0 -1
- package/public/assets/StageSlowness-BPRw-otN.js +0 -1
- package/public/assets/Straggler-CZCbnOWP.js +0 -1
- package/public/assets/TaskFailures-C57YcLFQ.js +0 -1
- package/public/assets/TinyTask-Bi84qU7n.js +0 -1
- package/public/assets/UnderBroadcast-DdQV7htE.js +0 -1
- package/public/assets/charts-vendor-0VMaLVLN.js +0 -59
- package/public/assets/finding-anchor-Cl6_5hfV.js +0 -1
- package/public/assets/impact-sort-CssZ6MT2.js +0 -1
- package/public/assets/index-2h_TgUAb.css +0 -2
- package/public/assets/index-B-03mG7K.js +0 -14
- package/public/assets/plan-graph-vendor-BYl-AzN5.js +0 -7
- package/public/assets/react-vendor-DEdfrRTJ.js +0 -9
- package/public/assets/select-CQjNhprm.js +0 -1
- package/public/assets/usePagedRows-Dyl6T8Ok.js +0 -1
- package/public/assets/useRecentFiles-XZEnLlal.js +0 -11
- package/public/assets/useSortMode-Dhvit8dt.js +0 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.u_e5bQz4.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.DYgUpIJB.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.wj6Et6UD.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.CKjWG56Y.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.CZv_ZGOp.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.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.DC1YqXBp.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.js +0 -1
- package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +0 -1
- package/public/docs/assets/contributor-guide_development-setup.md.D9bu4j_c.lean.js +0 -1
- package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +0 -1
- package/public/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/public/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/public/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/public/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/public/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/public/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/public/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/public/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/public/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/public/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/public/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/public/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/public/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/public/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/public/docs/assets/style.tmDhD3c8.css +0 -1
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.OUQqw3mI.js +0 -23
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.OUQqw3mI.lean.js +0 -1
- 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.C9YRruMT.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.C9YRruMT.lean.js +0 -1
- /package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.lean.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.lean.js} +0 -0
- /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
- /package/public/docs/assets/{user-guide_mcp-tools_get-run-summary.md.DpxZ4ZNp.lean.js → user-guide_mcp-tools_get-run-summary.md.EhaIsele.lean.js} +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// The facts that decide whether two runs can be read against each other: what configuration and
|
|
2
|
+
// plan they share, how much data they handled, the capacity each had, and how those figures
|
|
3
|
+
// bridge the wall-clock difference. Built once per comparison, so every surface reports the same.
|
|
4
|
+
import { allocatedCoreMs, computeAllocation, lastObservedTimestamp } from './allocation.js';
|
|
5
|
+
import { buildEffectiveConf } from './effective-conf.js';
|
|
6
|
+
import { MAX_NORMALIZE_PATTERN_LENGTH, MAX_NORMALIZE_PATTERNS } from './stage-alignment.js';
|
|
7
|
+
import { totalOutputRows, withEarlierAttempts } from './run-totals.js';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/** A configuration key whose value differs between the runs. A side that does not set the key is
|
|
13
|
+
* null. */
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/** One data figure of both runs. `relativeDifference` is (candidate - baseline) / baseline, null
|
|
38
|
+
* when either side is unmeasured or the baseline is 0 and the candidate is not. */
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/** wall clock = task time / (mean executors x cores x task time's share of allocated core time),
|
|
58
|
+
* so for runs with equal executor cores the wall-clock ratio is the task-time ratio over the
|
|
59
|
+
* product of the other two. `explainedWallClock` is that quotient. */
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/** Keys the cluster sets per run (application id, start time, driver address). Two runs of one job
|
|
90
|
+
* always differ here, so they never count as a configuration difference. */
|
|
91
|
+
const RUN_IDENTITY_CONF = /^spark\.(app\.(id|startTime|submitTime)|driver\.(host|port)|yarn\.app\.id|ui\.port)$/;
|
|
92
|
+
|
|
93
|
+
/** Compiles the caller's ignore patterns, matched against configuration keys. Throws on a
|
|
94
|
+
* non-string or empty pattern, an over-long one, an invalid one, or too many. */
|
|
95
|
+
export function compileConfIgnorePatterns(patterns ) {
|
|
96
|
+
if (!patterns || patterns.length === 0) return [];
|
|
97
|
+
if (patterns.length > MAX_NORMALIZE_PATTERNS) {
|
|
98
|
+
throw new Error(`At most ${MAX_NORMALIZE_PATTERNS} ignore patterns are accepted, got ${patterns.length}.`);
|
|
99
|
+
}
|
|
100
|
+
return patterns.map((source) => {
|
|
101
|
+
if (typeof source !== 'string' || source.length === 0) throw new Error('An ignore pattern must be a non-empty string.');
|
|
102
|
+
if (source.length > MAX_NORMALIZE_PATTERN_LENGTH) {
|
|
103
|
+
throw new Error(`Ignore pattern "${source.slice(0, 40)}..." is ${source.length} characters; the limit is ${MAX_NORMALIZE_PATTERN_LENGTH}.`);
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
return new RegExp(source);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
throw new Error(`Ignore pattern "${source}" is not a valid regular expression: ${(e ).message}`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function compareConf(baseSnap , candSnap , ignore , userPattern ) {
|
|
114
|
+
const base = buildEffectiveConf(baseSnap.app, { userPattern });
|
|
115
|
+
const cand = buildEffectiveConf(candSnap.app, { userPattern });
|
|
116
|
+
if (base == null || cand == null) return { equal: null, differences: [], ignoredKeys: [], withheldKeys: [] };
|
|
117
|
+
const withheld = new Set([...base.maskedKeys, ...cand.maskedKeys]);
|
|
118
|
+
const keys = [...new Set([...Object.keys(base.values), ...Object.keys(cand.values)])].filter((key) => !withheld.has(key)).sort();
|
|
119
|
+
const differences = [];
|
|
120
|
+
const ignoredKeys = [];
|
|
121
|
+
for (const key of keys) {
|
|
122
|
+
const baseline = base.values[key] ?? null;
|
|
123
|
+
const candidate = cand.values[key] ?? null;
|
|
124
|
+
if (baseline === candidate) continue;
|
|
125
|
+
if (RUN_IDENTITY_CONF.test(key) || ignore.some((pattern) => pattern.test(key))) ignoredKeys.push(key);
|
|
126
|
+
else differences.push({ key, baseline, candidate });
|
|
127
|
+
}
|
|
128
|
+
return { equal: differences.length === 0, differences, ignoredKeys, withheldKeys: [...withheld].sort() };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function comparePlan(alignment ) {
|
|
132
|
+
const unmatched = { baseline: alignment.unmatched.baseStageIds.length, candidate: alignment.unmatched.candStageIds.length };
|
|
133
|
+
return {
|
|
134
|
+
equal: unmatched.baseline === 0 && unmatched.candidate === 0 && alignment.replanned.length === 0,
|
|
135
|
+
unmatched,
|
|
136
|
+
replanned: alignment.replanned.length,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function relativeDifference(baseline , candidate ) {
|
|
141
|
+
if (baseline == null || candidate == null) return null;
|
|
142
|
+
if (baseline === 0) return candidate === 0 ? 0 : null;
|
|
143
|
+
return (candidate - baseline) / Math.abs(baseline);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function figure(baseline , candidate ) {
|
|
147
|
+
return { baseline, candidate, relativeDifference: relativeDifference(baseline, candidate) };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Whether two measured values sit within `noiseBand` of the baseline. A baseline of 0 only
|
|
151
|
+
* matches a candidate of 0. */
|
|
152
|
+
export function withinBand(baseline , candidate , noiseBand ) {
|
|
153
|
+
if (baseline === 0) return candidate === 0;
|
|
154
|
+
return Math.abs(candidate - baseline) / Math.abs(baseline) < noiseBand;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function compareVolume(
|
|
158
|
+
baseStages , candStages , metrics , noiseBand ,
|
|
159
|
+
) {
|
|
160
|
+
const row = (key ) => metrics.find((m) => m.key === key);
|
|
161
|
+
const inputBytes = figure(row('inputBytes')?.baseline ?? null, row('inputBytes')?.candidate ?? null);
|
|
162
|
+
const outputBytes = figure(row('outputBytes')?.baseline ?? null, row('outputBytes')?.candidate ?? null);
|
|
163
|
+
const outputRows = figure(totalOutputRows(baseStages), totalOutputRows(candStages));
|
|
164
|
+
const verdicts = [];
|
|
165
|
+
for (const fig of [inputBytes, outputBytes]) {
|
|
166
|
+
if (fig.baseline != null && fig.candidate != null) verdicts.push(withinBand(fig.baseline, fig.candidate, noiseBand));
|
|
167
|
+
}
|
|
168
|
+
if (outputRows.baseline != null && outputRows.candidate != null) verdicts.push(outputRows.baseline === outputRows.candidate);
|
|
169
|
+
const outputMeasured = [outputRows, outputBytes].some((fig) => fig.baseline != null && fig.candidate != null);
|
|
170
|
+
return { inputBytes, outputRows, outputBytes, equal: !outputMeasured ? null : verdicts.every(Boolean) };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function ratio(baseline , candidate ) {
|
|
174
|
+
return baseline == null || candidate == null || baseline === 0 ? null : candidate / baseline;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function factor(baseline , candidate ) {
|
|
178
|
+
return { baseline, candidate, ratio: ratio(baseline, candidate) };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function runWindow(snap ) {
|
|
182
|
+
const start = snap.app?.startTime;
|
|
183
|
+
const end = snap.app?.endTime ?? (snap.executors ? lastObservedTimestamp(snap) : null);
|
|
184
|
+
return start != null && end != null && end >= start ? [start, end] : null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The comparability facts of a comparison. `metrics` are the comparison's own rows, so the
|
|
188
|
+
* volume and bridge figures are the ones the metrics table shows. */
|
|
189
|
+
export function buildComparability(
|
|
190
|
+
baseSnap , candSnap ,
|
|
191
|
+
alignment ,
|
|
192
|
+
metrics ,
|
|
193
|
+
{ ignoreConf, confRedactRegex, noiseBand } ,
|
|
194
|
+
) {
|
|
195
|
+
const attempts = (snap ) => withEarlierAttempts([...snap.stages.values()]);
|
|
196
|
+
const allocation = (snap ) => (snap.executors ? computeAllocation(snap) : null);
|
|
197
|
+
const baseAlloc = allocation(baseSnap), candAlloc = allocation(candSnap);
|
|
198
|
+
const row = (key ) => metrics.find((m) => m.key === key);
|
|
199
|
+
const share = (snap , runTimeMs ) => {
|
|
200
|
+
const allocated = snap.executors ? allocatedCoreMs(snap) : null;
|
|
201
|
+
return runTimeMs != null && allocated != null ? runTimeMs / allocated : null;
|
|
202
|
+
};
|
|
203
|
+
const taskTime = factor(row('executorRunTime')?.baseline ?? null, row('executorRunTime')?.candidate ?? null);
|
|
204
|
+
const meanExecutors = factor(baseAlloc?.executorsMean ?? null, candAlloc?.executorsMean ?? null);
|
|
205
|
+
const taskShare = factor(share(baseSnap, taskTime.baseline), share(candSnap, taskTime.candidate));
|
|
206
|
+
const parts = [taskTime.ratio, meanExecutors.ratio, taskShare.ratio];
|
|
207
|
+
const explainedWallClock = parts.every((r) => r != null && r > 0)
|
|
208
|
+
? parts[0] / (parts[1] * parts[2] )
|
|
209
|
+
: null;
|
|
210
|
+
|
|
211
|
+
const baseWindow = runWindow(baseSnap), candWindow = runWindow(candSnap);
|
|
212
|
+
const overlapMs = baseWindow && candWindow
|
|
213
|
+
? Math.max(0, Math.min(baseWindow[1], candWindow[1]) - Math.max(baseWindow[0], candWindow[0]))
|
|
214
|
+
: null;
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
noiseBand,
|
|
218
|
+
conf: compareConf(baseSnap, candSnap, compileConfIgnorePatterns(ignoreConf), confRedactRegex),
|
|
219
|
+
plan: comparePlan(alignment),
|
|
220
|
+
volume: compareVolume(attempts(baseSnap), attempts(candSnap), metrics, noiseBand),
|
|
221
|
+
capacity: {
|
|
222
|
+
baseline: { meanExecutors: baseAlloc?.executorsMean ?? null, peakExecutors: baseAlloc?.executorsPeak ?? null },
|
|
223
|
+
candidate: { meanExecutors: candAlloc?.executorsMean ?? null, peakExecutors: candAlloc?.executorsPeak ?? null },
|
|
224
|
+
},
|
|
225
|
+
overlapMs,
|
|
226
|
+
wallClockBridge: {
|
|
227
|
+
wallClock: factor(row('wallClock')?.baseline ?? null, row('wallClock')?.candidate ?? null),
|
|
228
|
+
taskTime,
|
|
229
|
+
meanExecutors,
|
|
230
|
+
taskShareOfAllocation: taskShare,
|
|
231
|
+
explainedWallClock,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildComparison, } from './run-comparison.js';
|
|
2
2
|
import { comparisonVerdict, } from './comparison-verdict.js';
|
|
3
3
|
import { redactComparison } from './redact.js';
|
|
4
|
+
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
|
|
@@ -8,10 +9,11 @@ import { redactComparison } from './redact.js';
|
|
|
8
9
|
// The comparison a surface reports: the CLI's `comparison` object and the shared part of MCP
|
|
9
10
|
// compare_runs. A field added here reaches every surface.
|
|
10
11
|
// `stagePairs` is the one large field (a row per paired stage), so the `summary` view leaves it out.
|
|
11
|
-
|
|
12
|
+
// `generator` is the build that wrote the output, present when the surface supplies it.
|
|
13
|
+
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
/** `full` carries every field (the CLI); `summary` leaves out `stagePairs` (MCP, which returns them
|
|
@@ -19,9 +21,14 @@ import { redactComparison } from './redact.js';
|
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
/** The reported projection of a comparison. Key order is the CLI's JSON order. */
|
|
22
|
-
export function comparisonOutput(
|
|
24
|
+
export function comparisonOutput(
|
|
25
|
+
comparison , view = 'full', generator ,
|
|
26
|
+
) {
|
|
23
27
|
return {
|
|
28
|
+
...(generator ? { generator } : {}),
|
|
24
29
|
verdict: comparisonVerdict(comparison),
|
|
30
|
+
baselineApp: comparison.baselineApp,
|
|
31
|
+
candidateApp: comparison.candidateApp,
|
|
25
32
|
confidence: comparison.confidence,
|
|
26
33
|
reason: comparison.reason,
|
|
27
34
|
matchedCoverage: comparison.matchedCoverage,
|
|
@@ -34,6 +41,7 @@ export function comparisonOutput(comparison , view
|
|
|
34
41
|
replanned: comparison.replanned,
|
|
35
42
|
bookkeepingStageIds: comparison.bookkeepingStageIds,
|
|
36
43
|
executionAlignment: comparison.executionAlignment,
|
|
44
|
+
comparability: comparison.comparability,
|
|
37
45
|
};
|
|
38
46
|
}
|
|
39
47
|
|
|
@@ -42,9 +50,9 @@ export function comparisonOutput(comparison , view
|
|
|
42
50
|
* and Markdown; `output` is the projection to serialize. */
|
|
43
51
|
export function buildComparisonOutput(
|
|
44
52
|
baseline , candidate ,
|
|
45
|
-
{ redact, normalizePath, view }
|
|
53
|
+
{ redact, normalizePath, ignoreConf, confRedactRegex, noiseBand, view, generator } = {},
|
|
46
54
|
) {
|
|
47
|
-
const built = buildComparison(baseline, candidate, { normalizePath });
|
|
55
|
+
const built = buildComparison(baseline, candidate, { normalizePath, ignoreConf, confRedactRegex, noiseBand });
|
|
48
56
|
const comparison = redact ? redactComparison(built) : built;
|
|
49
|
-
return { comparison, output: comparisonOutput(comparison, view) };
|
|
57
|
+
return { comparison, output: comparisonOutput(comparison, view, generator) };
|
|
50
58
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { formatDuration, typeTag } from './format-utils.js';
|
|
2
2
|
import { TAG_HELP } from './finding-tag-help.js';
|
|
3
|
-
import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js';
|
|
3
|
+
import { DEFAULT_NOISE_BAND, isEscalation, NEUTRAL_METRIC_KEYS, } from './run-comparison.js';
|
|
4
|
+
|
|
4
5
|
|
|
5
6
|
/** The slice of a comparison metric row the verdict reads. */
|
|
6
7
|
|
|
@@ -10,15 +11,27 @@ import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
/** The slice of
|
|
18
|
+
/** The slice of the comparability block the verdict reads. */
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/** The slice of an introduced or resolved finding row the verdict reads. */
|
|
16
22
|
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
|
|
21
27
|
|
|
28
|
+
/** The slice of a changed finding row the verdict reads. */
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
22
35
|
/** How many of a run's ended jobs failed, counted as `summarizeRunOutcome`
|
|
23
36
|
* counts them. */
|
|
24
37
|
|
|
@@ -29,7 +42,9 @@ import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js
|
|
|
29
42
|
|
|
30
43
|
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
/** `equivalent`: configuration, plan and output agree, so a time difference is a difference in
|
|
46
|
+
* capacity or scheduling. `mixed`: wall clock and cost moved opposite ways. */
|
|
47
|
+
|
|
33
48
|
|
|
34
49
|
|
|
35
50
|
|
|
@@ -40,17 +55,58 @@ import { NEUTRAL_METRIC_KEYS, } from './run-comparison.js
|
|
|
40
55
|
/** A change under this share of the baseline's value reads as "about the same", for
|
|
41
56
|
* run time and cost metrics alike: run-to-run noise on a shared cluster easily
|
|
42
57
|
* moves a job a percent or two. */
|
|
43
|
-
export const SAME_CHANGE_SHARE =
|
|
58
|
+
export const SAME_CHANGE_SHARE = DEFAULT_NOISE_BAND;
|
|
44
59
|
|
|
45
60
|
function measured(metric ) {
|
|
46
61
|
return metric.baseline != null && metric.candidate != null;
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
function movedPastNoise(metric ) {
|
|
65
|
+
if (metric.withinNoise !== undefined) return !metric.withinNoise;
|
|
50
66
|
if (metric.baseline === 0) return metric.candidate !== 0;
|
|
51
67
|
return Math.abs(metric.candidate - metric.baseline) / Math.abs(metric.baseline) >= SAME_CHANGE_SHARE;
|
|
52
68
|
}
|
|
53
69
|
|
|
70
|
+
const percent = (share ) => `${Math.round(Math.abs(share) * 100)}%`;
|
|
71
|
+
const times = (ratio ) => `×${ratio.toFixed(2)}`;
|
|
72
|
+
|
|
73
|
+
/** The facts a time difference rests on: the factors of the wall-clock bridge that moved past the
|
|
74
|
+
* noise band, each stated for both runs. */
|
|
75
|
+
function bridgeReasons(comparability ) {
|
|
76
|
+
const { wallClockBridge: bridge, noiseBand } = comparability;
|
|
77
|
+
const moved = (ratio ) => ratio != null && Math.abs(ratio - 1) >= noiseBand;
|
|
78
|
+
const reasons = [];
|
|
79
|
+
const { meanExecutors, taskTime, taskShareOfAllocation: share } = bridge;
|
|
80
|
+
if (moved(meanExecutors.ratio)) reasons.push(`executors averaged ${meanExecutors.baseline .toFixed(1)} in the baseline and ${meanExecutors.candidate .toFixed(1)} in the candidate`);
|
|
81
|
+
if (moved(taskTime.ratio)) reasons.push(`task time was ${formatDuration(taskTime.baseline )} in the baseline and ${formatDuration(taskTime.candidate )} in the candidate`);
|
|
82
|
+
if (moved(share.ratio)) reasons.push(`task time filled ${percent(share.baseline )} of allocated core time in the baseline and ${percent(share.candidate )} in the candidate`);
|
|
83
|
+
return reasons;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** "Wall clock ×1.49 = task time ×0.82 / (mean executors ×0.57 × task share of allocation ×0.96)",
|
|
87
|
+
* candidate over baseline; null when a factor is unmeasured. */
|
|
88
|
+
function bridgeSentence({ wallClockBridge: bridge } ) {
|
|
89
|
+
const { wallClock, taskTime, meanExecutors, taskShareOfAllocation: share } = bridge;
|
|
90
|
+
if (wallClock.ratio == null || taskTime.ratio == null || meanExecutors.ratio == null || share.ratio == null) return null;
|
|
91
|
+
return `Candidate over baseline: wall clock ${times(wallClock.ratio)} is task time ${times(taskTime.ratio)} divided by mean executors ${times(meanExecutors.ratio)} times task time's share of allocated core time ${times(share.ratio)}.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function configAndPlanAgree({ conf, plan } ) {
|
|
95
|
+
return conf.equal === true && plan.equal;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The cost metrics that moved past noise against the direction wall clock moved, as phrases such
|
|
99
|
+
* as "executor run-time is 18% lower". Empty when wall clock stayed within noise. */
|
|
100
|
+
function opposingCost(wall , metrics ) {
|
|
101
|
+
if (!movedPastNoise(wall)) return [];
|
|
102
|
+
const wallUp = wall.candidate > wall.baseline;
|
|
103
|
+
return metrics
|
|
104
|
+
.filter((m) => m.key === 'executorRunTime' || m.key === 'allocatedCoreHours')
|
|
105
|
+
.filter(measured)
|
|
106
|
+
.filter((m) => m.baseline > 0 && movedPastNoise(m) && (m.candidate > m.baseline) !== wallUp)
|
|
107
|
+
.map((m) => `${m.label.toLowerCase()} is ${percent((m.candidate - m.baseline) / m.baseline)} ${m.candidate < m.baseline ? 'lower' : 'higher'}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
54
110
|
/** Plain name for a finding type ("Memory and disk spill"), falling back to
|
|
55
111
|
* its tag when the tag has no help entry. */
|
|
56
112
|
function categoryName(type ) {
|
|
@@ -58,13 +114,11 @@ function categoryName(type ) {
|
|
|
58
114
|
return TAG_HELP[tag]?.expansion ?? tag;
|
|
59
115
|
}
|
|
60
116
|
|
|
61
|
-
/** Net count change per displayed category name.
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* read as both "introduced" and "resolved". Order follows first appearance,
|
|
67
|
-
* introduced before resolved. */
|
|
117
|
+
/** Net count change per displayed category name. Several rules share one name (every Plan Advisor
|
|
118
|
+
* type reads "Plan advisor", every stage-shape sub-rule "Stage shape"), so two rules under one
|
|
119
|
+
* name moving opposite ways net out rather than read as both "new" and "less frequent". Findings
|
|
120
|
+
* that only changed impact band are not in `introduced` or `resolved`, so they do not move a
|
|
121
|
+
* category. Order follows first appearance, introduced before resolved. */
|
|
68
122
|
function netByCategory(findings ) {
|
|
69
123
|
const net = new Map ();
|
|
70
124
|
for (const item of [...findings.introduced, ...findings.resolved]) {
|
|
@@ -105,18 +159,18 @@ function failureHeadline(base , cand )
|
|
|
105
159
|
}
|
|
106
160
|
|
|
107
161
|
/** One plain answer to "did the candidate get better or worse than the baseline", from the
|
|
108
|
-
* comparison's
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* inherently better or worse. */
|
|
162
|
+
* comparison's whole-run metrics and finding-category tallies: run time, then the cost metrics
|
|
163
|
+
* that moved past run-to-run noise, then the finding categories that appeared or went away. Failed
|
|
164
|
+
* jobs lead when either run had any; an incomplete log states run time as what each log covers
|
|
165
|
+
* with a neutral tone. Volume and count metrics are left out (NEUTRAL_METRIC_KEYS). With the
|
|
166
|
+
* comparability block, runs that agree in configuration, plan and output read `equivalent`, and a
|
|
167
|
+
* wall clock that moved against the cost metrics reads `mixed`. The rules are in
|
|
168
|
+
* docs-site/user-guide/run-comparison.md. */
|
|
116
169
|
export function summarizeComparison(
|
|
117
170
|
metrics ,
|
|
118
|
-
findings
|
|
171
|
+
findings ,
|
|
119
172
|
jobs ,
|
|
173
|
+
comparability ,
|
|
120
174
|
) {
|
|
121
175
|
const wall = metrics.find((metric) => metric.key === 'wallClock');
|
|
122
176
|
let title = 'Run time could not be compared between the baseline and the candidate';
|
|
@@ -132,7 +186,7 @@ export function summarizeComparison(
|
|
|
132
186
|
} else if (wall && wall.baseline != null && wall.baseline > 0 && wall.candidate != null) {
|
|
133
187
|
const change = wall.candidate - wall.baseline;
|
|
134
188
|
const share = change / wall.baseline;
|
|
135
|
-
if (
|
|
189
|
+
if (!movedPastNoise({ ...wall, baseline: wall.baseline, candidate: wall.candidate })) {
|
|
136
190
|
title = 'The candidate took about as long as the baseline';
|
|
137
191
|
tone = 'same';
|
|
138
192
|
} else {
|
|
@@ -142,12 +196,36 @@ export function summarizeComparison(
|
|
|
142
196
|
}
|
|
143
197
|
}
|
|
144
198
|
|
|
199
|
+
const sentences = [];
|
|
200
|
+
const failure = jobs ? failureHeadline(jobs.baseline, jobs.candidate) : null;
|
|
201
|
+
if (comparability && !failure && incompleteRuns.length === 0 && wall && measured(wall) && wall.baseline > 0) {
|
|
202
|
+
const change = wall.candidate - wall.baseline;
|
|
203
|
+
const drift = movedPastNoise(wall)
|
|
204
|
+
? `Time differs by ${formatDuration(Math.abs(change))} (${percent(change / wall.baseline)})`
|
|
205
|
+
: 'The candidate took about as long as the baseline';
|
|
206
|
+
if (configAndPlanAgree(comparability) && comparability.volume.equal !== false) {
|
|
207
|
+
const outputAgrees = comparability.volume.equal === true;
|
|
208
|
+
const gap = movedPastNoise(wall);
|
|
209
|
+
const reasons = gap ? bridgeReasons(comparability) : [];
|
|
210
|
+
const lead = outputAgrees ? 'Same configuration, plan and output' : 'Same configuration and plan. Output was not compared';
|
|
211
|
+
const explanation = reasons.length > 0 ? `: ${reasons.join(', ')}` : gap ? ', and no measured factor explains it' : '';
|
|
212
|
+
title = `${lead}. ${drift}${explanation}.`;
|
|
213
|
+
if (outputAgrees && (!gap || reasons.length > 0)) tone = 'equivalent';
|
|
214
|
+
const bridge = gap ? bridgeSentence(comparability) : null;
|
|
215
|
+
if (bridge) sentences.push(bridge);
|
|
216
|
+
} else {
|
|
217
|
+
const opposed = opposingCost(wall, metrics);
|
|
218
|
+
if (opposed.length > 0) {
|
|
219
|
+
title = `The candidate finished ${formatDuration(Math.abs(change))} ${change < 0 ? 'faster' : 'slower'} than the baseline (${percent(change / wall.baseline)}), while ${opposed.join(' and ')}`;
|
|
220
|
+
tone = 'mixed';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
145
225
|
const cost = metrics.filter((metric) => metric.key !== 'wallClock' && !NEUTRAL_METRIC_KEYS.has(metric.key)).filter(measured);
|
|
146
226
|
const moved = cost.filter(movedPastNoise);
|
|
147
227
|
const worse = moved.filter((metric) => metric.direction === 'regression').map((metric) => metric.label);
|
|
148
228
|
const better = moved.filter((metric) => metric.direction === 'improvement').map((metric) => metric.label);
|
|
149
|
-
const sentences = [];
|
|
150
|
-
const failure = jobs ? failureHeadline(jobs.baseline, jobs.candidate) : null;
|
|
151
229
|
if (failure) {
|
|
152
230
|
sentences.push(`${title}.`);
|
|
153
231
|
title = failure.title;
|
|
@@ -167,11 +245,13 @@ export function summarizeComparison(
|
|
|
167
245
|
const resolved = namesWhere(net, (change) => change < 0);
|
|
168
246
|
if (introduced.length > 0) sentences.push(`New or more frequent in the candidate: ${introduced.join(', ')}.`);
|
|
169
247
|
if (resolved.length > 0) sentences.push(`Less frequent in the candidate: ${resolved.join(', ')}.`);
|
|
248
|
+
const escalated = [...new Set((findings.changed ?? []).filter(isEscalation).map((c) => categoryName(c.type)))];
|
|
249
|
+
if (escalated.length > 0) sentences.push(`More severe in the candidate: ${escalated.join(', ')}.`);
|
|
170
250
|
return { title, tone, sentences };
|
|
171
251
|
}
|
|
172
252
|
|
|
173
253
|
/** The comparison verdict for a core comparison result: the one the dashboard's comparison page
|
|
174
254
|
* leads with, and the one the CLI's --baseline report and MCP compare_runs carry. */
|
|
175
|
-
export function comparisonVerdict(result
|
|
176
|
-
return summarizeComparison(result.metrics, result.findings, result.jobOutcomes);
|
|
255
|
+
export function comparisonVerdict(result ) {
|
|
256
|
+
return summarizeComparison(result.metrics, result.findings, result.jobOutcomes, result.comparability);
|
|
177
257
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
06b1ffacd20e0b5e1d7b6a19ed645fb088c6049114893b77cf4662261d610924
|
|
@@ -105,7 +105,7 @@ export function buildLocalityChart(
|
|
|
105
105
|
const point = { t: Math.round((t - start) / 1000) };
|
|
106
106
|
// The series averages each bucket over its full width, so a last bucket
|
|
107
107
|
// that runs past the series' own end (or a run shorter than one bucket)
|
|
108
|
-
// reads diluted: a 10s stage in a 60s bucket
|
|
108
|
+
// reads diluted: a 10s stage in a 60s bucket shows well under its busy
|
|
109
109
|
// cores. Rescale to the part of the bucket the series actually covers.
|
|
110
110
|
const coveredMs = Math.min(bucketWidthMs, seriesEnd - t);
|
|
111
111
|
const scale = bucketWidthMs / coveredMs;
|
|
@@ -9,6 +9,15 @@ import { isCut, } from './write-target-args.js';
|
|
|
9
9
|
// scans and `DeltaLogFileIndex [<path>/_delta_log/<file>, ...]`.
|
|
10
10
|
const DELTA_LOG_PATH = /((?:[A-Za-z][A-Za-z0-9+.-]*:\/{1,3}|\/)[^\s,[\]()#]+?)\/_delta_log(?=[/[\],\s)]|$)/g;
|
|
11
11
|
|
|
12
|
+
// Hadoop prints a local path as `file:/p` and a URI as `file:///p`: one table, two spellings.
|
|
13
|
+
// Both read as `file:///p` so a table is not counted twice. A `file://host/p` is left alone.
|
|
14
|
+
const LOCAL_FILE_PREFIX = /^file:(?:\/|\/\/\/)(?=[^/])/;
|
|
15
|
+
|
|
16
|
+
/** The one spelling of a table path: the two local-file forms agree, other paths pass through. */
|
|
17
|
+
export function normalizeTablePath(path ) {
|
|
18
|
+
return path.replace(LOCAL_FILE_PREFIX, 'file:///').replace(/\/+$/, '');
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
|
|
13
22
|
|
|
14
23
|
// Every distinct table path named in the plans; `unreliable` when a plan is missing or a path cut.
|
|
@@ -26,7 +35,7 @@ function deltaLogPaths(executions ) {
|
|
|
26
35
|
if (matches.length < mentions) unreliable = true;
|
|
27
36
|
for (const m of matches) {
|
|
28
37
|
if (isCut(m[1])) unreliable = true;
|
|
29
|
-
else paths.add(m[1]
|
|
38
|
+
else paths.add(normalizeTablePath(m[1]));
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
}, { dedupe: true });
|