diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/access/nbtree/nbtpage.c | 7 | ||||
| -rw-r--r-- | src/test/modules/nbtree/expected/nbtree_half_dead_pages.out | 71 | ||||
| -rw-r--r-- | src/test/modules/nbtree/meson.build | 1 | ||||
| -rw-r--r-- | src/test/modules/nbtree/sql/nbtree_half_dead_pages.sql | 43 |
4 files changed, 122 insertions, 0 deletions
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 30b43a4dd18..a8d56fe5a7c 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -33,6 +33,7 @@ #include "storage/indexfsm.h" #include "storage/predicate.h" #include "storage/procarray.h" +#include "utils/injection_point.h" #include "utils/memdebug.h" #include "utils/memutils.h" #include "utils/snapmgr.h" @@ -2003,6 +2004,10 @@ _bt_pagedel(Relation rel, Buffer leafbuf, BTVacState *vstate) return; } } + else + { + INJECTION_POINT("nbtree-finish-half-dead-page-vacuum", NULL); + } /* * Then unlink it from its siblings. Each call to @@ -2349,6 +2354,8 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, BlockNumber scanblkno, _bt_unlockbuf(rel, leafbuf); + INJECTION_POINT("nbtree-leave-page-half-dead", NULL); + /* * Check here, as calling loops will have locks held, preventing * interrupts from being processed. diff --git a/src/test/modules/nbtree/expected/nbtree_half_dead_pages.out b/src/test/modules/nbtree/expected/nbtree_half_dead_pages.out new file mode 100644 index 00000000000..8fd472f8df2 --- /dev/null +++ b/src/test/modules/nbtree/expected/nbtree_half_dead_pages.out @@ -0,0 +1,71 @@ +-- +-- Test half-dead pages in B-tree indexes. +-- +-- Half-dead pages is an intermediate state while vacuum is deleting a +-- page. You can encounter them if you query concurrently with vacuum, +-- or if vacuum is interrupted while it's deleting a page. A B-tree +-- with half-dead pages is a valid state, but they rarely observed by +-- other backends in practice because, so it's good to have some +-- targeted tests to exercise them. +-- +-- This uses injection points to interrupt some page deletions +set client_min_messages TO 'warning'; +create extension if not exists injection_points; +reset client_min_messages; +-- Make all injection points local to this process, for concurrency. +SELECT injection_points_set_local(); + injection_points_set_local +---------------------------- + +(1 row) + +-- Use the index for all the queries +set enable_seqscan=off; +-- Print a NOTICE whenever a half-dead page is deleted +SELECT injection_points_attach('nbtree-finish-half-dead-page-vacuum', 'notice'); + injection_points_attach +------------------------- + +(1 row) + +create table nbtree_half_dead_pages(id bigint) with (autovacuum_enabled = off); +insert into nbtree_half_dead_pages SELECT g from generate_series(1, 150000) g; +create index nbtree_half_dead_pages_id_idx on nbtree_half_dead_pages using btree (id); +delete from nbtree_half_dead_pages where id > 100000 and id < 120000; +-- Run VACUUM and interrupt it so that it leaves behind a half-dead page +SELECT injection_points_attach('nbtree-leave-page-half-dead', 'error'); + injection_points_attach +------------------------- + +(1 row) + +vacuum nbtree_half_dead_pages; +ERROR: error triggered for injection point nbtree-leave-page-half-dead +CONTEXT: while vacuuming index "nbtree_half_dead_pages_id_idx" of relation "public.nbtree_half_dead_pages" +SELECT injection_points_detach('nbtree-leave-page-half-dead'); + injection_points_detach +------------------------- + +(1 row) + +select * from nbtree_half_dead_pages where id > 99998 and id < 120002; + id +-------- + 99999 + 100000 + 120000 + 120001 +(4 rows) + +-- Finish the deletion and re-check +vacuum nbtree_half_dead_pages; +NOTICE: notice triggered for injection point nbtree-finish-half-dead-page-vacuum +select * from nbtree_half_dead_pages where id > 99998 and id < 120002; + id +-------- + 99999 + 100000 + 120000 + 120001 +(4 rows) + diff --git a/src/test/modules/nbtree/meson.build b/src/test/modules/nbtree/meson.build index efebf30a16f..1b9a34d37ca 100644 --- a/src/test/modules/nbtree/meson.build +++ b/src/test/modules/nbtree/meson.build @@ -10,6 +10,7 @@ tests += { 'bd': meson.current_build_dir(), 'regress': { 'sql': [ + 'nbtree_half_dead_pages', 'nbtree_incomplete_splits', ], }, diff --git a/src/test/modules/nbtree/sql/nbtree_half_dead_pages.sql b/src/test/modules/nbtree/sql/nbtree_half_dead_pages.sql new file mode 100644 index 00000000000..d4b9a3f824d --- /dev/null +++ b/src/test/modules/nbtree/sql/nbtree_half_dead_pages.sql @@ -0,0 +1,43 @@ +-- +-- Test half-dead pages in B-tree indexes. +-- +-- Half-dead pages is an intermediate state while vacuum is deleting a +-- page. You can encounter them if you query concurrently with vacuum, +-- or if vacuum is interrupted while it's deleting a page. A B-tree +-- with half-dead pages is a valid state, but they rarely observed by +-- other backends in practice because, so it's good to have some +-- targeted tests to exercise them. +-- + +-- This uses injection points to interrupt some page deletions +set client_min_messages TO 'warning'; +create extension if not exists injection_points; +reset client_min_messages; + +-- Make all injection points local to this process, for concurrency. +SELECT injection_points_set_local(); + +-- Use the index for all the queries +set enable_seqscan=off; + +-- Print a NOTICE whenever a half-dead page is deleted +SELECT injection_points_attach('nbtree-finish-half-dead-page-vacuum', 'notice'); + +create table nbtree_half_dead_pages(id bigint) with (autovacuum_enabled = off); + +insert into nbtree_half_dead_pages SELECT g from generate_series(1, 150000) g; + +create index nbtree_half_dead_pages_id_idx on nbtree_half_dead_pages using btree (id); + +delete from nbtree_half_dead_pages where id > 100000 and id < 120000; + +-- Run VACUUM and interrupt it so that it leaves behind a half-dead page +SELECT injection_points_attach('nbtree-leave-page-half-dead', 'error'); +vacuum nbtree_half_dead_pages; +SELECT injection_points_detach('nbtree-leave-page-half-dead'); + +select * from nbtree_half_dead_pages where id > 99998 and id < 120002; + +-- Finish the deletion and re-check +vacuum nbtree_half_dead_pages; +select * from nbtree_half_dead_pages where id > 99998 and id < 120002; |
