Conversation
…tables first Signed-off-by: Thomas Kosiewski <tk@coder.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…able locks The chat table lock step lowers lock_timeout while it retries its locks and must restore it. No test failed when the restore was removed: later migrations would then give up on any lock wait longer than 100ms. The no-op migration in TestUpWithFSChatTableLocks now fails if lock_timeout differs from its session value. _Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high_ Signed-off-by: Thomas Kosiewski <tk@coder.com>
…orten the lock error Sustained chat traffic of one kind starved a fixed lock order: transactions that lock a chat row and then read chats_expanded defeat view-first attempts, and readers of the view defeat chats-first attempts. Attempts now alternate the order. The lock step's error no longer repeats the whole DO block, and the comment states the residual risk for later migrations. --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…ts up front An application write to chat_automations locks that table and then waits for chats in its foreign key check. The migration transaction holds chats from its start, so a later migration that locks chat_automations deadlocked with that write (found by the CODAGT-1223 lane on its 000612). The lock step now locks chats and every table with a foreign key path to it, found from pg_constraint, and a test covers an automation update during an upgrade from 610. --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a22894dca4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
lock_timeout applies to each lock acquisition, so an attempt that waited almost 100ms for each of several tables could hold chats for longer than deadlock_timeout. Each attempt now shares one budget of half of deadlock_timeout, and every lock wait is capped at the time left. --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Migration upgrades no longer deadlock with chat traffic from replicas that keep serving during a rolling deploy.
coder serverruns all pending migrations in one transaction, and older chat migrations (000600, 000601, 000607, 000608) take and upgrade locks onchats,chat_messagesandchats_expandedin orders that cycle with chatd. #30297 fixed 000609 and 000611 only. Upgrades from 585 (v2.37.3), 600, 605 (release/2.38) and 607 still deadlocked: from 585 PostgreSQL killed the migrator, from the others it killed user requests.The fix lives in the migrator, so it covers every entry point (
coder serverwith external or built-in Postgres,dbtestutil,gen/dump,scripts/migrate-ci,scripts/migrate-test), and future chat migrations get it for free:chatsand every table with a foreign key path to it (chat_messages,chat_queued_messages,chat_automationsand the other chat child tables, found frompg_constraint) inACCESS EXCLUSIVEmode, andchats_expandedwith a no-opALTER VIEW(it locks only the view;LOCK TABLEon the view would also lockusers). It uses the 000609 pattern: short waits (lock_timeout= min(100ms,deadlock_timeout/2)), release and retry, 2-minute deadline. Because the migrator gives up beforedeadlock_timeout, PostgreSQL does not pick a chat request as the victim. Attempts alternate between locking the view before and after the tables: with a fixed order, sustained traffic of the opposite order made every attempt time out (measured locally: never migrated in 20 s; alternating: under 0.3 s for each kind of load).40P01,UpWithFSreruns it from the committed version (up to 5 attempts, 100ms to 800ms backoff, one warning per retry). Other errors are never retried.Design choices and residual risk
Runonly for pending migrations). A normal restart with nothing pending takes no chat lock. When anything is pending, chat traffic waits until the migration transaction commits, even if no pending migration touches chat tables. That is the accepted cost: almost every release alterschats, and such a migration holdsACCESS EXCLUSIVEon it until commit anyway. Scanning migration SQL for chat references could miss an indirect reference.chatsdoes not exist and the block does nothing.deadlock_timeoutand then touches a chat table can be aborted (remote UAT reproduced this withconnection_logsand 000603 from 600; on main the same sequence does not deadlock, because chats is not locked yet at 000603). If it touches the chat table sooner, the migrator is aborted and the retry recovers. Locking more tables up front, such asusersfor 000609's foreign keys, would block all writes to them for the whole run.database.Errorhas noUnwrap, so the deadlock check walksOrigErrand joined errors by hand. A failed run leaves the golang-migrate instance locked, so each attempt builds a new one.Locknow rolls back when taking the advisory lock fails, so a retry never leaves an open transaction behind.Tests
TestUpWithFSChatTrafficDuringUpgradeupgrades databases at 585, 600, 605, 607 and 608 to latest with the realUpWithFS, under the four traffic patterns frommigration000609_test.go(lock chat then update, insert message then update chat, readchats_expandedwhile the migration waits, lock chat then readchats_expanded). It sequences withpg_blocking_pids, not sleeps, and requires no application40P01, no migrator retry, and a clean final version. With the lock block disabled, 585, 600, 605 and 607 fail with deadlocks on one side or the other.TestUpWithFSChatAutomationTrafficupgrades from 610 while an application transaction updates a chat automation'starget_chat_idand a later migration lockschat_automations. Before the lock set included tables that referencechats, the update's foreign key check deadlocked with that migration.TestUpWithFSRetriesDeadlockscovers one retry, no retry for other SQLSTATEs, and giving up after 5 attempts.TestUpWithFSChatTableLockscovers that nothing-pending runs take no chat lock and that the view lock leaveschats_expandedunchanged.Closes CODAGT-1298
Part of CODAGT-1135
Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high