Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions dojo/db_migrations/0295_drop_unused_finding_indexes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Drop four `dojo_finding` indexes that no query uses.

`dojo_finding` is the busiest table in the schema and carries close to fifty indexes, every
one of which is maintained on each row written. Import is where that is felt: a scan
creating thousands of findings pays for all of them per row.

These four were measured as completely unused -- `pg_stat_user_indexes.idx_scan = 0` -- on
four long-running production instances, three of which had accumulated statistics for about
seventeen months:

(epss_percentile) the largest of them; note (epss_score) IS used, so findings are
sorted by score and the percentile is stored and displayed but is
never an access path
(line)
(known_exploited)
idx_finding_sev_open_unver partial: (severity, -numerical_severity)
WHERE active AND NOT verified

Indexes that were unused on *some* instances but not others were deliberately left alone --
they track optional features, and dropping on that evidence would break the installs that do
use them.

Built with DROP INDEX CONCURRENTLY (non-atomic migration, following 0280) so it takes no
exclusive lock on a large `dojo_finding`. IF EXISTS makes it idempotent, and the reverse
rebuilds concurrently, so a downgrade does not lock the table either.
"""
from django.db import migrations, models


class Migration(migrations.Migration):
atomic = False

dependencies = [
("dojo", "0294_usercontactinfo_language"),
]

operations = [
# RunSQL does the concurrent DB work; state_operations keeps Django's model state in
# step with the Meta change, so a later makemigrations stays a no-op.
migrations.SeparateDatabaseAndState(
database_operations=[
migrations.RunSQL(
sql="DROP INDEX CONCURRENTLY IF EXISTS dojo_findin_epss_pe_567499_idx",
reverse_sql=(
"CREATE INDEX CONCURRENTLY IF NOT EXISTS dojo_findin_epss_pe_567499_idx "
"ON dojo_finding (epss_percentile)"
),
),
migrations.RunSQL(
sql="DROP INDEX CONCURRENTLY IF EXISTS dojo_findin_line_fea329_idx",
reverse_sql=(
"CREATE INDEX CONCURRENTLY IF NOT EXISTS dojo_findin_line_fea329_idx "
"ON dojo_finding (line)"
),
),
migrations.RunSQL(
sql="DROP INDEX CONCURRENTLY IF EXISTS dojo_findin_known_e_8c584e_idx",
reverse_sql=(
"CREATE INDEX CONCURRENTLY IF NOT EXISTS dojo_findin_known_e_8c584e_idx "
"ON dojo_finding (known_exploited)"
),
),
migrations.RunSQL(
sql="DROP INDEX CONCURRENTLY IF EXISTS idx_finding_sev_open_unver",
reverse_sql=(
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_finding_sev_open_unver "
"ON dojo_finding (severity, numerical_severity DESC) "
"WHERE (active AND NOT verified)"
),
),
],
state_operations=[
migrations.RemoveIndex(model_name="finding", name="dojo_findin_epss_pe_567499_idx"),
migrations.RemoveIndex(model_name="finding", name="dojo_findin_line_fea329_idx"),
migrations.RemoveIndex(model_name="finding", name="dojo_findin_known_e_8c584e_idx"),
migrations.RemoveIndex(model_name="finding", name="idx_finding_sev_open_unver"),
],
),
]
8 changes: 0 additions & 8 deletions dojo/finding/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ class Meta:

models.Index(fields=["cve"]),
models.Index(fields=["epss_score"]),
models.Index(fields=["epss_percentile"]),
models.Index(fields=["cwe"]),
models.Index(fields=["out_of_scope"]),
models.Index(fields=["false_p"]),
Expand All @@ -482,12 +481,10 @@ class Meta:
models.Index(fields=["hash_code"]),
models.Index(fields=["unique_id_from_tool"]),
# models.Index(fields=['file_path']), # can't add index because the field has max length 4000.
models.Index(fields=["line"]),
models.Index(fields=["component_name"]),
models.Index(fields=["duplicate"]),
models.Index(fields=["is_mitigated"]),
models.Index(fields=["duplicate_finding", "id"]),
models.Index(fields=["known_exploited"]),
models.Index(fields=["ransomware_used"]),
models.Index(fields=["kev_date"]),
models.Index(
Expand All @@ -514,11 +511,6 @@ class Meta:
name="idx_finding_open_active_sev",
condition=models.Q(active=True, is_mitigated=False),
),
models.Index(
fields=["severity", "-numerical_severity"],
name="idx_finding_sev_open_unver",
condition=models.Q(active=True, verified=False),
),
models.Index(
fields=["test", "sla_expiration_date", "date"],
name="idx_finding_sla_breach_cov",
Expand Down
Loading