Skip to content

ref(db): Drop unused Group.level index - #128156

Merged
beezz merged 2 commits into
masterfrom
junior/drop-group-level-index
Oct 20, 2026
Merged

beezz merged 2 commits into
masterfrom
junior/drop-group-level-index

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Drops sentry_groupedmessage_level_c2574e37, the single-column btree index that db_index=True creates on Group.level.

Why

  • Nothing in src/ filters or orders Group by level in Postgres, so the index only adds write overhead on sentry_groupedmessage.
  • It's a plain CREATE INDEX ... (level). It isn't unique and isn't a constraint.

How

  • Removed db_index=True from Group.level.
  • Migration 1228_drop_group_level_index uses SeparateDatabaseAndState:
    • The database side is a SafeRunSQL running DROP INDEX CONCURRENTLY IF EXISTS "sentry_groupedmessage_level_c2574e37". It drops this one index by name, so AlterField can't introspect and drop any other single-column index on level. IF EXISTS makes it a no-op wherever the index is absent.
    • The state side is an AlterField matching the model.
    • use_statement_timeout=False, because the concurrent drop waits for open transactions on a hot table. This follows the precedent in 1159_organizationmemberteam_new_id_unique_not_null.
    • is_post_deployment = True, so it runs manually outside the deploy.
    • Reversible: the reverse SQL recreates the index with CREATE INDEX CONCURRENTLY IF NOT EXISTS.
  • The index name is Django's deterministic name for (sentry_groupedmessage, level), so fresh installs drop it too.

Notes for reviewers

  • SafeRunSQL needs sign-off from owners-migrations.
  • I couldn't run makemigrations --check or sqlmigrate locally, so please rely on CI and the migration SQL comment.

via michal.

--

View Junior Session in Sentry

Nothing queries Group by level in Postgres, so the single-column index
created by db_index=True only adds write overhead. Drop it by name with
DROP INDEX CONCURRENTLY IF EXISTS as a post-deployment migration.

Co-Authored-By: michal <michal.kuffa@sentry.io>
@sentry-junior
sentry-junior Bot requested review from a team as code owners October 20, 2026 10:58
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Oct 20, 2026
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/1228_drop_group_level_index.py

for 1228_drop_group_level_index in sentry

--
-- Custom state/database change combination
--
DROP INDEX CONCURRENTLY IF EXISTS "sentry_groupedmessage_level_c2574e37";
DROP INDEX CONCURRENTLY IF EXISTS "sentry_groupedmessage_level";

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fcfe397. Configure here.

Comment thread src/sentry/migrations/1228_drop_group_level_index.py
Databases created before Django migrations may still carry the index as
sentry_groupedmessage_level rather than Django's hashed name.
@beezz
beezz merged commit 233536c into master Oct 20, 2026
89 checks passed
@beezz
beezz deleted the junior/drop-group-level-index branch October 20, 2026 12:00
beezz added a commit that referenced this pull request Oct 20, 2026
Drops `sentry_groupedmessage_times_seen_03d64079`, the single-column
btree index that `db_index=True` creates on `Group.times_seen`. This is
a follow-up to #128156, which did the same for `Group.level`.

### Why
- The Postgres callers that order `Group` by `times_seen` all filter by
project or status as well:
  - `seer/fetch_issues/by_function_name.py`
  - `tasks/summaries/utils.py` (two queries)
- Those queries are served by the composite `project`/`status` indexes,
so a standalone `times_seen` index only adds write overhead on a hot
column. `times_seen` changes on every buffered group update.
- It's a plain `CREATE INDEX ... (times_seen)`. It isn't unique and
isn't a constraint.

### How
Same pattern as `1228_drop_group_level_index`:
- Removed `db_index=True` from `Group.times_seen`.
- Migration `1229_drop_group_times_seen_index` uses
`SeparateDatabaseAndState`:
- The database side runs `DROP INDEX CONCURRENTLY IF EXISTS` for
Django's name (`..._03d64079`) and for the South-era name
(`sentry_groupedmessage_times_seen`). It drops these indexes by name
instead of letting `AlterField` introspect and drop other indexes.
  - The state side is an `AlterField` matching the model.
- `use_statement_timeout=False`, because the concurrent drop waits for
open transactions.
  - `is_post_deployment = True`.
- Reversible: the reverse SQL recreates the index under its current name
with `CREATE INDEX CONCURRENTLY IF NOT EXISTS`.

### Notes for reviewers
- `SafeRunSQL` needs sign-off from `owners-migrations`.
- I couldn't run `makemigrations --check` or `sqlmigrate` locally, so
please rely on CI and the migration SQL comment.

<!-- junior-request-attribution:start -->
via **michal**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3Ae4ce2a50461aaf3812f0494a -->

--

[View Junior Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Ae4ce2a50461aaf3812f0494a/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: michal <michal.kuffa@sentry.io>
joseph-sentry pushed a commit that referenced this pull request Oct 20, 2026
Drops `sentry_groupedmessage_level_c2574e37`, the single-column btree
index that `db_index=True` creates on `Group.level`.

### Why
- Nothing in `src/` filters or orders `Group` by `level` in Postgres, so
the index only adds write overhead on `sentry_groupedmessage`.
- It's a plain `CREATE INDEX ... (level)`. It isn't unique and isn't a
constraint.

### How
- Removed `db_index=True` from `Group.level`.
- Migration `1228_drop_group_level_index` uses
`SeparateDatabaseAndState`:
- The database side is a `SafeRunSQL` running `DROP INDEX CONCURRENTLY
IF EXISTS "sentry_groupedmessage_level_c2574e37"`. It drops this one
index by name, so `AlterField` can't introspect and drop any other
single-column index on `level`. `IF EXISTS` makes it a no-op wherever
the index is absent.
  - The state side is an `AlterField` matching the model.
- `use_statement_timeout=False`, because the concurrent drop waits for
open transactions on a hot table. This follows the precedent in
`1159_organizationmemberteam_new_id_unique_not_null`.
  - `is_post_deployment = True`, so it runs manually outside the deploy.
- Reversible: the reverse SQL recreates the index with `CREATE INDEX
CONCURRENTLY IF NOT EXISTS`.
- The index name is Django's deterministic name for
`(sentry_groupedmessage, level)`, so fresh installs drop it too.

### Notes for reviewers
- `SafeRunSQL` needs sign-off from `owners-migrations`.
- I couldn't run `makemigrations --check` or `sqlmigrate` locally, so
please rely on CI and the migration SQL comment.

<!-- junior-request-attribution:start -->
via **michal**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3Ae4ce2a50461aaf3812f0494a -->

--

[View Junior Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Ae4ce2a50461aaf3812f0494a/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: michal <michal.kuffa@sentry.io>
joseph-sentry pushed a commit that referenced this pull request Oct 20, 2026
Drops `sentry_groupedmessage_times_seen_03d64079`, the single-column
btree index that `db_index=True` creates on `Group.times_seen`. This is
a follow-up to #128156, which did the same for `Group.level`.

### Why
- The Postgres callers that order `Group` by `times_seen` all filter by
project or status as well:
  - `seer/fetch_issues/by_function_name.py`
  - `tasks/summaries/utils.py` (two queries)
- Those queries are served by the composite `project`/`status` indexes,
so a standalone `times_seen` index only adds write overhead on a hot
column. `times_seen` changes on every buffered group update.
- It's a plain `CREATE INDEX ... (times_seen)`. It isn't unique and
isn't a constraint.

### How
Same pattern as `1228_drop_group_level_index`:
- Removed `db_index=True` from `Group.times_seen`.
- Migration `1229_drop_group_times_seen_index` uses
`SeparateDatabaseAndState`:
- The database side runs `DROP INDEX CONCURRENTLY IF EXISTS` for
Django's name (`..._03d64079`) and for the South-era name
(`sentry_groupedmessage_times_seen`). It drops these indexes by name
instead of letting `AlterField` introspect and drop other indexes.
  - The state side is an `AlterField` matching the model.
- `use_statement_timeout=False`, because the concurrent drop waits for
open transactions.
  - `is_post_deployment = True`.
- Reversible: the reverse SQL recreates the index under its current name
with `CREATE INDEX CONCURRENTLY IF NOT EXISTS`.

### Notes for reviewers
- `SafeRunSQL` needs sign-off from `owners-migrations`.
- I couldn't run `makemigrations --check` or `sqlmigrate` locally, so
please rely on CI and the migration SQL comment.

<!-- junior-request-attribution:start -->
via **michal**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3Ae4ce2a50461aaf3812f0494a -->

--

[View Junior Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Ae4ce2a50461aaf3812f0494a/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: michal <michal.kuffa@sentry.io>
beezz added a commit that referenced this pull request Oct 21, 2026
Drops the two indexes that `db_index=True` creates on `Group.logger`:
- `sentry_groupedmessage_logger_993cb6d5`, a btree on `logger`
- `sentry_groupedmessage_logger_993cb6d5_like`, a btree on `logger
varchar_pattern_ops` that Django adds for `LIKE` lookups on `CharField`s

This is a follow-up to #128156 (`level`) and #128165 (`times_seen`).

### Why
- Nothing in `src/` filters, orders, or runs `LIKE` lookups on
`Group.logger` in Postgres, so both indexes only add write overhead on
`sentry_groupedmessage`.
- Neither is unique or a constraint.

### How
Same pattern as `1228`/`1229`:
- Removed `db_index=True` from `Group.logger`.
- Migration `1231_drop_group_logger_indexes` uses
`SeparateDatabaseAndState`:
- The database side runs `DROP INDEX CONCURRENTLY IF EXISTS` for both
Django names, and for the South-era names `sentry_groupedmessage_logger`
and `sentry_groupedmessage_logger_like`. Dropping by exact name keeps
`AlterField` from introspecting and dropping other indexes.
  - The state side is an `AlterField` matching the model.
- `use_statement_timeout=False`, because the concurrent drops wait for
open transactions.
  - `is_post_deployment = True`.
- Reversible: the reverse SQL recreates both indexes under their current
names, including `varchar_pattern_ops` on the `_like` index.

### Notes for reviewers
- `SafeRunSQL` needs sign-off from `owners-migrations`.
- I couldn't run `makemigrations --check` or `sqlmigrate` locally, so
please rely on CI and the migration SQL comment.

<!-- junior-request-attribution:start -->
via **michal**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3Ae4ce2a50461aaf3812f0494a -->

--

[View Junior Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Ae4ce2a50461aaf3812f0494a/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: michal <michal.kuffa@sentry.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants