fix: correct openedAt epoch corruption in PR merge MV (IN-1190)#4320
Conversation
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a data-corruption bug in the Tinybird materialized-view pipe pull_request_analysis_baseline_merge_MV.pipe, which feeds the Insights "Merge lead time" widget. In the pipe's ANY LEFT JOIN, unmatched baseline rows have their non-nullable openedAt (DateTime64(3)) column backfilled by ClickHouse with the type's zero-default (1970-01-01) rather than SQL NULL. The previous COALESCE(existing.openedAt, new.openedAt) therefore always selected the epoch sentinel over the real timestamp, corrupting openedAt for every new PR on first merge and producing nonsensical lead-time values.
The fix replaces the COALESCE with if(existing.openedAt != toDateTime(0), existing.openedAt, new.openedAt), matching the zero-sentinel guard already used for the other non-nullable fields in the same SELECT.
Changes:
- Replace
COALESCE(existing.openedAt, new.openedAt)with atoDateTime(0)zero-sentinel guard so a brand-new PR's realopenedAtis preserved instead of the epoch backfill value. - Prevents new corruption only; historical rows are handled by a separate out-of-band backfill (noted in the description).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Fixes a data-corruption bug in the Tinybird
pull_request_analysis_baseline_merge_MVpipe that caused the Insights "Merge lead time" widget to show absurd values (e.g. "53 years average merge time" for Project Jupiter — IN-1190 / linuxfoundation/insights#1997).Root cause:
openedAtis a non-nullableDateTime64(3)column. In the pipe'sANY LEFT JOINbetween newly-aggregated PR events and the existing baseline row, when no baseline row exists yet (a brand-new PR), ClickHouse backfillsexisting.openedAtwith the type's zero-default (1970-01-01, epoch) rather than SQL NULL. The oldCOALESCE(existing.openedAt, new.openedAt)therefore always picked the non-NULL epoch value over the real timestamp, permanently corruptingopenedAton first merge for any new PR.Fix:
if(existing.openedAt != toDateTime(0), existing.openedAt, new.openedAt) as openedAt— mirrors the exact zero-sentinel guard already used for every other non-nullable field in the sameSELECT(id,sourceId,segmentId,channel,memberId,platform).Confirmed via crowd.dev's production Postgres that the source data (
activityRelations, CDC-replicated) is clean — zero epoch-corruptedpull_request-opened/merge_request-openedtimestamps for Project Jupiter. The corruption is 100% isolated to this Tinybird MV transformation, not an upstream ingestion issue.Scope
services/libs/tinybird/pipes/pull_request_analysis_baseline_merge_MV.pipe), single SQL node.ALTER TABLE ... UPDATErepair, scoped bysegmentId, sourced fromactivityRelations(confirmed 100% match rate against the true historical values) — being coordinated separately since it needs direct ClickHouse/Tinybird admin access outside the standard pipe deploy workflow.Verification
Deploy order
Test plan
pull_requests_merge_lead_timeforproject=project-jupiter—openedToReviewAssignedSecondsshould NOT change yet (existing rows are already corrupted; this only affects new merges going forward)openedAtrows appear inpull_requests_analyzedfor newly-ingested PRs after deployhttps://claude.ai/code/session_01X6BudzWBdNg4C5A5DKo325
Note
Medium Risk
Touches core PR analytics merge logic in a materialized pipe; behavior is a targeted bugfix but wrong deploy timing could still leave bad metrics until historical backfill.
Overview
Fixes openedAt corruption in the
pull_request_analysis_results_mergednode ofpull_request_analysis_baseline_merge_MV.pipewhen a new PR has no baseline row yet.The merge expression changes from
COALESCE(existing.openedAt, new.openedAt)toif(existing.openedAt != toDateTime(0), existing.openedAt, new.openedAt), matching the zero-sentinel pattern already used for other non-nullable string fields. On anANY LEFT JOINmiss, ClickHouse fillsexisting.openedAtwith1970-01-01instead of NULL, so COALESCE kept the epoch and overwrote the real open time—skewing downstream duration fields (e.g. merge lead time). This only stops new bad rows; existing corruptedpull_requests_analyzeddata is out of scope for this change.Reviewed by Cursor Bugbot for commit 792e4f5. Bugbot is set up for automated code reviews on this repo. Configure here.