sparkforensics-server 0.6.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +16 -7
- package/package.json +1 -1
- package/public/assets/AutoscalingChurn-DenWKTv-.js +1 -0
- package/public/assets/CacheUtilization-DLz7NdjV.js +1 -0
- package/public/assets/CachingOpportunity-zpQIqhzm.js +1 -0
- package/public/assets/ChartTheme-DAiARbLP.js +8 -0
- package/public/assets/ColdStart-C-9HjxWl.js +1 -0
- package/public/assets/ConfigAudit-D0V9tcrJ.js +1 -0
- package/public/assets/CoreUsageArea-yeerrljd.js +1 -0
- package/public/assets/Dashboard-DkUPrHKV.js +1 -0
- package/public/assets/DisclosureContext-DK1_iz6J.js +1 -0
- package/public/assets/DuplicatePlanSubtree-xDNDbCd3.js +1 -0
- package/public/assets/DurationHistogram-B6jsE0Ov.js +1 -0
- package/public/assets/EmptyState-C1uL40VU.js +1 -0
- package/public/assets/ExecutorUtilization-DXDZKmSc.js +1 -0
- package/public/assets/GcPressure-MRf7I4IG.js +1 -0
- package/public/assets/ImpactEstimate-Dm3xWbzV.js +1 -0
- package/public/assets/IncompleteRun-CaLOhMtf.js +1 -0
- package/public/assets/JobFailures-CzmzlvhO.js +1 -0
- package/public/assets/MemoryUtilization-AWUMUjCH.js +1 -0
- package/public/assets/NestedLoopJoin-C9sNr6Ss.js +1 -0
- package/public/assets/OverBroadcast-BOmRE6ca.js +1 -0
- package/public/assets/PartitionHint-BL2EmJfE.js +1 -0
- package/public/assets/PartitionSizing-BzBySvUq.js +1 -0
- package/public/assets/PlanExplorer-DdiQRcJU.js +1 -0
- package/public/assets/PlanGraphRoute-CIH9gBuR.js +1 -0
- package/public/assets/PlanView-B1hxzFRs.js +2 -0
- package/public/assets/PythonUdf-DvgCCueD.js +1 -0
- package/public/assets/RetryWaste-uXXu_CyM.js +1 -0
- package/public/assets/RowPagination-C-LzWkI5.js +1 -0
- package/public/assets/RowStatusCluster-Dbtmte7l.js +1 -0
- package/public/assets/ShuffleIO-nALwzC1_.js +1 -0
- package/public/assets/Skew-CHMEKgvk.js +1 -0
- package/public/assets/SlowHost-yyvjpHk6.js +1 -0
- package/public/assets/SmallFiles-y6odJ97n.js +1 -0
- package/public/assets/SpeculationWaste-C-SD2LOt.js +1 -0
- package/public/assets/Spill-DHTMmOup.js +1 -0
- package/public/assets/StageFailed-CQ3DxC9R.js +1 -0
- package/public/assets/StageFindingGroup-BgYLyj5q.js +1 -0
- package/public/assets/StageHeader-CIETkjVZ.js +1 -0
- package/public/assets/StagePill--P7PDd3s.js +1 -0
- package/public/assets/StageShape-BIv8vx_o.js +1 -0
- package/public/assets/StageSlowness-MMqzERxN.js +1 -0
- package/public/assets/Straggler-BgrY7AyW.js +1 -0
- package/public/assets/TaskFailures-BHHyKYTs.js +1 -0
- package/public/assets/TinyTask-DDScNhCl.js +1 -0
- package/public/assets/UnderBroadcast-CGIC04TI.js +1 -0
- package/public/assets/WidgetLeadSummary-CXFZP00C.js +1 -0
- package/public/assets/button-DbpTz2yk.js +1 -0
- package/public/assets/charts-vendor-CtTxwTeu.js +59 -0
- package/public/assets/composite-C9Nu2EI9.js +1 -0
- package/public/assets/d3-vendor-DNlD2-cz.js +1 -0
- package/public/assets/detector-registry-Bp8hrLua.js +2 -0
- package/public/assets/downsample-WmNM1qc9.js +1 -0
- package/public/assets/finding-anchor-Dr40LkS9.js +1 -0
- package/public/assets/findings-of-type-BL2u3RGZ.js +1 -0
- package/public/assets/impact-sort-mC5ISgX8.js +1 -0
- package/public/assets/inbox-KPU1Gppm.js +1 -0
- package/public/assets/index-CaRVPvAT.css +2 -0
- package/public/assets/index-fDiwfuoO.js +14 -0
- package/public/assets/inertValue-L9VxV0VE.js +1 -0
- package/public/assets/layout.worker-CLcBHxVp.js +3 -0
- package/public/assets/{parser-worker-CA5RxqKc.js → parser-worker-CRIOPMOW.js} +8 -8
- package/public/assets/plan-graph-vendor-Ce6d4HU6.js +7 -0
- package/public/assets/react-vendor-C6KcUD36.js +9 -0
- package/public/assets/select-BHqOqun5.js +1 -0
- package/public/assets/toggle-DPeN6aNa.js +1 -0
- package/public/assets/toggle-group-Kl3uyHSr.js +1 -0
- package/public/assets/usePagedRows-BafD6bEN.js +1 -0
- package/public/assets/useRecentFiles-DzJZAoOT.js +3 -0
- package/public/assets/useRegisterFieldControl-pQv-ZDoU.js +1 -0
- package/public/assets/useScrollLock-BCxn_dMp.js +1 -0
- package/public/assets/useSortMode-DjrAN-z4.js +1 -0
- package/public/docs/404.html +3 -3
- package/public/docs/assets/{app.CnWL6bKV.js → app.1gvzd5Zb.js} +1 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.DS2TQP5_.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.C0IBKeRQ.js → VPLocalSearchBox.CNxT3s0I.js} +1 -1
- package/public/docs/assets/chunks/{theme.BH-2HuRf.js → theme.C5lLqUv7.js} +2 -2
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.CqKM_jEG.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.js → contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.WqY74T0T.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.CHHgywoP.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.N4hhenrA.lean.js → contributor-guide_architecture_detector-contract.md.CHHgywoP.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.DIz3_oz9.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.lean.js → contributor-guide_architecture_detector-contract_threshold-tables.md.WH4tRHPL.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_drill-down.md.CqZPrdo9.lean.js → contributor-guide_architecture_drill-down.md.yB-Hz2XT.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.lean.js → contributor-guide_architecture_drill-down_plan-graph.md.BQZE04sw.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.K71YB-Rr.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.DfvbqEUu.lean.js → contributor-guide_architecture_impact-estimation.md.K71YB-Rr.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.lean.js → contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.DLCAffwn.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.JFrzDMwo.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_overview.md.nxkpOhgf.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.lean.js → contributor-guide_architecture_state-and-history_run-comparison-internals.md.U-9KMDFK.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.B7rjSF_h.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BAnhi2-d.js → contributor-guide_architecture_worker-protocol.md.Br6OE7RW.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.Br6OE7RW.lean.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.lean.js → contributor-guide_architecture_worker-protocol_cli-and-mcp.md.IVyaXrGe.lean.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.js} +1 -1
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.js} +2 -2
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-availability.md.CjimIDBY.lean.js → contributor-guide_architecture_worker-protocol_evidence-availability.md.D3S5cyOW.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.lean.js → contributor-guide_architecture_worker-protocol_evidence-report.md.CtmCU57I.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_contributing.md.DBW4drtZ.js +1 -0
- package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.js +15 -0
- package/public/docs/assets/contributor-guide_development-setup.md.B21ZTDhn.lean.js +1 -0
- package/public/docs/assets/contributor-guide_testing.md.DpkozNoG.js +4 -0
- package/public/docs/assets/{contributor-guide_testing.md.By6XGR9a.lean.js → contributor-guide_testing.md.DpkozNoG.lean.js} +1 -1
- package/public/docs/assets/{index.md.CHJVslga.js → index.md.lMm7SUMz.js} +1 -1
- package/public/docs/assets/{index.md.CHJVslga.lean.js → index.md.lMm7SUMz.lean.js} +1 -1
- package/public/docs/assets/{style.CyK9Ss73.css → style.Cfxqlj0y.css} +1 -1
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.C0pcQoNc.js +29 -0
- package/public/docs/assets/{user-guide_getting-started_ci-and-automation.md.BEepbDDL.lean.js → user-guide_getting-started_ci-and-automation.md.C0pcQoNc.lean.js} +1 -1
- package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.js +1 -0
- package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.js} +2 -2
- package/public/docs/assets/{user-guide_getting-started_tuning-thresholds.md.B_MyjyZm.lean.js → user-guide_getting-started_tuning-thresholds.md.BGVE-wrc.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.js → user-guide_mcp-tools.md.49r3L2YE.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_compare-runs.md.Cc_gLoix.js → user-guide_mcp-tools_compare-runs.md.BQ_kIptC.js} +11 -6
- package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.BQ_kIptC.lean.js +1 -0
- package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.azCZXkH8.js +44 -0
- package/public/docs/assets/{user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.lean.js → user-guide_mcp-tools_diagnose-run.md.azCZXkH8.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.js} +2 -2
- package/public/docs/assets/{user-guide_mcp-tools_evaluate-budgets.md.BB0nDY3Z.lean.js → user-guide_mcp-tools_evaluate-budgets.md.dpQpRQ7z.lean.js} +1 -1
- package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.js} +2 -1
- package/public/docs/assets/{user-guide_mcp-tools_get-finding-evidence.md.rKyu4wts.lean.js → user-guide_mcp-tools_get-finding-evidence.md.CH6FkNLX.lean.js} +1 -1
- package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.js +1 -0
- package/public/docs/assets/user-guide_run-comparison.md.B_LOkdBf.lean.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.js +1 -0
- package/public/docs/assets/user-guide_understanding-findings.md.C4riKCKl.lean.js +1 -0
- package/public/docs/contributor-guide/architecture/board-widgets/caching-and-memory.html +6 -6
- package/public/docs/contributor-guide/architecture/board-widgets/plan-advisor.html +6 -6
- package/public/docs/contributor-guide/architecture/board-widgets/visual-system-and-templating.html +6 -6
- package/public/docs/contributor-guide/architecture/board-widgets.html +6 -6
- package/public/docs/contributor-guide/architecture/detector-contract/plan-attribution.html +6 -6
- package/public/docs/contributor-guide/architecture/detector-contract/threshold-tables.html +6 -6
- package/public/docs/contributor-guide/architecture/detector-contract.html +6 -6
- package/public/docs/contributor-guide/architecture/drill-down/plan-graph.html +6 -6
- package/public/docs/contributor-guide/architecture/drill-down.html +6 -6
- package/public/docs/contributor-guide/architecture/impact-estimation/caveats-tuning-and-coverage.html +6 -6
- package/public/docs/contributor-guide/architecture/impact-estimation/stage-union-rollup.html +6 -6
- package/public/docs/contributor-guide/architecture/impact-estimation.html +6 -6
- package/public/docs/contributor-guide/architecture/index.html +5 -5
- package/public/docs/contributor-guide/architecture/overview.html +6 -6
- package/public/docs/contributor-guide/architecture/state-and-history/history-server-intake.html +5 -5
- package/public/docs/contributor-guide/architecture/state-and-history/run-comparison-internals.html +6 -6
- package/public/docs/contributor-guide/architecture/state-and-history.html +5 -5
- package/public/docs/contributor-guide/architecture/widget-rendering/findings-tab.html +6 -6
- package/public/docs/contributor-guide/architecture/widget-rendering/investigation-routing.html +5 -5
- package/public/docs/contributor-guide/architecture/widget-rendering/list-sort-mode.html +6 -6
- package/public/docs/contributor-guide/architecture/widget-rendering/reference-section.html +5 -5
- package/public/docs/contributor-guide/architecture/widget-rendering/row-expand-contract.html +6 -6
- package/public/docs/contributor-guide/architecture/widget-rendering.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/cli-and-mcp.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/core-and-event-validation.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-availability.html +7 -7
- package/public/docs/contributor-guide/architecture/worker-protocol/evidence-report.html +6 -6
- package/public/docs/contributor-guide/architecture/worker-protocol.html +6 -6
- package/public/docs/contributor-guide/contributing.html +6 -6
- package/public/docs/contributor-guide/development-setup.html +8 -8
- package/public/docs/contributor-guide/testing.html +9 -6
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +6 -6
- package/public/docs/logo-dark.svg +1 -0
- package/public/docs/logo-light.svg +1 -0
- package/public/docs/tuning-reference/anti-patterns.html +5 -5
- package/public/docs/tuning-reference/aqe.html +5 -5
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +5 -5
- package/public/docs/tuning-reference/bottleneck-cold-start.html +5 -5
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +5 -5
- package/public/docs/tuning-reference/bottleneck-failures.html +5 -5
- package/public/docs/tuning-reference/bottleneck-gc.html +5 -5
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +5 -5
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +5 -5
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +5 -5
- package/public/docs/tuning-reference/bottleneck-shuffle.html +5 -5
- package/public/docs/tuning-reference/bottleneck-skew.html +5 -5
- package/public/docs/tuning-reference/bottleneck-slow-host.html +5 -5
- package/public/docs/tuning-reference/bottleneck-small-files.html +5 -5
- package/public/docs/tuning-reference/bottleneck-spill.html +5 -5
- package/public/docs/tuning-reference/bottleneck-straggler.html +5 -5
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +5 -5
- package/public/docs/tuning-reference/bottleneck-utilization.html +5 -5
- package/public/docs/tuning-reference/caching.html +5 -5
- package/public/docs/tuning-reference/cluster-config.html +5 -5
- package/public/docs/tuning-reference/config.html +5 -5
- package/public/docs/tuning-reference/data-formats.html +5 -5
- package/public/docs/tuning-reference/index.html +5 -5
- package/public/docs/tuning-reference/intro.html +5 -5
- package/public/docs/tuning-reference/joins.html +5 -5
- package/public/docs/tuning-reference/memory-model.html +5 -5
- package/public/docs/tuning-reference/metrics.html +5 -5
- package/public/docs/tuning-reference/partitioning.html +5 -5
- package/public/docs/tuning-reference/pyspark.html +5 -5
- package/public/docs/tuning-reference/shuffle.html +5 -5
- package/public/docs/tuning-reference/spark-architecture.html +5 -5
- package/public/docs/tuning-reference/table-formats.html +5 -5
- package/public/docs/user-guide/alternative-log-retrieval/managed-platforms.html +5 -5
- package/public/docs/user-guide/alternative-log-retrieval/ssh-bastion.html +5 -5
- package/public/docs/user-guide/alternative-log-retrieval.html +5 -5
- package/public/docs/user-guide/getting-started/ci-and-automation.html +16 -11
- package/public/docs/user-guide/getting-started/reading-the-dashboard.html +6 -6
- package/public/docs/user-guide/getting-started/regression-metric-keys.html +5 -5
- package/public/docs/user-guide/getting-started/tuning-thresholds.html +7 -7
- package/public/docs/user-guide/getting-started.html +5 -5
- package/public/docs/user-guide/mcp-tools/compare-runs.html +16 -11
- package/public/docs/user-guide/mcp-tools/diagnose-run.html +28 -7
- package/public/docs/user-guide/mcp-tools/evaluate-budgets.html +6 -6
- package/public/docs/user-guide/mcp-tools/get-finding-documentation.html +5 -5
- package/public/docs/user-guide/mcp-tools/get-finding-evidence.html +7 -6
- package/public/docs/user-guide/mcp-tools/get-reference-doc.html +5 -5
- package/public/docs/user-guide/mcp-tools/get-run-summary.html +5 -5
- package/public/docs/user-guide/mcp-tools/list-runs.html +5 -5
- package/public/docs/user-guide/mcp-tools.html +6 -6
- package/public/docs/user-guide/run-comparison/how-stages-are-matched.html +5 -5
- package/public/docs/user-guide/run-comparison.html +6 -6
- package/public/docs/user-guide/understanding-findings.html +6 -6
- package/public/export-template.html +45 -45
- package/public/index.html +8 -7
- package/public/sample-runs/sample-run.ndjson.gz +0 -0
- package/vendor-core/allocation.js +25 -23
- package/vendor-core/analyzer.js +85 -58
- package/vendor-core/api-merge-writes.js +2 -0
- package/vendor-core/aqe-skew.js +347 -0
- package/vendor-core/cli/budgets.js +6 -4
- package/vendor-core/cli/collect-run-pair.js +89 -0
- package/vendor-core/cli/collect-run-worker.js +16 -0
- package/vendor-core/cli/collect-run.js +1 -2
- package/vendor-core/cli/native-zstd.js +12 -16
- package/vendor-core/comparability.js +234 -0
- package/vendor-core/comparison-output.js +15 -7
- package/vendor-core/comparison-verdict.js +105 -25
- package/vendor-core/core-source-hash.txt +1 -1
- package/vendor-core/core-time-series.js +0 -1
- package/vendor-core/core-usage-locality.js +1 -1
- package/vendor-core/detectors.js +1007 -380
- package/vendor-core/docs-config.js +0 -4
- package/vendor-core/docs-content/detection/cache.md +2 -1
- package/vendor-core/docs-content/detection/cfg.md +19 -11
- package/vendor-core/docs-content/detection/cold.md +2 -1
- package/vendor-core/docs-content/detection/gc.md +7 -3
- package/vendor-core/docs-content/detection/host.md +11 -1
- package/vendor-core/docs-content/detection/mem.md +17 -6
- package/vendor-core/docs-content/detection/part.md +3 -1
- package/vendor-core/docs-content/detection/plan.md +55 -13
- package/vendor-core/docs-content/detection/shfl.md +11 -5
- package/vendor-core/docs-content/detection/skew.md +47 -1
- package/vendor-core/docs-content/detection/slow.md +4 -2
- package/vendor-core/docs-content/detection/spec.md +6 -1
- package/vendor-core/docs-content/detection/spill.md +6 -3
- package/vendor-core/docs-content/detection/strag.md +19 -6
- package/vendor-core/docs-content/detection/tiny.md +9 -3
- package/vendor-core/effective-conf.js +7 -10
- package/vendor-core/efficiency-model.js +2 -2
- package/vendor-core/etl-phases.js +1 -2
- package/vendor-core/event-handlers.js +175 -57
- package/vendor-core/event-schemas.js +21 -16
- package/vendor-core/evidence-availability.js +9 -2
- package/vendor-core/evidence-report.js +118 -35
- package/vendor-core/executor-peaks.js +41 -0
- package/vendor-core/export-data.js +5 -5
- package/vendor-core/finding-identity.js +78 -0
- package/vendor-core/finding-presentation.js +116 -33
- package/vendor-core/finding-types.js +88 -21
- package/vendor-core/format-utils.js +14 -15
- package/vendor-core/html-export.js +1 -1
- package/vendor-core/impact-band.js +2 -2
- package/vendor-core/impact-format.js +47 -19
- package/vendor-core/impact-model.js +26 -15
- package/vendor-core/job-groups.js +4 -4
- package/vendor-core/mcp-server-factory.js +48 -12
- package/vendor-core/mcp-tools.js +179 -24
- package/vendor-core/median.js +2 -2
- package/vendor-core/nested-loop-join.js +73 -0
- package/vendor-core/occupancy.js +1 -2
- package/vendor-core/parser-worker.js +1 -1
- package/vendor-core/plan-graph-model.js +14 -22
- package/vendor-core/plan-node-detail.js +2 -3
- package/vendor-core/plan-summary.js +24 -19
- package/vendor-core/plan-tree-walk.js +6 -8
- package/vendor-core/python-stage.js +12 -1
- package/vendor-core/recommendation-rollup.js +13 -12
- package/vendor-core/redact.js +52 -46
- package/vendor-core/run-aggregates.js +6 -8
- package/vendor-core/run-comparison.js +226 -66
- package/vendor-core/run-identity.js +12 -0
- package/vendor-core/run-interpretation.js +21 -0
- package/vendor-core/run-metrics.js +2 -2
- package/vendor-core/run-output.js +32 -10
- package/vendor-core/run-totals.js +12 -0
- package/vendor-core/run-verdict.js +76 -3
- package/vendor-core/scaling-sim.js +3 -4
- package/vendor-core/snappy-block.js +2 -2
- package/vendor-core/spark-conf.js +168 -0
- package/vendor-core/sql-stages.js +2 -2
- package/vendor-core/stage-alignment.js +2 -12
- package/vendor-core/stage-identity.js +58 -3
- package/vendor-core/stage-label.js +14 -0
- package/vendor-core/stage-plan-nodes.js +17 -3
- package/vendor-core/stage-quantiles.js +141 -9
- package/vendor-core/stage-structure.js +17 -3
- package/vendor-core/threshold-overrides.js +9 -3
- package/vendor-core/types.js +58 -21
- package/vendor-core/wasted-core-hours.js +0 -1
- package/vendor-core/write-targets.js +114 -14
- package/vendor-core/zstd-worker-client.js +1 -1
- package/vendor-core/zstd-worker.js +1 -1
- package/public/assets/AutoscalingChurn-CRA2Mb7F.js +0 -1
- package/public/assets/CacheUtilization-Bh9OY_V9.js +0 -1
- package/public/assets/CachingOpportunity-oCiQwmTJ.js +0 -1
- package/public/assets/ColdStart-BJrtjfmn.js +0 -1
- package/public/assets/ConfigAudit-DMvKxySP.js +0 -1
- package/public/assets/CoreUsageArea-REC2zsKw.js +0 -1
- package/public/assets/Dashboard-BHZ76JCu.js +0 -2
- package/public/assets/DisclosureContext-OO7X1HAx.js +0 -1
- package/public/assets/DuplicatePlanSubtree-BgG-L-cK.js +0 -1
- package/public/assets/ExecutorUtilization-D2KhSVbY.js +0 -1
- package/public/assets/GcPressure-D1BTE8qh.js +0 -1
- package/public/assets/IncompleteRun-DD40xUpy.js +0 -1
- package/public/assets/JobFailures-YwNzCF19.js +0 -1
- package/public/assets/MemoryUtilization-9b8ee_3_.js +0 -1
- package/public/assets/OverBroadcast-CfctS8a8.js +0 -1
- package/public/assets/PartitionHint-CUvbnnxy.js +0 -1
- package/public/assets/PartitionSizing-VG8kBgG5.js +0 -1
- package/public/assets/PlanExplorer-DmubTAOP.js +0 -1
- package/public/assets/PlanGraphRoute-DctPZjS0.js +0 -1
- package/public/assets/RetryWaste-CnpM8bB7.js +0 -1
- package/public/assets/ShuffleIO-D13vGmas.js +0 -1
- package/public/assets/Skew-DlagstYP.js +0 -1
- package/public/assets/SlowHost-cSkLRZ_e.js +0 -1
- package/public/assets/SmallFiles-B-kERMcq.js +0 -1
- package/public/assets/SpeculationWaste-DaysAzXO.js +0 -1
- package/public/assets/Spill-H44Gz4US.js +0 -1
- package/public/assets/StageFailed-DCfii9h_.js +0 -1
- package/public/assets/StageFindingGroup-BXJ0Mj5n.js +0 -1
- package/public/assets/StageHeader--oN2J-oD.js +0 -1
- package/public/assets/StageShape-IZ80CZiJ.js +0 -1
- package/public/assets/StageSlowness--BRIaBc1.js +0 -1
- package/public/assets/Straggler-Ctq0Df_o.js +0 -1
- package/public/assets/TaskFailures-Bks_gqhL.js +0 -1
- package/public/assets/TinyTask-BssfzGFB.js +0 -1
- package/public/assets/UnderBroadcast-DsScrxc-.js +0 -1
- package/public/assets/charts-vendor-0VMaLVLN.js +0 -59
- package/public/assets/finding-anchor-BYW1oz5R.js +0 -1
- package/public/assets/impact-sort-xzy0g-Vh.js +0 -1
- package/public/assets/index-DhpRGMZc.css +0 -2
- package/public/assets/index-DwqkFUFk.js +0 -14
- package/public/assets/plan-graph-vendor-BYl-AzN5.js +0 -7
- package/public/assets/react-vendor-DfpuPQZf.js +0 -9
- package/public/assets/select-C8edjYTq.js +0 -1
- package/public/assets/usePagedRows-CsuUFeYx.js +0 -1
- package/public/assets/useRecentFiles-BvkKo3JN.js +0 -11
- package/public/assets/useSortMode-joX71BZN.js +0 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.tE5pz54z.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets.md.YQcj5EI4.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_board-widgets_visual-system-and-templating.md.wulOueeB.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract.md.N4hhenrA.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_plan-attribution.md.yNex4bys.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_detector-contract_threshold-tables.md.Dp3c40rx.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_drill-down_plan-graph.md.DwOozIEs.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DfvbqEUu.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_caveats-tuning-and-coverage.md.Cwm1jHmn.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation_stage-union-rollup.md.DdCGtAGu.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.C1sSvm9i.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_state-and-history_run-comparison-internals.md.CZA0JLtV.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.BOZ1azXG.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_widget-rendering_findings-tab.md.BQXycK6G.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol.md.BAnhi2-d.lean.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_cli-and-mcp.md.BX1XnFbq.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_worker-protocol_evidence-report.md.CiQl1MLb.js +0 -1
- package/public/docs/assets/contributor-guide_contributing.md.CQMvGwFt.js +0 -1
- package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.js +0 -15
- package/public/docs/assets/contributor-guide_development-setup.md.DQdzera7.lean.js +0 -1
- package/public/docs/assets/contributor-guide_testing.md.By6XGR9a.js +0 -1
- package/public/docs/assets/user-guide_getting-started_ci-and-automation.md.BEepbDDL.js +0 -24
- package/public/docs/assets/user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.js +0 -1
- package/public/docs/assets/user-guide_mcp-tools_compare-runs.md.Cc_gLoix.lean.js +0 -1
- package/public/docs/assets/user-guide_mcp-tools_diagnose-run.md.BP1vnHA5.js +0 -23
- package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.js +0 -1
- package/public/docs/assets/user-guide_run-comparison.md.BUALkohi.lean.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.js +0 -1
- package/public/docs/assets/user-guide_understanding-findings.md.3C7KCEZO.lean.js +0 -1
- /package/public/docs/assets/{contributor-guide_architecture_board-widgets_caching-and-memory.md.CQgn1t_M.lean.js → contributor-guide_architecture_board-widgets_caching-and-memory.md.BYDo3ClR.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_board-widgets_plan-advisor.md.RlzvUmNm.lean.js → contributor-guide_architecture_board-widgets_plan-advisor.md.ajUAVdbc.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_overview.md.C1sSvm9i.lean.js → contributor-guide_architecture_overview.md.nxkpOhgf.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_widget-rendering.md.BOZ1azXG.lean.js → contributor-guide_architecture_widget-rendering.md.BrSz8Ezh.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_list-sort-mode.md.BuHKgH0C.lean.js → contributor-guide_architecture_widget-rendering_list-sort-mode.md.37hcZJ7q.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_widget-rendering_row-expand-contract.md.ZT89u9f1.lean.js → contributor-guide_architecture_widget-rendering_row-expand-contract.md.mK_VmAWk.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DSVFPpWb.lean.js → contributor-guide_architecture_worker-protocol_core-and-event-validation.md.DVSXn-bb.lean.js} +0 -0
- /package/public/docs/assets/{contributor-guide_contributing.md.CQMvGwFt.lean.js → contributor-guide_contributing.md.DBW4drtZ.lean.js} +0 -0
- /package/public/docs/assets/{user-guide_getting-started_reading-the-dashboard.md.Obf3W3__.lean.js → user-guide_getting-started_reading-the-dashboard.md.CGaxPX_1.lean.js} +0 -0
- /package/public/docs/assets/{user-guide_mcp-tools.md.C65SjWUL.lean.js → user-guide_mcp-tools.md.49r3L2YE.lean.js} +0 -0
|
@@ -91,19 +91,17 @@ const SparkPlanInfoFieldSchema = z
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
// Fully-specified event schemas.
|
|
95
|
-
|
|
96
94
|
// SparkListenerLogStart case (inline: sets state.pendingSparkVersion).
|
|
97
95
|
export const LogStartEventSchema = z.object({
|
|
98
96
|
Event: z.literal('SparkListenerLogStart'),
|
|
99
97
|
'Spark Version': z.string().optional(),
|
|
100
98
|
});
|
|
101
99
|
|
|
102
|
-
// startApplication.
|
|
103
100
|
export const ApplicationStartEventSchema = z.object({
|
|
104
101
|
Event: z.literal('SparkListenerApplicationStart'),
|
|
105
102
|
'App ID': z.string().optional(),
|
|
106
103
|
'App Name': z.string().optional(),
|
|
104
|
+
'App Attempt ID': z.string().optional(),
|
|
107
105
|
Timestamp: z.number().optional(),
|
|
108
106
|
'Spark Version': z.string().optional(),
|
|
109
107
|
});
|
|
@@ -121,7 +119,6 @@ export const EnvironmentUpdateEventSchema = z.object({
|
|
|
121
119
|
]).optional(),
|
|
122
120
|
});
|
|
123
121
|
|
|
124
|
-
// inline ApplicationEnd case.
|
|
125
122
|
export const ApplicationEndEventSchema = z.object({
|
|
126
123
|
Event: z.literal('SparkListenerApplicationEnd'),
|
|
127
124
|
Timestamp: z.number().optional(),
|
|
@@ -150,8 +147,6 @@ export const ExecutorRemovedEventSchema = z.object({
|
|
|
150
147
|
'Removed Reason': z.string().optional(),
|
|
151
148
|
});
|
|
152
149
|
|
|
153
|
-
// Schemas transcribed from their handlers.
|
|
154
|
-
|
|
155
150
|
// startJob. 'spark.sql.execution.id' is read then parseInt'd, which coerces to string internally,
|
|
156
151
|
// so it tolerates the raw value being a number; widened to string|number (some logs emit a JSON number).
|
|
157
152
|
export const JobStartEventSchema = z.object({
|
|
@@ -164,7 +159,6 @@ export const JobStartEventSchema = z.object({
|
|
|
164
159
|
}).optional(),
|
|
165
160
|
});
|
|
166
161
|
|
|
167
|
-
// endJob.
|
|
168
162
|
export const JobEndEventSchema = z.object({
|
|
169
163
|
Event: z.literal('SparkListenerJobEnd'),
|
|
170
164
|
'Job ID': z.number(),
|
|
@@ -230,7 +224,6 @@ export const BlockUpdatedEventSchema = z.object({
|
|
|
230
224
|
}),
|
|
231
225
|
});
|
|
232
226
|
|
|
233
|
-
// inline StageCompleted case.
|
|
234
227
|
export const StageCompletedEventSchema = z.object({
|
|
235
228
|
Event: z.literal('SparkListenerStageCompleted'),
|
|
236
229
|
'Stage Info': z.object({
|
|
@@ -239,10 +232,17 @@ export const StageCompletedEventSchema = z.object({
|
|
|
239
232
|
'Submission Time': z.number().optional(),
|
|
240
233
|
'Completion Time': z.number().optional(),
|
|
241
234
|
'Failure Reason': z.string().optional(),
|
|
235
|
+
// The stage's accumulators, read only for SQL metrics (Metadata "sql"): their Value is the
|
|
236
|
+
// accumulator's running total, which Spark writes as a decimal string. Value stays unknown
|
|
237
|
+
// because internal accumulators write arrays and other shapes there.
|
|
238
|
+
Accumulables: z.array(z.object({
|
|
239
|
+
ID: z.number(),
|
|
240
|
+
Value: z.unknown().optional(),
|
|
241
|
+
Metadata: z.string().optional(),
|
|
242
|
+
})).optional(),
|
|
242
243
|
}),
|
|
243
244
|
});
|
|
244
245
|
|
|
245
|
-
// recordStageExecutorMetrics.
|
|
246
246
|
export const StageExecutorMetricsEventSchema = z.object({
|
|
247
247
|
Event: z.literal('SparkListenerStageExecutorMetrics'),
|
|
248
248
|
'Stage ID': z.number(),
|
|
@@ -250,9 +250,8 @@ export const StageExecutorMetricsEventSchema = z.object({
|
|
|
250
250
|
'Executor Metrics': z.record(z.string(), z.number()).optional(),
|
|
251
251
|
});
|
|
252
252
|
|
|
253
|
-
const MAX_ACCUMULABLES_PER_TASK = 10_000;
|
|
253
|
+
export const MAX_ACCUMULABLES_PER_TASK = 10_000;
|
|
254
254
|
|
|
255
|
-
// accumulateTask.
|
|
256
255
|
export const TaskEndEventSchema = z.object({
|
|
257
256
|
Event: z.literal('SparkListenerTaskEnd'),
|
|
258
257
|
'Stage ID': z.number(),
|
|
@@ -286,12 +285,16 @@ export const TaskEndEventSchema = z.object({
|
|
|
286
285
|
// Bounded well above any real per-task metric count: caps how much a crafted TaskEnd can grow
|
|
287
286
|
// taskAccumStages, which is never pruned for the life of the parse. Only ID is read. Update and
|
|
288
287
|
// Value stay undeclared: Spark writes some as JSON arrays (internal.metrics.updatedBlockStatuses),
|
|
289
|
-
// and a string|number check on them
|
|
290
|
-
// stage's stats.
|
|
288
|
+
// and a string|number check on them would reject the whole TaskEnd, dropping the task from its
|
|
289
|
+
// stage's stats. Validating them would also add ~15% to TaskEnd validation time.
|
|
291
290
|
Accumulables: z.array(z.object({
|
|
292
291
|
ID: z.number(),
|
|
293
292
|
})).max(MAX_ACCUMULABLES_PER_TASK).optional(),
|
|
294
293
|
}).optional(),
|
|
294
|
+
// Spark 3.0+: the executor's metric peaks as of this task's end, one number per ExecutorMetricType
|
|
295
|
+
// name (the same names as SparkListenerStageExecutorMetrics). Values are read by name and kept
|
|
296
|
+
// only when numeric, so a stray value never drops the task.
|
|
297
|
+
'Task Executor Metrics': z.record(z.string(), z.unknown()).optional(),
|
|
295
298
|
'Task Metrics': z.object({
|
|
296
299
|
'Peak Execution Memory': z.number().optional(),
|
|
297
300
|
'JVM GC Time': z.number().optional(),
|
|
@@ -303,12 +306,14 @@ export const TaskEndEventSchema = z.object({
|
|
|
303
306
|
'Remote Bytes Read': z.number().optional(),
|
|
304
307
|
'Local Bytes Read': z.number().optional(),
|
|
305
308
|
'Fetch Wait Time': z.number().optional(),
|
|
309
|
+
'Total Records Read': z.number().optional(),
|
|
306
310
|
}).optional(),
|
|
307
311
|
'Shuffle Write Metrics': z.object({
|
|
308
312
|
'Shuffle Bytes Written': z.number().optional(),
|
|
309
313
|
}).optional(),
|
|
310
314
|
'Input Metrics': z.object({
|
|
311
315
|
'Bytes Read': z.number().optional(),
|
|
316
|
+
'Records Read': z.number().optional(),
|
|
312
317
|
}).optional(),
|
|
313
318
|
'Output Metrics': z.object({
|
|
314
319
|
'Bytes Written': z.number().optional(),
|
|
@@ -317,7 +322,6 @@ export const TaskEndEventSchema = z.object({
|
|
|
317
322
|
}).optional(),
|
|
318
323
|
});
|
|
319
324
|
|
|
320
|
-
// startSqlExecution.
|
|
321
325
|
export const SqlExecutionStartEventSchema = z.object({
|
|
322
326
|
Event: z.literal('org.apache.spark.sql.execution.ui.SparkListenerSQLExecutionStart'),
|
|
323
327
|
executionId: z.number(),
|
|
@@ -326,6 +330,9 @@ export const SqlExecutionStartEventSchema = z.object({
|
|
|
326
330
|
physicalPlanDescription: z.string().optional(),
|
|
327
331
|
time: z.number(),
|
|
328
332
|
sparkPlanInfo: SparkPlanInfoFieldSchema,
|
|
333
|
+
// Session settings of this execution that differ from the SparkContext's (Spark 3.3+; redacted
|
|
334
|
+
// values are masked by Spark). Anything but a string-to-string map is dropped, not a parse error.
|
|
335
|
+
modifiedConfigs: z.record(z.string(), z.string()).optional().catch(undefined),
|
|
329
336
|
});
|
|
330
337
|
|
|
331
338
|
// applyAdaptiveExecutionUpdate: AQE re-plans mid-query and re-emits sparkPlanInfo for the same
|
|
@@ -337,14 +344,12 @@ export const SqlAdaptiveExecutionUpdateEventSchema = z.object({
|
|
|
337
344
|
sparkPlanInfo: SparkPlanInfoFieldSchema,
|
|
338
345
|
});
|
|
339
346
|
|
|
340
|
-
// endSqlExecution.
|
|
341
347
|
export const SqlExecutionEndEventSchema = z.object({
|
|
342
348
|
Event: z.literal('org.apache.spark.sql.execution.ui.SparkListenerSQLExecutionEnd'),
|
|
343
349
|
executionId: z.number(),
|
|
344
350
|
time: z.number(),
|
|
345
351
|
});
|
|
346
352
|
|
|
347
|
-
// applyDriverAccumUpdates.
|
|
348
353
|
export const DriverAccumUpdatesEventSchema = z.object({
|
|
349
354
|
Event: z.literal('org.apache.spark.sql.execution.ui.SparkListenerDriverAccumUpdates'),
|
|
350
355
|
executionId: z.number(),
|
|
@@ -6,7 +6,7 @@ export const EVIDENCE_AVAILABILITY_SCHEMA_VERSION = 1;
|
|
|
6
6
|
|
|
7
7
|
const SUMMARIES = {
|
|
8
8
|
observed: 'Observed in this event log.',
|
|
9
|
-
explicitlyDisabled: '
|
|
9
|
+
explicitlyDisabled: 'Disabled in this event log, by its configuration or by Spark\'s default.',
|
|
10
10
|
noObservedExecutorMetrics: 'Not emitted by this event log.',
|
|
11
11
|
noObservedStageSubmission: 'Not emitted by this event log.',
|
|
12
12
|
noRddStorageSnapshot: 'Stages were submitted without RDD storage snapshots.',
|
|
@@ -66,6 +66,13 @@ function absent(key , reasonCode , trustworthy
|
|
|
66
66
|
return trustworthy ? entry(key, reasonCode === 'noSqlExecution' ? 'notApplicable' : 'notEmitted', reasonCode) : entry(key, 'unknown', 'parseIncomplete');
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// The run's effective spark.eventLog.logStageExecutorMetrics: the parser's resolution of it (the
|
|
70
|
+
// logged value, else Spark's default of off) when it recorded one, else the logged value alone.
|
|
71
|
+
function stageExecutorMetricsLogging(app ) {
|
|
72
|
+
const resolved = app?.resources?.stageExecutorMetricsLogging;
|
|
73
|
+
return resolved != null ? resolved : app?.config?.['spark.eventLog.logStageExecutorMetrics'];
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
export function deriveEvidenceAvailability(appModel , { skippedLines = 0 } = {}) {
|
|
70
77
|
const app = appModel?.app;
|
|
71
78
|
const inputs = app?.evidenceInputs ?? {};
|
|
@@ -80,7 +87,7 @@ export function deriveEvidenceAvailability(appModel , { skippedLines =
|
|
|
80
87
|
|
|
81
88
|
const executorMetrics = metricRows > 0
|
|
82
89
|
? observed('executorMetrics', 'executorMetricRows', metricRows)
|
|
83
|
-
: configIs(app
|
|
90
|
+
: configIs(stageExecutorMetricsLogging(app), 'false')
|
|
84
91
|
? entry('executorMetrics', 'disabled', 'explicitlyDisabled')
|
|
85
92
|
: absent('executorMetrics', 'noObservedExecutorMetrics', trustworthy);
|
|
86
93
|
const rddStorageSnapshots = snapshots > 0
|
|
@@ -14,15 +14,16 @@ import { findingName, titleCase } from './finding-names.js';
|
|
|
14
14
|
import { redactReport, redactRunModel } from './redact.js';
|
|
15
15
|
import { formatTaskFailureHeadline, } from './task-failure.js';
|
|
16
16
|
import { findingActionLabel } from './finding-action-label.js';
|
|
17
|
+
import { findingKeyer } from './run-comparison.js';
|
|
17
18
|
import { matchesFindingFilterCriteria, singleStageId } from './finding-filter-predicate.js';
|
|
18
19
|
import { buildRecommendationRollup, isEligible, isRealFinding, rankFindings, resourceGroupTotal, } from './recommendation-rollup.js';
|
|
19
20
|
import { checkCoverage, isCleanRun } from './check-coverage.js';
|
|
20
|
-
import { buildRunVerdict, stepCopyRecommendation, stepCopyText,
|
|
21
|
+
import { buildRunVerdict, stepCopyRecommendation, stepCopyText, } from './run-verdict.js';
|
|
21
22
|
import {
|
|
22
|
-
estimateProvenance, impactEstimateFigure, impactFigure,
|
|
23
|
+
estimateProvenance, impactEstimateFigure, impactFigure, savingsMeaning,
|
|
23
24
|
} from './impact-format.js';
|
|
24
25
|
import { computeRunShape, } from './run-shape.js';
|
|
25
|
-
import { extractWriteTargets, } from './write-targets.js';
|
|
26
|
+
import { extractWriteTargets, isPlainWrite, } from './write-targets.js';
|
|
26
27
|
import { detectorInfoByType } from './detector-docs.js';
|
|
27
28
|
|
|
28
29
|
|
|
@@ -41,7 +42,11 @@ export const EVIDENCE_SCHEMA_VERSION = 5;
|
|
|
41
42
|
// configAudit's current setting, incompleteRun's 'missing') carry theirs in `valueText` instead,
|
|
42
43
|
// present only on those rows.
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
45
50
|
|
|
46
51
|
|
|
47
52
|
|
|
@@ -56,6 +61,9 @@ export const EVIDENCE_SCHEMA_VERSION = 5;
|
|
|
56
61
|
|
|
57
62
|
|
|
58
63
|
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
59
67
|
|
|
60
68
|
|
|
61
69
|
|
|
@@ -128,12 +136,29 @@ export const EVIDENCE_SCHEMA_VERSION = 5;
|
|
|
128
136
|
|
|
129
137
|
|
|
130
138
|
|
|
139
|
+
// One cost-lane entry: a finding ranked by the measured busy core time its fix removes.
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
131
152
|
// The dashboard's run verdict (run-verdict.ts): title, summary sentences, the first steps in the
|
|
132
|
-
// same order, how many more places the full list holds, and the "Copy next steps" text.
|
|
153
|
+
// same order, how many more places the full list holds, and the "Copy next steps" text. `steps`
|
|
154
|
+
// rank by wall clock; `costSteps` rank by measured busy core time and list only findings that
|
|
155
|
+
// carry one; `idle` is the run's allocated core time that ran no task.
|
|
133
156
|
|
|
134
157
|
|
|
135
158
|
|
|
136
159
|
|
|
160
|
+
|
|
161
|
+
|
|
137
162
|
|
|
138
163
|
|
|
139
164
|
|
|
@@ -180,17 +205,17 @@ export const EVIDENCE_SCHEMA_VERSION = 5;
|
|
|
180
205
|
// module (stageShape's totalCores, utilization's unrounded fraction) is left off both, so it never
|
|
181
206
|
// reaches the report; adding, renaming or dropping a key here changes the report contract.
|
|
182
207
|
const EVIDENCE_KEYS = {
|
|
183
|
-
skew: ['origin'],
|
|
208
|
+
skew: ['origin', 'aqeSkew', 'cause', 'dataRatio'],
|
|
184
209
|
stageShape: ['rule'],
|
|
185
210
|
shuffle: ['partitions'],
|
|
186
|
-
partitionSizing: ['rule', 'origin', 'partitions'],
|
|
211
|
+
partitionSizing: ['rule', 'origin', 'aqeSkew', 'partitions'],
|
|
187
212
|
spill: ['spillMagnitude', 'reads'],
|
|
188
213
|
gc: ['direction'],
|
|
189
|
-
slowHost: ['variant', 'host', 'hostTaskShare', 'hostMeanMs', 'dimension', 'executorId', 'execMaxValue'],
|
|
214
|
+
slowHost: ['variant', 'host', 'hostTaskShare', 'hostMeanMs', 'dimension', 'executorId', 'execMaxValue', 'executorCount', 'taskCount'],
|
|
190
215
|
stageSlowness: ['reads'],
|
|
191
216
|
stageFailed: ['variant', 'numTasks', 'memoryBytesSpilled', 'failedTaskDetails'],
|
|
192
217
|
failures: ['failedTasks', 'dominantReason', 'dominantError', 'failureGroups', 'otherFailedTasks'],
|
|
193
|
-
straggler: ['unit', 'speculativeTasks', 'stragglerCount', 'origin'],
|
|
218
|
+
straggler: ['unit', 'speculativeTasks', 'stragglerCount', 'origin', 'aqeSkew', 'cause', 'causeSharePct', 'host', 'hostTasks', 'cpuPct'],
|
|
194
219
|
speculationWaste: [],
|
|
195
220
|
retryWaste: ['numTasks', 'memoryBytesSpilled', 'retriedTaskDetails'],
|
|
196
221
|
tinyTask: ['reads'],
|
|
@@ -211,7 +236,9 @@ const EVIDENCE_KEYS = {
|
|
|
211
236
|
'groupIndex',
|
|
212
237
|
],
|
|
213
238
|
smallFiles: ['executionId', 'stageIds', 'fileCount', 'direction', 'nodeName'],
|
|
214
|
-
|
|
239
|
+
pythonUdf: ['executionId', 'stageIds', 'dataSentBytes', 'dataReturnedBytes', 'stageDurationMs'],
|
|
240
|
+
nestedLoopJoin: ['executionId', 'stageIds', 'nodeName', 'joinType', 'condition', 'outputRows', 'leftRows', 'rightRows'],
|
|
241
|
+
underBroadcast: ['executionId', 'stageIds', 'largerSideBytes', 'joinType', 'buildSide', 'broadcastThreshold'],
|
|
215
242
|
overBroadcast: ['executionId', 'stageIds', 'broadcastThreshold'],
|
|
216
243
|
} ;
|
|
217
244
|
|
|
@@ -233,11 +260,12 @@ function projectEvidence(f ) {
|
|
|
233
260
|
return evidence;
|
|
234
261
|
}
|
|
235
262
|
|
|
236
|
-
function findingRow(f ) {
|
|
263
|
+
function findingRow(f , key ) {
|
|
237
264
|
// Cast: projectEvidence's keys come from EVIDENCE_KEYS[f.type], so `evidence` is that type's
|
|
238
265
|
// FindingEvidenceMap entry, which TypeScript can't correlate with `type` on its own.
|
|
239
266
|
const row = {
|
|
240
267
|
id: f.id ?? null,
|
|
268
|
+
key,
|
|
241
269
|
type: f.type,
|
|
242
270
|
name: titleCase(findingName(f.type)),
|
|
243
271
|
tag: typeTag(f.type),
|
|
@@ -262,6 +290,8 @@ function findingRow(f ) {
|
|
|
262
290
|
row.impact = figure.text;
|
|
263
291
|
row.impactMeaning = figure.meaning;
|
|
264
292
|
}
|
|
293
|
+
const provenance = estimateProvenance(f);
|
|
294
|
+
if (provenance) row.estimateProvenance = provenance;
|
|
265
295
|
if (f.tunedThresholds != null) row.tunedThresholds = f.tunedThresholds;
|
|
266
296
|
return row;
|
|
267
297
|
}
|
|
@@ -284,8 +314,8 @@ function sortFindings(rows ) {
|
|
|
284
314
|
// file's isEligible call omits FixTheseFirst's REGISTRY check: every finding here already came out
|
|
285
315
|
// of analyze()/auditConfig() so it's a known type, and REGISTRY (.tsx) isn't importable here anyway.
|
|
286
316
|
|
|
287
|
-
// The impact-ranked "highest-leverage fix" rollup,
|
|
288
|
-
//
|
|
317
|
+
// The impact-ranked "highest-leverage fix" rollup, so CLI, MCP and download share the dashboard's
|
|
318
|
+
// ranking. buildRecommendationRollup already returns groups in the correct cross-group
|
|
289
319
|
// order, so this only maps each group to its JSON row without re-sorting.
|
|
290
320
|
function buildRecommendations(
|
|
291
321
|
findings ,
|
|
@@ -324,7 +354,7 @@ function buildRecommendations(
|
|
|
324
354
|
unit: group.unit,
|
|
325
355
|
total: group.total,
|
|
326
356
|
impact: shown,
|
|
327
|
-
impactMeaning: shown ?
|
|
357
|
+
impactMeaning: shown ? savingsMeaning(representative) : null,
|
|
328
358
|
};
|
|
329
359
|
}
|
|
330
360
|
return {
|
|
@@ -368,6 +398,26 @@ function buildCheckLists(
|
|
|
368
398
|
return { cleanChecks, notRunChecks };
|
|
369
399
|
}
|
|
370
400
|
|
|
401
|
+
// A plain write with no output row count (no node metric, and its root execution's stages report no
|
|
402
|
+
// rows written or the root holds another write) is not a check the log could run, so a consumer that
|
|
403
|
+
// compares row counts sees it listed here instead of reading the null as a match.
|
|
404
|
+
const WRITE_FIGURES_CHECK = 'writeOutputRows';
|
|
405
|
+
const LISTED_WRITES = 5;
|
|
406
|
+
|
|
407
|
+
function notRunWriteFigures(report ) {
|
|
408
|
+
const missing = report.writes.filter((w) => isPlainWrite(w) && w.outputRows === null);
|
|
409
|
+
if (missing.length === 0) return null;
|
|
410
|
+
const named = missing.slice(0, LISTED_WRITES)
|
|
411
|
+
.map((w) => `execution ${w.sqlExecutionId} ${w.command}`).join(', ');
|
|
412
|
+
const more = missing.length > LISTED_WRITES ? ` and ${missing.length - LISTED_WRITES} more` : '';
|
|
413
|
+
return {
|
|
414
|
+
type: WRITE_FIGURES_CHECK,
|
|
415
|
+
tag: 'INCMP',
|
|
416
|
+
thresholdSummary: 'an output row count for every plain write, from its node metric or from the stages of its root execution',
|
|
417
|
+
reason: `${missing.length} write${missing.length === 1 ? ' has' : 's have'} no output row count (${named}${more}): the write node has no row metric and the stages under its root execution report no rows written, or that root holds more than one write.`,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
371
421
|
function countByImpactBand(findings ) {
|
|
372
422
|
const counts = { critical: 0, warning: 0, info: 0 };
|
|
373
423
|
for (const f of findings) if (f.impactBand in counts) counts[f.impactBand ] += 1;
|
|
@@ -394,6 +444,16 @@ function verdictJson(model ) {
|
|
|
394
444
|
text: stepCopyText(step.lead, recommendation, step.stageId),
|
|
395
445
|
};
|
|
396
446
|
}),
|
|
447
|
+
costSteps: model.costLane.map(({ finding, coreTimeMs, figure }) => ({
|
|
448
|
+
stageId: singleStageId(finding),
|
|
449
|
+
type: finding.type,
|
|
450
|
+
tag: typeTag(finding.type),
|
|
451
|
+
leadFindingId: finding.id ?? null,
|
|
452
|
+
actionLabel: findingActionLabel(finding),
|
|
453
|
+
coreTimeMs,
|
|
454
|
+
coreTime: figure,
|
|
455
|
+
})),
|
|
456
|
+
idle: model.idle,
|
|
397
457
|
remainingPlaces: model.remaining,
|
|
398
458
|
copyText: model.copyText,
|
|
399
459
|
};
|
|
@@ -401,17 +461,17 @@ function verdictJson(model ) {
|
|
|
401
461
|
|
|
402
462
|
// Keyed by appModel object identity: mcp-tools.ts caches one fixed appModel per runId (never
|
|
403
463
|
// mutated), so re-running analyze()/auditConfig() reproduces the same catalog. getFindingEvidence
|
|
404
|
-
// calls buildEvidenceReport once per drill-down;
|
|
464
|
+
// calls buildEvidenceReport once per drill-down; the cache spares each lookup a detector re-run.
|
|
405
465
|
// A WeakMap needs no invalidation: once mcp-tools.ts evicts the appModel, this entry is collectible.
|
|
406
466
|
// Each cache is split first by the overrides object the report ran under (one fixed, frozen object
|
|
407
467
|
// per CLI invocation or MCP server process; DEFAULT_THRESHOLDS for the specification's).
|
|
408
|
-
|
|
468
|
+
|
|
409
469
|
const DEFAULT_THRESHOLDS = {};
|
|
410
470
|
const jsonCache = new WeakMap();
|
|
411
471
|
// The redacted report, keyed by the unredacted appModel it was built from.
|
|
412
472
|
const redactedJsonCache = new WeakMap();
|
|
413
473
|
|
|
414
|
-
function cacheFor (cache , thresholds ) {
|
|
474
|
+
export function cacheFor (cache , thresholds ) {
|
|
415
475
|
const key = thresholds ?? DEFAULT_THRESHOLDS;
|
|
416
476
|
let byModel = cache.get(key);
|
|
417
477
|
if (!byModel) {
|
|
@@ -435,7 +495,7 @@ export function runFindings(appModel , thresholds
|
|
|
435
495
|
jobs ?? new Map(), sql ?? new Map(),
|
|
436
496
|
runAggregates ?? null, { thresholds },
|
|
437
497
|
);
|
|
438
|
-
const result = { catalog, config: auditConfig(app) };
|
|
498
|
+
const result = { catalog, config: auditConfig(app, stages) };
|
|
439
499
|
cache.set(appModel, result);
|
|
440
500
|
return result;
|
|
441
501
|
}
|
|
@@ -449,8 +509,8 @@ function buildRedactedJson(appModel , thresholds
|
|
|
449
509
|
if (cached) return cached;
|
|
450
510
|
const { catalog, config } = runFindings(appModel, thresholds);
|
|
451
511
|
const run = redactRunModel(appModel, catalog, config);
|
|
452
|
-
// redactReport
|
|
453
|
-
// structured fields (summary.app.id)
|
|
512
|
+
// redactReport runs as a last pass: idempotent over pseudonyms, and it covers the report's own
|
|
513
|
+
// structured fields (summary.app.id).
|
|
454
514
|
const result = redactReport(buildJson(run.appModel, thresholds, { catalog: run.catalog, config: run.configFindings }));
|
|
455
515
|
cache.set(appModel, result);
|
|
456
516
|
return result;
|
|
@@ -465,9 +525,15 @@ function buildJson(
|
|
|
465
525
|
const { app, stages, executors, sql, jobs, evidenceAvailability } = appModel;
|
|
466
526
|
const { catalog, config } = findings ?? runFindings(appModel, thresholds);
|
|
467
527
|
const allFindings = [...catalog, ...config];
|
|
468
|
-
const
|
|
528
|
+
const keyOf = findingKeyer({ stages: stages ?? new Map(), sql: sql ?? new Map() });
|
|
529
|
+
const rows = sortFindings(allFindings.map((f) => findingRow(f, keyOf(f))));
|
|
469
530
|
const recommendations = buildRecommendations(allFindings, stages ?? new Map());
|
|
470
531
|
const { cleanChecks, notRunChecks } = buildCheckLists(allFindings, stages ?? new Map(), thresholds);
|
|
532
|
+
const writeTargets = extractWriteTargets(sql ?? new Map(), {
|
|
533
|
+
stages, skippedLines: appModel.skippedLines, unreadableSqlExecutions: appModel.unreadableSqlExecutions,
|
|
534
|
+
});
|
|
535
|
+
const unfiguredWrites = notRunWriteFigures(writeTargets);
|
|
536
|
+
if (unfiguredWrites) notRunChecks.push(unfiguredWrites);
|
|
471
537
|
const tuned = tunedDetectors(thresholds);
|
|
472
538
|
const actionable = allFindings.filter(isEligible);
|
|
473
539
|
const fullModel = {
|
|
@@ -480,8 +546,6 @@ function buildJson(
|
|
|
480
546
|
schemaVersion: EVIDENCE_SCHEMA_VERSION,
|
|
481
547
|
summary: {
|
|
482
548
|
app: {
|
|
483
|
-
// `?? null`, not `?? undefined`: JSON.stringify drops undefined keys but keeps null, and
|
|
484
|
-
// sparkVersion's null is a deliberate "unknown/absent" sentinel; undefined would drop these.
|
|
485
549
|
id: app?.id ?? null,
|
|
486
550
|
name: app?.name ?? null,
|
|
487
551
|
sparkVersion: app?.sparkVersion ?? null,
|
|
@@ -509,9 +573,7 @@ function buildJson(
|
|
|
509
573
|
// Order follows DETECTORS (stable) => byte-stable serialization.
|
|
510
574
|
detectors: tunedDetectorCatalog(thresholds),
|
|
511
575
|
findings: rows,
|
|
512
|
-
writeTargets
|
|
513
|
-
skippedLines: appModel.skippedLines, unreadableSqlExecutions: appModel.unreadableSqlExecutions,
|
|
514
|
-
}),
|
|
576
|
+
writeTargets,
|
|
515
577
|
recommendations,
|
|
516
578
|
cleanChecks,
|
|
517
579
|
notRunChecks,
|
|
@@ -592,8 +654,9 @@ function renderRunShape(shape ) {
|
|
|
592
654
|
function renderVerdict(verdict ) {
|
|
593
655
|
const lines = ['## Verdict', '', verdict.title];
|
|
594
656
|
if (verdict.summary.length > 0) lines.push('', verdict.summary.join(' '));
|
|
657
|
+
if (verdict.idle) lines.push('', verdict.idle.text);
|
|
595
658
|
if (verdict.steps.length > 0) {
|
|
596
|
-
lines.push('');
|
|
659
|
+
lines.push('', 'By wall clock (run time):', '');
|
|
597
660
|
verdict.steps.forEach((step, i) => {
|
|
598
661
|
lines.push(`${i + 1}. [${step.tag}] ${step.text}`);
|
|
599
662
|
if (step.relatedTypes.length > 0) {
|
|
@@ -606,6 +669,13 @@ function renderVerdict(verdict ) {
|
|
|
606
669
|
lines.push('', `${places} to look at in the Findings section below.`);
|
|
607
670
|
}
|
|
608
671
|
}
|
|
672
|
+
if (verdict.costSteps.length > 0) {
|
|
673
|
+
lines.push('', 'By core time (measured busy core time a fix removes):', '');
|
|
674
|
+
verdict.costSteps.forEach((step, i) => {
|
|
675
|
+
const where = step.stageId != null ? ` in Stage ${step.stageId}` : '';
|
|
676
|
+
lines.push(`${i + 1}. [${step.tag}] ${step.actionLabel}${where}: ${step.coreTime} of core time.`);
|
|
677
|
+
});
|
|
678
|
+
}
|
|
609
679
|
lines.push('');
|
|
610
680
|
return lines;
|
|
611
681
|
}
|
|
@@ -713,6 +783,10 @@ function renderMarkdown(json , incomplete ) {
|
|
|
713
783
|
|
|
714
784
|
|
|
715
785
|
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
|
|
716
790
|
|
|
717
791
|
|
|
718
792
|
// CLI/MCP-facing filter over FindingRow, delegating to the shared core predicate that also backs
|
|
@@ -721,21 +795,24 @@ function matchesFindingsFilter(row , filter )
|
|
|
721
795
|
// A sql-scope finding carries its stages in evidence.stageIds, not a stageId column: it matches
|
|
722
796
|
// the one stage it touches, as the dashboard's Stage details lists it.
|
|
723
797
|
const stageIds = 'stageIds' in row.evidence ? row.evidence.stageIds : null;
|
|
798
|
+
if (filter.minBand && (IMPACT_BAND_ORDER[row.impactBand] ?? 9) > IMPACT_BAND_ORDER[filter.minBand]) return false;
|
|
724
799
|
return matchesFindingFilterCriteria({ ...row, stageId: singleStageId({ stageId: row.stageId, stageIds }) }, filter);
|
|
725
800
|
}
|
|
726
801
|
|
|
727
|
-
/** Build a FindingsFilter from the
|
|
728
|
-
*
|
|
802
|
+
/** Build a FindingsFilter from the optional CLI/MCP filter dimensions, or undefined when none were
|
|
803
|
+
* passed (the "is anything set" gate before calling buildEvidenceReport). */
|
|
729
804
|
export function toFindingsFilter(
|
|
730
|
-
impactBand , type , stageId ,
|
|
805
|
+
impactBand , type , stageId , minBand , maxFindings ,
|
|
731
806
|
) {
|
|
732
|
-
return (impactBand || type || stageId !== undefined
|
|
807
|
+
return (impactBand || type || stageId !== undefined || minBand || maxFindings !== undefined)
|
|
808
|
+
? { impactBand, type, stageId, minBand, maxFindings }
|
|
809
|
+
: undefined;
|
|
733
810
|
}
|
|
734
811
|
|
|
735
812
|
/**
|
|
736
813
|
* Build a portable evidence report from an appModel.
|
|
737
814
|
* @param opts redact=true pseudonymizes app ids / hosts; markdown=false skips the Markdown string;
|
|
738
|
-
* findingsFilter narrows json.findings (and the Markdown Findings section) only, summary,
|
|
815
|
+
* findingsFilter (including its minBand and maxFindings limits) narrows json.findings (and the Markdown Findings section) only, summary,
|
|
739
816
|
* recommendations, cleanChecks and notRunChecks stay computed from the full set, so a narrow filter never
|
|
740
817
|
* hides that other checks passed or other fixes exist. thresholds runs the detectors with a user's
|
|
741
818
|
* validated overrides (CLI/MCP only) and labels whatever they changed.
|
|
@@ -745,12 +822,18 @@ export function buildEvidenceReport(
|
|
|
745
822
|
{ redact = false, markdown: computeMarkdown = true, findingsFilter, thresholds }
|
|
746
823
|
|
|
747
824
|
= {},
|
|
748
|
-
)
|
|
825
|
+
) {
|
|
749
826
|
let json = redact ? buildRedactedJson(appModel, thresholds) : buildJson(appModel, thresholds);
|
|
750
827
|
const incomplete = json.findings.some((row) => row.type === 'incompleteRun');
|
|
751
828
|
// Filter after redact, not before: redaction only replaces string values on surviving rows,
|
|
752
829
|
// never adds/removes rows, so the two orderings produce identical final content.
|
|
753
|
-
|
|
830
|
+
// `findingsMatched`: the rows that passed every filter but `maxFindings`, so a caller can say how many that limit left out.
|
|
831
|
+
let findingsMatched = json.findings.length;
|
|
832
|
+
if (findingsFilter) {
|
|
833
|
+
const kept = json.findings.filter((row) => matchesFindingsFilter(row, findingsFilter));
|
|
834
|
+
findingsMatched = kept.length;
|
|
835
|
+
json = { ...json, findings: findingsFilter.maxFindings !== undefined ? kept.slice(0, findingsFilter.maxFindings) : kept };
|
|
836
|
+
}
|
|
754
837
|
const markdown = computeMarkdown ? renderMarkdown(json, incomplete) : '';
|
|
755
|
-
return { markdown, json };
|
|
838
|
+
return { markdown, json, findingsMatched };
|
|
756
839
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Per-executor JVM heap peaks from the two places a Spark event log can carry them: the executor
|
|
2
|
+
// metrics every TaskEnd reports (Spark 3.0+, always logged) and SparkListenerStageExecutorMetrics
|
|
3
|
+
// rows (only with spark.eventLog.logStageExecutorMetrics=true). Spark's own AppStatusListener
|
|
4
|
+
// folds the TaskEnd values into executor peak memory
|
|
5
|
+
// (https://github.com/apache/spark/blob/v4.0.0/core/src/main/scala/org/apache/spark/status/AppStatusListener.scala).
|
|
6
|
+
|
|
7
|
+
/** The id Spark gives the driver's own metrics row; the driver's heap is sized by spark.driver.memory. */
|
|
8
|
+
export const DRIVER_EXECUTOR_ID = 'driver';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// One analyze() context is asked once per stage detector call: fold it once.
|
|
16
|
+
const cache = new WeakMap ();
|
|
17
|
+
|
|
18
|
+
/** Executor id to its largest sampled JVMHeapMemory in bytes. Spark polls executor metrics at
|
|
19
|
+
* heartbeat (spark.executor.metrics.pollingInterval defaults to 0), so a peak is a lower bound.
|
|
20
|
+
* JVMHeapMemory is `MemoryMXBean.getHeapMemoryUsage().getUsed()`, which counts uncollected garbage.
|
|
21
|
+
* An executor whose every sample is zero (local mode reports zeros) is left out: no measurement. */
|
|
22
|
+
export function executorHeapPeaks(source ) {
|
|
23
|
+
const known = cache.get(source);
|
|
24
|
+
if (known) return known;
|
|
25
|
+
const peaks = new Map ();
|
|
26
|
+
const take = (executorId , heap ) => {
|
|
27
|
+
// The driver's heap is sized by spark.driver.memory, not the executor setting.
|
|
28
|
+
if (executorId === DRIVER_EXECUTOR_ID) return;
|
|
29
|
+
if (typeof heap === 'number' && heap > (peaks.get(executorId) ?? 0)) peaks.set(executorId, heap);
|
|
30
|
+
};
|
|
31
|
+
for (const [executorId, metrics] of Object.entries(source.runAggregates?.executorPeakMetrics ?? {})) {
|
|
32
|
+
take(executorId, metrics?.jvmHeapMemory);
|
|
33
|
+
}
|
|
34
|
+
for (const stage of source.stages.values()) {
|
|
35
|
+
const rows = stage.executorMetrics;
|
|
36
|
+
if (!(rows instanceof Map)) continue;
|
|
37
|
+
for (const [executorId, metrics] of rows) take(executorId, metrics?.jvmHeapMemory);
|
|
38
|
+
}
|
|
39
|
+
cache.set(source, peaks);
|
|
40
|
+
return peaks;
|
|
41
|
+
}
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* findings per detector: a text figure (stageFailed, configAudit, incompleteRun)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
/** The export payload format. It carries the interpretation layer (`interpretation`) and `provenance`,
|
|
8
|
+
* and types findings per detector: a text figure (stageFailed, configAudit, incompleteRun) is in
|
|
9
|
+
* `valueText`, and Plan Advisor findings always carry `planNodeIds`. The export bundle renders only
|
|
10
|
+
* this version and refuses any other: a payload of another version lacks the conclusions the bundle
|
|
11
|
+
* would otherwise have to derive, or carries them in fields it does not read. */
|
|
12
12
|
export const EXPORT_DATA_SCHEMA_VERSION = 3;
|
|
13
13
|
|
|
14
14
|
/** @sparkforensics/core's own version (packages/core/package.json, which a test keeps in sync).
|