Skip to content

fix(data-imports): treat a racy optimize/vacuum scan failure as transient - #74635

Merged
trunk-io[bot] merged 1 commit into
masterfrom
posthog-code/fix-delta-optimize-race-error
Jul 29, 2026
Merged

fix(data-imports): treat a racy optimize/vacuum scan failure as transient#74635
trunk-io[bot] merged 1 commit into
masterfrom
posthog-code/fix-delta-optimize-race-error

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

An error tracking issue surfaced a DeltaError during a Postgres sync's append write:

Failed to parse parquet: Parquet error: Optimize selected-file scan failed while scanning data:
Parquet error: Failed to fetch metadata for file ...: Object Store error: Object at location ...
not found: ... 404 Not Found

The stack trace traces through run_pre_write_defensive_compact (common/extract.py) → DeltaTableHelper.run_maintenancecompact_if_fragmentedcompact_table, landing in delta-rs's optimize.compact call.

optimize.compact plans its rewrite against the table's file list at the start of a scan, then reads those files. If a concurrent maintenance pass on the same table (compact/vacuum can run from more than one call site across a sync, and a heartbeat-timed-out Temporal activity attempt can keep running as a "zombie" alongside its own retry — a race this package's README already documents for the equivalent repartition path) vacuums one of those files out from under the scan before it's read, delta-rs raises exactly this error.

Since the scan fails before anything commits, the table is left exactly as it was — just still fragmented — so this is safe to skip and retry on the next maintenance pass. run_pre_write_defensive_compact already treats maintenance failures as best-effort (the sync isn't affected either way), but this specific error was still being captured to error tracking as if it were a bug, alongside an existing, narrower classifier (is_transient_object_store_error) for other known-transient blips.

Changes

  • Added is_transient_delta_maintenance_error next to the existing is_transient_object_store_error in delta_table_helper.py, matching this specific delta-rs error signature.
  • run_pre_write_defensive_compact now logs a warning instead of capturing when this signature is hit, the same treatment already given to transient object-store errors.

The same call chain also fans out into a couple of other pre-write/post-load maintenance call sites (common/load.py, pipeline_v2/pipeline.py), where this same race is theoretically reachable too. I left those out of this PR: #74630 is already open and actively editing those exact same try/except blocks for a related but distinct S3-throttling signature, so extending them here risked a needless merge conflict. Once that one lands, extending is_transient_delta_maintenance_error to those call sites would be a natural, low-risk follow-up.

No retry policy, timeout, or NonRetryableErrors classification changed — this only reclassifies an already-non-fatal exception path.

How did you test this code?

  • Added TestIsTransientDeltaMaintenanceError to test_delta_table_helper.py, parameterized over the racy scan signature, an unrelated DeltaError, and a non-DeltaError exception carrying the same message — locks in that only the specific delta-rs signature matches.
  • Added test_logs_transient_delta_maintenance_race_without_capturing to test_extract.py's TestRunPreWriteDefensiveCompact, mirroring the existing object-store-error case, asserting capture_exception is not called and a warning is logged instead.
  • Ran test_delta_table_helper.py, test_extract.py, test_load.py, and pipeline_v2/test_pipeline.py: 122 passed, 4 pre-existing failures in TestGetDeltaTableUnrecoverableErrors (needs a live object-storage service unavailable in this sandbox; identical on unmodified master), several DB-backed tests erroring for the same sandbox reason.
  • ruff check / ruff format --check clean on all changed files.
  • Full-repo uv run mypy --cache-fine-grained . clean (17074 source files).

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

N/A — internal pipeline implementation detail, no user-facing or API surface change.

🤖 Agent context

Autonomy: Fully autonomous

Triaged from a live PostHog error-tracking issue (webhook-delivered) using Claude Code. Pulled the stack trace, exception sample, and warehouse_sources_* properties via the PostHog MCP tools to confirm the exact call path and that it's a v2 (non-DLT) pipeline sync. Searched open PRs for duplicates: found #74630 touching the same load.py/pipeline_v2/pipeline.py try/except blocks for a different error signature (S3 throttling, not this optimize/vacuum race) — scoped this PR down to the file this issue's stack trace actually implicates (extract.py + delta_table_helper.py) to avoid duplicating or conflicting with that in-flight work. Also checked #74541 (a different delta_table_helper.py bug, redundant re-fetch inside compact/vacuum) for overlap — none, since it doesn't touch the classifier or call-site error handling this PR changes. Invoked /writing-tests before adding coverage.

@trunk-io

trunk-io Bot commented Jul 29, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 29, 2026 11:41
@Gilbert09 Gilbert09 added the stamphog Request AI approval (no full review) label Jul 29, 2026 — with PostHog
stamphog[bot]
stamphog Bot previously approved these changes Jul 29, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small, well-tested reclassification of a known-transient delta-rs maintenance error to warning-level logging instead of error capture; no behavior change to sync correctness, retries, or data models. Author owns this code with strong familiarity and added targeted regression tests.

  • Author wrote 100% of the modified lines and has 11 merged PRs in these paths (familiarity STRONG).
  • 👍 on the PR from hex-security-app[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 28L, 2F substantive, 73L/4F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (73L, 4F, single-area, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ 545937f · reviewed head 2af85ae

@talyn-app

talyn-app Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

/trunk merge

…ient

Pre-write delta maintenance (`run_pre_write_defensive_compact`) can hit a `DeltaError` when `optimize.compact`'s scan reads a file a concurrent maintenance pass has already vacuumed away. The scan fails before anything commits, so the table is left exactly as it was — just still fragmented — making this safe to skip and retry on the next pass, the same way an existing transient object-store error is already handled.

Classify this specific delta-rs error signature the same way and log a warning instead of capturing it to error tracking.

Generated-By: PostHog Code
Task-Id: ee9ef592-a2cd-471f-a4a4-1520a5e26b1b
@Gilbert09
Gilbert09 force-pushed the posthog-code/fix-delta-optimize-race-error branch from 2af85ae to 383de70 Compare July 29, 2026 14:40
@stamphog
stamphog Bot dismissed their stale review July 29, 2026 14:42

New commits pushed (delta classified non_linear_history) — stamphog approval dismissed; re-review running automatically.

@talyn-app

talyn-app Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

/trunk merge

@stamphog

stamphog Bot commented Jul 29, 2026

Copy link
Copy Markdown

Note

🤖 stamphog reviewed 383de709485dd2ec045d7fb0772b85c86e0e98df — verdict: WAIT

@hex-security-app[bot] still has a review in flight (👀) after 5 minutes — not approving over an unfinished review. The stamphog label has been kept; the review re-runs on the next push, or remove and re-apply the label once the reviewer finishes.

  • 👍 on the PR from hex-security-app[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 27L, 2F substantive, 72L/4F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (72L, 4F, single-area, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ 30ad54b · reviewed head 383de70

@Gilbert09
Gilbert09 requested a review from a team July 29, 2026 15:49
@Gilbert09 Gilbert09 added stamphog Request AI approval (no full review) and removed stamphog Request AI approval (no full review) labels Jul 29, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small, well-tested reclassification of an already best-effort maintenance error path from captured-exception to warning-logged; the underlying try/except already prevented this from affecting the sync, so behavior for the actual data path is unchanged. Author is on the owning team with strong familiarity and full test coverage.

  • Author wrote 100% of the modified lines and has 15 merged PRs in these paths (familiarity STRONG).
  • 👍 on the PR from hex-security-app[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 27L, 2F substantive, 72L/4F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (72L, 4F, single-area, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ b21458f · reviewed head 383de70

@fuziontech fuziontech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review on behalf of @fuziontech.

Blocking: products/warehouse_sources/backend/temporal/data_imports/pipelines/core/delta_table_helper.py:88-94 classifies every DeltaError containing Optimize selected-file scan failed as transient, but that text identifies the optimize scan phase rather than the specific concurrent-vacuum 404 race. Corrupt Parquet, authorization failures, or persistently missing active files can carry the same wrapper and will now be omitted from error tracking by run_pre_write_defensive_compact. Please require the known missing-object/404 cause (or another structured indication of that race) and add a negative test with the same optimize prefix but a non-404 scan failure.

@Gilbert09
Gilbert09 dismissed fuziontech’s stale review July 29, 2026 16:02

bad review bot

@trunk-io
trunk-io Bot merged commit f17bebf into master Jul 29, 2026
382 of 432 checks passed
@trunk-io
trunk-io Bot deleted the posthog-code/fix-delta-optimize-race-error branch July 29, 2026 17:27
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 29, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-29 18:37 UTC Run
prod-us ✅ Deployed 2026-07-29 18:59 UTC Run
prod-eu ✅ Deployed 2026-07-29 19:01 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants