Skip to content

fix(tracing): serialize retention updates - #108170

Open
pauldambra wants to merge 2 commits into
posthog/limit-evaluation-context-namesfrom
posthog/serialize-trace-retention-updates
Open

pauldambra wants to merge 2 commits into
posthog/limit-evaluation-context-namesfrom
posthog/serialize-trace-retention-updates

Conversation

@pauldambra

Copy link
Copy Markdown
Member

[Robot] Prepared by PostHog Desktop.

Problem

Concurrent tracing retention updates can pass the same update limit check.

Changes

Lock and re-read the config before checking the update limit and saving. Run flag checks before the transaction.

This fixes existing behavior before the module split in #99248.

How did you test this code?

API tests cover stale config reads, unchanged settings, and flag checks outside the update transaction. The focused tests ran against both module layouts. The full type check covers the combined fixes.

Release status

  • This change is behind a feature flag and is not available to users

Retention changes use the existing tracing-settings-retention flag. This PR does not change flag access.

Automatic notifications

  • Publish to changelog?

Docs update

No existing document under docs/ covers this validation detail.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: PostHog Desktop / Codex, GPT-6

Tools: Git, GitHub CLI, hogli, pytest, Ruff, mypy, and CodeRabbit.
Skills: stacking-prs, improving-drf-endpoints, writing-tests, writing-code-comments, writing-user-facing-copy, setting-up-devbox, running-ci-preflight, reviewing-with-coderabbit, writing-pr-descriptions.
Each existing bug has its own layer below the refactor. No matching open fix appeared in the PR search.
The combined CodeRabbit review found two deployment tradeoffs in the membership layer. That PR records the decisions.


Created with PostHog Desktop

@pauldambra pauldambra self-assigned this Sep 29, 2026
@posthog

posthog Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

🦔 PostHog Review reviewed this pull request

Nothing worth raising this time. Enjoy the moment:

A happy dog on a sunny path

@github-actions

github-actions Bot commented Sep 29, 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.

⚠️ Duplication (Python) — 11 new duplicated blocks (worst 203 tokens)

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
posthog/temporal/ai_observability/run_aggregate_evaluation.py:692 posthog/temporal/ai_observability/run_trace_evaluation.py:928 38 203
products/warehouse_sources/backend/temporal/data_imports/sources/companycam/source.py:1 products/warehouse_sources/backend/temporal/data_imports/sources/wix/source.py:1 21 164
posthog/temporal/ai_observability/run_aggregate_evaluation.py:584 posthog/temporal/ai_observability/run_trace_evaluation.py:855 24 135
products/signals/backend/scout_harness/tools/report.py:1475 products/signals/backend/scout_harness/tools/report.py:1647 21 133
products/signals/backend/scout_harness/tools/report.py:1604 products/signals/backend/scout_harness/tools/report.py:1754 30 118
posthog/temporal/ai_observability/run_session_evaluation.py:466 posthog/temporal/ai_observability/run_trace_evaluation.py:664 24 115
posthog/management/commands/backfill_hogflow_billable_action_types.py:49 posthog/management/commands/refresh_hog_flows.py:69 23 112
products/batch_exports/backend/api/batch_export.py:2092 products/batch_exports/backend/api/batch_export.py:2131 24 112
products/warehouse_sources/backend/temporal/data_imports/sources/pardot/pardot.py:2 products/warehouse_sources/backend/temporal/data_imports/sources/wix/wix.py:1 12 97
products/signals/backend/scout_harness/tools/report.py:1516 products/signals/backend/scout_harness/tools/report.py:1675 12 71
products/signals/backend/scout_harness/tools/report.py:1531 products/signals/backend/scout_harness/tools/report.py:1690 16 70
⚠️ Duplication (TypeScript) — 2 new duplicated blocks (worst 158 tokens)

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
products/business_knowledge/frontend/scenes/settings/businessKnowledgeSettingsLogic.test.ts:10 products/data_catalog/frontend/certificationsLogic.test.ts:17 23 158
products/business_knowledge/frontend/scenes/settings/businessKnowledgeSettingsLogic.test.ts:10 products/data_quality/frontend/dataQualityGateLogic.test.ts:14 23 158
✅ Playwright — all passed

All tests passed.

View test results →

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: f7cc2740-be11-4d91-9075-87ae41cae1c7

📥 Commits

Reviewing files that changed from the base of the PR and between 688f743 and 59f465a.

📒 Files selected for processing (1)
  • posthog/api/test/test_team_tracing_config.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The PATCH handler locks the current tracing config row inside a transaction. For retention changes, it checks throttling against the locked row and rejects stale configuration with a validation error. Other PATCH fields also save against the locked row. Tests cover stale and refreshed configurations and check the savepoint depth observed by the feature-flag callback.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 59f46

The handler currently serializes these updates, but a lock regression could go undetected by the sequential tests. This is a bounded test-coverage risk, not an observed current failure.

Architecture Summary

Architecture risk: 🔵 Low · up to 59f46

The change affects 1 system.

Changed systems: posthog

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — posthog (api) was modified; 2 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in posthog/api/team.py: handle_tracing_config now locks the config row and saves within an atomic transaction. For a submitted retention change, it rechecks the throttle against the locked row and returns a retention_days validation error if throttled; if the originally loaded retention differs from the locked retention, it returns a reload-and-retry validation error instead of saving stale data. Other PATCH fields save against the locked row as well.
  • observed — Modified behavior in posthog/api/test/test_team_tracing_config.py: The test module imports Django’s database connection to inspect savepoint depth.
  • observed — Modified behavior in posthog/api/test/test_team_tracing_config.py: The route comment now names handle_tracing_config without the previous posthog/api/team.py module path.
  • observed — Modified behavior in posthog/api/test/test_team_tracing_config.py: The free-tier retention test records the current savepoint depth and asserts that the feature-flag callback observes the same depth during the PATCH request.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is complete and matches the template. It states the concurrency problem, user-visible fix, test coverage, feature-flag status, documentation status, and agent context. The testing sect…
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
posthog/api/test/test_team_tracing_config.py (1)

272-273: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Add a separate-transaction concurrency regression test.

test_second_change_within_24_hours_is_refused sends both PATCH requests sequentially. The stale_config case checks the locked-row recheck, but it would still pass if select_for_update() were removed while that recheck remained. Add a TransactionTestCase test with two database connections. Hold the first update before commit, then assert that the second update waits for the row lock and rejects the retention change after the first transaction commits.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 6ac46be6-c028-4398-b5db-39dfa7ab35bf

📥 Commits

Reviewing files that changed from the base of the PR and between 2f3929c and 4c3bd30.

📒 Files selected for processing (2)
  • posthog/api/team.py
  • posthog/api/test/test_team_tracing_config.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 3 remain after this review.

@posthog

posthog Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot 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.

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

PostHog Review

Found 1 consider.

Comment thread posthog/api/team.py
Comment on lines +353 to +357
with transaction.atomic():
locked_config = TeamTracingConfig.objects.select_for_update().get(team=team)
retention = serializer.validated_data.get("retention_days")
if retention is not None and retention != locked_config.retention_days:
throttle_error = retention_update_throttle_error(locked_config.retention_last_updated)

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.

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

Throttle validation still runs before the row lock

consider bug

Issue description

serializer.is_valid() runs before this lock, and validate_retention_days() checks the throttle using the earlier config read. If validation runs just before the 24-hour limit expires, it can reject the request even if the limit has expired by the time the locked config is read. The fresh check inside the transaction cannot correct that rejection.

Why we think it's a valid issue
  • Checked: Traced handle_tracing_config from serializer validation through the locked config check, and reviewed retention_update_throttle_error.
  • Found: TeamTracingConfigSerializer.validate_retention_days checks the throttle at posthog/api/team.py:326-330. handle_tracing_config calls serializer.is_valid() at posthog/api/team.py:352, before acquiring the lock and checking the locked timestamp at posthog/api/team.py:353-359.
  • Found: retention_update_throttle_error compares the current time with the timestamp at posthog/models/team/logs_retention.py:63-69. If validation rejects the request, execution never reaches the fresh check. The handler’s retention is not None and retention != locked_config.retention_days guard at posthog/api/team.py:356 already limits the locked check to changed values.
  • Impact: A request validated just before the 24-hour boundary can be rejected even though the throttle has expired by the locked check. This is a narrow but reachable correctness issue, so keeping the finding at consider is appropriate.
Suggested fix

Keep flag and entitlement checks in serializer validation, but perform the throttle check only against locked_config inside the transaction. Preserve the unchanged-retention behavior so unrelated updates are not throttled.

Prompt to fix with AI (copy-paste)
## Context
@posthog/api/team.py#L353-357

<issue_description>
`serializer.is_valid()` runs before this lock, and `validate_retention_days()` checks the throttle using the earlier config read. If validation runs just before the 24-hour limit expires, it can reject the request even if the limit has expired by the time the locked config is read. The fresh check inside the transaction cannot correct that rejection.
</issue_description>

<issue_validation>
- **Checked:** Traced `handle_tracing_config` from serializer validation through the locked config check, and reviewed `retention_update_throttle_error`.
- **Found:** `TeamTracingConfigSerializer.validate_retention_days` checks the throttle at `posthog/api/team.py:326-330`. `handle_tracing_config` calls `serializer.is_valid()` at `posthog/api/team.py:352`, before acquiring the lock and checking the locked timestamp at `posthog/api/team.py:353-359`.
- **Found:** `retention_update_throttle_error` compares the current time with the timestamp at `posthog/models/team/logs_retention.py:63-69`. If validation rejects the request, execution never reaches the fresh check. The handler’s `retention is not None and retention != locked_config.retention_days` guard at `posthog/api/team.py:356` already limits the locked check to changed values.
- **Impact:** A request validated just before the 24-hour boundary can be rejected even though the throttle has expired by the locked check. This is a narrow but reachable correctness issue, so keeping the finding at `consider` is appropriate.
</issue_validation>

## Task
Investigate the issue and solve it

<potential_solution>
Keep flag and entitlement checks in serializer validation, but perform the throttle check only against `locked_config` inside the transaction. Preserve the unchanged-retention behavior so unrelated updates are not throttled.
</potential_solution>

@pauldambra
pauldambra added this pull request to stack #108171 September 29, 2026 08:52
@trunk-io

trunk-io Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@pauldambra
pauldambra force-pushed the posthog/serialize-trace-retention-updates branch from 4c3bd30 to 688f743 Compare September 29, 2026 15:23
Generated-By: PostHog Desktop
Task-Id: 160dfc49-dceb-4887-b9f5-a93b2da017ed
Generated-By: PostHog Desktop
Task-Id: 160dfc49-dceb-4887-b9f5-a93b2da017ed
@pauldambra
pauldambra force-pushed the posthog/serialize-trace-retention-updates branch from 688f743 to 59f465a Compare September 29, 2026 16:09
@pauldambra
pauldambra marked this pull request as ready for review September 29, 2026 16:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-29T16:29:41.790061Z 59f465a Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant