Thanks to visit codestin.com
Credit goes to github.com

Skip to content

perf: cap count queries and emit native UUID comparisons for audit/connection logs - #23835

Merged
geokat merged 12 commits into
mainfrom
george/plat-31-connection-and-audit-logs-page-timeouts
Apr 7, 2026
Merged

geokat merged 12 commits into
mainfrom
george/plat-31-connection-and-audit-logs-page-timeouts

Conversation

@geokat

@geokat geokat commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

When audit_logs and connection_logs tables grow to tens of millions of rows, two performance bottlenecks can cause page timeouts:

Unbounded count queries: COUNT(*) queries do full sequential scans.

Solution: wrap count queries in a subquery with LIMIT count_cap + 1 so PostgreSQL stops scanning early. The backend includes count_cap in the response; the frontend detects capping when count > count_cap and displays "... of N+".

image

Text-based UUID comparisons in RBAC filters: authorized queries (both COUNT and SELECT) are slowed by RBAC emitting 'uuid' = COALESCE(organization_id::text, '') which prevents index usage.

Solution: add a UUIDVarMatcher type that emits organization_id = 'uuid'::uuid instead, allowing PostgreSQL to use indexes on the organization_id column. This improvement is most significant at medium offsets where the index saves substantial work; at small offsets PG finds rows quickly regardless, and at very large offsets PG switches to a sequential scan anyway.

Audit/connection log page load times without filters (tested with 100M+ rows in PG16 on Core Ultra 7 268V, 32GB, SSD):

Role Page Before After
Owner (non-authorized) 1 16s 150ms
Owner (non-authorized) 150 16s 150ms
Owner (non-authorized) 1000 16s 210ms
Org auditor (authorized) 1 17s 180ms
Org auditor (authorized) 150 17s 200ms
Org auditor (authorized) 1000 timed out 370ms

Related to:
https://linear.app/codercom/issue/PLAT-31/connectionaudit-log-performance-issue

@github-actions github-actions Bot added the community Pull Requests and issues created by the community. label Mar 31, 2026
@geokat geokat removed the community Pull Requests and issues created by the community. label Mar 31, 2026
@geokat
geokat force-pushed the george/plat-31-connection-and-audit-logs-page-timeouts branch 2 times, most recently from 594783e to a9036cc Compare March 31, 2026 02:09
@geokat geokat changed the title fix: optimize connection and audit logs pages response time perf: optimize connection and audit logs pages response time Mar 31, 2026
@geokat geokat changed the title perf: optimize connection and audit logs pages response time perf: improve connection and audit log pages response time Mar 31, 2026
@geokat geokat changed the title perf: improve connection and audit log pages response time perf: cap count queries and emit native UUID comparisons for audit/connection logs Mar 31, 2026
@geokat
geokat force-pushed the george/plat-31-connection-and-audit-logs-page-timeouts branch from a9036cc to 53fe52a Compare March 31, 2026 04:45
…nnection logs

When audit_logs and connection_logs tables grow to tens of millions
of rows, they start causing two performance bottlenecks:

1. COUNT(*) queries become extremely slow due to full sequential scans.
   Solution: wrap count queries in a subquery with LIMIT 2001 so
   PostgreSQL stops scanning early. The frontend infers capping from
   count > 2000 and displays "of 2,000+".

2. Authorized queries (both COUNT and SELECT) are slowed down by RBAC
   authorization that emits text-based UUID comparisons like 'uuid' =
   COALESCE(organization_id::text, '') which prevents index usage.
   Solution: add a UUIDVarMatcher type that emits `organization_id =
   'uuid'::uuid` instead, allowing PostgreSQL to use indexes on the
   organization_id column.

Related to:
https://linear.app/codercom/issue/PLAT-31/connectionaudit-log-performance-issue
@geokat
geokat force-pushed the george/plat-31-connection-and-audit-logs-page-timeouts branch from 53fe52a to aacd95e Compare March 31, 2026 05:29
@geokat
geokat marked this pull request as ready for review March 31, 2026 05:35
@geokat
geokat requested a review from Emyrk as a code owner March 31, 2026 05:35
@geokat
geokat requested a review from aslilac March 31, 2026 05:35
@Emyrk

Emyrk commented Mar 31, 2026

Copy link
Copy Markdown
Member

I like the creativity here. Losing the total count is unfortunate. But probably not a big deal?

@geokat

geokat commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

Losing the total count is unfortunate. But probably not a big deal?

I honestly don't see a way around it. COUNT(*) queries are going to be slow on lots of rows, no matter what.
From what I understand, the customer can't even open the first page, let alone apply a filter.

@geokat

geokat commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

The frontend count-capping logic is generic - it will display "of 2,000+" for any paginated query returning count > 2000, even if the backend count isn't capped. In practice this only affects audit logs and connection logs since no other table approaches that size? But if that feels too broad, I can add a count_is_capped flag to the backend response and key off that instead - please let me know.

EDIT: pushed an update that makes count capping opt-in via count_cap response field. Endpoints that set it will have their counts displayed as capped in the frontend; others display as exact totals.

@aslilac aslilac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we'll really want some stories to look at to make sure that the math on the frontend is correct, but the backend changes look good

Comment thread coderd/database/queries/auditlogs.sql Outdated
Comment thread site/src/hooks/usePaginatedQuery.ts
Comment thread site/src/components/PaginationWidget/PaginationAmount.tsx
geokat added 10 commits April 1, 2026 10:57
Also add a test for hasPreviousPage when count is capped.
The frontend used a hardcoded COUNT_CAP = 2000 constant to detect
capped counts, which would incorrectly cap any paginated endpoint
returning >2000 records. Instead, have the backend include count_cap
in the response so the frontend only activates capping when the
endpoint explicitly signals it. The SQL LIMIT is also parameterized
so the cap value flows from a named Go constant through to the query.
@geokat
geokat requested a review from aslilac April 2, 2026 18:04

@aslilac aslilac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thank you!

@geokat
geokat merged commit 86ca61d into main Apr 7, 2026
32 checks passed
@geokat
geokat deleted the george/plat-31-connection-and-audit-logs-page-timeouts branch April 7, 2026 14:24
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants