Add indexes for chat_files purge query when chats graduate from experimental
- Dominant language
- No language data
- Stars
- 3
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
## Context
PR https://github.com/coder/coder/pull/23833 adds periodic cleanup of `chat_files` to dbpurge. The `DeleteOldChatFiles` SQL query does a sequential scan of both `chats` and `chat_files` because there are no supporting indexes. This is acceptable while chats are experimental with low row counts, but needs to be addressed before chats see production-scale traffic.
Flagged by Database Reviewer (P2), Edge Case Analyst (P2), and Go Architect during deep-review.
## What needs indexing
### 1. `chats` table — `kept_file_ids` CTE
The CTE `SELECT DISTINCT unnest(file_ids) FROM chats WHERE archived = false OR updated_at >= @before_time` does a full seq scan with no index on `archived` or `(archived, updated_at)`. The `OR` condition prevents the planner from using existing indexes. Cost scales with `total_chats × avg(file_ids length)`.
Options:
- `CREATE INDEX ON chats (updated_at) WHERE archived = true` + split CTE into two UNIONed queries
- Composite index on `(archived, updated_at)`
### 2. `chat_files` table — `deletable` CTE
The CTE filters `WHERE cf.created_at < @before_time` and orders by `created_at ASC` with a `LIMIT`. No index on `created_at` means full scan + sort. Since `chat_files` rows carry `bytea` blob data, rows are wide — making the scan expensive per row.
Fix: `CREATE INDEX idx_chat_files_created_at ON chat_files (created_at)`
## When
Before chats graduate from experimental status / see production-scale traffic.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.