Skip to main content

Command Palette

Search for a command to run...

Root Cause Analysis Without the War Room

AI-Augmented Data Engineering — Article 4 of 7

Updated
10 min readView as Markdown
Root Cause Analysis Without the War Room
K
Karthik Darbha is a Data Engineering & AI Leader with over 23 years of experience in Healthcare, Pharma, Retail, Insurance, and Financial Services. He writes at tech4nirvana.com, exploring the intersection of data architecture and timeless wisdom.

1. The Problem

A pipeline fails at 2 AM. The on-call engineer opens the job logs, sees a generic Spark exception, and starts the ritual: check the source table, check the schema, check the upstream job that feeds it, check whether someone touched the ADF trigger, check Slack for any "quick change" nobody documented. Forty-five minutes later, the cause turns out to be a column rename three hops upstream, in a table this pipeline doesn't even directly reference but depends on transitively.

None of that forty-five minutes was wasted effort exactly — it was necessary detective work. But it was manual detective work, repeated by a different engineer, slightly differently, every time a pipeline breaks. The knowledge of "how we traced the last five incidents" rarely survives past the postmortem doc, if a postmortem doc even gets written.

Three things make root cause analysis (RCA) structurally hard at scale:

  • Lineage complexity. A single Gold-layer table might sit six or seven transformations downstream of a dozen source systems. Tracing "what changed upstream" by hand means walking a graph humans were never built to hold in working memory.

  • Unstructured logs. Spark stack traces, ADF activity logs, and Databricks job-run history are text-heavy, inconsistent in format, and scattered across systems. Correlating "this job failed" with "that upstream job also had an unusual run at the same time" requires cross-referencing timestamps across sources that don't share a common schema.

  • Institutional memory gaps. The senior engineer who instinctively knows "when Table X looks weird, check the CRM sync job first" is a single point of failure. When they're on leave, in a different time zone, or have left the team, that pattern-matching disappears with them.

The war room — pulling in five engineers to stare at dashboards and logs together — is not a process. It's an admission that no single person, and no tooling, can trace the failure alone.

2. The AI Opportunity

RCA is a search-and-correlate problem before it's anything else: given a failure, search the lineage graph for what it depends on, search the logs and metadata for what changed in a relevant time window, and correlate the two. That search-and-correlate step is exactly what AI is well-suited to accelerate — not to replace the engineer's judgment on what the failure means, but to compress the time spent gathering the evidence the judgment is applied to.

Two capabilities matter here, and they do different jobs:

Lineage-graph traversal answers "what could have caused this" by mechanically walking upstream dependencies from the failed asset — tables, jobs, notebooks — and surfacing what else touches the same objects. This is deterministic graph work. No LLM required, no hallucination risk. It's the evidentiary backbone.

LLM-assisted synthesis answers "given this evidence, what's the most likely story" by taking the structured output of the lineage walk plus a bundle of correlated log excerpts and schema-change events, and producing a ranked set of hypotheses in plain language — the kind of narrative a senior engineer would sketch on a whiteboard, but generated in seconds and grounded in the specific evidence retrieved, not general knowledge.

The important architectural point: the LLM never gets to invent the lineage or the log content. It only synthesizes from structured evidence that a deterministic system already retrieved. This is the difference between an RCA assistant and an RCA hallucination generator.

Karta — the doer — is the engineer who used to manually walk every log and every lineage edge. Sakshi — the witness — is the engineer who now reviews a pre-assembled evidence bundle and a ranked hypothesis list, and applies judgment to accept, reject, or redirect. The investigation still requires a human. What changes is where the forty-five minutes goes: from gathering evidence to evaluating it.

3. Implementation Sketch

A practical shape on Azure + Databricks + Unity Catalog: three stages — evidence retrieval, evidence bundling, and LLM-assisted synthesis — kept strictly separate so the LLM only ever reasons over facts you've already retrieved.

Stage 1 — Lineage retrieval. Unity Catalog's system tables expose table- and column-level lineage. Walk upstream from the failed asset within a bounded time window around the failure:

from pyspark.sql import functions as F

failed_table = "prod.sales.gold_daily_revenue"
failure_ts = "2026-07-28T02:14:00Z"
lookback_hours = 24

lineage_df = spark.table("system.access.table_lineage")

upstream = (
    lineage_df
    .filter(F.col("target_table_full_name") == failed_table)
    .filter(F.col("event_time").between(
        F.to_timestamp(F.lit(failure_ts)) - F.expr(f"INTERVAL {lookback_hours} HOURS"),
        F.to_timestamp(F.lit(failure_ts))
    ))
    .select("source_table_full_name", "source_type", "event_time")
    .distinct()
)

Stage 2 — Correlated event bundling. For each upstream table surfaced, pull job-run history and any schema-change events in the same window — the goal is a compact, structured "evidence bundle," not raw logs:

job_runs = spark.table("system.lakeflow.job_run_timeline")

correlated_runs = (
    job_runs
    .join(upstream, job_runs.table_full_name == upstream.source_table_full_name, "inner")
    .filter(job_runs.period_start_time.between(
        F.to_timestamp(F.lit(failure_ts)) - F.expr(f"INTERVAL {lookback_hours} HOURS"),
        F.to_timestamp(F.lit(failure_ts))
    ))
    .select("table_full_name", "run_id", "result_state",
            "period_start_time", "period_end_time")
)

evidence_bundle = {
    "failed_asset": failed_table,
    "failure_time": failure_ts,
    "upstream_assets": [row.source_table_full_name for row in upstream.collect()],
    "correlated_job_runs": [row.asDict() for row in correlated_runs.collect()],
    "error_excerpt": get_error_excerpt(failed_table, failure_ts, max_chars=800),
}

get_error_excerpt is a custom helper, not a native PySpark or system-table function — you write it to pull from wherever your job-run error output actually lands (Databricks job-run API, system.lakeflow.job_run_timeline error fields, or your logging sink), truncate it, and hand back a plain string.

Note the error_excerpt cap and the absence of raw row-level data anywhere in the bundle — only table names, job metadata, and a bounded error message make it into what eventually reaches the LLM.

Stage 3 — LLM-assisted synthesis. Pass the structured bundle — never raw data, never full logs — to the model with a prompt constrained to reason only over what's supplied:

system_prompt = """You are assisting a data engineer with root cause analysis.
You will be given a structured evidence bundle: a failed table, upstream
dependencies, correlated job-run outcomes, and an error excerpt.

Rules:
- Base every hypothesis strictly on the evidence provided. Do not assume
  facts not present in the bundle.
- Produce a ranked list of up to 3 hypotheses with a confidence label
  (high / medium / low) and the specific evidence supporting each.
- For each hypothesis, state one concrete next diagnostic step the
  engineer should take to confirm or rule it out.
- If the evidence is insufficient to form a hypothesis, say so explicitly
  rather than speculating."""

response = call_llm(system_prompt=system_prompt, user_content=json.dumps(evidence_bundle))

The output is a starting hypothesis list with pointers back to evidence — not a verdict. It gets logged alongside the incident and reviewed by the engineer before any action is taken.

4. Limitations & Risks

Hallucination in the synthesis step. Even constrained to a structured bundle, an LLM can still overstate confidence or invent plausible-sounding connections between unrelated events. This is maya again — the same appearance-mistaken-for-substance risk this series keeps returning to, because it keeps showing up wherever an LLM sits between evidence and a decision. The constraint isn't optional: every hypothesis needs a "confirm or rule out" step attached, and no hypothesis should move to remediation without that confirmation.

Lineage completeness gaps. Unity Catalog lineage only captures what runs through Unity Catalog–governed compute. Jobs that read or write outside that boundary — legacy ADF activities hitting non-UC storage, external ETL tools — create blind spots in the graph. A confident-looking lineage walk that's silently incomplete is worse than an honest "lineage unavailable beyond this point."

Correlation is not causation. Two upstream jobs running near the failure time doesn't mean either caused it. The LLM synthesis step can make coincidental timing look like a causal narrative if the prompt and evidence bundle aren't explicit about what "correlated" means. Confidence labels help, but they don't eliminate the risk — engineer review is the actual safeguard, not a formality.

Data exposure through error excerpts. Error messages and stack traces can contain fragments of actual data values — a failed row, a bad key, a customer identifier embedded in an exception message. Sending that verbatim to an external LLM API is a PII exposure path that's easy to miss because it doesn't look like "sending data" the way a table export does.

Cost and latency at incident volume. A single evidence bundle plus LLM call is cheap. Hundreds of failures a week, each triggering lineage walks and LLM synthesis, is not free — and if RCA assist becomes another noisy layer that fires on every transient job retry, it adds cost without adding signal.

5. How to Overcome

Redact before synthesis, not after. Run error excerpts through a redaction pass — pattern-matching known PII formats, masking literal values in exception messages — before they enter the evidence bundle. Treat this as a mandatory pipeline stage, not a best-effort filter.

Scope lineage claims honestly. Have the retrieval stage explicitly flag when it hits the edge of Unity Catalog's visibility, and pass that flag into the evidence bundle so the LLM prompt — and the engineer — knows the lineage picture is partial, not complete.

Require evidence citations in every hypothesis. Enforce, at the prompt level, that each hypothesis names the specific evidence field it draws from. A hypothesis with no traceable evidence pointer gets discarded before it reaches the engineer, not treated as a lower-confidence option.

Gate on severity, not on every failure. Trigger the full RCA-assist pipeline only for failures above a severity threshold — SLA-critical tables, repeated failures on the same asset, failures affecting downstream reporting. Transient retries and known-flaky jobs don't need lineage walks and LLM calls.

Close the loop with a feedback table. Log whether each engineer confirmed, rejected, or modified the top hypothesis. Review this monthly. If confirmed-hypothesis rate drops, that's a signal to revisit the prompt, the evidence bundle composition, or the lineage retrieval scope — not to quietly stop trusting the tool.

6. The Takeaway

For engineers: Next incident you trace manually, write down the actual evidence trail you followed — which tables you checked, in what order, what made you rule things in or out. That trail is the specification for what an RCA-assist bundle needs to contain. You cannot automate a search you haven't first done deliberately by hand.

For leads: Ask your team how postmortem knowledge currently gets reused — is it a doc nobody rereads, or a live system that makes the next incident faster to trace? RCA-assist tooling is only worth building if the evidence-gathering step is genuinely the bottleneck, not the judgment step. Measure time-to-root-cause before and after, not just adoption.


AI doesn't eliminate engineering judgment — it demands better judgment, faster.


Next: Article 5 — Metadata Intelligence: Making Your Catalog Work for You. Turning Unity Catalog from a static inventory into an actively maintained knowledge base with LLM-assisted description generation and PII classification.


Karthik Darbha is a Senior Data Engineering & AI Leader with 23 years of professional experience, including 20+ years building enterprise data platforms across Healthcare, Pharma, Retail, Insurance, and Financial Services. He writes about data engineering, program management, and the intersection of technology and philosophy at tech4nirvana.com.

AI-AUGMENTED DATA ENGINEERING

Part 4 of 4

Modern data platforms have outpaced our ability to see them clearly. This series explores how AI restores that clarity — across data quality, observability, root cause analysis, metadata intelligence, and cost optimisation. Written for engineers who build and leads who decide, each article pairs implementation depth with an honest reckoning of risk. The lens is Advaita: viveka — discriminative discernment — applied to the hardest problems in data engineering.

Start from the beginning

The Case for AI in Data Engineering

Series: AI-Augmented Data Engineering | Article 1 of 7 There is a quiet crisis in most data engineering teams. Pipelines fail at 2 AM. The on-call engineer spends three hours tracing a root cause tha