From 514661d0f1903e26e5c936f0575331bfe8157541 Mon Sep 17 00:00:00 2001 From: Abhinav Chordia Date: Fri, 6 Nov 2026 03:43:55 +0000 Subject: [PATCH] db: drop the lowercased sandbox name index The admin list's search no longer matches names (#1523), so nothing reads idx_sandboxes_name_lower. Most sandbox rows have no name, and every non-HOT write to the table still inserts into it. Drop it concurrently; the down rebuilds it as its create migration did. --- ...5_drop_sandboxes_name_lower_index.down.sql | 22 ++++++++++++++ ...255_drop_sandboxes_name_lower_index.up.sql | 29 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 db/postgres/20261106034255_drop_sandboxes_name_lower_index.down.sql create mode 100644 db/postgres/20261106034255_drop_sandboxes_name_lower_index.up.sql diff --git a/db/postgres/20261106034255_drop_sandboxes_name_lower_index.down.sql b/db/postgres/20261106034255_drop_sandboxes_name_lower_index.down.sql new file mode 100644 index 0000000000..ece2046be9 --- /dev/null +++ b/db/postgres/20261106034255_drop_sandboxes_name_lower_index.down.sql @@ -0,0 +1,22 @@ +-- no-transaction +-- squawk-disable-assume-in-transaction +-- Rebuilds idx_sandboxes_name_lower as 20261105161136_sandboxes_name_lower_index +-- created it: the lowercased name's first 256 characters, COLLATE "C". +-- +-- CONCURRENTLY because sandboxes takes writes on every lifecycle change: a +-- plain build holds SHARE on the table and would stall them for its +-- duration. That makes this a single-statement `-- no-transaction` migration. +-- A failed build can leave an INVALID index behind while this migration stays +-- recorded as applied. There is no IF NOT EXISTS so that a rerun fails on that +-- index instead of skipping it and removing the history row. To recover, run +-- this migration's up statement by hand and rerun the down. +-- +-- The statement must be the file's only one, so it cannot set its own lock or +-- statement timeout and runs under the session's (see db/README.md). Building +-- concurrently takes SHARE UPDATE EXCLUSIVE, which does not block writers. It +-- waits for every transaction in the database whose snapshot is older than its +-- second scan, whatever tables that transaction reads, so a long-running one +-- can hold the build past the session's lock timeout and leave an INVALID +-- index. +CREATE INDEX CONCURRENTLY idx_sandboxes_name_lower + ON public.sandboxes USING btree ((left(lower(name), 256) COLLATE "C")); diff --git a/db/postgres/20261106034255_drop_sandboxes_name_lower_index.up.sql b/db/postgres/20261106034255_drop_sandboxes_name_lower_index.up.sql new file mode 100644 index 0000000000..114c21c4f5 --- /dev/null +++ b/db/postgres/20261106034255_drop_sandboxes_name_lower_index.up.sql @@ -0,0 +1,29 @@ +-- no-transaction +-- squawk-disable-assume-in-transaction +-- idx_sandboxes_name_lower keyed every sandbox row by its lowercased name, cut +-- to 256 characters, for an admin list search that matched the start of a +-- name ignoring case. The admin list's `search` now matches IDs only, so no +-- statement reads the index. Most rows have no name to search by: an ECW/E2B +-- create never sets one and a native create sets one only when the caller +-- passes it, so the index mostly holds NULL keys. It still costs every write +-- that is not HOT, such as one that changes the lifecycle, an entry in it on +-- the busiest table. Names are still matched by the list's case-sensitive +-- `prefix`, which needs no index of its own. +-- +-- CONCURRENTLY because sandboxes takes writes on every lifecycle change: a +-- plain DROP INDEX takes ACCESS EXCLUSIVE on the table, so it waits behind +-- every transaction using the table and blocks every read and write queued +-- after it until it has dropped the index. That makes this a +-- single-statement `-- no-transaction` migration, which the migrator does not +-- retry. IF EXISTS so a rerun, or a database whose index was already dropped +-- by hand, drops nothing and still records the migration. A failure leaves +-- the index, possibly marked INVALID, and no history row, so the next +-- migrator run drops it again. +-- +-- The statement must be the file's only one, so it cannot set its own lock or +-- statement timeout and runs under the session's (see db/README.md). Dropping +-- concurrently takes SHARE UPDATE EXCLUSIVE, which does not block writers. It +-- waits for every transaction that holds a lock on sandboxes, so a +-- long-running one can hold the drop past the session's lock timeout. +-- squawk-ignore require-lock-timeout, require-statement-timeout +DROP INDEX CONCURRENTLY IF EXISTS public.idx_sandboxes_name_lower;