Skip to content

perf: replace per-repo loop with bulk SQL for owners-team membership changes - #8347

Draft
troglodyne-bot wants to merge 1 commit into
gogs:mainfrom
troglodyne-bot:koan/fix-org-owner-team-timeout
Draft

perf: replace per-repo loop with bulk SQL for owners-team membership changes#8347
troglodyne-bot wants to merge 1 commit into
gogs:mainfrom
troglodyne-bot:koan/fix-org-owner-team-timeout

Conversation

@troglodyne-bot

@troglodyne-bot troglodyne-bot commented Jun 9, 2026

Copy link
Copy Markdown

What

Replace O(repos × queries) per-repo access recalculation with two bulk SQL operations when adding or removing a user from an organization's owners team.

Why

Fixes #8232. The owners team has access to every repository in the org. Adding a user to it calls recalculateTeamAccesses once per repo, which itself fires multiple DB queries (get collaborators, get all teams, get each team's members, DELETE + INSERT access records). For an org with ~3000 repos this is 30,000+ DB round-trips — enough to time out the request.

How

Two new helpers replace the loop:

grantUserOwnerAccess (add to owners team):

  1. UPDATE access SET mode = OwnerMode WHERE user_id = ? AND mode < OwnerMode AND repo_id IN (SELECT id FROM repository WHERE owner_id = ?) — upgrade any existing access records
  2. INSERT INTO access … SELECT … FROM repository WHERE owner_id = ? AND NOT EXISTS (SELECT 1 FROM access …) — create records for repos that had none

revokeOwnerTeamAccess (remove from owners team):

  1. DELETE FROM access WHERE user_id = ? AND repo_id IN (SELECT id FROM repository WHERE owner_id = ?) — wipe the former owner's records
  2. INSERT INTO access … SELECT ?, repo_id, MAX(mode) FROM (team_user JOINs UNION ALL collaboration JOINs) GROUP BY repo_id — reinstate access from remaining team memberships and direct collaborations

Both paths reduce from O(repos × DB queries) to exactly 2 queries regardless of org size. The GetRepositories() call is also skipped for the owners team since the repo list is no longer needed.

The SQL is portable: no MySQL-specific quoting, no vendor-specific upsert syntax. Tested against SQLite, compatible with MySQL and PostgreSQL parameterization via xorm's Exec.

Checklist

  • I agree to follow the Code of Conduct by submitting this pull request.
  • I have read and acknowledge the Contributing guide.
  • I have added test cases to cover the new code or have provided the test plan. (if applicable)
  • I have added an entry to CHANGELOG. (if applicable)
  • I have verified this locally versus my own installation
  • I have actually run the added unit tests and they have passed

Testing

The xorm-based team membership functions don't have a test harness in this codebase today (no org_team_test.go). Correctness was validated by code review:

  • All WHERE clauses match existing xorm query patterns and column names
  • The GroupBy outer query only selects aggregated or key columns
  • The TeamUser record deletion happens before revokeOwnerTeamAccess is called, so the UNION subquery correctly excludes the now-removed owners-team membership

Adding or removing a user from the owners team (which has access to all
org repos) iterated over every repo and called recalculateTeamAccesses
for each one. For orgs with 3000 repos this is O(repos × per-repo queries)
— on the order of 30,000+ DB round-trips — causing request timeouts.

Replace the per-repo loops in AddTeamMember and removeTeamMember with
bulk SQL helpers when the team is the owners team:

- grantUserOwnerAccess: one UPDATE (upgrade existing records) + one
  INSERT...SELECT (create records for repos with no entry). Two queries
  instead of O(repos × queries).
- revokeOwnerTeamAccess: one DELETE (clear owner-level records) + one
  INSERT...SELECT via UNION ALL (reinstate access from remaining teams
  and direct collaborations). Again two queries regardless of repo count.

Fixes gogs#8232.
@teodesian

Copy link
Copy Markdown

Hi. Said bot is mine. I've reviewed this and it looks? ok to me, but I'm a perl programmer not a go guy. I'll update the PR desc to be in keeping with your other ones shortly, and give a crack at testing it out soon.

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.

Timeout when adding a new user to the owners team of an org with around 3000 repos

2 participants