Skip to content

chore(devex): flag hot parent row locks - #89930

Draft
langesven wants to merge 1 commit into
masterfrom
audit-select-for-update-team-org
Draft

chore(devex): flag hot parent row locks#89930
langesven wants to merge 1 commit into
masterfrom
audit-select-for-update-team-org

Conversation

@langesven

Copy link
Copy Markdown
Contributor

Problem

Developers and coding agents can introduce broad write contention when they use Team or Organization rows as local mutexes.

PostgreSQL foreign-key checks can take KEY SHARE locks on parent rows. FOR UPDATE conflicts with those locks, so unrelated child writes can wait.

Changes

  • New parent-row locks now fail the baseline-aware Semgrep pass unless the callsite includes a short justification.
  • The rule ignores FOR NO KEY UPDATE, which remains compatible with foreign-key KEY SHARE locks.
  • Root agent guidance recommends resource-scoped rows, constraints, conditional updates, or transaction-scoped advisory locks.

Examples for DevEx review:

Shape Existing example Review question
Team used for a child-table limit assign_slot Should a slot-allocation row or constraint serialize this invariant?
Team used for ticket numbering create_with_number Should a per-team sequence row allocate ticket numbers?
Team used for child singleton creation create_unlisted_dashboard Could a unique invariant or tag-scoped mutex replace the Team lock?
Resource-scoped row lock ensure_managed_warehouse_direct_source This locks the lifecycle or source row instead of Team.
External work before a short lock _persist_columns This completes ClickHouse work before locking the target table row.
Resource-keyed advisory locks create_workflow_task This keeps external work outside the transaction and avoids a parent-row lock.

This change is mechanical and has no user-visible effect.

How did you test this code?

  • uvx semgrep --test --config .semgrep/rules/devex .semgrep/rules/devex covers direct and chained locks, safe no_key=True calls, unrelated models, and justified suppression.
  • hogli ci:preflight --fix completed without failures.
  • Mypy was not run because the Python change is a Ruff-disabled Semgrep fixture, not runtime Python.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Automatic notifications

  • Publish to changelog?

Docs update

Updated root AGENTS.md with PostgreSQL parent-row locking guidance for coding agents.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

OpenAI Codex in OMP authored the rule, fixture, guidance, and draft PR. Skills used: /writing-code-comments, /writing-tests, /writing-user-facing-copy, /running-ci-preflight, /writing-pr-descriptions, and /reviewing-before-pr.

The Greptile CLI was unavailable locally, so the harness fallback reviewer checked the committed diff. It found two false positives: no_key=True and test-module paths. Both were fixed before this PR opened. No private operational data or session-derived examples appear in this public change.

@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@langesven langesven self-assigned this Aug 27, 2026
@github-actions

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.

@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@webjunkie webjunkie 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.

We could probably steer towards using the XYZConfig models that map 1:1 to Team? Or are these also hot tables?

@langesven

Copy link
Copy Markdown
Contributor Author

We could probably steer towards using the XYZConfig models that map 1:1 to Team? Or are these also hot tables?

I am not nearly as deep in this codebase as you are, but having glimpsed at it - yes that looks like a good pattern!

Then lock contention would at most be limited to everyone that uses the same XYZConfig and not everyone that interacts with the global Team. That would severely reduce possible blast radius. It can still cause an issue if all of this happens on the same pgbouncer and it runs out of connections and such, but it's a lot less likely, so it'd be a pretty good improvement regardless imo

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants