Skip to content

fix(vahter): rebuild trgm index with fastupdate off, mark forensic indexes - #502

Merged
Szer merged 2 commits into
mainfrom
fix/vahter-trgm-fastupdate-off
Sep 17, 2026
Merged

Szer merged 2 commits into
mainfrom
fix/vahter-trgm-fastupdate-off

Conversation

@Szer

@Szer Szer commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Why

Every vahter 504 in the Envoy access logs (response_flags=UT, the 15 s upstream timeout on the Telegram webhook POST /bot) traces to one INSERT INTO event (the MessageReceived append) stalling for 8–30 s server-side. Nothing else runs on the DB at those moments, SELECTs on event keep working, and the second pod's identical INSERT (Telegram's 15 s redelivery) blocks until the first finishes.

Counting MessageReceived rows between consecutive stalls (2026-09-14 → 16, from Tempo + the event table):

stall intervals rows between stalls rawMessage bytes
12 consecutive 474 – 526 605 – 920 kB

A stall every ~500 inserts of ~1.4 kB text is the GIN pending list of idx_event_rawmessage_trgm (V47 forensic index, 653 MB, gin_pending_list_limit 4 MB) hitting its limit. The inserting backend flushes it synchronously and blocks other GIN inserters; on B1ms + P4 (120 IOPS) that takes tens of seconds. Envoy 504 UT on the vahter route: 1–5/day since 2026-08-28 (V47 landed), sporadic before.

What

V51DROP INDEX CONCURRENTLY IF EXISTS (old and new name, so a retry after a failed build is clean) + CREATE INDEX CONCURRENTLY idx_forensic_event_rawmessage_trgm ... WITH (fastupdate = off). Trigram entries then go straight into the index on each insert instead of accumulating into a periodic flush. The idx_forensic_ prefix records that this is a debug-session index and that GIN ones are built with fastupdate = off.

V52ALTER INDEX IF EXISTS ... RENAME TO idx_forensic_... (metadata only) for the other indexes that no bot query reads:

index created by evidence it is debug-only
idx_event_username_lower V30 "debug indexes" for v_users / v_recent_bans views; prod looks up usernames by stream_id (GetVahterActionStats)
idx_event_user_ban_by_stream V30 "debug indexes" no SELECT in DB.fs touches UserBanned/UserUnbanned rows; 418 scans since July come from debug views
idx_event_mlscored_score_created_at V48 (#460) LLM-band analysis query run by hand; no prod query reads score; 1 scan ever
idx_snapshot_user_username V38 snapshot_user is only written by prod code (upsert + rebuild); 0 scans
idx_snapshot_user_banned V38 same; 0 scans
idx_snapshot_user_spam_protection V43 migration comment: "debug/ops queries"; 0 scans

Kept as-is despite low scan counts: idx_event_msg_marked_ham_text (0 scans, but GetUserStats' is_ham subquery filters on data->>'text' for MessageMarkedHam), the snapshot_message indexes (read by the report queries), and ix_bot_setting_feature_group (16 kB, not worth a migration).

Deploy notes

  • The CONCURRENTLY build of a ~650 MB GIN index over 2.5 M rows on B1ms will take a while (tens of minutes is plausible). It does not block writes; the forensic index is simply absent until the build finishes. If the deploy job dies mid-build, the unconditional repair before the next migrate clears the failed row and V51 retries (the two IF EXISTS drops handle any leftover invalid index).
  • Migrations run as the admin user, which owns every index here.
  • Renames are metadata-only; no bot code or test references an index name.

Verify

  • Tempo: { resource.service.name="vahter-bot" && name="postgresql" && duration > 2s } should stop showing the INSERT INTO event spans.
  • Loki: {namespace="envoy-gateway-system"} |= "response_timeout" |= "vahter-bot-route" should go to zero per day.
  • SELECT indexrelname, pg_get_indexdef(indexrelid) FROM pg_stat_user_indexes WHERE indexrelname LIKE 'idx_forensic_%' → 7 rows, the trgm one showing WITH (fastupdate='off').

🤖 Generated with Claude Code

Szer and others added 2 commits September 17, 2026 08:35
Every vahter 504 in Envoy (response_flags=UT, 15s upstream timeout on the
Telegram webhook POST /bot) traces to a single INSERT INTO event stalling
for 8-30s, and Tempo + the event table show the stall recurring every
474-526 MessageReceived rows (~600-900 kB of rawMessage), 3-5 times a day.
That cadence is the GIN pending list of idx_event_rawmessage_trgm
(V47 forensic index, 653 MB, gin_pending_list_limit 4 MB) hitting its
limit: the inserting backend flushes it synchronously, and on B1ms/P4
(120 IOPS) that takes tens of seconds while blocking other inserters,
so Telegram's 15s redelivery to the second pod stalls on the same flush.

Recreate the index CONCURRENTLY with fastupdate=off so trigram entries go
straight into the index on each insert instead of accumulating into a
periodic flush. Same idempotent DROP/CREATE CONCURRENTLY shape as V49.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The rebuilt trigram index becomes idx_forensic_event_rawmessage_trgm so
its name records that it is a debug-session index built with
fastupdate=off. V52 gives the same prefix to the other indexes no bot
query reads: idx_event_username_lower and idx_event_user_ban_by_stream
(V30 "debug indexes"), idx_event_mlscored_score_created_at (V48, LLM-band
analysis query run by hand), and the three snapshot_user indexes (V38/V43;
snapshot_user is only ever written by bot code, the debug/ops queries read
it). Verified by grepping DB.fs for each index's predicate; the only
low-scan event index that IS read by prod code, idx_event_msg_marked_ham_text
(GetUserStats' is_ham subquery), keeps its name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Szer Szer changed the title fix(vahter): rebuild idx_event_rawmessage_trgm with fastupdate off fix(vahter): rebuild trgm index with fastupdate off, mark forensic indexes Sep 17, 2026
@Szer
Szer merged commit eac1e28 into main Sep 17, 2026
7 checks passed
@Szer
Szer deleted the fix/vahter-trgm-fastupdate-off branch September 17, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant