Skip to content

fix(cohorts): stop server-side cursor scan in dependency cache warm - #75972

Merged
trunk-io[bot] merged 3 commits into
masterfrom
posthog-self-driving/fixcohorts-stop-server-side-cursor-scan-deac49
Aug 28, 2026
Merged

trunk-io[bot] merged 3 commits into
masterfrom
posthog-self-driving/fixcohorts-stop-server-side-cursor-scan-deac49

Conversation

@posthog

@posthog posthog Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Saving a cohort can 500: cohort_changed's post_save handler fires transaction.on_commit(...) synchronously, and cohort creation isn't wrapped in an outer atomic() block, so the callback runs inline with the request.
  • That callback lands in warm_team_cohort_dependency_cache, which scans the team's cohorts with .iterator() — opening a named PostgreSQL server-side cursor — then does cache round-trips and more ORM work between fetchmany batches.
  • If the connection is recycled in that window (e.g. behind a pooler), psycopg raises InvalidCursorName: cursor "..." does not exist, and nothing catches it, so it escapes perform_create as a 500.
  • The generated cursor name embeds a connection id and counter, so recurrences of this failure fragment across many distinct error-tracking fingerprints instead of grouping into one issue.

Changes

  • Replace .iterator(chunk_size=batch_size) in warm_team_cohort_dependency_cache with keyset pagination on id, so no server-side cursor is held open across the per-cohort cache writes.

How did you test this code?

  • This sandbox has no reachable Postgres/dev stack (hogli test errors trying to resolve host db), so I could not run the existing test suite (products/cohorts/backend/models/test/test_dependencies.py, which already covers warm_team_cohort_dependency_cache including batching behavior).
  • Ran ruff check/ruff format and mypy on the changed file; both pass clean.
  • Did not add a new test: the existing test_warm_team_cohort_dependency_cache_batching tests already exercise the batched-scan behavior end-to-end (result correctness across batch_size values), and this change preserves that contract while swapping the underlying query strategy — a cursor-recycling failure isn't reproducible in the Django test harness.

Automatic notifications

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

Docs update

N/A — internal implementation detail, no documented behavior changes.

🤖 Agent context

Autonomy: Fully autonomous

I (Claude, via PostHog's coding agent) investigated an inbox report describing intermittent 500s on cohort save caused by InvalidCursorName errors during dependency-cache warming. Traced the failure to .iterator() in warm_team_cohort_dependency_cache (products/cohorts/backend/models/dependencies.py) holding a named server-side cursor open across cache calls in the synchronous on_commit path. Fixed by switching to keyset pagination, which addresses the root cause without changing the function's public behavior or moving the warm off the request path (a larger, separate change the report flagged as a nice-to-have, not required to fix the reported symptom).


Created with PostHog Desktop from this inbox report.

warm_team_cohort_dependency_cache() used .iterator(), which opens a named
PostgreSQL server-side cursor and holds it open across per-cohort cache
round-trips. If the connection is recycled in that window, psycopg raises
InvalidCursorName, which escapes the synchronous transaction.on_commit
callback fired by cohort creation/update and surfaces as a 500. Replaced
with keyset pagination on id so no cursor is held open between batches.

Generated-By: PostHog Code
Task-Id: 0e296c94-4ea3-4fad-ac3c-4685d393defc
@trunk-io

trunk-io Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@scheduled-actions-posthog

Copy link
Copy Markdown
Contributor

This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the stale label – otherwise this will be closed in another week. If you want to permanently keep it open, use the waiting label.

@haacked haacked moved this to Todo in Feature Flags Aug 10, 2026
@gustavohstrassburger gustavohstrassburger moved this from Todo to In Progress in Feature Flags Aug 18, 2026
@gustavohstrassburger
gustavohstrassburger marked this pull request as ready for review August 18, 2026 19:44
Copilot AI lite review requested due to automatic review settings August 18, 2026 19:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates cohort dependency cache warming to avoid holding a PostgreSQL server-side cursor open across cache/ORM work, preventing intermittent InvalidCursorName errors when connections are recycled.

Changes:

  • Replaced QuerySet.iterator(chunk_size=...) scan with keyset pagination (id__gt last_id, ordered by id) in warm_team_cohort_dependency_cache.
  • Expanded the function docstring to document the rationale for avoiding .iterator() here.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@trunk-io

trunk-io Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

✅ ClickHouse migration SQL — none

No ClickHouse migrations in the latest push.

@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 fix that swaps a server-side cursor iterator for keyset pagination with identical behavior; not risky territory, and a human reviewer already approved with no unresolved concerns.

Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 28L, 1F substantive — within ceiling
tier ✓ T1-agent / T1b-small (28L, 1F, single-area, fix)
stamphog 2.0.0b4 .stamphog/policy.yml @ 04b6060 · reviewed head 04b6060

@trunk-io
trunk-io Bot merged commit e376772 into master Aug 28, 2026
355 of 357 checks passed
@trunk-io
trunk-io Bot deleted the posthog-self-driving/fixcohorts-stop-server-side-cursor-scan-deac49 branch August 28, 2026 14:12
@github-project-automation github-project-automation Bot moved this from Approved to Done in Feature Flags Aug 28, 2026
@posthog

posthog Bot commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor Author

🦔 PostHog Review reviewed this pull request

Nothing worth raising this time, so here's a calming picture instead:

Someone relaxing in a sunny garden

@deployment-status-posthog

deployment-status-posthog Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-08-28 14:45 UTC Run
prod-us ✅ Deployed 2026-08-28 15:10 UTC Run
prod-eu ✅ Deployed 2026-08-28 15:09 UTC Run

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

Labels

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants