Fix pg_stat_io buffer reuse test instability

The stats regression test attempts to ensure that Buffer Access Strategy
"reuses" are being counted in pg_stat_io by vacuuming a table which is larger
than the size of the strategy ring. However, when shared buffers are in
sufficiently high demand, another backend could evict one of the blocks in the
strategy ring before the first backend has a chance to reuse the buffer. The
backend using the strategy would then evict another shared buffer and add that
buffer to the strategy ring. This counts as an eviction and not a reuse in
pg_stat_io. Count both evictions and reuses in the test to ensure it does not
fail incorrectly.

Reported-by: Jeff Davis <pgsql@j-davis.com>,
Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/CAAKRu_bNG27AxG9TdPtwsL6wg8AWbVckjmTL2t1HF=miDQuNtw@mail.gmail.com
This commit is contained in:
Andres Freund 2023-08-01 11:22:03 -07:00
parent 4e2e75cd29
commit 803660ea4c
2 changed files with 32 additions and 17 deletions

View File

@ -1385,7 +1385,11 @@ SELECT :io_sum_local_new_tblspc_writes > :io_sum_local_after_writes;
RESET temp_buffers;
-- Test that reuse of strategy buffers and reads of blocks into these reused
-- buffers while VACUUMing are tracked in pg_stat_io.
-- buffers while VACUUMing are tracked in pg_stat_io. If there is sufficient
-- demand for shared buffers from concurrent queries, some buffers may be
-- pinned by other backends before they can be reused. In such cases, the
-- backend will evict a buffer from outside the ring and add it to the
-- ring. This is considered an eviction and not a reuse.
-- Set wal_skip_threshold smaller than the expected size of
-- test_io_vac_strategy so that, even if wal_level is minimal, VACUUM FULL will
-- fsync the newly rewritten test_io_vac_strategy instead of writing it to WAL.
@ -1393,15 +1397,15 @@ RESET temp_buffers;
-- shared buffers -- preventing us from testing BAS_VACUUM BufferAccessStrategy
-- reads.
SET wal_skip_threshold = '1 kB';
SELECT sum(reuses) AS reuses, sum(reads) AS reads
SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions
FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_before_
CREATE TABLE test_io_vac_strategy(a int, b int) WITH (autovacuum_enabled = 'false');
INSERT INTO test_io_vac_strategy SELECT i, i from generate_series(1, 4500)i;
-- Ensure that the next VACUUM will need to perform IO by rewriting the table
-- first with VACUUM (FULL).
VACUUM (FULL) test_io_vac_strategy;
-- Use the minimum BUFFER_USAGE_LIMIT to cause reuses with the smallest table
-- possible.
-- Use the minimum BUFFER_USAGE_LIMIT to cause reuses or evictions with the
-- smallest table possible.
VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) test_io_vac_strategy;
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
@ -1409,13 +1413,19 @@ SELECT pg_stat_force_next_flush();
(1 row)
SELECT sum(reuses) AS reuses, sum(reads) AS reads
SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions
FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_after_
SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads,
:io_sum_vac_strategy_after_reuses > :io_sum_vac_strategy_before_reuses;
?column? | ?column?
----------+----------
t | t
SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads;
?column?
----------
t
(1 row)
SELECT (:io_sum_vac_strategy_after_reuses + :io_sum_vac_strategy_after_evictions) >
(:io_sum_vac_strategy_before_reuses + :io_sum_vac_strategy_before_evictions);
?column?
----------
t
(1 row)
RESET wal_skip_threshold;

View File

@ -678,7 +678,11 @@ SELECT :io_sum_local_new_tblspc_writes > :io_sum_local_after_writes;
RESET temp_buffers;
-- Test that reuse of strategy buffers and reads of blocks into these reused
-- buffers while VACUUMing are tracked in pg_stat_io.
-- buffers while VACUUMing are tracked in pg_stat_io. If there is sufficient
-- demand for shared buffers from concurrent queries, some buffers may be
-- pinned by other backends before they can be reused. In such cases, the
-- backend will evict a buffer from outside the ring and add it to the
-- ring. This is considered an eviction and not a reuse.
-- Set wal_skip_threshold smaller than the expected size of
-- test_io_vac_strategy so that, even if wal_level is minimal, VACUUM FULL will
@ -687,21 +691,22 @@ RESET temp_buffers;
-- shared buffers -- preventing us from testing BAS_VACUUM BufferAccessStrategy
-- reads.
SET wal_skip_threshold = '1 kB';
SELECT sum(reuses) AS reuses, sum(reads) AS reads
SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions
FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_before_
CREATE TABLE test_io_vac_strategy(a int, b int) WITH (autovacuum_enabled = 'false');
INSERT INTO test_io_vac_strategy SELECT i, i from generate_series(1, 4500)i;
-- Ensure that the next VACUUM will need to perform IO by rewriting the table
-- first with VACUUM (FULL).
VACUUM (FULL) test_io_vac_strategy;
-- Use the minimum BUFFER_USAGE_LIMIT to cause reuses with the smallest table
-- possible.
-- Use the minimum BUFFER_USAGE_LIMIT to cause reuses or evictions with the
-- smallest table possible.
VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) test_io_vac_strategy;
SELECT pg_stat_force_next_flush();
SELECT sum(reuses) AS reuses, sum(reads) AS reads
SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions
FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_after_
SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads,
:io_sum_vac_strategy_after_reuses > :io_sum_vac_strategy_before_reuses;
SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads;
SELECT (:io_sum_vac_strategy_after_reuses + :io_sum_vac_strategy_after_evictions) >
(:io_sum_vac_strategy_before_reuses + :io_sum_vac_strategy_before_evictions);
RESET wal_skip_threshold;
-- Test that extends done by a CTAS, which uses a BAS_BULKWRITE