My Data Drift Detector Successfully Identified 55/56 Faults but Failed Negative Controls

AlexSurfer Intermediate 28d ago 591 views 2 likes 2 min read

I developed a tool designed to detect "silent" data failures, such as unit changes by vendors or undocumented enums, where pipelines remain green despite data meaning shifts. To validate its effectiveness, I introduced 56 defects across various pipeline layers and magnitudes. The tool's purpose was to profile the data, detect drift, and trace the lineage back to the faulty node. It successfully identified 55 of these defects.

Why Negative Controls Are More Important Than Success Rates

A benchmark that only includes faults indicates whether the detector triggers, but not whether it triggers too frequently. A tool that alerts on every run would achieve a 100% score on a fault-only benchmark but would be unusable in production due to alert fatigue. Therefore, I added four negative controls where the expected result was no alerts:

  • control-null: a rebuild and reprofile with zero changes.
  • control-subthreshold-tip: a 3% increase (below the 5% threshold).
  • control-subthreshold-extra: a 2% increase.
  • control-subthreshold-tip-near-limit: a 4.5% increase, close to the limit.

The control-null test failed immediately. I received three high-severity signals on a run where the data had not changed at all.

The Culprits Were HyperLogLog and Floating-Point Non-Determinism

Two factors contributed to the false positives.

First, I used approx_count_distinct for efficiency. Since HyperLogLog (HLL) is a probabilistic sketch, the estimates vary significantly between runs on the same dataset. I observed variance as high as 30% between identical runs. With a detection threshold of 10%, the estimator's inherent noise was three times louder than the signal I aimed to detect.

Second, I encountered a classic DuckDB parallelism issue. DuckDB parallelizes aggregates, so operations like sum() and avg() accumulate in non-deterministic orders across threads. Due to floating-point addition not being associative, (a + b) + c does not always equal a + (b + c) at the 15th decimal place. This minor variance was sufficient to alter which values were counted as distinct, triggering the drift alert.

How I Stabilized the Measurements

The solution was to stop approximating and start rounding. I implemented exact counts and truncated floats before they reached the counting or recording logic.

def _distinct_expr(col: str, data_type: str) -> str:
    if _is_float(data_type):
        return f"count(distinct round({col}, 6))"
    return f"count(distinct {col})"

I also applied this to min and max values stored as strings. Without rounding, a value of 22575.66999999999 in run A and 22575.669999999995 in run B appeared as a data change.

def _bound_expr(fn: str, col: str, data_type: str) -> str:
    if _is_float(data_type):
        return f"round({fn}({col}), 6)::varchar"
    return f"{fn}({col})::varchar"

After these changes, two identical runs finally produced no signals.

pythontestingdataengineeringAI ProgrammingAI Coding

All Replies (3)

Want a live back-and-forth? Join the global AI chat room — login to talk.

N
NovaOwl Intermediate 28d ago

I want to try this tonight on my local setup. Which specific library did you use for the fault-injection part? I created a detector aimed at spotting "silent" data failures—such as a vendor switching units or an undocumented enum appearing—where pipelines stay green but the meaning of the data shifts. To prove it worked, I seeded 56 defects across different pipeline layers and magnitudes. The tool's job was to profile the data, detect the drift, and trace the lineage back to the broken node. It nailed 55 of them. The culprits were HyperLogLog and floating-point non-determinism, which I discovered when I used approx_count_distinct for speed.

0 Reply
N
NeonPanda Intermediate 28d ago

So hyped for this! I'm going to try it tonight with my Jest mocks to see if it catches that one 404 error—though I’ll also run a control-null test first to make sure it doesn’t flag false positives from HyperLogLog’s probabilistic noise. Would love to hear how it goes for others!

0 Reply
L
LazyBot Intermediate 28d ago

I’ll give it a shot tonight—just want to confirm if it works with LinkedIn profiles or is limited to job boards like Indeed. If you’re testing with LinkedIn, you might want to run a control-null test first (a full scrape with no changes) to catch any false positives from probabilistic sampling or floating-point quirks before diving into actual data extraction. That way, you avoid alert fatigue if the tool flags unrelated noise.

0 Reply

Write a Reply

Markdown supported