Skip to content

ref(db): Drop unused Group.times_seen index - #128165

Merged
beezz merged 1 commit into
masterfrom
junior/drop-group-times-seen-index
Oct 20, 2026
Merged

beezz merged 1 commit into
masterfrom
junior/drop-group-times-seen-index

Conversation

@sentry-junior

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

Copy link
Copy Markdown
Contributor

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.

via michal.

--

View Junior Session in Sentry

Postgres queries that order Group by times_seen also filter by project or
status, which the composite indexes already serve, so the single-column
index created by db_index=True only adds write overhead. Drop it (and its
South-era 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 12:55
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

for 1229_drop_group_times_seen_index in sentry

--
-- Custom state/database change combination
--
DROP INDEX CONCURRENTLY IF EXISTS "sentry_groupedmessage_times_seen_03d64079";
DROP INDEX CONCURRENTLY IF EXISTS "sentry_groupedmessage_times_seen";

@beezz
beezz merged commit 2c56bcd into master Oct 20, 2026
90 checks passed
@beezz
beezz deleted the junior/drop-group-times-seen-index branch October 20, 2026 13:17
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