coder / coder/internal

Add indexes for chat_files purge query when chats graduate from experimental

Open
#1,438 0 comments 0 reactions 0 assignees View on GitHub
tech-debt
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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.