pgstat: add/extend tests for resetting various kinds of stats.
- subscriber stats reset path was untested - slot stat sreset path for all slots was untested - pg_stat_database.sessions etc was untested - pg_stat_reset_shared() was untested, for any kind of shared stats - pg_stat_reset() was untested Author: Melanie Plageman <melanieplageman@gmail.com> Author: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/20220303021600.hs34ghqcw6zcokdh@alap3.anarazel.de
This commit is contained in:
parent
10a8d13823
commit
5264add784
|
@ -1,6 +1,9 @@
|
|||
-- predictability
|
||||
SET synchronous_commit = on;
|
||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_stats', 'test_decoding');
|
||||
SELECT 'init' FROM
|
||||
pg_create_logical_replication_slot('regression_slot_stats1', 'test_decoding') s1,
|
||||
pg_create_logical_replication_slot('regression_slot_stats2', 'test_decoding') s2,
|
||||
pg_create_logical_replication_slot('regression_slot_stats3', 'test_decoding') s3;
|
||||
?column?
|
||||
----------
|
||||
init
|
||||
|
@ -10,7 +13,19 @@ CREATE TABLE stats_test(data text);
|
|||
-- non-spilled xact
|
||||
SET logical_decoding_work_mem to '64MB';
|
||||
INSERT INTO stats_test values(1);
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats1', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats2', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
count
|
||||
-------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats3', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
count
|
||||
-------
|
||||
3
|
||||
|
@ -22,31 +37,65 @@ SELECT pg_stat_force_next_flush();
|
|||
|
||||
(1 row)
|
||||
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots;
|
||||
slot_name | spill_txns | spill_count | total_txns | total_bytes
|
||||
-----------------------+------------+-------------+------------+-------------
|
||||
regression_slot_stats | t | t | t | t
|
||||
(1 row)
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
|
||||
slot_name | spill_txns | spill_count | total_txns | total_bytes
|
||||
------------------------+------------+-------------+------------+-------------
|
||||
regression_slot_stats1 | t | t | t | t
|
||||
regression_slot_stats2 | t | t | t | t
|
||||
regression_slot_stats3 | t | t | t | t
|
||||
(3 rows)
|
||||
|
||||
RESET logical_decoding_work_mem;
|
||||
-- reset the slot stats
|
||||
SELECT pg_stat_reset_replication_slot('regression_slot_stats');
|
||||
-- reset stats for one slot, others should be unaffected
|
||||
SELECT pg_stat_reset_replication_slot('regression_slot_stats1');
|
||||
pg_stat_reset_replication_slot
|
||||
--------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT slot_name, spill_txns, spill_count, total_txns, total_bytes FROM pg_stat_replication_slots;
|
||||
slot_name | spill_txns | spill_count | total_txns | total_bytes
|
||||
-----------------------+------------+-------------+------------+-------------
|
||||
regression_slot_stats | 0 | 0 | 0 | 0
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
|
||||
slot_name | spill_txns | spill_count | total_txns | total_bytes
|
||||
------------------------+------------+-------------+------------+-------------
|
||||
regression_slot_stats1 | t | t | f | f
|
||||
regression_slot_stats2 | t | t | t | t
|
||||
regression_slot_stats3 | t | t | t | t
|
||||
(3 rows)
|
||||
|
||||
-- reset stats for all slots
|
||||
SELECT pg_stat_reset_replication_slot(NULL);
|
||||
pg_stat_reset_replication_slot
|
||||
--------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
|
||||
slot_name | spill_txns | spill_count | total_txns | total_bytes
|
||||
------------------------+------------+-------------+------------+-------------
|
||||
regression_slot_stats1 | t | t | f | f
|
||||
regression_slot_stats2 | t | t | f | f
|
||||
regression_slot_stats3 | t | t | f | f
|
||||
(3 rows)
|
||||
|
||||
-- verify accessing/resetting stats for non-existent slot does something reasonable
|
||||
SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
|
||||
slot_name | spill_txns | spill_count | spill_bytes | stream_txns | stream_count | stream_bytes | total_txns | total_bytes | stats_reset
|
||||
--------------+------------+-------------+-------------+-------------+--------------+--------------+------------+-------------+-------------
|
||||
do-not-exist | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
|
||||
(1 row)
|
||||
|
||||
SELECT pg_stat_reset_replication_slot('do-not-exist');
|
||||
ERROR: replication slot "do-not-exist" does not exist
|
||||
SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
|
||||
slot_name | spill_txns | spill_count | spill_bytes | stream_txns | stream_count | stream_bytes | total_txns | total_bytes | stats_reset
|
||||
--------------+------------+-------------+-------------+-------------+--------------+--------------+------------+-------------+-------------
|
||||
do-not-exist | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
|
||||
(1 row)
|
||||
|
||||
-- spilling the xact
|
||||
BEGIN;
|
||||
INSERT INTO stats_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1, 5000) g(i);
|
||||
COMMIT;
|
||||
SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot_stats', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot_stats1', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
count
|
||||
-------
|
||||
5002
|
||||
|
@ -62,31 +111,39 @@ SELECT pg_stat_force_next_flush();
|
|||
(1 row)
|
||||
|
||||
SELECT slot_name, spill_txns > 0 AS spill_txns, spill_count > 0 AS spill_count FROM pg_stat_replication_slots;
|
||||
slot_name | spill_txns | spill_count
|
||||
-----------------------+------------+-------------
|
||||
regression_slot_stats | t | t
|
||||
(1 row)
|
||||
slot_name | spill_txns | spill_count
|
||||
------------------------+------------+-------------
|
||||
regression_slot_stats1 | t | t
|
||||
regression_slot_stats2 | f | f
|
||||
regression_slot_stats3 | f | f
|
||||
(3 rows)
|
||||
|
||||
-- Ensure stats can be repeatedly accessed using the same stats snapshot. See
|
||||
-- https://postgr.es/m/20210317230447.c7uc4g3vbs4wi32i%40alap3.anarazel.de
|
||||
BEGIN;
|
||||
SELECT slot_name FROM pg_stat_replication_slots;
|
||||
slot_name
|
||||
-----------------------
|
||||
regression_slot_stats
|
||||
(1 row)
|
||||
slot_name
|
||||
------------------------
|
||||
regression_slot_stats1
|
||||
regression_slot_stats2
|
||||
regression_slot_stats3
|
||||
(3 rows)
|
||||
|
||||
SELECT slot_name FROM pg_stat_replication_slots;
|
||||
slot_name
|
||||
-----------------------
|
||||
regression_slot_stats
|
||||
(1 row)
|
||||
slot_name
|
||||
------------------------
|
||||
regression_slot_stats1
|
||||
regression_slot_stats2
|
||||
regression_slot_stats3
|
||||
(3 rows)
|
||||
|
||||
COMMIT;
|
||||
DROP TABLE stats_test;
|
||||
SELECT pg_drop_replication_slot('regression_slot_stats');
|
||||
pg_drop_replication_slot
|
||||
--------------------------
|
||||
|
||||
SELECT pg_drop_replication_slot('regression_slot_stats1'),
|
||||
pg_drop_replication_slot('regression_slot_stats2'),
|
||||
pg_drop_replication_slot('regression_slot_stats3');
|
||||
pg_drop_replication_slot | pg_drop_replication_slot | pg_drop_replication_slot
|
||||
--------------------------+--------------------------+--------------------------
|
||||
| |
|
||||
(1 row)
|
||||
|
||||
|
|
|
@ -1,27 +1,41 @@
|
|||
-- predictability
|
||||
SET synchronous_commit = on;
|
||||
|
||||
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_stats', 'test_decoding');
|
||||
SELECT 'init' FROM
|
||||
pg_create_logical_replication_slot('regression_slot_stats1', 'test_decoding') s1,
|
||||
pg_create_logical_replication_slot('regression_slot_stats2', 'test_decoding') s2,
|
||||
pg_create_logical_replication_slot('regression_slot_stats3', 'test_decoding') s3;
|
||||
|
||||
CREATE TABLE stats_test(data text);
|
||||
|
||||
-- non-spilled xact
|
||||
SET logical_decoding_work_mem to '64MB';
|
||||
INSERT INTO stats_test values(1);
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats1', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats2', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats3', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT pg_stat_force_next_flush();
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots;
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
|
||||
RESET logical_decoding_work_mem;
|
||||
|
||||
-- reset the slot stats
|
||||
SELECT pg_stat_reset_replication_slot('regression_slot_stats');
|
||||
SELECT slot_name, spill_txns, spill_count, total_txns, total_bytes FROM pg_stat_replication_slots;
|
||||
-- reset stats for one slot, others should be unaffected
|
||||
SELECT pg_stat_reset_replication_slot('regression_slot_stats1');
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
|
||||
|
||||
-- reset stats for all slots
|
||||
SELECT pg_stat_reset_replication_slot(NULL);
|
||||
SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
|
||||
|
||||
-- verify accessing/resetting stats for non-existent slot does something reasonable
|
||||
SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
|
||||
SELECT pg_stat_reset_replication_slot('do-not-exist');
|
||||
SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
|
||||
|
||||
-- spilling the xact
|
||||
BEGIN;
|
||||
INSERT INTO stats_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1, 5000) g(i);
|
||||
COMMIT;
|
||||
SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot_stats', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot_stats1', NULL, NULL, 'skip-empty-xacts', '1');
|
||||
|
||||
-- Check stats. We can't test the exact stats count as that can vary if any
|
||||
-- background transaction (say by autovacuum) happens in parallel to the main
|
||||
|
@ -37,4 +51,6 @@ SELECT slot_name FROM pg_stat_replication_slots;
|
|||
COMMIT;
|
||||
|
||||
DROP TABLE stats_test;
|
||||
SELECT pg_drop_replication_slot('regression_slot_stats');
|
||||
SELECT pg_drop_replication_slot('regression_slot_stats1'),
|
||||
pg_drop_replication_slot('regression_slot_stats2'),
|
||||
pg_drop_replication_slot('regression_slot_stats3');
|
||||
|
|
|
@ -200,6 +200,69 @@ chomp($logical_restart_lsn_post);
|
|||
ok(($logical_restart_lsn_pre cmp $logical_restart_lsn_post) == 0,
|
||||
"logical slot advance persists across restarts");
|
||||
|
||||
my $stats_test_slot1 = 'test_slot';
|
||||
my $stats_test_slot2 = 'logical_slot';
|
||||
|
||||
# Test that reset works for pg_stat_replication_slots
|
||||
|
||||
# Stats exist for stats test slot 1
|
||||
is($node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT total_bytes > 0, stats_reset IS NULL FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot1')
|
||||
), qq(t|t), qq(Total bytes is > 0 and stats_reset is NULL for slot '$stats_test_slot1'.));
|
||||
|
||||
# Do reset of stats for stats test slot 1
|
||||
$node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT pg_stat_reset_replication_slot('$stats_test_slot1'))
|
||||
);
|
||||
|
||||
# Get reset value after reset
|
||||
my $reset1 = $node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT stats_reset FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot1')
|
||||
);
|
||||
|
||||
# Do reset again
|
||||
$node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT pg_stat_reset_replication_slot('$stats_test_slot1'))
|
||||
);
|
||||
|
||||
is($node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT stats_reset > '$reset1'::timestamptz, total_bytes = 0 FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot1')
|
||||
), qq(t|t), qq(Check that reset timestamp is later after the second reset of stats for slot '$stats_test_slot1' and confirm total_bytes was set to 0.));
|
||||
|
||||
# Check that test slot 2 has NULL in reset timestamp
|
||||
is($node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT stats_reset IS NULL FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot2')
|
||||
), qq(t), qq(Stats_reset is NULL for slot '$stats_test_slot2' before reset.));
|
||||
|
||||
# Get reset value again for test slot 1
|
||||
$reset1 = $node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT stats_reset FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot1')
|
||||
);
|
||||
|
||||
# Reset stats for all replication slots
|
||||
$node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT pg_stat_reset_replication_slot(NULL))
|
||||
);
|
||||
|
||||
# Check that test slot 2 reset timestamp is no longer NULL after reset
|
||||
is($node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT stats_reset IS NOT NULL FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot2')
|
||||
), qq(t), qq(Stats_reset is not NULL for slot '$stats_test_slot2' after reset all.));
|
||||
|
||||
is($node_primary->safe_psql(
|
||||
'postgres',
|
||||
qq(SELECT stats_reset > '$reset1'::timestamptz FROM pg_stat_replication_slots WHERE slot_name = '$stats_test_slot1')
|
||||
), qq(t), qq(Check that reset timestamp is later after resetting stats for slot '$stats_test_slot1' again.));
|
||||
|
||||
# done with the node
|
||||
$node_primary->stop;
|
||||
|
||||
|
|
|
@ -554,6 +554,170 @@ SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
|
|||
|
||||
DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
|
||||
DROP TABLE prevstats;
|
||||
-----
|
||||
-- Test that various stats views are being properly populated
|
||||
-----
|
||||
-- Test that sessions is incremented when a new session is started in pg_stat_database
|
||||
SELECT sessions AS db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
|
||||
\c
|
||||
SELECT sessions > :db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database());
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- Test pg_stat_bgwriter checkpointer-related stats, together with pg_stat_wal
|
||||
SELECT checkpoints_req AS rqst_ckpts_before FROM pg_stat_bgwriter \gset
|
||||
-- Test pg_stat_wal
|
||||
SELECT wal_bytes AS wal_bytes_before FROM pg_stat_wal \gset
|
||||
CREATE TABLE test_stats_temp AS SELECT 17;
|
||||
DROP TABLE test_stats_temp;
|
||||
-- Checkpoint twice: The checkpointer reports stats after reporting completion
|
||||
-- of the checkpoint. But after a second checkpoint we'll see at least the
|
||||
-- results of the first.
|
||||
CHECKPOINT;
|
||||
CHECKPOINT;
|
||||
SELECT checkpoints_req > :rqst_ckpts_before FROM pg_stat_bgwriter;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-----
|
||||
-- Test that resetting stats works for reset timestamp
|
||||
-----
|
||||
-- Test that reset_slru with a specified SLRU works.
|
||||
SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'CommitTs' \gset
|
||||
SELECT stats_reset AS slru_notify_reset_ts FROM pg_stat_slru WHERE name = 'Notify' \gset
|
||||
SELECT pg_stat_reset_slru('CommitTs');
|
||||
pg_stat_reset_slru
|
||||
--------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'CommitTs';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'CommitTs' \gset
|
||||
-- Test that multiple SLRUs are reset when no specific SLRU provided to reset function
|
||||
SELECT pg_stat_reset_slru(NULL);
|
||||
pg_stat_reset_slru
|
||||
--------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'CommitTs';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'slru_notify_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'Notify';
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- Test that reset_shared with archiver specified as the stats type works
|
||||
SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
|
||||
SELECT pg_stat_reset_shared('archiver');
|
||||
pg_stat_reset_shared
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
|
||||
-- Test that reset_shared with bgwriter specified as the stats type works
|
||||
SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
|
||||
SELECT pg_stat_reset_shared('bgwriter');
|
||||
pg_stat_reset_shared
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
|
||||
-- Test that reset_shared with wal specified as the stats type works
|
||||
SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
|
||||
SELECT pg_stat_reset_shared('wal');
|
||||
pg_stat_reset_shared
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
|
||||
-- Test that reset_shared with no specified stats type doesn't reset anything
|
||||
SELECT pg_stat_reset_shared(NULL);
|
||||
pg_stat_reset_shared
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset = :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset = :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset = :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- Test that reset works for pg_stat_database
|
||||
-- Since pg_stat_database stats_reset starts out as NULL, reset it once first so we have something to compare it to
|
||||
SELECT pg_stat_reset();
|
||||
pg_stat_reset
|
||||
---------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset AS db_reset_ts FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
|
||||
SELECT pg_stat_reset();
|
||||
pg_stat_reset
|
||||
---------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT stats_reset > :'db_reset_ts'::timestamptz FROM pg_stat_database WHERE datname = (SELECT current_database());
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
----
|
||||
-- pg_stat_get_snapshot_timestamp behavior
|
||||
----
|
||||
|
|
|
@ -123,6 +123,9 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr
|
|||
|
||||
# ----------
|
||||
# Another group of parallel tests
|
||||
#
|
||||
# The stats test resets stats, so nothing else needing stats access can be in
|
||||
# this group.
|
||||
# ----------
|
||||
test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain compression memoize stats
|
||||
|
||||
|
|
|
@ -286,6 +286,84 @@ SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
|
|||
DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
|
||||
DROP TABLE prevstats;
|
||||
|
||||
|
||||
-----
|
||||
-- Test that various stats views are being properly populated
|
||||
-----
|
||||
|
||||
-- Test that sessions is incremented when a new session is started in pg_stat_database
|
||||
SELECT sessions AS db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
|
||||
\c
|
||||
SELECT sessions > :db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database());
|
||||
|
||||
-- Test pg_stat_bgwriter checkpointer-related stats, together with pg_stat_wal
|
||||
SELECT checkpoints_req AS rqst_ckpts_before FROM pg_stat_bgwriter \gset
|
||||
|
||||
-- Test pg_stat_wal
|
||||
SELECT wal_bytes AS wal_bytes_before FROM pg_stat_wal \gset
|
||||
|
||||
CREATE TABLE test_stats_temp AS SELECT 17;
|
||||
DROP TABLE test_stats_temp;
|
||||
|
||||
-- Checkpoint twice: The checkpointer reports stats after reporting completion
|
||||
-- of the checkpoint. But after a second checkpoint we'll see at least the
|
||||
-- results of the first.
|
||||
CHECKPOINT;
|
||||
CHECKPOINT;
|
||||
|
||||
SELECT checkpoints_req > :rqst_ckpts_before FROM pg_stat_bgwriter;
|
||||
SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
|
||||
|
||||
|
||||
-----
|
||||
-- Test that resetting stats works for reset timestamp
|
||||
-----
|
||||
|
||||
-- Test that reset_slru with a specified SLRU works.
|
||||
SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'CommitTs' \gset
|
||||
SELECT stats_reset AS slru_notify_reset_ts FROM pg_stat_slru WHERE name = 'Notify' \gset
|
||||
SELECT pg_stat_reset_slru('CommitTs');
|
||||
SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'CommitTs';
|
||||
SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'CommitTs' \gset
|
||||
|
||||
-- Test that multiple SLRUs are reset when no specific SLRU provided to reset function
|
||||
SELECT pg_stat_reset_slru(NULL);
|
||||
SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'CommitTs';
|
||||
SELECT stats_reset > :'slru_notify_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'Notify';
|
||||
|
||||
-- Test that reset_shared with archiver specified as the stats type works
|
||||
SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
|
||||
SELECT pg_stat_reset_shared('archiver');
|
||||
SELECT stats_reset > :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
|
||||
SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
|
||||
|
||||
-- Test that reset_shared with bgwriter specified as the stats type works
|
||||
SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
|
||||
SELECT pg_stat_reset_shared('bgwriter');
|
||||
SELECT stats_reset > :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
|
||||
SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
|
||||
|
||||
-- Test that reset_shared with wal specified as the stats type works
|
||||
SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
|
||||
SELECT pg_stat_reset_shared('wal');
|
||||
SELECT stats_reset > :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
|
||||
SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
|
||||
|
||||
-- Test that reset_shared with no specified stats type doesn't reset anything
|
||||
SELECT pg_stat_reset_shared(NULL);
|
||||
SELECT stats_reset = :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
|
||||
SELECT stats_reset = :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
|
||||
SELECT stats_reset = :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
|
||||
|
||||
-- Test that reset works for pg_stat_database
|
||||
|
||||
-- Since pg_stat_database stats_reset starts out as NULL, reset it once first so we have something to compare it to
|
||||
SELECT pg_stat_reset();
|
||||
SELECT stats_reset AS db_reset_ts FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
|
||||
SELECT pg_stat_reset();
|
||||
SELECT stats_reset > :'db_reset_ts'::timestamptz FROM pg_stat_database WHERE datname = (SELECT current_database());
|
||||
|
||||
|
||||
----
|
||||
-- pg_stat_get_snapshot_timestamp behavior
|
||||
----
|
||||
|
|
|
@ -18,83 +18,255 @@ my $node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
|
|||
$node_subscriber->init(allows_streaming => 'logical');
|
||||
$node_subscriber->start;
|
||||
|
||||
# Initial table setup on both publisher and subscriber. On subscriber we
|
||||
# create the same tables but with primary keys. Also, insert some data that
|
||||
# will conflict with the data replicated from publisher later.
|
||||
$node_publisher->safe_psql(
|
||||
'postgres',
|
||||
qq[
|
||||
BEGIN;
|
||||
CREATE TABLE test_tab1 (a int);
|
||||
INSERT INTO test_tab1 VALUES (1);
|
||||
COMMIT;
|
||||
]);
|
||||
$node_subscriber->safe_psql(
|
||||
'postgres',
|
||||
qq[
|
||||
BEGIN;
|
||||
CREATE TABLE test_tab1 (a int primary key);
|
||||
INSERT INTO test_tab1 VALUES (1);
|
||||
COMMIT;
|
||||
]);
|
||||
|
||||
# Setup publication.
|
||||
my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE PUBLICATION tap_pub FOR TABLE test_tab1;");
|
||||
sub create_sub_pub_w_errors
|
||||
{
|
||||
my ($node_publisher, $node_subscriber, $db, $table_name) = @_;
|
||||
# Initial table setup on both publisher and subscriber. On subscriber we
|
||||
# create the same tables but with primary keys. Also, insert some data that
|
||||
# will conflict with the data replicated from publisher later.
|
||||
$node_publisher->safe_psql(
|
||||
$db,
|
||||
qq[
|
||||
BEGIN;
|
||||
CREATE TABLE $table_name(a int);
|
||||
INSERT INTO $table_name VALUES (1);
|
||||
COMMIT;
|
||||
]);
|
||||
$node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq[
|
||||
BEGIN;
|
||||
CREATE TABLE $table_name(a int primary key);
|
||||
INSERT INTO $table_name VALUES (1);
|
||||
COMMIT;
|
||||
]);
|
||||
|
||||
# Set up publication.
|
||||
my $pub_name = $table_name . '_pub';
|
||||
my $publisher_connstr = $node_publisher->connstr . qq( dbname=$db);
|
||||
|
||||
$node_publisher->safe_psql($db,
|
||||
qq(CREATE PUBLICATION $pub_name FOR TABLE $table_name));
|
||||
|
||||
# Create subscription. The tablesync for table on subscription will enter into
|
||||
# infinite error loop due to violating the unique constraint.
|
||||
my $sub_name = $table_name . '_sub';
|
||||
$node_subscriber->safe_psql($db,
|
||||
qq(CREATE SUBSCRIPTION $sub_name CONNECTION '$publisher_connstr' PUBLICATION $pub_name)
|
||||
);
|
||||
|
||||
$node_publisher->wait_for_catchup($sub_name);
|
||||
|
||||
# Wait for the tablesync error to be reported.
|
||||
$node_subscriber->poll_query_until(
|
||||
$db,
|
||||
qq[
|
||||
SELECT sync_error_count > 0
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub_name'
|
||||
])
|
||||
or die
|
||||
qq(Timed out while waiting for tablesync errors for subscription '$sub_name');
|
||||
|
||||
# Truncate test_tab1 so that tablesync worker can continue.
|
||||
$node_subscriber->safe_psql($db, qq(TRUNCATE $table_name));
|
||||
|
||||
# Wait for initial tablesync to finish.
|
||||
$node_subscriber->poll_query_until(
|
||||
$db,
|
||||
qq[
|
||||
SELECT count(1) = 1 FROM pg_subscription_rel
|
||||
WHERE srrelid = '$table_name'::regclass AND srsubstate in ('r', 's')
|
||||
])
|
||||
or die
|
||||
qq(Timed out while waiting for subscriber to synchronize data for table '$table_name'.);
|
||||
|
||||
# Check test table on the subscriber has one row.
|
||||
my $result =
|
||||
$node_subscriber->safe_psql($db, qq(SELECT a FROM $table_name));
|
||||
is($result, qq(1), qq(Check that table '$table_name' now has 1 row.));
|
||||
|
||||
# Insert data to test table on the publisher, raising an error on the
|
||||
# subscriber due to violation of the unique constraint on test table.
|
||||
$node_publisher->safe_psql($db, qq(INSERT INTO $table_name VALUES (1)));
|
||||
|
||||
# Wait for the apply error to be reported.
|
||||
$node_subscriber->poll_query_until(
|
||||
$db,
|
||||
qq[
|
||||
SELECT apply_error_count > 0
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub_name'
|
||||
])
|
||||
or die
|
||||
qq(Timed out while waiting for apply error for subscription '$sub_name');
|
||||
|
||||
# Truncate test table so that apply worker can continue.
|
||||
$node_subscriber->safe_psql($db, qq(TRUNCATE $table_name));
|
||||
|
||||
return ($pub_name, $sub_name);
|
||||
}
|
||||
|
||||
my $db = 'postgres';
|
||||
|
||||
# There shouldn't be any subscription errors before starting logical replication.
|
||||
my $result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(1) FROM pg_stat_subscription_stats");
|
||||
is($result, qq(0), 'check no subscription error');
|
||||
|
||||
# Create subscription. The tablesync for test_tab1 on tap_sub will enter into
|
||||
# infinite error loop due to violating the unique constraint.
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE SUBSCRIPTION tap_sub CONNECTION '$publisher_connstr' PUBLICATION tap_pub;"
|
||||
my $result = $node_subscriber->safe_psql($db,
|
||||
qq(SELECT count(1) FROM pg_stat_subscription_stats));
|
||||
is($result, qq(0),
|
||||
'Check that there are no subscription errors before starting logical replication.'
|
||||
);
|
||||
|
||||
$node_publisher->wait_for_catchup('tap_sub');
|
||||
# Create the publication and subscription with sync and apply errors
|
||||
my $table1_name = 'test_tab1';
|
||||
my ($pub1_name, $sub1_name) =
|
||||
create_sub_pub_w_errors($node_publisher, $node_subscriber, $db,
|
||||
$table1_name);
|
||||
|
||||
# Wait for the tablesync error to be reported.
|
||||
$node_subscriber->poll_query_until(
|
||||
'postgres',
|
||||
qq[
|
||||
SELECT sync_error_count > 0
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = 'tap_sub'
|
||||
]) or die "Timed out while waiting for tablesync error";
|
||||
# Apply and Sync errors are > 0 and reset timestamp is NULL
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT apply_error_count > 0,
|
||||
sync_error_count > 0,
|
||||
stats_reset IS NULL
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub1_name')
|
||||
),
|
||||
qq(t|t|t),
|
||||
qq(Check that apply errors and sync errors are both > 0 and stats_reset is NULL for subscription '$sub1_name'.)
|
||||
);
|
||||
|
||||
# Truncate test_tab1 so that tablesync worker can continue.
|
||||
$node_subscriber->safe_psql('postgres', "TRUNCATE test_tab1;");
|
||||
# Reset a single subscription
|
||||
$node_subscriber->safe_psql($db,
|
||||
qq(SELECT pg_stat_reset_subscription_stats((SELECT subid FROM pg_stat_subscription_stats WHERE subname = '$sub1_name')))
|
||||
);
|
||||
|
||||
# Wait for initial tablesync for test_tab1 to finish.
|
||||
$node_subscriber->poll_query_until(
|
||||
'postgres',
|
||||
qq[
|
||||
SELECT count(1) = 1 FROM pg_subscription_rel
|
||||
WHERE srrelid = 'test_tab1'::regclass AND srsubstate in ('r', 's')
|
||||
]) or die "Timed out while waiting for subscriber to synchronize data";
|
||||
# Apply and Sync errors are 0 and stats reset is not NULL
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT apply_error_count = 0,
|
||||
sync_error_count = 0,
|
||||
stats_reset IS NOT NULL
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub1_name')
|
||||
),
|
||||
qq(t|t|t),
|
||||
qq(Confirm that apply errors and sync errors are both 0 and stats_reset is not NULL after reset for subscription '$sub1_name'.)
|
||||
);
|
||||
|
||||
# Check test_tab1 on the subscriber has one row.
|
||||
$result = $node_subscriber->safe_psql('postgres', "SELECT a FROM test_tab1");
|
||||
is($result, qq(1), 'check the table has now row');
|
||||
# Get reset timestamp
|
||||
my $reset_time1 = $node_subscriber->safe_psql($db,
|
||||
qq(SELECT stats_reset FROM pg_stat_subscription_stats WHERE subname = '$sub1_name')
|
||||
);
|
||||
|
||||
# Insert data to test_tab1 on the publisher, raising an error on the subscriber
|
||||
# due to violation of the unique constraint on test_tab1.
|
||||
$node_publisher->safe_psql('postgres', "INSERT INTO test_tab1 VALUES (1)");
|
||||
# Reset single sub again
|
||||
$node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT pg_stat_reset_subscription_stats((SELECT subid FROM
|
||||
pg_stat_subscription_stats WHERE subname = '$sub1_name')))
|
||||
);
|
||||
|
||||
# Wait for the apply error to be reported.
|
||||
$node_subscriber->poll_query_until(
|
||||
'postgres',
|
||||
qq[
|
||||
SELECT apply_error_count > 0
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = 'tap_sub'
|
||||
]) or die "Timed out while waiting for apply error";
|
||||
# check reset timestamp is newer after reset
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT stats_reset > '$reset_time1'::timestamptz FROM
|
||||
pg_stat_subscription_stats WHERE subname = '$sub1_name')
|
||||
),
|
||||
qq(t),
|
||||
qq(Check reset timestamp for '$sub1_name' is newer after second reset.));
|
||||
|
||||
# Make second subscription and publication
|
||||
my $table2_name = 'test_tab2';
|
||||
my ($pub2_name, $sub2_name) =
|
||||
create_sub_pub_w_errors($node_publisher, $node_subscriber, $db,
|
||||
$table2_name);
|
||||
|
||||
# Apply and Sync errors are > 0 and reset timestamp is NULL
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT apply_error_count > 0,
|
||||
sync_error_count > 0,
|
||||
stats_reset IS NULL
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub2_name')
|
||||
),
|
||||
qq(t|t|t),
|
||||
qq(Confirm that apply errors and sync errors are both > 0 and stats_reset is NULL for sub '$sub2_name'.)
|
||||
);
|
||||
|
||||
# Reset all subscriptions
|
||||
$node_subscriber->safe_psql($db,
|
||||
qq(SELECT pg_stat_reset_subscription_stats(NULL)));
|
||||
|
||||
# Apply and Sync errors are 0 and stats reset is not NULL
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT apply_error_count = 0,
|
||||
sync_error_count = 0,
|
||||
stats_reset IS NOT NULL
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub1_name')
|
||||
),
|
||||
qq(t|t|t),
|
||||
qq(Confirm that apply errors and sync errors are both 0 and stats_reset is not NULL for sub '$sub1_name' after reset.)
|
||||
);
|
||||
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT apply_error_count = 0,
|
||||
sync_error_count = 0,
|
||||
stats_reset IS NOT NULL
|
||||
FROM pg_stat_subscription_stats
|
||||
WHERE subname = '$sub2_name')
|
||||
),
|
||||
qq(t|t|t),
|
||||
qq(Confirm that apply errors and sync errors are both 0 and stats_reset is not NULL for sub '$sub2_name' after reset.)
|
||||
);
|
||||
|
||||
$reset_time1 = $node_subscriber->safe_psql($db,
|
||||
qq(SELECT stats_reset FROM pg_stat_subscription_stats WHERE subname = '$sub1_name')
|
||||
);
|
||||
my $reset_time2 = $node_subscriber->safe_psql($db,
|
||||
qq(SELECT stats_reset FROM pg_stat_subscription_stats WHERE subname = '$sub2_name')
|
||||
);
|
||||
|
||||
# Reset all subscriptions
|
||||
$node_subscriber->safe_psql($db,
|
||||
qq(SELECT pg_stat_reset_subscription_stats(NULL)));
|
||||
|
||||
# check reset timestamp for sub1 is newer after reset
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT stats_reset > '$reset_time1'::timestamptz FROM
|
||||
pg_stat_subscription_stats WHERE subname = '$sub1_name')
|
||||
),
|
||||
qq(t),
|
||||
qq(Confirm that reset timestamp for '$sub1_name' is newer after second reset.)
|
||||
);
|
||||
|
||||
# check reset timestamp for sub2 is newer after reset
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db,
|
||||
qq(SELECT stats_reset > '$reset_time2'::timestamptz FROM
|
||||
pg_stat_subscription_stats WHERE subname = '$sub2_name')
|
||||
),
|
||||
qq(t),
|
||||
qq(Confirm that reset timestamp for '$sub2_name' is newer after second reset.)
|
||||
);
|
||||
|
||||
# Get subscription 1 oid
|
||||
my $sub1_oid = $node_subscriber->safe_psql($db,
|
||||
qq(SELECT oid FROM pg_subscription WHERE subname = '$sub1_name'));
|
||||
|
||||
# Drop subscription 1
|
||||
$node_subscriber->safe_psql($db, qq(DROP SUBSCRIPTION $sub1_name));
|
||||
|
||||
# Subscription stats for sub1 should be gone
|
||||
is( $node_subscriber->safe_psql(
|
||||
$db, qq(SELECT pg_stat_have_stats('subscription', 0, $sub1_oid))),
|
||||
qq(f),
|
||||
qq(Subscription stats for subscription '$sub1_name' should be removed.));
|
||||
|
||||
# Truncate test_tab1 so that apply worker can continue.
|
||||
$node_subscriber->safe_psql('postgres', "TRUNCATE test_tab1;");
|
||||
|
||||
$node_subscriber->stop('fast');
|
||||
$node_publisher->stop('fast');
|
||||
|
|
Loading…
Reference in New Issue