Skip to content

fix: index slot recovery lookups by LSN - #58

Merged
psteinroe merged 2 commits into
mainfrom
fix/slot-recovery-brin-lookup
Aug 26, 2026
Merged

fix: index slot recovery lookups by LSN#58
psteinroe merged 2 commits into
mainfrom
fix/slot-recovery-brin-lookup

Conversation

@psteinroe

@psteinroepsteinroe commented Aug 26, 2026

Copy link
Copy Markdown
Owner

add a partitioned BRIN index on pgstream.events(lsn) so invalidated-slot recovery is fast.

chose brin because writes are cheap, lsns closely follow physical row order and in most recovery scenarios the filtered set is small enough for a cheap top-n sort by (created_at, id).

Production rollout

run the following script before deploying the new version. do not wrap it in a transaction.

\set ON_ERROR_STOP on-- A valid parent means this rollout already completed.SELECT coalesce((
SELECTindex_metadata.indisvalidFROM pg_index AS index_metadata
WHEREindex_metadata.indexrelid= to_regclass('pgstream.events_lsn_brin_idx')
), false) AS rollout_complete
\gset
\if :rollout_complete
\echo 'pgstream.events_lsn_brin_idx is already valid; nothing to do.'
\else
SELECT pg_advisory_lock(hashtext('pgstream.events_lsn_brin_idx'));
-- Remove invalid standalone indexes left by an interrupted build.SELECT format('DROP INDEX CONCURRENTLY %I.%I;', namespace.nspname, child_index.relname)
FROM pg_partition_tree('pgstream.events'::regclass) AS partition_tree
JOIN pg_class AS partition ONpartition.oid=partition_tree.relidJOIN pg_namespace AS namespace ONnamespace.oid=partition.relnamespaceJOIN pg_class AS child_index
ONchild_index.relnamespace=namespace.oidANDchild_index.relname=partition.relname||'_lsn_brin_idx'JOIN pg_index AS child_metadata ONchild_metadata.indexrelid=child_index.oidWHEREpartition_tree.isleafANDchild_metadata.indrelid=partition.oidAND NOT child_metadata.indisvalidAND NOT EXISTS (
SELECT1FROM pg_inherits WHERE inhrelid =child_index.oid
)
ORDER BYpartition.relname
\gexec
-- Build one leaf index at a time without blocking writes.SELECT format(
'CREATE INDEX CONCURRENTLY %I ON %I.%I USING brin (lsn) WITH (pages_per_range = 64, autosummarize = on);',
partition.relname||'_lsn_brin_idx', namespace.nspname, partition.relname
)
FROM pg_partition_tree('pgstream.events'::regclass) AS partition_tree
JOIN pg_class AS partition ONpartition.oid=partition_tree.relidJOIN pg_namespace AS namespace ONnamespace.oid=partition.relnamespaceLEFT JOIN pg_class AS expected_index
ONexpected_index.relnamespace=namespace.oidANDexpected_index.relname=partition.relname||'_lsn_brin_idx'WHEREpartition_tree.isleafANDexpected_index.oid IS NULLAND NOT EXISTS (
SELECT1FROM pg_inherits AS attachment
JOIN pg_index AS inherited_index ONinherited_index.indexrelid=attachment.inhrelidWHEREattachment.inhparent= to_regclass('pgstream.events_lsn_brin_idx')
ANDinherited_index.indrelid=partition.oid
)
ORDER BYpartition.relname
\gexec
-- Create only the parent metadata, then attach the concurrently-built children.SET lock_timeout ='5s';
CREATEINDEXIF NOT EXISTS events_lsn_brin_idx
ON ONLY pgstream.events
USING brin (lsn)
WITH (pages_per_range =64, autosummarize =on);
SELECT format(
'ALTER INDEX pgstream.events_lsn_brin_idx ATTACH PARTITION %I.%I;',
namespace.nspname, child_index.relname
)
FROM pg_partition_tree('pgstream.events'::regclass) AS partition_tree
JOIN pg_class AS partition ONpartition.oid=partition_tree.relidJOIN pg_namespace AS namespace ONnamespace.oid=partition.relnamespaceJOIN pg_class AS child_index
ONchild_index.relnamespace=namespace.oidANDchild_index.relname=partition.relname||'_lsn_brin_idx'JOIN pg_index AS child_metadata ONchild_metadata.indexrelid=child_index.oidWHEREpartition_tree.isleafANDchild_metadata.indrelid=partition.oidANDchild_metadata.indisreadyANDchild_metadata.indisvalidAND NOT EXISTS (
SELECT1FROM pg_inherits AS attachment
JOIN pg_index AS inherited_index ONinherited_index.indexrelid=attachment.inhrelidWHEREattachment.inhparent='pgstream.events_lsn_brin_idx'::regclass
ANDinherited_index.indrelid=partition.oid
)
ORDER BYpartition.relname
\gexec
RESET lock_timeout;
DO $$
BEGIN
IF NOT EXISTS (
SELECT1FROM pg_index
WHERE indexrelid ='pgstream.events_lsn_brin_idx'::regclass
AND indisready
AND indisvalid
) THEN
RAISE EXCEPTION 'pgstream.events_lsn_brin_idx is not valid; rerun the rollout before deploying';
END IF;
END
$$;
SELECT pg_advisory_unlock(hashtext('pgstream.events_lsn_brin_idx'));
\endif

@psteinroe
psteinroeforce-pushed the fix/slot-recovery-brin-lookup branch from 3e1454c to 190d567CompareAugust 26, 2026 07:05
@psteinroe
psteinroe merged commit b35bb33 into mainAug 26, 2026
6 checks passed
Sign up for freeto 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

@psteinroe