Sentry: FLAGSMITH-API-6BK
User impact
GET /api/v1/organisations/{organisation_pk}/groups/ takes ~2.6 seconds, and
users feel all of it — the org Groups page just sits there loading.
- 14 users affected, 16 occurrences
- First seen 2026-08-03T16:38Z, still occurring (last seen 2026-08-05T16:04Z)
- Not an error — nothing breaks, it's purely slow. This is why it was classified
info/low by Sentry and initially archived.
Where the time goes
One span accounts for essentially the whole request:
| Span |
Duration |
db — org permission check (below) |
2599 ms |
db — count user permission groups |
3 ms |
| remaining 8 spans |
1 ms each |
The query
An org-level permission check that LEFT OUTER JOINs the entire RBAC graph —
userorganisationpermission (+ its permissions M2M), userpermissiongroup
(+ users, + its org-permissions M2M), rbac_role, rbac_userrole,
rbac_grouprole, rbac_roleorganisationpermission (+ its permissions M2M) —
then WHERE … LIMIT 1:
SELECT %s AS "a" FROM "organisations_organisation"
LEFT OUTER JOIN "organisation_permissions_userorganisationpermission" …
LEFT OUTER JOIN "users_userpermissiongroup" …
LEFT OUTER JOIN "rbac_role" …
LEFT OUTER JOIN "rbac_userrole" …
LEFT OUTER JOIN "rbac_grouprole" …
LEFT OUTER JOIN "rbac_roleorganisationpermission" …
INNER JOIN "organisations_userorganisation" …
WHERE (… user_id = %s … OR … OR …) AND organisation_id = %s LIMIT 1
Suggested direction
It only needs to answer "does this user hold this permission on this org?", but
builds a full cross-product of three independent permission paths (direct,
group, role) before discarding all but one row. Splitting it into three cheap
EXISTS checks short-circuited with OR, or a UNION ALL … LIMIT 1, would
avoid the join explosion. Worth checking indexes on the RBAC join columns too.
Triage note
Initially archived --until auto by the sentry-triage skill because Sentry
rates it info/low. That was wrong: auto only re-alerts on a frequency
spike, and a steady 2.6s regression affecting 14 users never spikes, so it
would have stayed silent indefinitely. Reopened and filed.
Slack alert · filed by the sentry-triage skill
Sentry: FLAGSMITH-API-6BK
User impact
GET /api/v1/organisations/{organisation_pk}/groups/takes ~2.6 seconds, andusers feel all of it — the org Groups page just sits there loading.
info/lowby Sentry and initially archived.Where the time goes
One span accounts for essentially the whole request:
db— org permission check (below)db— count user permission groupsThe query
An org-level permission check that
LEFT OUTER JOINs the entire RBAC graph —userorganisationpermission(+ its permissions M2M),userpermissiongroup(+ users, + its org-permissions M2M),
rbac_role,rbac_userrole,rbac_grouprole,rbac_roleorganisationpermission(+ its permissions M2M) —then
WHERE … LIMIT 1:Suggested direction
It only needs to answer "does this user hold this permission on this org?", but
builds a full cross-product of three independent permission paths (direct,
group, role) before discarding all but one row. Splitting it into three cheap
EXISTSchecks short-circuited withOR, or aUNION ALL … LIMIT 1, wouldavoid the join explosion. Worth checking indexes on the RBAC join columns too.
Triage note
Initially archived
--until autoby thesentry-triageskill because Sentryrates it
info/low. That was wrong:autoonly re-alerts on a frequencyspike, and a steady 2.6s regression affecting 14 users never spikes, so it
would have stayed silent indefinitely. Reopened and filed.
Slack alert · filed by the
sentry-triageskill