sparkforensics-server 0.5.1 → 0.6.0
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/package.json +1 -1
- package/public/assets/AutoscalingChurn-CRA2Mb7F.js +1 -0
- package/public/assets/CacheUtilization-Bh9OY_V9.js +1 -0
- package/public/assets/CachingOpportunity-oCiQwmTJ.js +1 -0
- package/public/assets/{ColdStart-B38OiAOp.js → ColdStart-BJrtjfmn.js} +1 -1
- package/public/assets/ConfigAudit-DMvKxySP.js +1 -0
- package/public/assets/CoreUsageArea-REC2zsKw.js +1 -0
- package/public/assets/Dashboard-BHZ76JCu.js +2 -0
- package/public/assets/DisclosureContext-OO7X1HAx.js +1 -0
- package/public/assets/{DuplicatePlanSubtree-BqS2A2j4.js → DuplicatePlanSubtree-BgG-L-cK.js} +1 -1
- package/public/assets/ExecutorUtilization-D2KhSVbY.js +1 -0
- package/public/assets/{GcPressure-CT1yzR_s.js → GcPressure-D1BTE8qh.js} +1 -1
- package/public/assets/{IncompleteRun-C7Qkjtjn.js → IncompleteRun-DD40xUpy.js} +1 -1
- package/public/assets/{JobFailures-WHl3ELfh.js → JobFailures-YwNzCF19.js} +1 -1
- package/public/assets/MemoryUtilization-9b8ee_3_.js +1 -0
- package/public/assets/{OverBroadcast-6oSSAQ_R.js → OverBroadcast-CfctS8a8.js} +1 -1
- package/public/assets/PartitionSizing-VG8kBgG5.js +1 -0
- package/public/assets/{PlanExplorer-DcxmiQWL.js → PlanExplorer-DmubTAOP.js} +1 -1
- package/public/assets/PlanGraphRoute-DctPZjS0.js +1 -0
- package/public/assets/RetryWaste-CnpM8bB7.js +1 -0
- package/public/assets/{ShuffleIO-CRhrYNE0.js → ShuffleIO-D13vGmas.js} +1 -1
- package/public/assets/Skew-DlagstYP.js +1 -0
- package/public/assets/SlowHost-cSkLRZ_e.js +1 -0
- package/public/assets/{SmallFiles-C082c2rd.js → SmallFiles-B-kERMcq.js} +1 -1
- package/public/assets/{SpeculationWaste-BS2vpadr.js → SpeculationWaste-DaysAzXO.js} +1 -1
- package/public/assets/Spill-H44Gz4US.js +1 -0
- package/public/assets/{StageFailed-D3ac9Sou.js → StageFailed-DCfii9h_.js} +1 -1
- package/public/assets/StageFindingGroup-BXJ0Mj5n.js +1 -0
- package/public/assets/{StageHeader-pI1nTwVf.js → StageHeader--oN2J-oD.js} +1 -1
- package/public/assets/StageShape-IZ80CZiJ.js +1 -0
- package/public/assets/{StageSlowness-BaNyfNvL.js → StageSlowness--BRIaBc1.js} +1 -1
- package/public/assets/Straggler-Ctq0Df_o.js +1 -0
- package/public/assets/TaskFailures-Bks_gqhL.js +1 -0
- package/public/assets/{TinyTask-8_5lpq_2.js → TinyTask-BssfzGFB.js} +1 -1
- package/public/assets/{UnderBroadcast-DJ0XSwth.js → UnderBroadcast-DsScrxc-.js} +1 -1
- package/public/assets/{finding-anchor-Cys8Z6Yn.js → finding-anchor-BYW1oz5R.js} +1 -1
- package/public/assets/{impact-sort-BTQndbof.js → impact-sort-xzy0g-Vh.js} +1 -1
- package/public/assets/index-DhpRGMZc.css +2 -0
- package/public/assets/index-DwqkFUFk.js +14 -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/{parser-worker-DmeCnLWU.js → parser-worker-CA5RxqKc.js} +8 -8
- package/public/assets/react-vendor-DfpuPQZf.js +9 -0
- package/public/assets/{select-DIYN6D3y.js → select-C8edjYTq.js} +1 -1
- package/public/assets/{usePagedRows-BxvdFnPk.js → usePagedRows-CsuUFeYx.js} +1 -1
- package/public/assets/useRecentFiles-BvkKo3JN.js +11 -0
- package/public/assets/{useSortMode-B8SRfAoG.js → useSortMode-joX71BZN.js} +1 -1
- package/public/docs/404.html +3 -7
- package/public/docs/assets/{app.D2GqAkhy.js → app.CnWL6bKV.js} +1 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.tE5pz54z.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.BS4cSmtW.js → VPLocalSearchBox.C0IBKeRQ.js} +1 -1
- package/public/docs/assets/chunks/{theme.BuxVpb0H.js → theme.BH-2HuRf.js} +2 -2
- package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.8bH1HnZp.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.wj6Et6UD.js → contributor-guide_architecture_detector-contract.md.N4hhenrA.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DfvbqEUu.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CZv_ZGOp.lean.js → contributor-guide_architecture_impact-estimation.md.DfvbqEUu.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.BUucRc0A.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.js} +1 -1
- 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.Cwm1jHmn.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_state-and-history_run-comparison-internals.md.uuAKpY0Y.lean.js → contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.js} +1 -1
- package/public/docs/assets/{contributor-guide_development-setup.md.D9bu4j_c.js → contributor-guide_development-setup.md.DQdzera7.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.CyK9Ss73.css +1 -0
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.js +24 -0
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.lean.js +1 -0
- package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.CPowlb2q.js → user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js} +3 -2
- package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.CPowlb2q.lean.js → user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.Dq5Azk8G.js → user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js} +2 -2
- package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.Dq5Azk8G.lean.js → user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.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.BUALkohi.js +1 -0
- package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +1 -0
- package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.DSXGu2tA.js +1 -0
- package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.DSXGu2tA.lean.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.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 +4 -8
- package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -10
- package/public/docs/contributor-guide/architecture/board-widgets.html +4 -8
- package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +4 -8
- 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 +4 -8
- package/public/docs/contributor-guide/architecture/drill-down.html +4 -8
- 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 +4 -8
- package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -10
- package/public/docs/contributor-guide/architecture/index.html +4 -8
- package/public/docs/contributor-guide/architecture/overview.html +4 -8
- package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +4 -8
- 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 +4 -8
- package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +4 -8
- package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +4 -8
- package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +4 -8
- package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +4 -8
- package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +4 -8
- package/public/docs/contributor-guide/architecture/widget-rendering.html +4 -8
- 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 +4 -8
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +4 -8
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -10
- package/public/docs/contributor-guide/architecture/worker-protocol.html +4 -8
- package/public/docs/contributor-guide/contributing.html +4 -8
- package/public/docs/contributor-guide/development-setup.html +6 -10
- package/public/docs/contributor-guide/testing.html +4 -8
- package/public/docs/favicon.svg +2 -2
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +4 -8
- package/public/docs/tuning-reference/anti-patterns.html +4 -8
- package/public/docs/tuning-reference/aqe.html +4 -8
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +4 -8
- package/public/docs/tuning-reference/bottleneck-cold-start.html +4 -8
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +4 -8
- package/public/docs/tuning-reference/bottleneck-failures.html +4 -8
- package/public/docs/tuning-reference/bottleneck-gc.html +4 -8
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +4 -8
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +4 -8
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +4 -8
- package/public/docs/tuning-reference/bottleneck-shuffle.html +4 -8
- package/public/docs/tuning-reference/bottleneck-skew.html +4 -8
- package/public/docs/tuning-reference/bottleneck-slow-host.html +4 -8
- package/public/docs/tuning-reference/bottleneck-small-files.html +4 -8
- package/public/docs/tuning-reference/bottleneck-spill.html +4 -8
- package/public/docs/tuning-reference/bottleneck-straggler.html +4 -8
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +4 -8
- package/public/docs/tuning-reference/bottleneck-utilization.html +4 -8
- package/public/docs/tuning-reference/caching.html +4 -8
- package/public/docs/tuning-reference/cluster-config.html +4 -8
- package/public/docs/tuning-reference/config.html +4 -8
- package/public/docs/tuning-reference/data-formats.html +4 -8
- package/public/docs/tuning-reference/index.html +5 -9
- package/public/docs/tuning-reference/intro.html +4 -8
- package/public/docs/tuning-reference/joins.html +4 -8
- package/public/docs/tuning-reference/memory-model.html +4 -8
- package/public/docs/tuning-reference/metrics.html +4 -8
- package/public/docs/tuning-reference/partitioning.html +4 -8
- package/public/docs/tuning-reference/pyspark.html +4 -8
- package/public/docs/tuning-reference/shuffle.html +4 -8
- package/public/docs/tuning-reference/spark-architecture.html +4 -8
- package/public/docs/tuning-reference/table-formats.html +4 -8
- package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +4 -8
- package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +4 -8
- package/public/docs/user-guide/alternative-log-retrieval.html +4 -8
- package/public/docs/user-guide/getting-started/ci-and-automation.html +11 -13
- package/public/docs/user-guide/getting-started/reading-the-dashboard.html +4 -8
- package/public/docs/user-guide/getting-started/regression-metric-keys.html +4 -8
- package/public/docs/user-guide/getting-started/tuning-thresholds.html +4 -8
- package/public/docs/user-guide/getting-started.html +4 -8
- package/public/docs/user-guide/mcp-tools/compare-runs.html +8 -11
- package/public/docs/user-guide/mcp-tools/diagnose-run.html +4 -8
- package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -10
- package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +4 -8
- package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +4 -8
- package/public/docs/user-guide/mcp-tools/get-reference-doc.html +4 -8
- package/public/docs/user-guide/mcp-tools/get-run-summary.html +6 -10
- package/public/docs/user-guide/mcp-tools/list-runs.html +4 -8
- package/public/docs/user-guide/mcp-tools.html +4 -8
- package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +6 -10
- 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 +4 -7
- package/vendor-core/allocation.js +105 -20
- package/vendor-core/api-merge-writes.js +1 -0
- package/vendor-core/cli/budgets.js +62 -2
- package/vendor-core/cli/regression-budgets.js +35 -0
- package/vendor-core/comparison-output.js +2 -1
- package/vendor-core/core-source-hash.txt +1 -1
- package/vendor-core/delta-log-paths.js +10 -1
- package/vendor-core/detectors.js +290 -92
- package/vendor-core/docs-content/detection/chrn.md +3 -1
- package/vendor-core/docs-content/detection/cold.md +2 -1
- package/vendor-core/docs-content/detection/mem.md +1 -1
- package/vendor-core/docs-content/detection/plan.md +9 -2
- package/vendor-core/docs-content/detection/shfl.md +8 -2
- package/vendor-core/docs-content/detection/skew.md +12 -5
- package/vendor-core/docs-content/detection/slow.md +6 -2
- package/vendor-core/docs-content/detection/spill.md +4 -1
- package/vendor-core/docs-content/detection/strag.md +5 -3
- package/vendor-core/docs-content/detection/tiny.md +3 -2
- package/vendor-core/docs-content/detection/util.md +6 -2
- package/vendor-core/efficiency-model.js +22 -14
- package/vendor-core/evidence-report.js +11 -11
- package/vendor-core/finding-presentation.js +52 -19
- package/vendor-core/finding-types.js +71 -16
- package/vendor-core/impact-model.js +2 -2
- package/vendor-core/mcp-server-factory.js +6 -2
- package/vendor-core/mcp-tools.js +10 -1
- package/vendor-core/median.js +8 -0
- package/vendor-core/remediation.js +10 -5
- package/vendor-core/run-comparison.js +2 -0
- package/vendor-core/run-output.js +14 -3
- package/vendor-core/sequence-alignment.js +83 -0
- package/vendor-core/stage-alignment.js +387 -63
- package/vendor-core/stage-quantiles.js +3 -1
- package/vendor-core/stage-structure.js +83 -0
- package/vendor-core/types.js +12 -2
- package/vendor-core/wall-clock.js +8 -0
- package/vendor-core/wasted-core-hours.js +12 -11
- package/vendor-core/write-targets.js +32 -0
- package/public/assets/AutoscalingChurn-5hDGIXi4.js +0 -1
- package/public/assets/CacheUtilization-CT2SCnJJ.js +0 -1
- package/public/assets/CachingOpportunity-D_O_WkbH.js +0 -1
- package/public/assets/ConfigAudit-BoplryCH.js +0 -1
- package/public/assets/CoreUsageArea-csgwNxkl.js +0 -1
- package/public/assets/Dashboard-BgKodJl2.js +0 -2
- package/public/assets/DisclosureContext-B0siMVdg.js +0 -1
- package/public/assets/ExecutorUtilization-Dp52QGWo.js +0 -1
- package/public/assets/MemoryUtilization-D8IUDHJI.js +0 -1
- package/public/assets/PartitionSizing-BwdFL-mw.js +0 -1
- package/public/assets/PlanGraphRoute-pqnHTt4i.js +0 -1
- package/public/assets/RetryWaste-BTbeHkJc.js +0 -1
- package/public/assets/Skew-DSNXRG9V.js +0 -1
- package/public/assets/SlowHost-B7Lwu2EU.js +0 -1
- package/public/assets/Spill-B4DVKHXx.js +0 -1
- package/public/assets/StageFindingGroup-D9naK109.js +0 -1
- package/public/assets/StageShape-eO5le8iG.js +0 -1
- package/public/assets/Straggler-D5Fnok_y.js +0 -1
- package/public/assets/TaskFailures-BWnC-K_G.js +0 -1
- package/public/assets/index-BcqTLKFp.css +0 -2
- package/public/assets/index-DCeWge12.js +0 -13
- package/public/assets/react-vendor-DEdfrRTJ.js +0 -9
- package/public/assets/useRecentFiles-C3Ov47U7.js +0 -11
- package/public/docs/assets/chunks/@localSearchIndexroot.D4OdkQjy.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_detector-contract_threshold-tables.md.fkPBXsU2.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_state-and-history_run-comparison-internals.md.uuAKpY0Y.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.Dk08VS7n.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.DpnfKz3x.js +0 -22
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.DpnfKz3x.lean.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.B6aNtDYF.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.B6aNtDYF.lean.js +0 -1
- package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.XBzQ1oEU.js +0 -1
- package/public/docs/assets/user-guide_run-comparison_how-stages-are-matched.md.XBzQ1oEU.lean.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.Dqt2H_C1.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.Dqt2H_C1.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.CQgn1t_M.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_board-widgets_visual-system-and-templating.md.DYgUpIJB.lean.js → contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.wj6Et6UD.lean.js → contributor-guide_architecture_detector-contract.md.N4hhenrA.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.fkPBXsU2.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.Dk08VS7n.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.DVGZOWvW.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_development-setup.md.D9bu4j_c.lean.js → contributor-guide_development-setup.md.DQdzera7.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
package/public/favicon.svg
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
|
2
|
-
<rect width="32" height="32" rx="
|
|
3
|
-
<path d="M17.5 5 8 18h6.5l-1 9L24 14h-6.5l1-9z" fill="#
|
|
2
|
+
<rect width="32" height="32" rx="6" fill="#4f46e5"/>
|
|
3
|
+
<path d="M17.5 5 8 18h6.5l-1 9L24 14h-6.5l1-9z" fill="#eceafd"/>
|
|
4
4
|
</svg>
|
package/public/index.html
CHANGED
|
@@ -10,9 +10,6 @@
|
|
|
10
10
|
<meta property="og:url" content="https://shuffle-works.github.io/sparkforensics/">
|
|
11
11
|
<meta property="og:title" content="SparkForensics">
|
|
12
12
|
<meta property="og:description" content="Zero-backend browser dashboard that parses Apache Spark event logs to diagnose performance bottlenecks: skew, spill, GC pressure, stragglers, cache utilization, and more.">
|
|
13
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
14
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
15
|
-
<link href="https://fonts.googleapis.com/css2?family=Recursive:wght,CASL@400..700,0..1&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
16
13
|
<script>
|
|
17
14
|
try {
|
|
18
15
|
var stored = localStorage.getItem('shuffle-works-theme');
|
|
@@ -23,14 +20,14 @@
|
|
|
23
20
|
document.documentElement.setAttribute('data-theme', 'light');
|
|
24
21
|
} catch (_) {}
|
|
25
22
|
</script>
|
|
26
|
-
<script type="module" crossorigin src="./assets/index-
|
|
23
|
+
<script type="module" crossorigin src="./assets/index-DwqkFUFk.js"></script>
|
|
27
24
|
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-hePW80VL.js">
|
|
28
25
|
<link rel="modulepreload" crossorigin href="./assets/charts-vendor-0VMaLVLN.js">
|
|
29
26
|
<link rel="modulepreload" crossorigin href="./assets/plan-graph-vendor-BYl-AzN5.js">
|
|
30
|
-
<link rel="modulepreload" crossorigin href="./assets/react-vendor-
|
|
31
|
-
<link rel="modulepreload" crossorigin href="./assets/DisclosureContext-
|
|
27
|
+
<link rel="modulepreload" crossorigin href="./assets/react-vendor-DfpuPQZf.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="./assets/DisclosureContext-OO7X1HAx.js">
|
|
32
29
|
<link rel="stylesheet" crossorigin href="./assets/plan-graph-vendor-DLioOiRN.css">
|
|
33
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
30
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DhpRGMZc.css">
|
|
34
31
|
</head>
|
|
35
32
|
<body>
|
|
36
33
|
<div id="root"></div>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { computePeakConcurrentExecutorCount } from './core-count.js';
|
|
1
2
|
import { parseSparkMemoryMB } from './spark-memory.js';
|
|
2
|
-
|
|
3
|
+
|
|
3
4
|
|
|
4
5
|
const MS_PER_HOUR = 3_600_000;
|
|
6
|
+
const MS_PER_SECOND = 1000;
|
|
5
7
|
const MIB_PER_GIB = 1024;
|
|
6
8
|
// Spark's documented floor and factor for the default executor memory overhead
|
|
7
9
|
// (spark.executor.memoryOverhead = max(factor * executor memory, 384 MiB)).
|
|
@@ -14,12 +16,29 @@ const DEFAULT_OVERHEAD_FACTOR = 0.1;
|
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
17
31
|
|
|
18
32
|
|
|
33
|
+
// The fields the allocation reads from the parsed model's executor events, so a detector's
|
|
34
|
+
// narrower event shapes qualify too.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
19
38
|
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
|
|
22
40
|
|
|
41
|
+
|
|
23
42
|
|
|
24
43
|
|
|
25
44
|
/** The latest timestamp the log records: the application start and end and every stage and
|
|
@@ -43,7 +62,7 @@ const DEFAULT_EXECUTOR_MEMORY_MIB = 1024;
|
|
|
43
62
|
// plus overhead, plus off-heap and PySpark worker memory when configured. Null when the log
|
|
44
63
|
// records no Spark properties (nothing to tell a default from a missing config) or a memory key it
|
|
45
64
|
// does record cannot be read.
|
|
46
|
-
function executorMemoryMiB(app
|
|
65
|
+
function executorMemoryMiB(app ) {
|
|
47
66
|
const config = app?.config;
|
|
48
67
|
if (config == null) return null;
|
|
49
68
|
// undefined: key absent; null: present but unreadable.
|
|
@@ -63,6 +82,11 @@ function executorMemoryMiB(app ) {
|
|
|
63
82
|
return heap + overhead + offHeap + pyspark;
|
|
64
83
|
}
|
|
65
84
|
|
|
85
|
+
function loggedDynamicAllocation(app ) {
|
|
86
|
+
const raw = app?.config?.['spark.dynamicAllocation.enabled'];
|
|
87
|
+
return raw == null ? null : raw.trim().toLowerCase() === 'true' ? 'on' : 'off';
|
|
88
|
+
}
|
|
89
|
+
|
|
66
90
|
/** Allocated core-hours and memory GiB-hours from the executor lifecycle: each executor counts
|
|
67
91
|
* from its ExecutorAdded timestamp to its first later ExecutorRemoved timestamp. One with no
|
|
68
92
|
* removal closes at the application end when the log has one, else at the last timestamp the log
|
|
@@ -72,35 +96,96 @@ function executorMemoryMiB(app ) {
|
|
|
72
96
|
* else the larger of 384 MiB and spark.executor.memoryOverheadFactor, default 0.1, times the
|
|
73
97
|
* memory), plus spark.memory.offHeap.size when spark.memory.offHeap.enabled is true, plus
|
|
74
98
|
* spark.executor.pyspark.memory.
|
|
99
|
+
* The executor counts and seconds come from the same alive intervals.
|
|
75
100
|
* Null, never 0, for a figure whose inputs the log lacks. */
|
|
76
101
|
export function computeAllocation(input ) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (e.kind !== 'removed') continue;
|
|
82
|
-
const times = removedAt.get(e.executorId);
|
|
83
|
-
if (times) times.push(e.timestamp); else removedAt.set(e.executorId, [e.timestamp]);
|
|
102
|
+
const dynamicAllocation = loggedDynamicAllocation(input.app);
|
|
103
|
+
const alive = aliveIntervals(input);
|
|
104
|
+
if (alive.length === 0) {
|
|
105
|
+
return { coreHours: null, memoryGbHours: null, dynamicAllocation, executorsPeak: null, executorsMean: null, executorCores: null, executorSeconds: null };
|
|
84
106
|
}
|
|
85
107
|
const closeAt = input.app?.endTime ?? lastObservedTimestamp(input);
|
|
86
|
-
const configuredCores = Number.parseInt(input.app?.config?.['spark.executor.cores'] ?? '', 10);
|
|
87
108
|
const memoryMiB = executorMemoryMiB(input.app);
|
|
88
109
|
|
|
89
110
|
let coreMs = 0;
|
|
90
111
|
let memoryMiBMs = 0;
|
|
112
|
+
let aliveMsTotal = 0;
|
|
91
113
|
let coresKnown = true;
|
|
92
|
-
const
|
|
93
|
-
for (const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const aliveMs = Math.max(0, (removal ?? closeAt ?? e.timestamp) - e.timestamp);
|
|
98
|
-
const cores = e.totalCores > 0 ? e.totalCores : Number.isFinite(configuredCores) ? configuredCores : null;
|
|
99
|
-
if (cores == null) coresKnown = false; else coreMs += cores * aliveMs;
|
|
114
|
+
const coreCounts = new Set ();
|
|
115
|
+
for (const { start, end, cores } of alive) {
|
|
116
|
+
const aliveMs = end - start;
|
|
117
|
+
aliveMsTotal += aliveMs;
|
|
118
|
+
if (cores == null) coresKnown = false; else { coreMs += cores * aliveMs; coreCounts.add(cores); }
|
|
100
119
|
memoryMiBMs += (memoryMiB ?? 0) * aliveMs;
|
|
101
120
|
}
|
|
121
|
+
const startTime = input.app?.startTime;
|
|
122
|
+
const windowMs = closeAt != null && startTime != null ? closeAt - startTime : 0;
|
|
123
|
+
const firstRemovals = alive.flatMap((a) => (a.removedAt == null ? [] : [{ executorId: a.executorId, timestamp: a.removedAt }]));
|
|
102
124
|
return {
|
|
103
125
|
coreHours: coresKnown ? coreMs / MS_PER_HOUR : null,
|
|
104
126
|
memoryGbHours: memoryMiB != null ? memoryMiBMs / MIB_PER_GIB / MS_PER_HOUR : null,
|
|
127
|
+
dynamicAllocation,
|
|
128
|
+
executorsPeak: computePeakConcurrentExecutorCount(alive.map((a) => ({ executorId: a.executorId, timestamp: a.start })), firstRemovals),
|
|
129
|
+
executorsMean: windowMs > 0 ? aliveMsTotal / windowMs : null,
|
|
130
|
+
executorCores: coresKnown && coreCounts.size === 1 ? [...coreCounts][0] : null,
|
|
131
|
+
executorSeconds: aliveMsTotal / MS_PER_SECOND,
|
|
105
132
|
};
|
|
106
133
|
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
// Each executor's alive interval: from its ExecutorAdded timestamp to its first later
|
|
145
|
+
// ExecutorRemoved timestamp, else the application end, else the last timestamp the log records.
|
|
146
|
+
// The one definition computeAllocation and allocatedCoreMsWithin share.
|
|
147
|
+
function aliveIntervals(input ) {
|
|
148
|
+
const removedAt = new Map ();
|
|
149
|
+
for (const e of input.executors.removed) {
|
|
150
|
+
const times = removedAt.get(e.executorId);
|
|
151
|
+
if (times) times.push(e.timestamp); else removedAt.set(e.executorId, [e.timestamp]);
|
|
152
|
+
}
|
|
153
|
+
const closeAt = input.app?.endTime ?? lastObservedTimestamp(input);
|
|
154
|
+
const configuredCores = Number.parseInt(input.app?.config?.['spark.executor.cores'] ?? '', 10);
|
|
155
|
+
const out = [];
|
|
156
|
+
const seen = new Set ();
|
|
157
|
+
for (const e of input.executors.added) {
|
|
158
|
+
if (seen.has(e.executorId)) continue; // a replayed ExecutorAdded is the same executor
|
|
159
|
+
seen.add(e.executorId);
|
|
160
|
+
const removal = (removedAt.get(e.executorId) ?? []).filter((t) => t >= e.timestamp).sort((a, b) => a - b)[0] ?? null;
|
|
161
|
+
out.push({
|
|
162
|
+
executorId: e.executorId,
|
|
163
|
+
start: e.timestamp,
|
|
164
|
+
end: Math.max(e.timestamp, removal ?? closeAt ?? e.timestamp),
|
|
165
|
+
removedAt: removal,
|
|
166
|
+
cores: e.totalCores != null && e.totalCores > 0 ? e.totalCores : Number.isFinite(configuredCores) ? configuredCores : null,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Allocated core-milliseconds: the figure behind `coreHours`, the capacity idle-core figures are
|
|
173
|
+
* measured against. Null when the log has no executor events or an executor's cores are unknown. */
|
|
174
|
+
export function allocatedCoreMs(input ) {
|
|
175
|
+
const { coreHours } = computeAllocation(input);
|
|
176
|
+
return coreHours != null && coreHours > 0 ? coreHours * MS_PER_HOUR : null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** The part of the allocated core-milliseconds held inside `windows` (disjoint, sorted
|
|
180
|
+
* intervals, such as the union of the stages' windows), counted from the same alive intervals as
|
|
181
|
+
* `allocatedCoreMs`. Null when `allocatedCoreMs` is. */
|
|
182
|
+
export function allocatedCoreMsWithin(input , windows ) {
|
|
183
|
+
if (allocatedCoreMs(input) == null) return null;
|
|
184
|
+
let coreMs = 0;
|
|
185
|
+
for (const { start, end, cores } of aliveIntervals(input)) {
|
|
186
|
+
for (const [from, to] of windows) {
|
|
187
|
+
coreMs += (cores ?? 0) * Math.max(0, Math.min(end, to) - Math.max(start, from));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return coreMs;
|
|
191
|
+
}
|
|
@@ -3,6 +3,7 @@ import { computeSkewRatio, ENTRY_BY_TYPE, } from '../det
|
|
|
3
3
|
import { effectiveThresholds } from '../threshold-overrides.js';
|
|
4
4
|
import { IMPACT_BAND_ORDER } from '../format-utils.js';
|
|
5
5
|
|
|
6
|
+
import { PAIR_DELTA_METRICS, } from '../stage-alignment.js';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
|
|
@@ -14,13 +15,23 @@ const IMPACT_BANDS = Object.keys(IMPACT_BAND_ORDER) ;
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
|
|
24
|
+
|
|
25
|
+
export const STAGE_QUALITIES = ['exact', 'structural', 'aligned'];
|
|
26
|
+
export const DEFAULT_STAGE_BUDGET_QUALITIES = ['exact', 'structural'];
|
|
27
|
+
/** Metrics with a regression direction (higher is worse). Input and output volume are workload, not performance. */
|
|
28
|
+
export const NEUTRAL_PAIR_METRICS = new Set(['inputBytes', 'outputBytes']);
|
|
18
29
|
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
|
|
21
32
|
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
|
|
24
35
|
|
|
25
36
|
|
|
26
37
|
|
|
@@ -146,6 +157,50 @@ function checkRegression(comparison , maxRegressionPct
|
|
|
146
157
|
: { name: 'max-regression', metric: regressionMetric, status: 'pass', detail: `Metric "${regressionMetric}" ${pctLabel}, within budget ${maxRegressionPct}%.` };
|
|
147
158
|
}
|
|
148
159
|
|
|
160
|
+
function checkStageRegression(
|
|
161
|
+
comparison , { metric, maxPct } , qualities ,
|
|
162
|
+
) {
|
|
163
|
+
const name = 'max-stage-regression' ;
|
|
164
|
+
if (!(PAIR_DELTA_METRICS ).includes(metric)) {
|
|
165
|
+
return { name, metric, status: 'inconclusive', detail: `Metric "${metric}" is not a paired-stage metric (expected one of: ${PAIR_DELTA_METRICS.join(', ')}).` };
|
|
166
|
+
}
|
|
167
|
+
if (NEUTRAL_PAIR_METRICS.has(metric)) {
|
|
168
|
+
return { name, metric, status: 'inconclusive', detail: `Metric "${metric}" measures workload volume, not performance: it has no regression direction to check.` };
|
|
169
|
+
}
|
|
170
|
+
const eligible = comparison.stagePairs.filter((p) => qualities.includes(p.quality));
|
|
171
|
+
if (eligible.length === 0) {
|
|
172
|
+
return { name, metric, status: 'inconclusive', detail: comparison.stagePairs.length === 0
|
|
173
|
+
? 'No stages were paired between the two runs.'
|
|
174
|
+
: `None of the ${comparison.stagePairs.length} paired stage(s) has quality ${qualities.join(' or ')}.` };
|
|
175
|
+
}
|
|
176
|
+
const measured = eligible
|
|
177
|
+
.map((pair) => ({ pair, delta: pair.deltas[metric ] }))
|
|
178
|
+
.filter(({ delta }) => delta.baseline != null && delta.delta != null);
|
|
179
|
+
if (measured.length === 0) {
|
|
180
|
+
return { name, metric, status: 'inconclusive', detail: `Metric "${metric}" is unavailable for every eligible paired stage.` };
|
|
181
|
+
}
|
|
182
|
+
const regressions = measured
|
|
183
|
+
.filter(({ delta }) => delta.delta > 0)
|
|
184
|
+
.map(({ pair, delta }) => ({ pair, delta, pct: delta.baseline === 0 ? Infinity : (delta.delta / delta.baseline ) * 100 }))
|
|
185
|
+
.sort((a, b) => b.pct - a.pct || a.pair.pairId.localeCompare(b.pair.pairId));
|
|
186
|
+
const over = regressions.filter((r) => r.pct > maxPct);
|
|
187
|
+
const scope = `${measured.length} paired stage(s)`;
|
|
188
|
+
if (over.length === 0) {
|
|
189
|
+
const worst = regressions[0];
|
|
190
|
+
return { name, metric, status: 'pass', detail: worst
|
|
191
|
+
? `No paired stage regressed "${metric}" beyond ${maxPct}% across ${scope}; the largest was ${formatPct(worst.pct)} (pair ${worst.pair.pairId}).`
|
|
192
|
+
: `No paired stage regressed "${metric}" across ${scope}.` };
|
|
193
|
+
}
|
|
194
|
+
const listed = over.slice(0, 3).map((r) => `pair ${r.pair.pairId} (${r.pair.quality}) ${formatPct(r.pct)}`).join(', ');
|
|
195
|
+
const more = over.length > 3 ? `, and ${over.length - 3} more` : '';
|
|
196
|
+
return { name, metric, status: 'violation',
|
|
197
|
+
detail: `${over.length} of ${scope} regressed "${metric}" beyond ${maxPct}%: ${listed}${more}.` };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function formatPct(pct ) {
|
|
201
|
+
return pct === Infinity ? 'from 0' : `+${pct.toFixed(1)}%`;
|
|
202
|
+
}
|
|
203
|
+
|
|
149
204
|
function checkFailOnIntroduced(comparison , band ) {
|
|
150
205
|
if (band !== 'all' && !IMPACT_BANDS.includes(band )) {
|
|
151
206
|
return { name: 'fail-on-introduced', status: 'inconclusive', detail: `Impact band "${band}" is not recognized (expected "all" or one of ${IMPACT_BANDS.join(', ')}).` };
|
|
@@ -198,6 +253,11 @@ export function evaluateBudgets({ appModel, catalog, budgets, comparison, thresh
|
|
|
198
253
|
for (const { metric, maxPct } of budgets.regressionBudgets ?? []) {
|
|
199
254
|
pushComparisonBudget(results, comparison, 'max-regression', (c) => checkRegression(c, maxPct, metric), metric);
|
|
200
255
|
}
|
|
256
|
+
const stageQualities = budgets.stageQualities ?? DEFAULT_STAGE_BUDGET_QUALITIES;
|
|
257
|
+
for (const budget of budgets.stageRegressionBudgets ?? []) {
|
|
258
|
+
pushComparisonBudget(results, comparison, 'max-stage-regression',
|
|
259
|
+
(c) => checkStageRegression(c, budget, stageQualities), budget.metric);
|
|
260
|
+
}
|
|
201
261
|
if (budgets.failOnIntroduced !== undefined) {
|
|
202
262
|
pushComparisonBudget(results, comparison, 'fail-on-introduced',
|
|
203
263
|
(c) => checkFailOnIntroduced(c, budgets.failOnIntroduced ));
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
5
|
import { resolve } from 'node:path';
|
|
6
6
|
import { COMPARISON_METRIC_KEYS } from '../run-comparison.js';
|
|
7
|
+
import { PAIR_DELTA_METRICS } from '../stage-alignment.js';
|
|
8
|
+
import { NEUTRAL_PAIR_METRICS, STAGE_QUALITIES } from './budgets.js';
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
|
|
@@ -81,3 +84,35 @@ export function combineRegressionBudgets(sources )
|
|
|
81
84
|
}
|
|
82
85
|
return sources.map((s) => s.budget);
|
|
83
86
|
}
|
|
87
|
+
|
|
88
|
+
/** Refuses a stage budget metric that is not a paired-stage metric or has no regression direction. */
|
|
89
|
+
export function assertStageBudgetMetric(metric , origin ) {
|
|
90
|
+
if (!(PAIR_DELTA_METRICS ).includes(metric)) {
|
|
91
|
+
throw new Error(`${origin}: unknown paired-stage metric "${metric}" (expected one of: ${PAIR_DELTA_METRICS.filter((m) => !NEUTRAL_PAIR_METRICS.has(m)).join(', ')}).`);
|
|
92
|
+
}
|
|
93
|
+
if (NEUTRAL_PAIR_METRICS.has(metric)) {
|
|
94
|
+
throw new Error(`${origin}: "${metric}" measures workload volume, not performance, so it has no regression direction to budget.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** One `--stage-regression-budget` value, `<metric>:<pct>`, over the paired-stage metrics. */
|
|
99
|
+
export function parseStageRegressionBudgetFlag(spec ) {
|
|
100
|
+
const origin = `--stage-regression-budget "${spec}"`;
|
|
101
|
+
const colon = spec.indexOf(':');
|
|
102
|
+
if (colon === -1) throw new Error(`${origin}: expected <metric>:<pct>.`);
|
|
103
|
+
const metric = spec.slice(0, colon);
|
|
104
|
+
const pct = spec.slice(colon + 1);
|
|
105
|
+
assertStageBudgetMetric(metric, origin);
|
|
106
|
+
if (!PCT_PATTERN.test(pct)) throw new Error(`${origin}: "${pct}" is not a non-negative percentage.`);
|
|
107
|
+
return { metric, maxPct: Number(pct) };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The pair qualities a stage budget reads, from a comma list such as `exact,structural`. */
|
|
111
|
+
export function parseStageQualities(list , origin ) {
|
|
112
|
+
const qualities = list.split(',').map((q) => q.trim()).filter(Boolean);
|
|
113
|
+
const bad = qualities.find((q) => !(STAGE_QUALITIES ).includes(q));
|
|
114
|
+
if (qualities.length === 0 || bad !== undefined) {
|
|
115
|
+
throw new Error(`${origin}: ${bad === undefined ? 'no quality given' : `unknown quality "${bad}"`} (expected a list from: ${STAGE_QUALITIES.join(', ')}).`);
|
|
116
|
+
}
|
|
117
|
+
return qualities ;
|
|
118
|
+
}
|
|
@@ -11,7 +11,7 @@ import { redactComparison } from './redact.js';
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
/** `full` carries every field (the CLI); `summary` leaves out `stagePairs` (MCP, which returns them
|
|
@@ -33,6 +33,7 @@ export function comparisonOutput(comparison , view
|
|
|
33
33
|
unmatched: comparison.unmatched,
|
|
34
34
|
replanned: comparison.replanned,
|
|
35
35
|
bookkeepingStageIds: comparison.bookkeepingStageIds,
|
|
36
|
+
executionAlignment: comparison.executionAlignment,
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
9c33274bcae34f425c7d625bdea9cfacdb400f333bc3a5ced9c86a0e75094916
|
|
@@ -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 });
|