Skip to content

feat(analyze): correct metrics and evidence-based findings - #21

Merged
jeffbrennan merged 1 commit into
mainfrom
feature/analysis-correctness-and-depth
Sep 12, 2026
Merged

jeffbrennan merged 1 commit into
mainfrom
feature/analysis-correctness-and-depth

Conversation

@jeffbrennan

Copy link
Copy Markdown
Owner

Implements brief 03, all three increments.

Metric contract

  • New sparkparse/metrics.py: a canonical registry mapping verified classic and Photon/Connect raw metric names onto canonical names with unit, scope, aggregation, coverage and derivation. Unmapped metrics are preserved with canonical: null rather than guessed — numRowsScanned ≠ numOutputRows, data size is not scan bytes, operator spill is not task memory/disk spill, and Photon's numBytesRead (shuffle on an exchange, files on a scan) stays unmapped.
  • Exact integer counts survive end to end: accumulator_totals now carries value_exact (Int64) beside the Float64 value in both ingestion paths, populated for every metric that is not rescaled. A count of 2**53 + 1 makes it from the event log or Connect server through to the JSON export.
  • A missing metric is null, a measured zero is 0. node_duration_minutes is now null when a node reported no timing metric, instead of claiming it ran instantly.
  • Empty task frames yield null totals, not zeros.

Rules

  • find_cartesian_joins means cartesian: CartesianProduct, nested loops with no condition, or join_type=Cross, matched per (query_id, node_id). Conditional nested loops moved to find_nested_loop_joins — the nested_loop_join fixture's BNLJ is LeftOuter with a condition and is no longer reported as cartesian.
  • Row expansion is computed from a join's immediate inputs, descending only through row-preserving operators. Scans identify lineage only.
  • Repeated scans count occurrences, so repeats inside one query are visible, and track distinct read schemas.
  • find_largest_scans labels bytes_source and falls back to rows; nothing observed means nothing is presented as "largest".
  • New: scan efficiency, task stragglers (promoted to data skew only with size evidence), GC overhead, shuffle volume (sides reported separately), AQE runtime adjustments.
  • Every rule returns a RuleAssessment (evaluated / unsupported / insufficient_data) and checks its counters are actually populated before reporting evaluated, so a null column can't read as a confident zero.

Presentation

  • Findings carry evidence with units, thresholds, confidence, caveats and a next investigation, and stay separate from the raw plan summary.
  • sparkparse analyze gains --compact, --top-n, --redact, --findings. Redaction covers node names, which embed the server's operator text for unmapped Connect operators.
  • The dashboard shows confidence and caveats plus a "Checks not run" panel, so missing telemetry is explained rather than hidden.

Known limits, stated rather than papered over

  • ScanDetail does not retain PushedFilters/PartitionFilters, so scan efficiency reports rows read but discarded — not a pruning verdict.
  • parse.py keeps only isFinalPlan=true snapshots, so aqe_plan_change reports optimizer-recorded adjustments on the surviving plan and says so in its assessment reason.

Validation

Offline only — no live Spark or Databricks run. just ci: 377 passed, 2 skipped; ruff clean; pyrefly unchanged at the two pre-existing errors (pages/home.py:132, pages/summary.py:172).

New tests: tests/test_metrics.py, synthetic-fixture and Photon-fixture coverage in tests/test_analyze.py (reused node IDs across queries, CartesianProduct, conditional nested loops, zero-row inputs, filters before joins, reused exchange, repeated scan within one query, incomplete scan detail, plan spill without task rows, all-null scan ranking, unsupported/insufficient statuses, redaction of an unknown operator carrying a literal, and ingestion-to-JSON precision above 2**53). Helpers live in tests/synthetic.py.

Expected-output fixtures were regenerated; the only field changes are node_duration_minutes (0.0 → null) and the new value_exact.

Implements plans/improvements-2026-09/03-analysis.md (all three increments).

Metric contract:
- new sparkparse/metrics.py: canonical registry mapping verified classic and
  Photon/Connect raw metric names onto canonical names with unit, scope,
  aggregation, coverage and derivation. Unmapped metrics are preserved with
  canonical=null rather than guessed.
- exact integer counts end to end: accumulator_totals carries value_exact
  (Int64) beside the Float64 value in both ingestion paths, so counts above
  2**53 survive parse -> clean -> summary -> JSON.
- a missing metric is null and a measured zero is 0; node_duration_minutes is
  null when a node reported no timing metric instead of claiming zero.

Rules:
- cartesian joins mean CartesianProduct, unconditional nested loops, or
  join_type=Cross, matched per (query_id, node_id). Conditional nested loops
  are reported separately and are no longer called cartesian.
- row expansion is computed from a join's immediate inputs, descending only
  through row-preserving operators; scans are lineage only.
- repeated scans count occurrences, including repeats inside one query.
- largest scans label their byte source and fall back to rows; an unobserved
  scan is never presented as the largest.
- new rules: scan efficiency, task stragglers (data skew only with size
  evidence), GC overhead, shuffle volume, AQE runtime adjustments.
- every rule returns a RuleAssessment (evaluated / unsupported /
  insufficient_data) and verifies its counters are populated before claiming
  it evaluated anything.

Presentation:
- findings carry evidence with units, thresholds, confidence, caveats and a
  next investigation, and stay separate from the raw plan summary.
- analyze CLI gains --compact, --top-n, --redact, --findings; redaction covers
  node names, which embed server operator text for unmapped Connect operators.
- the dashboard shows confidence, caveats and a "Checks not run" panel so
  missing telemetry is visible.
@jeffbrennan
jeffbrennan merged commit 5b2eb0e into main Sep 12, 2026
3 checks passed
@jeffbrennan
jeffbrennan deleted the feature/analysis-correctness-and-depth branch September 12, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant