Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ccc4c07

Browse files
committed
Close some holes in BRIN page assignment
In some corner cases, it is possible for the BRIN index relation to be extended by brin_getinsertbuffer but the new page not be used immediately for anything by its callers; when this happens, the page is initialized and the FSM is updated (by brin_getinsertbuffer) with the info about that page, but these actions are not WAL-logged. A later index insert/update can use the page, but since the page is already initialized, the initialization itself is not WAL-logged then either. Replay of this sequence of events causes recovery to fail altogether. There is a related corner case within brin_getinsertbuffer itself, in which we extend the relation to put a new index tuple there, but later find out that we cannot do so, and do not return the buffer; the page obtained from extension is not even initialized. The resulting page is lost forever. To fix, shuffle the code so that initialization is not the responsibility of brin_getinsertbuffer anymore, in normal cases; instead, the initialization is done by its callers (brin_doinsert and brin_doupdate) once they're certain that the page is going to be used. When either those functions determine that the new page cannot be used, before bailing out they initialize the page as an empty regular page, enter it in FSM and WAL-log all this. This way, the page is usable for future index insertions, and WAL replay doesn't find trying to insert tuples in pages whose initialization didn't make it to the WAL. The same strategy is used in brin_getinsertbuffer when it cannot return the new page. Additionally, add a new step to vacuuming so that all pages of the index are scanned; whenever an uninitialized page is found, it is initialized as empty and WAL-logged. This closes the hole that the relation is extended but the system crashes before anything is WAL-logged about it. We also take this opportunity to update the FSM, in case it has gotten out of date. Thanks to Heikki Linnakangas for finding the problem that kicked some additional analysis of BRIN page assignment code. Backpatch to 9.5, where BRIN was introduced. Discussion: https://www.postgresql.org/message-id/20150723204810.GY5596@postgresql.org
1 parent a4b059f commit ccc4c07

File tree

4 files changed

+258
-22
lines changed

4 files changed

+258
-22
lines changed

src/backend/access/brin/brin.c

+43
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ static void brinsummarize(Relation index, Relation heapRel,
6868
static void form_and_insert_tuple(BrinBuildState *state);
6969
static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
7070
BrinTuple *b);
71+
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
7172

7273

7374
/*
@@ -736,6 +737,8 @@ brinvacuumcleanup(PG_FUNCTION_ARGS)
736737
heapRel = heap_open(IndexGetRelation(RelationGetRelid(info->index), false),
737738
AccessShareLock);
738739

740+
brin_vacuum_scan(info->index, info->strategy);
741+
739742
brinsummarize(info->index, heapRel,
740743
&stats->num_index_tuples, &stats->num_index_tuples);
741744

@@ -1150,3 +1153,43 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b)
11501153

11511154
MemoryContextDelete(cxt);
11521155
}
1156+
1157+
/*
1158+
* brin_vacuum_scan
1159+
* Do a complete scan of the index during VACUUM.
1160+
*
1161+
* This routine scans the complete index looking for uncatalogued index pages,
1162+
* i.e. those that might have been lost due to a crash after index extension
1163+
* and such.
1164+
*/
1165+
static void
1166+
brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
1167+
{
1168+
bool vacuum_fsm = false;
1169+
BlockNumber blkno;
1170+
1171+
/*
1172+
* Scan the index in physical order, and clean up any possible mess in
1173+
* each page.
1174+
*/
1175+
for (blkno = 0; blkno < RelationGetNumberOfBlocks(idxrel); blkno++)
1176+
{
1177+
Buffer buf;
1178+
1179+
CHECK_FOR_INTERRUPTS();
1180+
1181+
buf = ReadBufferExtended(idxrel, MAIN_FORKNUM, blkno,
1182+
RBM_NORMAL, strategy);
1183+
1184+
vacuum_fsm |= brin_page_cleanup(idxrel, buf);
1185+
1186+
ReleaseBuffer(buf);
1187+
}
1188+
1189+
/*
1190+
* If we made any change to the FSM, make sure the new info is visible all
1191+
* the way to the top.
1192+
*/
1193+
if (vacuum_fsm)
1194+
FreeSpaceMapVacuum(idxrel);
1195+
}

0 commit comments

Comments
 (0)