Skip to content

fix(data-imports): retry transient pooler blip on repartition bookkeeping saves - #75747

Merged
Gilbert09 merged 2 commits into
masterfrom
posthog-code/fix-repartition-bookkeeping-save-retry
Jul 31, 2026
Merged

fix(data-imports): retry transient pooler blip on repartition bookkeeping saves#75747
Gilbert09 merged 2 commits into
masterfrom
posthog-code/fix-repartition-bookkeeping-save-retry

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

Error tracking surfaced an OperationalError: query_wait_timeout (chained from psycopg.errors.ProtocolViolation) from the data-imports pipeline, with the top in-app frame in ExternalDataSchema.save. It fired during a GoogleAds sync, but the failing code has nothing GoogleAds-specific about it.

The call chain: maybe_flag_for_repartitionschema.record_partition_measurement()_save_sync_type_config()schema.save(). This bookkeeping write backs every repartition-controller state update on the schema (partition-size measurement, pending/swap/claim markers, revive markers) and fires once per batch, across every schema sync — a hot-ish path. _save_sync_type_config already skips the activity-log's extra SELECT for exactly this class of pooler issue (see the comment on save()), but the .save() call itself had no retry.

query_wait_timeout is PgBouncer signalling that a query waited too long for a free backend connection under pool pressure — a transient infra blip, not a data-shape or config bug. maybe_flag_for_repartition already swallows any exception here (detection is deliberately best-effort and must never fail post-load), so the sync itself was never at risk. The gap is that the bookkeeping write for that run is silently lost, and the blip gets reported as an unhandled exception.

This mirrors an established pattern already in the same file: is_auto_repartition_enabled (one function up in repartition_controller.py) already wraps its Team.objects.get() lookup with retry_on_db_connection_drop for the same reason.

Changes

  • _save_sync_type_config now wraps its .save() call with the existing retry_on_db_connection_drop helper (posthog/temporal/common/utils.py) — one retry on OperationalError/InterfaceError, evicting the stale pooled connection first. A second consecutive failure still propagates, unchanged.
  • Lazy-imports the helper inside the method (matching the existing pattern in external_data_source.reload_schemas) so temporalio stays off this model's module-level import path.

How did you test this code?

Added TestSaveSyncTypeConfigRetriesOnConnectionDrop to test_models.py:

  • test_retries_once_on_operational_error_then_succeeds — the first .save() call raises OperationalError, the second succeeds; asserts the write lands. Catches the regression this fixes: without the retry, the write silently drops on a single pooler blip.
  • test_second_consecutive_operational_error_propagates — guards that a persistent failure still surfaces, rather than being silently retried forever.

Ran:

  • hogli test products/warehouse_sources/backend/tests/test_models.py — 123 passed
  • hogli test products/warehouse_sources/backend/temporal/data_imports/pipelines/core/test_repartition_controller.py — 30 passed
  • uv run mypy --cache-fine-grained products/warehouse_sources/ (repo-wide mypy OOM'd in this sandbox — reported via hogli devex:feedback; ran scoped to the touched product instead) — clean, 6152 files
  • ruff check / ruff format — clean
  • tach check --dependencies --interfaces — clean
  • hogli ci:preflight --fix — 0 failures

Automatic notifications

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

Docs update

N/A — internal reliability fix, no user-facing or API behavior change.

🤖 Agent context

Autonomy: Fully autonomous

Triaged directly from the linked error-tracking issue via PostHog's error-tracking MCP tools (query-error-tracking-issue, query-error-tracking-issue-events), which surfaced the full stack trace and confirmed the failure originates in shared repartition-controller bookkeeping, not the GoogleAds connector. Used Explore subagents to trace the call frequency/concurrency of maybe_flag_for_repartition, confirm no existing retry wrapping on this save path, and check for a lighter non-Temporal retry helper before settling on reusing retry_on_db_connection_drop.

Checked for a duplicate fix first: searched open PRs by exception type, message phrase, and module path, and reviewed the maintainer's own open PR queue. Found several related-but-non-overlapping PRs applying the same "classify/retry transient PgBouncer blip" pattern to other call sites in this pipeline (#75733, #75735, #75737, #75530) — none touch _save_sync_type_config or the repartition-controller bookkeeping save path.

Invoked /writing-tests before adding the regression test.

…ping saves

Repartition controller bookkeeping writes (partition-size measurement, pending/swap/claim markers) go through `ExternalDataSchema._save_sync_type_config`, an unretried `.save()` fired once per batch across every schema sync. A transient PgBouncer `query_wait_timeout` there was falling straight through to the caller's broad exception handler, silently losing that run's write.

Wrap the save with the existing `retry_on_db_connection_drop` helper (already used one call up in the same file for the rollout-flag team lookup) so a one-off pool blip retries once instead of losing the write.
@trunk-io

trunk-io Bot commented Jul 30, 2026

Copy link
Copy Markdown

😎 This pull request was merged.

@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 30, 2026 19:19
…ave-retry

Generated-By: PostHog Code
Task-Id: eff56fcd-4f09-40c7-a2b1-7ef7eed9fecd
@Gilbert09 Gilbert09 added the stamphog Request AI approval (no full review) label Jul 31, 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, contained reliability fix wrapping an existing DB retry helper around one bookkeeping save; not in risky territory (no schema/API/auth/billing changes), matches an established in-file pattern, and ships with regression tests covering both the retry-succeeds and retry-exhausted paths.

  • Author wrote 50% of the modified lines and has 33 merged PRs in these paths (familiarity MODERATE).
  • 👍 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 11L, 1F substantive, 58L/2F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (58L, 2F, single-area, fix)
stamphog 2.0.0b4 .stamphog/policy.yml @ c551d59 · reviewed head 239f7a4

@Gilbert09
Gilbert09 merged commit 527e148 into master Jul 31, 2026
263 checks passed
@Gilbert09
Gilbert09 deleted the posthog-code/fix-repartition-bookkeeping-save-retry branch July 31, 2026 14:25
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 31, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-31 15:01 UTC Run
prod-us ✅ Deployed 2026-07-31 15:18 UTC Run
prod-eu ✅ Deployed 2026-07-31 15:20 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.

1 participant