diff options
| author | Tomas Vondra | 2021-11-30 18:15:14 +0000 |
|---|---|---|
| committer | Tomas Vondra | 2021-11-30 19:04:38 +0000 |
| commit | 5753d4ee320b3f6fb2ff734667a1ce1d9d8615a1 (patch) | |
| tree | aa64a9b6c092246eb9f23e47be7c6011fdd10976 /src/test | |
| parent | 4c83e59e01a89b0b19245b8e0317d87ae60226eb (diff) | |
Ignore BRIN indexes when checking for HOT udpates
When determining whether an index update may be skipped by using HOT, we
can ignore attributes indexed only by BRIN indexes. There are no index
pointers to individual tuples in BRIN, and the page range summary will
be updated anyway as it relies on visibility info.
This also removes rd_indexattr list, and replaces it with rd_attrsvalid
flag. The list was not used anywhere, and a simple flag is sufficient.
Patch by Josef Simanek, various fixes and improvements by me.
Author: Josef Simanek
Reviewed-by: Tomas Vondra, Alvaro Herrera
Discussion: https://postgr.es/m/CAFp7QwpMRGcDAQumN7onN9HjrJ3u4X3ZRXdGFT0K5G2JWvnbWg%40mail.gmail.com
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/modules/dummy_index_am/dummy_index_am.c | 1 | ||||
| -rw-r--r-- | src/test/regress/expected/brin.out | 85 | ||||
| -rw-r--r-- | src/test/regress/sql/brin.sql | 63 |
3 files changed, 149 insertions, 0 deletions
diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c index 5365b0639ec..9d409faff54 100644 --- a/src/test/modules/dummy_index_am/dummy_index_am.c +++ b/src/test/modules/dummy_index_am/dummy_index_am.c @@ -298,6 +298,7 @@ dihandler(PG_FUNCTION_ARGS) amroutine->amcanparallel = false; amroutine->amcaninclude = false; amroutine->amusemaintenanceworkmem = false; + amroutine->amhotblocking = true; amroutine->amparallelvacuumoptions = VACUUM_OPTION_NO_PARALLEL; amroutine->amkeytype = InvalidOid; diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out index e53d6e48856..d4c03788a35 100644 --- a/src/test/regress/expected/brin.out +++ b/src/test/regress/expected/brin.out @@ -567,3 +567,88 @@ SELECT * FROM brintest_3 WHERE b < '0'; DROP TABLE brintest_3; RESET enable_seqscan; +-- test BRIN index doesn't block HOT update +CREATE TABLE brin_hot ( + id integer PRIMARY KEY, + val integer NOT NULL +) WITH (autovacuum_enabled = off, fillfactor = 70); +INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235); +CREATE INDEX val_brin ON brin_hot using brin(val); +CREATE FUNCTION wait_for_hot_stats() RETURNS void AS $$ +DECLARE + start_time timestamptz := clock_timestamp(); + updated bool; +BEGIN + -- we don't want to wait forever; loop will exit after 30 seconds + FOR i IN 1 .. 300 LOOP + SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0) INTO updated; + EXIT WHEN updated; + + -- wait a little + PERFORM pg_sleep_for('100 milliseconds'); + -- reset stats snapshot so we can test again + PERFORM pg_stat_clear_snapshot(); + END LOOP; + -- report time waited in postmaster log (where it won't change test output) + RAISE log 'wait_for_hot_stats delayed % seconds', + EXTRACT(epoch FROM clock_timestamp() - start_time); +END +$$ LANGUAGE plpgsql; +UPDATE brin_hot SET val = -3 WHERE id = 42; +-- We can't just call wait_for_hot_stats() at this point, because we only +-- transmit stats when the session goes idle, and we probably didn't +-- transmit the last couple of counts yet thanks to the rate-limiting logic +-- in pgstat_report_stat(). But instead of waiting for the rate limiter's +-- timeout to elapse, let's just start a new session. The old one will +-- then send its stats before dying. +\c - +SELECT wait_for_hot_stats(); + wait_for_hot_stats +-------------------- + +(1 row) + +SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); + pg_stat_get_tuples_hot_updated +-------------------------------- + 1 +(1 row) + +DROP TABLE brin_hot; +DROP FUNCTION wait_for_hot_stats(); +-- Test handling of index predicates - updating attributes in precicates +-- should block HOT even for BRIN. We update a row that was not indexed +-- due to the index predicate, and becomes indexable. +CREATE TABLE brin_hot_2 (a int, b int); +INSERT INTO brin_hot_2 VALUES (1, 100); +CREATE INDEX ON brin_hot_2 USING brin (b) WHERE a = 2; +UPDATE brin_hot_2 SET a = 2; +EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100; + QUERY PLAN +----------------------------------- + Seq Scan on brin_hot_2 + Filter: ((a = 2) AND (b = 100)) +(2 rows) + +SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100; + count +------- + 1 +(1 row) + +SET enable_seqscan = off; +EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100; + QUERY PLAN +--------------------------------------------- + Bitmap Heap Scan on brin_hot_2 + Recheck Cond: ((b = 100) AND (a = 2)) + -> Bitmap Index Scan on brin_hot_2_b_idx + Index Cond: (b = 100) +(4 rows) + +SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100; + count +------- + 1 +(1 row) + diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql index 3bd866d947a..1d9ace83a8f 100644 --- a/src/test/regress/sql/brin.sql +++ b/src/test/regress/sql/brin.sql @@ -509,3 +509,66 @@ SELECT * FROM brintest_3 WHERE b < '0'; DROP TABLE brintest_3; RESET enable_seqscan; + +-- test BRIN index doesn't block HOT update +CREATE TABLE brin_hot ( + id integer PRIMARY KEY, + val integer NOT NULL +) WITH (autovacuum_enabled = off, fillfactor = 70); + +INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235); +CREATE INDEX val_brin ON brin_hot using brin(val); + +CREATE FUNCTION wait_for_hot_stats() RETURNS void AS $$ +DECLARE + start_time timestamptz := clock_timestamp(); + updated bool; +BEGIN + -- we don't want to wait forever; loop will exit after 30 seconds + FOR i IN 1 .. 300 LOOP + SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0) INTO updated; + EXIT WHEN updated; + + -- wait a little + PERFORM pg_sleep_for('100 milliseconds'); + -- reset stats snapshot so we can test again + PERFORM pg_stat_clear_snapshot(); + END LOOP; + -- report time waited in postmaster log (where it won't change test output) + RAISE log 'wait_for_hot_stats delayed % seconds', + EXTRACT(epoch FROM clock_timestamp() - start_time); +END +$$ LANGUAGE plpgsql; + +UPDATE brin_hot SET val = -3 WHERE id = 42; + +-- We can't just call wait_for_hot_stats() at this point, because we only +-- transmit stats when the session goes idle, and we probably didn't +-- transmit the last couple of counts yet thanks to the rate-limiting logic +-- in pgstat_report_stat(). But instead of waiting for the rate limiter's +-- timeout to elapse, let's just start a new session. The old one will +-- then send its stats before dying. +\c - + +SELECT wait_for_hot_stats(); +SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); + +DROP TABLE brin_hot; +DROP FUNCTION wait_for_hot_stats(); + +-- Test handling of index predicates - updating attributes in precicates +-- should block HOT even for BRIN. We update a row that was not indexed +-- due to the index predicate, and becomes indexable. +CREATE TABLE brin_hot_2 (a int, b int); +INSERT INTO brin_hot_2 VALUES (1, 100); +CREATE INDEX ON brin_hot_2 USING brin (b) WHERE a = 2; + +UPDATE brin_hot_2 SET a = 2; + +EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100; +SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100; + +SET enable_seqscan = off; + +EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100; +SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100; |
