# Copyright (c) 2021-2025, PostgreSQL Global Development Group
# Tests statistics handling around restarts, including handling of crashes and # invalid stats files, as well as restoring stats after "normal" restarts.
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
use File::Copy;
my $connect_db = 'postgres';
my $db_under_test = 'test';
my $sect = "startup";
# Check some WAL statistics after a fresh startup. The startup process # should have done WAL reads, and initialization some WAL writes.
my $standalone_io_stats = io_stats('init', 'wal', 'standalone backend');
my $startup_io_stats = io_stats('normal', 'wal', 'startup');
cmp_ok( '0', '<',
$standalone_io_stats->{writes}, "$sect: increased standalone backend IO writes");
cmp_ok( '0', '<',
$startup_io_stats->{reads}, "$sect: increased startup IO reads");
# create test objects
$node->safe_psql($connect_db, "CREATE DATABASE $db_under_test");
$node->safe_psql($db_under_test, "CREATE TABLE tab_stats_crash_discard_test1 AS SELECT generate_series(1,100) AS a"
);
$node->safe_psql($db_under_test, "CREATE FUNCTION func_stats_crash_discard1() RETURNS VOID AS 'select 2;' LANGUAGE SQL IMMUTABLE"
);
# collect object oids
my $dboid = $node->safe_psql($db_under_test, "SELECT oid FROM pg_database WHERE datname = '$db_under_test'");
my $funcoid = $node->safe_psql($db_under_test, "SELECT 'func_stats_crash_discard1()'::regprocedure::oid");
my $tableoid = $node->safe_psql($db_under_test, "SELECT 'tab_stats_crash_discard_test1'::regclass::oid");
# generate stats and flush them
trigger_funcrel_stat();
# verify stats objects exist
$sect = "initial";
is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
is(have_stats('function', $dboid, $funcoid), 't', "$sect: function stats do exist");
is(have_stats('relation', $dboid, $tableoid), 't', "$sect: relation stats do exist");
my $datadir = $node->data_dir();
my $og_stats = "$datadir/pg_stat/pgstat.stat";
ok(-f "$og_stats", "origin stats file must exist"); copy($og_stats, $statsfile) or die "Copy failed: $!";
## test discarding of stats file after crash etc
$node->start;
$sect = "copy";
is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
is(have_stats('function', $dboid, $funcoid), 't', "$sect: function stats do exist");
is(have_stats('relation', $dboid, $tableoid), 't', "$sect: relation stats do exist");
$node->stop('immediate');
ok(!-f "$og_stats", "no stats file should exist after immediate shutdown");
# copy the old stats back to test we discard stats after crash restart copy($statsfile, $og_stats) or die "Copy failed: $!";
$node->start;
# stats should have been discarded
$sect = "post immediate";
is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
is(have_stats('function', $dboid, $funcoid), 'f', "$sect: function stats do exist");
is(have_stats('relation', $dboid, $tableoid), 'f', "$sect: relation stats do not exist");
# get rid of backup statsfile
unlink $statsfile or die "cannot unlink $statsfile $!";
# generate new stats and flush them
trigger_funcrel_stat();
$sect = "post immediate, new";
is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
is(have_stats('function', $dboid, $funcoid), 't', "$sect: function stats do exist");
is(have_stats('relation', $dboid, $tableoid), 't', "$sect: relation stats do exist");
# regular shutdown
$node->stop();
## check an invalid stats file is handled
overwrite_file($og_stats, "ZZZZZZZZZZZZZ");
# normal startup and no issues despite invalid stats file
$node->start;
# no stats present due to invalid stats file
$sect = "invalid_overwrite";
is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
is(have_stats('function', $dboid, $funcoid), 'f', "$sect: function stats do not exist");
is(have_stats('relation', $dboid, $tableoid), 'f', "$sect: relation stats do not exist");
## check invalid stats file starting with valid contents, but followed by ## invalid content is handled.
$sect = "invalid_append";
is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
is(have_stats('function', $dboid, $funcoid), 'f', "$sect: function stats do not exist");
is(have_stats('relation', $dboid, $tableoid), 'f', "$sect: relation stats do not exist");
## checks related to stats persistency around restarts and resets
# Ensure enough checkpoints to protect against races for test after reset, # even on very slow machines.
$node->safe_psql($connect_db, "CHECKPOINT; CHECKPOINT;");
## check checkpoint and wal stats are incremented due to restart
my $ckpt_start = checkpoint_stats();
my $wal_start = wal_stats();
$node->restart;
$sect = "post restart";
my $ckpt_restart = checkpoint_stats();
my $wal_restart = wal_stats();
cmp_ok(
$wal_restart->{records}, '<=',
$wal_ckpt_reset->{records}, "$sect: wal record count not affected by reset");
is( $wal_start->{reset},
$wal_ckpt_reset->{reset}, "$sect: wal stats_reset equal");
## check that checkpoint stats stay reset after restart
$node->restart;
$sect = "post ckpt reset & restart";
my $ckpt_restart_reset = checkpoint_stats();
my $wal_restart2 = wal_stats();
# made sure above there's enough checkpoints that this will be stable even on slow machines
cmp_ok(
$ckpt_restart_reset->{count}, '<',
$ckpt_restart->{count}, "$sect: checkpoint still reset");
is($ckpt_restart_reset->{reset},
$ckpt_reset->{reset}, "$sect: stats_reset same");
sub overwrite_file
{
my ($filename, $str) = @_;
open my $fh, ">", $filename
or die "could not overwrite \"$filename\": $!";
print $fh $str;
close $fh;
return;
}
sub append_file
{
my ($filename, $str) = @_;
open my $fh, ">>", $filename
or die "could not append to \"$filename\": $!";
print $fh $str;
close $fh;
return;
}
sub checkpoint_stats
{
my %results;
$results{count} = $node->safe_psql($connect_db, "SELECT num_timed + num_requested FROM pg_stat_checkpointer");
$results{reset} = $node->safe_psql($connect_db, "SELECT stats_reset FROM pg_stat_checkpointer");
return \%results;
}
sub wal_stats
{
my %results;
$results{records} =
$node->safe_psql($connect_db, "SELECT wal_records FROM pg_stat_wal");
$results{bytes} =
$node->safe_psql($connect_db, "SELECT wal_bytes FROM pg_stat_wal");
$results{reset} =
$node->safe_psql($connect_db, "SELECT stats_reset FROM pg_stat_wal");
return \%results;
}
sub io_stats
{
my ($context, $object, $backend_type) = @_;
my %results;
$results{writes} = $node->safe_psql(
$connect_db, qq{SELECT writes FROM pg_stat_io
WHERE context = '$context' AND object = '$object' AND
backend_type = '$backend_type'});
$results{reads} = $node->safe_psql(
$connect_db, qq{SELECT reads FROM pg_stat_io
WHERE context = '$context' AND object = '$object' AND
backend_type = '$backend_type'});
return \%results;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet am 2026-08-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.