DNM: feat: backfill chat_messages.search_tsv - #26995
Conversation
Docs preview📖 View docs preview for |
6a771de to
55d9604
Compare
e11a3a8 to
f367661
Compare
|
/coder-agents-review |
|
Chat: Review posted | View chat Review historydeep-review v0.9.0 | Round 3 | Last posted: Round 3, 11 findings (5 P3, 6 Nit), APPROVE. Review Finding inventoryFindings
Round logRound 1Panel (15 reviewers: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Gon, Leorio, Ging-Go, Knuckle, Killua, Meruem, Komugi, Chopper, Knov, Zoro). Netero: no findings. Panel: 4 P3, 1 Nit, 0 dropped. Reviewed against 55d9604..f367661. Round 2Churn guard: PROCEED (6/6 addressed). Panel (6 reviewers: Bisky, Mafuuu, Pariston, Gon, Leorio, Meruem). Netero: no findings. Panel: 5 Nit (all comment verbosity, downgraded from Gon P2), 0 P3+. Reviewed against b7239ec..554ef39. Round 3Churn guard: PROCEED (5/5 addressed). Panel (3 reviewers: Bisky, Mafuuu, Pariston). Netero: no findings. Panel: no findings. All 11 findings resolved. Reviewed against b7239ec..01a47d9. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
7381fb1 to
2d5bcc9
Compare
e0c2ed2 to
fc26db0
Compare
c6a884b to
7ee0e38
Compare
7ee0e38 to
554ef39
Compare
|
/coder-agents-review |
There was a problem hiding this comment.
All six R1 findings fixed cleanly in 7381fb1. Six reviewers verified the fixes; all confirmed. No regression from the fix commit.
The code is solid. 5 Nits remain (all comment verbosity, same pattern as R1 but milder, 3-10 words each). These are marginal trims that don't affect correctness or readability.
"I tried to build a simpler alternative and couldn't find one that doesn't introduce its own complexity. The solution is proportional." (Pariston R2)
🤖 This review was automatically generated with Coder Agents.
| return func(i *instance) { i.clk = clk } | ||
| } | ||
|
|
||
| // WithChatSearchBackfillLimits overrides the per-batch row limit and the |
There was a problem hiding this comment.
Nit [CRF-7] batchSize int32 = "per-batch row limit", maxBatches int = "per-tick batch cap", function name = "chat message search backfill". The signature says it all.
Trim to: // WithChatSearchBackfillLimits overrides backfill batch size and cap. For tests.
(Gon P2, downgraded: standard Go doc comment, slight verbosity doesn't harm readability)
🤖
There was a problem hiding this comment.
Verified fixed in 01a47d9975. Adopted verbatim.
🤖
| }, []string{"record_type"}) | ||
| reg.MustRegister(recordsPurged) | ||
|
|
||
| // The backfill updates rows rather than purging them, so it gets its own |
There was a problem hiding this comment.
Nit [CRF-8] "so it gets its own counter instead of a records_purged_total label" is visible from the code (separate prometheus.Counter vs label on CounterVec).
Trim to: // Separate counter: the backfill updates rows, not purges them.
(Gon P2, downgraded: the why ("updates, not purges") is the real content, but the restatement doesn't confuse)
🤖
There was a problem hiding this comment.
Verified fixed in 01a47d9975. Adopted verbatim.
🤖
| defer trapReset.Close() | ||
| defer trapStop.Close() | ||
| defer trapNow.Close() | ||
| // Wait for the initial tick signified by a call to Now(), then |
There was a problem hiding this comment.
Nit [CRF-9] "signified by a call to" and "that signifies" are 6 extra words. Reads fine, but can be tighter.
Trim to: // Initial tick: Now() trap. Completion: TickerReset trap.
(Gon P2, downgraded: test-internal comment, readability benefits from the connectors)
🤖
There was a problem hiding this comment.
Verified fixed in 01a47d9975. Adopted verbatim.
🤖
| _, err := rawDB.ExecContext(ctx, "UPDATE chat_messages SET deleted = true WHERE id = $1", id) | ||
| require.NoError(t, err) | ||
| } | ||
| // countPending repeats the predicate of |
There was a problem hiding this comment.
Nit [CRF-10] "countPending" restates the function name. The trap (index predicate coupling) is the value.
Trim to: // Repeats the predicate of idx_chat_messages_search_tsv_pending.
(Gon P2, downgraded: the clarification aids readers unfamiliar with the index)
🤖
There was a problem hiding this comment.
Verified fixed in 01a47d9975. Adopted verbatim.
🤖
| isNull, _ := searchTsv(ctx, t, rawDB, id) | ||
| require.False(t, isNull, msg) | ||
| } | ||
| // requireTsvFor asserts the row was backfilled with the tsvector of |
There was a problem hiding this comment.
Nit [CRF-11] "requireTsvFor" restates the function name. "such as the sentinel" implied by "not just non-NULL."
Trim to: // Asserts the row's tsvector matches expectedText, not just non-NULL.
(Gon P2, downgraded: new helper, slightly verbose doc aids first-time readers)
🤖
There was a problem hiding this comment.
Verified fixed in 01a47d9975. Adopted verbatim.
🤖
|
/coder-agents-review All prior findings addressed and replied to inline. Requesting a convergence pass. 🤖 Requested via Coder Agents on behalf of @johnstcn. |
There was a problem hiding this comment.
All 11 findings across 3 rounds resolved. The R2 nits (CRF-7 through CRF-11) were adopted verbatim in 01a47d9. Netero R3 confirmed no regressions.
11/11 findings fixed, 0 open. Clean convergence.
🤖 This review was automatically generated with Coder Agents.
Adds a chat message search backfill step to the dbpurge tick. Migration
000541(CODAGT-721) addedchat_messages.search_tsvwith all rows NULL; this drains that pending queue in batches, newest first, and keeps it drained for new messages.Stacked on #26968 (
cian/codagt-721-add-chat_message_search_text-extraction-function). Do not merge before it.BackfillChatMessagesSearchTsv: single select+update whose WHERE clause repeats theidx_chat_messages_search_tsv_pendingpredicate so the partial index serves it. Rows with no extractable text get an empty-tsvector sentinel so they leave the queue.purgeTicktransaction (dbpurge advisory lock already held), capped at 5 batches of 10k rows per 10-minute tick to bound transaction growth; larger backlogs drain across ticks.coderd_dbpurge_chat_search_rows_backfilled_total.chat:update, matching theAutoArchiveInactiveChatsprecedent; no new permissions for the dbpurge subject.Issue: https://linear.app/codercom/issue/CODAGT-722
Implementation plan
CODAGT-722: dbpurge backfill to populate and maintain chat_messages.search_tsv
Issue: https://linear.app/codercom/issue/CODAGT-722
Depends on: CODAGT-721 schema (PR #26968, migration
000541_chat_search_schema)Branch:
cian/codagt-722-dbpurge-sweep-to-backfill-and-maintain, stacked on the 721 branch until #26968 merges. Deliverable: one draft PR.Context
Migration
000541addedchat_messages.search_tsv(all NULL), the search GIN index, and the pending-queue partial index (idx_chat_messages_search_tsv_pending:search_tsv IS NULL AND deleted = false AND visibility IN ('user','both') AND role IN ('user','assistant'), btree onid DESC). This task drains that queue: it computes tsvectors for pending rows in batches, newest first, inside the existing dbpurge tick.Design
Query (sqlc, in
coderd/database/queries/chats.sql)One statement, select+update combined (mirrors the benchmarked form):
COALESCE(..., '')sentinel: distinguishes "backfilled, no text" from "pending". Comment this.:execrowsreturns affected rows; the backfill loop stops when < batch size.DeleteOldWorkspaceAgentLogs) for the generated authz wrapper; context is alreadydbauthz.AsDBPurge.Backfill loop (in
coderd/database/dbpurge/dbpurge.go)purgeTicktransaction, after the current purge work (lock already held viaTryAcquireLock(LockIDDBPurge)).chatSearchBackfillBatchSize = 10_000(benchmark sweet spot, ~800ms/batch),chatSearchBackfillMaxBatches = 5per tick.BackfillChatMessagesSearchTsvup to maxBatches times; stop early when rows affected < batch size. Caps per-tick transaction growth at ~4-5s even on a cold 1M-row backlog.coderd_dbpurge_records_purged_totalpattern is wrong semantically ("purged"); addcoderd_dbpurge_chat_search_rows_backfilled_totalcounter (no labels), registered alongside the existing ones.Drain-rate trade-off (flagged decision)
5 batches x 10k per 10-minute tick = 50k rows/tick. A dev.coder.com-scale backlog (~500k eligible rows) drains in ~100 minutes; typical customer deployments in one or two ticks. Alternative: loop until empty on the first tick (simple, but a single multi-minute transaction holding the dbpurge lock delays all other purge work and bloats the tx). Recommendation: ship the cap; revisit only if field feedback demands faster initial drain.
Red: tests first
Postgres-backed, in
coderd/database/dbpurge/dbpurge_test.go, following the existing quartz +awaitDoTickpatterns (non-parallel, shared lock ID):search_tsv; ineligible rows remain NULL.''::tsvector(not NULL) and do not reappear in the pending query.Batch size/max batches need to be overridable for tests (package-level vars or an Option, following how dbpurge handles clock injection; prefer an Option to keep tests race-safe).
Green: implementation
make gen(querier, dbauthz, dbmetrics, dbmock all regenerate; no audit-table entry, internal operation).make fmt && make lint; runmake test RUN=TestPurge(or the relevant test names) andmake test-racefor the new tests.Refactor
Verification
make genno stray diff;make test,make test-race,make lintpass.idx_chat_messages_search_tsv_pending.This PR was generated by Coder Agents on behalf of @johnstcn.