Skip to content

Add partial indexes for authorized finding-list queries#15064

Open
rossops wants to merge 1 commit into
bugfixfrom
ree/finding_visibility_perf_indexes
Open

Add partial indexes for authorized finding-list queries#15064
rossops wants to merge 1 commit into
bugfixfrom
ree/finding_visibility_perf_indexes

Conversation

@rossops

@rossops rossops commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds two partial indexes on dojo_finding that back the heaviest finding-list queries — the ones that combine a filtered finding list with ORDER BY … LIMIT/OFFSET:

  • idx_finding_sev_active(severity, numerical_severity DESC) WHERE active
    Serves severity-filtered active-finding lists ordered by severity (default finding views, dashboard widgets). Without it, WHERE severity=? AND active ORDER BY numerical_severity DESC LIMIT … OFFSET … walks the numerical_severity index backward and filters millions of rows away.
  • idx_finding_riskaccepted_date(date DESC) WHERE risk_accepted
    Serves the accepted-risks list ordered by date.

Both are declared on Finding.Meta.indexes and created via AddIndexConcurrently (atomic = False) so the migration takes no ACCESS EXCLUSIVE lock on the large dojo_finding table.

Why

On a large instance (~10M findings) these list endpoints did full-table-scale work. For the severity-ordered active list:

EXPLAIN ANALYZE  SELECT … FROM dojo_finding
  WHERE severity IN ('High') AND active
  ORDER BY numerical_severity DESC LIMIT 25 OFFSET 100;

-- before:
Index Scan Backward using dojo_findin_numeric_83d93b_idx
  Filter: active AND severity='High'
  Rows Removed by Filter: 8,119,981      (~28 GB buffers)
  Execution Time: 4614 ms

-- after (with idx_finding_sev_active):
Index Scan using idx_finding_sev_active
  Index Cond: severity='High'            (80 buffers)
  Execution Time: 0.19 ms

The accepted-risks list similarly drops from a full-table backward date scan to a direct ordered scan of the (much smaller) risk-accepted set.

WHERE active (rather than a narrower active AND NOT verified) is deliberate: it also serves the open/unverified variants (severity AND active AND NOT verified) via the same index + a residual filter, so one index covers both rather than maintaining two.

How it was tested

On a stage DB (dojo_finding ≈ 10.1M rows / 21 GB):

  • EXPLAIN (ANALYZE, BUFFERS) before/after as above — the severity-ordered active list goes 4,614 ms → 0.19 ms; the accepted-risks list and the open/unverified severity COUNT(*) likewise become index scans.
  • Concurrent build cost (measured non-disruptively via identical throwaway indexes on the live table): idx_finding_sev_active ≈ 6.6 s / 56 MB; idx_finding_riskaccepted_date ≈ 5 s / 2.8 MB.
  • Migration: sqlmigrate emits CREATE INDEX CONCURRENTLY … WHERE … with no transaction wrapping; applied and reversed cleanly against a dev DB; makemigrations --check reports no drift.

Notes

  • CONCURRENTLY build time is dominated by waiting for in-flight transactions to drain (twice); on a busy instance it can take longer than measured here, but never blocks reads or writes.
  • Identified profiling the finding-list endpoints under load. A companion Pro-plugin change flattens the multi-product authorization filter, but these indexes stand on their own and benefit OS installs directly.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the New Migration Adding a new migration file. Take care when merging. label Jun 23, 2026
@Maffooch Maffooch added this to the 3.0.200 milestone Jun 23, 2026
Two partial indexes on dojo_finding backing the heaviest finding-list
queries, which combine sparse per-user visibility with ORDER BY/LIMIT:

* idx_finding_sev_active (severity, numerical_severity DESC) WHERE active --
  severity-filtered lists ordered by severity (e.g. the default active-
  findings views and dashboard widgets). Replaces a backward numerical_severity
  scan that filtered millions of rows away with a bounded index range scan.
* idx_finding_riskaccepted_date (date DESC) WHERE risk_accepted --
  accepted-risks list; replaces a full-table backward scan with a direct
  ordered scan of just the risk-accepted rows.

Built CONCURRENTLY (atomic = False) so the migration takes no
ACCESS EXCLUSIVE lock on the large dojo_finding table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rossops rossops force-pushed the ree/finding_visibility_perf_indexes branch from 5a41918 to 5503ec6 Compare June 23, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

New Migration Adding a new migration file. Take care when merging.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants