chore(relay): drop the live-basis partial index that the planner never picks (#21305)

Added in #21301 on the reasoning that the composite (active, deadline)
index spans all 6.65M rows to find a few hundred live ones. Measured
post-merge against production-shaped history, that reasoning does not
hold: a basis is inserted active and flipped to 0, so the partial index
accumulates one dead entry per deactivation exactly as the composite one
does. Scan buffers are identical to composite-only in every state, 11,099
cold, 2,522 warm, 9 after VACUUM, and the planner picks the composite
index throughout. The extra index costs ~65 bytes of WAL per basis insert,
about 22% more.

The relief is the reaper plus vacuum, which #21301 already ships. Removes
the statement from SCHEMA, restores the plan test to pinning the composite
index by name, and records why a narrower index is not the cure so the
next reader does not re-derive it.
This commit is contained in:
Jinwoo Hong
2026-09-17 19:07:11 -04:00
committed by GitHub
parent 40b2230508
commit ddbad2218b
4 changed files with 11 additions and 28 deletions
@@ -120,10 +120,11 @@ describePostgres('credential cleanup against PostgreSQL', () => {
expect(expiry).toContain('relay_invites_sweep_expiry')
})
it('plans the live-basis sweep off an index rather than the 1.5 GB heap', async () => {
it('plans the basis sweep off the composite index rather than the 1.5 GB heap', async () => {
// The shape that made this the most expensive statement in the sweep: 20,000 settled bases to
// 50 live ones, so the composite (active, deadline) index spans 400x the rows the sweep wants.
// Which index serves it is the planner's call, the same as for the two invite sweeps below.
// 50 live ones. A partial index on active = 1 looks like the answer to that ratio and is not:
// a basis is inserted active and flipped to 0, so it accumulates the same dead entries, and
// the planner picks the composite index anyway. See the schema comment beside it.
await database.query(
`INSERT INTO relay_connection_bases
(basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation,
@@ -148,7 +149,7 @@ describePostgres('credential cleanup against PostgreSQL', () => {
)
expect(sweep).not.toContain('Seq Scan on relay_connection_bases')
expect(sweep).toMatch(/using relay_connection_bases_(active|live)_deadline/)
expect(sweep).toContain('using relay_connection_bases_active_deadline')
})
it('plans the drained basis reaper off the composite index, not the heap', async () => {
+5 -7
View File
@@ -172,16 +172,14 @@ CREATE TABLE IF NOT EXISTS relay_connection_bases (
-- accumulate unboundedly. Unindexed it seq-scans millions of rows every cycle
-- and holds the maintenance transaction open long enough to time out
-- assignment lock waits.
-- Why not a partial index on active = 1: a basis is inserted active and flipped to 0, so each
-- deactivation leaves a dead entry in that index too. Measured on production-shaped history it
-- carries the same dead entries as this one, the planner picks this one in every state, and it
-- costs ~65 bytes of WAL per insert. Bloat here is cured by reaping and vacuum, not by a narrower
-- index.
CREATE INDEX IF NOT EXISTS relay_connection_bases_active_deadline
ON relay_connection_bases(active, deadline);
-- schema-deferrable: created out of band, so a boot that cannot take the lock must retry
-- Why: the index above spans every row, and inactive bases outnumber live ones by ~6.6M to a few
-- hundred, so the sweep still walked ~283 MB of index to find them. This one holds only the rows
-- the sweep can act on. Keeping both: the composite is also what makes the reaper an index range.
CREATE INDEX IF NOT EXISTS relay_connection_bases_live_deadline
ON relay_connection_bases(deadline) WHERE active = 1;
CREATE TABLE IF NOT EXISTS relay_direct_authorizations (
direct_auth_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
@@ -56,9 +56,6 @@ describePostgres('PostgreSQL maintenance sweep plans', () => {
const plan = result.rows.map((row) => String(row['QUERY PLAN'])).join('\n')
expect(plan).not.toMatch(/Seq Scan on relay_connection_bases/)
// Either index keeps the sweep off the table. It used to be the composite one; the partial
// relay_connection_bases_live_deadline now wins on cost, because it spans only the live rows
// rather than all ~6.6M, and that is the improvement, not a regression in this invariant.
expect(plan).toMatch(/relay_connection_bases_(active|live)_deadline/)
expect(plan).toMatch(/relay_connection_bases_active_deadline/)
})
})
@@ -27,12 +27,6 @@ const GOLDEN_LOCK_TAKING: SchemaLockTarget[] = [
{ kind: 'index', table: 'relay_devices', name: 'relay_devices_current_hash', skipWhen: 'present' },
{ kind: 'index', table: 'relay_devices', name: 'relay_devices_grace_hash', skipWhen: 'present' },
{ kind: 'index', table: 'relay_connection_bases', name: 'relay_connection_bases_active_deadline', skipWhen: 'present' },
{
kind: 'index',
table: 'relay_connection_bases',
name: 'relay_connection_bases_live_deadline',
skipWhen: 'present'
},
{
kind: 'index',
table: 'relay_direct_authorizations',
@@ -251,7 +245,6 @@ describe('relay boot-time lock targets', () => {
expect(deferrable.map((statement) => sqlWithoutComments(statement).replace(/\s+/g, ' '))).toEqual([
"CREATE INDEX IF NOT EXISTS relay_invites_sweep_expiry ON relay_invites(expires_at) WHERE state IN ('available', 'reserved', 'cooldown')",
"CREATE INDEX IF NOT EXISTS relay_invites_sweep_reservation ON relay_invites(reservation_expires_at) WHERE state = 'reserved'",
'CREATE INDEX IF NOT EXISTS relay_connection_bases_live_deadline ON relay_connection_bases(deadline) WHERE active = 1',
'CREATE INDEX IF NOT EXISTS relay_direct_authorizations_pending_deadline ON relay_direct_authorizations(deadline) WHERE consumed_at IS NULL',
'CREATE INDEX IF NOT EXISTS relay_rate_windows_started ON relay_rate_windows(window_started_at)',
'DROP INDEX IF EXISTS relay_assignment_activity_expiry',
@@ -274,12 +267,6 @@ describe('relay boot-time lock targets', () => {
name: 'relay_invites_sweep_reservation',
skipWhen: 'present'
},
{
kind: 'index',
table: 'relay_connection_bases',
name: 'relay_connection_bases_live_deadline',
skipWhen: 'present'
},
{
kind: 'index',
table: 'relay_direct_authorizations',