mirror of https://github.com/postgres/postgres
Tests that standbys: - drop stats for objects when the those records are replayed - persist stats across graceful restarts - discard stats after immediate / crash restarts Author: Melanie Plageman <melanieplageman@gmail.com> Author: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/20220303021600.hs34ghqcw6zcokdh@alap3.anarazel.depull/81/head
parent
a90641eac2
commit
53b9cd20d4
@ -0,0 +1,206 @@ |
||||
# Copyright (c) 2021-2022, PostgreSQL Global Development Group |
||||
|
||||
# Tests that standbys: |
||||
# - drop stats for objects when the those records are replayed |
||||
# - persist stats across graceful restarts |
||||
# - discard stats after immediate / crash restarts |
||||
|
||||
use strict; |
||||
use warnings; |
||||
use PostgreSQL::Test::Cluster; |
||||
use PostgreSQL::Test::Utils; |
||||
use Test::More; |
||||
|
||||
my $node_primary = PostgreSQL::Test::Cluster->new('primary'); |
||||
$node_primary->init(allows_streaming => 1); |
||||
$node_primary->append_conf('postgresql.conf', "track_functions = 'all'"); |
||||
$node_primary->start; |
||||
|
||||
my $backup_name = 'my_backup'; |
||||
$node_primary->backup($backup_name); |
||||
|
||||
my $node_standby = PostgreSQL::Test::Cluster->new('standby'); |
||||
$node_standby->init_from_backup($node_primary, $backup_name, |
||||
has_streaming => 1); |
||||
$node_standby->start; |
||||
|
||||
|
||||
## Test that stats are cleaned up on standby after dropping table or function |
||||
|
||||
my $sect = 'initial'; |
||||
|
||||
my ($dboid, $tableoid, $funcoid) = |
||||
populate_standby_stats('postgres', 'public'); |
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 't'); |
||||
|
||||
drop_table_by_oid('postgres', $tableoid); |
||||
drop_function_by_oid('postgres', $funcoid); |
||||
|
||||
$sect = 'post drop'; |
||||
my $primary_lsn = $node_primary->lsn('flush'); |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', $primary_lsn); |
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 'f'); |
||||
|
||||
|
||||
## Test that stats are cleaned up on standby after dropping indirectly |
||||
|
||||
$sect = "schema creation"; |
||||
|
||||
$node_primary->safe_psql('postgres', "CREATE SCHEMA drop_schema_test1"); |
||||
$primary_lsn = $node_primary->lsn('flush'); |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', $primary_lsn); |
||||
|
||||
($dboid, $tableoid, $funcoid) = |
||||
populate_standby_stats('postgres', 'drop_schema_test1'); |
||||
|
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 't'); |
||||
$node_primary->safe_psql('postgres', "DROP SCHEMA drop_schema_test1 CASCADE"); |
||||
|
||||
$sect = "post schema drop"; |
||||
|
||||
$primary_lsn = $node_primary->lsn('flush'); |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', $primary_lsn); |
||||
|
||||
# verify table and function stats removed from standby |
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 'f'); |
||||
|
||||
|
||||
## Test that stats are cleaned up on standby after dropping database |
||||
|
||||
$sect = "createdb"; |
||||
|
||||
$node_primary->safe_psql('postgres', "CREATE DATABASE test"); |
||||
$primary_lsn = $node_primary->lsn('flush'); |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', $primary_lsn); |
||||
|
||||
($dboid, $tableoid, $funcoid) = populate_standby_stats('test', 'public'); |
||||
|
||||
# verify stats are present |
||||
test_standby_func_tab_stats_status('test', $dboid, $tableoid, $funcoid, 't'); |
||||
test_standby_db_stats_status('test', $dboid, 't'); |
||||
|
||||
$node_primary->safe_psql('postgres', "DROP DATABASE test"); |
||||
$sect = "post dropdb"; |
||||
$primary_lsn = $node_primary->lsn('flush'); |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', $primary_lsn); |
||||
|
||||
# Test that the stats were cleaned up on standby |
||||
# Note that this connects to 'postgres' but provides the dboid of dropped db |
||||
# 'test' which we acquired previously |
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 'f'); |
||||
|
||||
test_standby_db_stats_status('postgres', $dboid, 'f'); |
||||
|
||||
|
||||
## verify that stats persist across graceful restarts on a replica |
||||
|
||||
# NB: Can't test database stats, they're immediately repopulated when |
||||
# reconnecting... |
||||
$sect = "pre restart"; |
||||
($dboid, $tableoid, $funcoid) = populate_standby_stats('postgres', 'public'); |
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 't'); |
||||
|
||||
$node_standby->restart(); |
||||
|
||||
$sect = "post non-immediate"; |
||||
|
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 't'); |
||||
|
||||
# but gone after an immediate restart |
||||
$node_standby->stop('immediate'); |
||||
$node_standby->start(); |
||||
|
||||
$sect = "post immediate restart"; |
||||
|
||||
test_standby_func_tab_stats_status('postgres', |
||||
$dboid, $tableoid, $funcoid, 'f'); |
||||
|
||||
|
||||
done_testing(); |
||||
|
||||
|
||||
sub populate_standby_stats |
||||
{ |
||||
my ($connect_db, $schema) = @_; |
||||
|
||||
# create objects on primary |
||||
$node_primary->safe_psql($connect_db, |
||||
"CREATE TABLE $schema.drop_tab_test1 AS SELECT generate_series(1,100) AS a" |
||||
); |
||||
$node_primary->safe_psql($connect_db, |
||||
"CREATE FUNCTION $schema.drop_func_test1() RETURNS VOID AS 'select 2;' LANGUAGE SQL IMMUTABLE" |
||||
); |
||||
my $primary_lsn = $node_primary->lsn('flush'); |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', $primary_lsn); |
||||
|
||||
# collect object oids |
||||
my $dboid = $node_standby->safe_psql($connect_db, |
||||
"SELECT oid FROM pg_database WHERE datname = '$connect_db'"); |
||||
my $tableoid = $node_standby->safe_psql($connect_db, |
||||
"SELECT '$schema.drop_tab_test1'::regclass::oid"); |
||||
my $funcoid = $node_standby->safe_psql($connect_db, |
||||
"SELECT '$schema.drop_func_test1()'::regprocedure::oid"); |
||||
|
||||
# generate stats on standby |
||||
$node_standby->safe_psql($connect_db, |
||||
"SELECT * FROM $schema.drop_tab_test1"); |
||||
$node_standby->safe_psql($connect_db, "SELECT $schema.drop_func_test1()"); |
||||
|
||||
return ($dboid, $tableoid, $funcoid); |
||||
} |
||||
|
||||
sub drop_function_by_oid |
||||
{ |
||||
my ($connect_db, $funcoid) = @_; |
||||
|
||||
# Get function name from returned oid |
||||
my $func_name = $node_primary->safe_psql($connect_db, |
||||
"SELECT '$funcoid'::regprocedure"); |
||||
$node_primary->safe_psql($connect_db, "DROP FUNCTION $func_name"); |
||||
} |
||||
|
||||
sub drop_table_by_oid |
||||
{ |
||||
my ($connect_db, $tableoid) = @_; |
||||
|
||||
# Get table name from returned oid |
||||
my $table_name = |
||||
$node_primary->safe_psql($connect_db, "SELECT '$tableoid'::regclass"); |
||||
$node_primary->safe_psql($connect_db, "DROP TABLE $table_name"); |
||||
} |
||||
|
||||
sub test_standby_func_tab_stats_status |
||||
{ |
||||
local $Test::Builder::Level = $Test::Builder::Level + 1; |
||||
my ($connect_db, $dboid, $tableoid, $funcoid, $present) = @_; |
||||
|
||||
my %expected = (rel => $present, func => $present); |
||||
my %stats; |
||||
|
||||
$stats{rel} = $node_standby->safe_psql($connect_db, |
||||
"SELECT pg_stat_have_stats('relation', $dboid, $tableoid)"); |
||||
$stats{func} = $node_standby->safe_psql($connect_db, |
||||
"SELECT pg_stat_have_stats('function', $dboid, $funcoid)"); |
||||
|
||||
is_deeply(\%stats, \%expected, "$sect: standby stats as expected"); |
||||
|
||||
return; |
||||
} |
||||
|
||||
sub test_standby_db_stats_status |
||||
{ |
||||
local $Test::Builder::Level = $Test::Builder::Level + 1; |
||||
my ($connect_db, $dboid, $present) = @_; |
||||
|
||||
is( $node_standby->safe_psql( |
||||
$connect_db, "SELECT pg_stat_have_stats('database', $dboid, 0)"), |
||||
$present, |
||||
"$sect: standby db stats as expected"); |
||||
} |
||||
Loading…
Reference in new issue