mirror of
https://github.com/postgres/postgres.git
synced 2025-07-20 05:03:10 +03:00
Allow on-line enabling and disabling of data checksums
This makes it possible to turn checksums on in a live cluster, without the previous need for dump/reload or logical replication (and to turn it off). Enabling checkusm starts a background process in the form of a launcher/worker combination that goes through the entire database and recalculates checksums on each and every page. Only when all pages have been checksummed are they fully enabled in the cluster. Any failure of the process will revert to checksums off and the process has to be started. This adds a new WAL record that indicates the state of checksums, so the process works across replicated clusters. Authors: Magnus Hagander and Daniel Gustafsson Review: Tomas Vondra, Michael Banck, Heikki Linnakangas, Andrey Borodin
This commit is contained in:
@ -12,7 +12,8 @@ subdir = src/test
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
SUBDIRS = perl regress isolation modules authentication recovery subscription
|
||||
SUBDIRS = perl regress isolation modules authentication recovery subscription \
|
||||
checksum
|
||||
|
||||
# Test suites that are not safe by default but can be run if selected
|
||||
# by the user via the whitespace-separated list in variable
|
||||
|
2
src/test/checksum/.gitignore
vendored
Normal file
2
src/test/checksum/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Generated by test suite
|
||||
/tmp_check/
|
24
src/test/checksum/Makefile
Normal file
24
src/test/checksum/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile for src/test/checksum
|
||||
#
|
||||
# Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
||||
# Portions Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
# src/test/checksum/Makefile
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
subdir = src/test/checksum
|
||||
top_builddir = ../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
check:
|
||||
$(prove_check)
|
||||
|
||||
installcheck:
|
||||
$(prove_installcheck)
|
||||
|
||||
clean distclean maintainer-clean:
|
||||
rm -rf tmp_check
|
||||
|
22
src/test/checksum/README
Normal file
22
src/test/checksum/README
Normal file
@ -0,0 +1,22 @@
|
||||
src/test/checksum/README
|
||||
|
||||
Regression tests for data checksums
|
||||
===================================
|
||||
|
||||
This directory contains a test suite for enabling data checksums
|
||||
in a running cluster with streaming replication.
|
||||
|
||||
Running the tests
|
||||
=================
|
||||
|
||||
make check
|
||||
|
||||
or
|
||||
|
||||
make installcheck
|
||||
|
||||
NOTE: This creates a temporary installation (in the case of "check"),
|
||||
with multiple nodes, be they master or standby(s) for the purpose of
|
||||
the tests.
|
||||
|
||||
NOTE: This requires the --enable-tap-tests argument to configure.
|
101
src/test/checksum/t/001_standby_checksum.pl
Normal file
101
src/test/checksum/t/001_standby_checksum.pl
Normal file
@ -0,0 +1,101 @@
|
||||
# Test suite for testing enabling data checksums with streaming replication
|
||||
use strict;
|
||||
use warnings;
|
||||
use PostgresNode;
|
||||
use TestLib;
|
||||
use Test::More tests => 10;
|
||||
|
||||
my $MAX_TRIES = 30;
|
||||
|
||||
# Initialize master node
|
||||
my $node_master = get_new_node('master');
|
||||
$node_master->init(allows_streaming => 1);
|
||||
$node_master->start;
|
||||
my $backup_name = 'my_backup';
|
||||
|
||||
# Take backup
|
||||
$node_master->backup($backup_name);
|
||||
|
||||
# Create streaming standby linking to master
|
||||
my $node_standby_1 = get_new_node('standby_1');
|
||||
$node_standby_1->init_from_backup($node_master, $backup_name,
|
||||
has_streaming => 1);
|
||||
$node_standby_1->start;
|
||||
|
||||
# Create some content on master to have un-checksummed data in the cluster
|
||||
$node_master->safe_psql('postgres',
|
||||
"CREATE TABLE t AS SELECT generate_series(1,10000) AS a;");
|
||||
|
||||
# Wait for standbys to catch up
|
||||
$node_master->wait_for_catchup($node_standby_1, 'replay',
|
||||
$node_master->lsn('insert'));
|
||||
|
||||
# Check that checksums are turned off
|
||||
my $result = $node_master->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
is($result, "off", 'ensure checksums are turned off on master');
|
||||
|
||||
$result = $node_standby_1->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
is($result, "off", 'ensure checksums are turned off on standby_1');
|
||||
|
||||
# Enable checksums for the cluster
|
||||
$node_master->safe_psql('postgres', "SELECT pg_enable_data_checksums();");
|
||||
|
||||
# Ensure that the master has switched to inprogress immediately
|
||||
$result = $node_master->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
is($result, "inprogress", 'ensure checksums are in progress on master');
|
||||
|
||||
# Wait for checksum enable to be replayed
|
||||
$node_master->wait_for_catchup($node_standby_1, 'replay');
|
||||
|
||||
# Ensure that the standby has switched to inprogress
|
||||
$result = $node_standby_1->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
is($result, "inprogress", 'ensure checksums are in progress on standby_1');
|
||||
|
||||
# Insert some more data which should be checksummed on INSERT
|
||||
$node_master->safe_psql('postgres',
|
||||
"INSERT INTO t VALUES (generate_series(1,10000));");
|
||||
|
||||
# Wait for checksums enabled on the master
|
||||
for (my $i = 0; $i < $MAX_TRIES; $i++)
|
||||
{
|
||||
$result = $node_master->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
last if ($result eq 'on');
|
||||
sleep(1);
|
||||
}
|
||||
is ($result, "on", 'ensure checksums are enabled on master');
|
||||
|
||||
# Wait for checksums enabled on the standby
|
||||
for (my $i = 0; $i < $MAX_TRIES; $i++)
|
||||
{
|
||||
$result = $node_standby_1->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
last if ($result eq 'on');
|
||||
sleep(1);
|
||||
}
|
||||
is ($result, "on", 'ensure checksums are enabled on standby');
|
||||
|
||||
$result = $node_master->safe_psql('postgres', "SELECT count(a) FROM t");
|
||||
is ($result, "20000", 'ensure we can safely read all data with checksums');
|
||||
|
||||
# Disable checksums and ensure it's propagated to standby and that we can
|
||||
# still read all data
|
||||
$node_master->safe_psql('postgres', "SELECT pg_disable_data_checksums();");
|
||||
$result = $node_master->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
is($result, "off", 'ensure checksums are in progress on master');
|
||||
|
||||
# Wait for checksum disable to be replayed
|
||||
$node_master->wait_for_catchup($node_standby_1, 'replay');
|
||||
|
||||
# Ensure that the standby has switched to off
|
||||
$result = $node_standby_1->safe_psql('postgres',
|
||||
"SELECT setting FROM pg_catalog.pg_settings WHERE name = 'data_checksums';");
|
||||
is($result, "off", 'ensure checksums are in progress on standby_1');
|
||||
|
||||
$result = $node_master->safe_psql('postgres', "SELECT count(a) FROM t");
|
||||
is ($result, "20000", 'ensure we can safely read all data without checksums');
|
27
src/test/isolation/expected/checksum_cancel.out
Normal file
27
src/test/isolation/expected/checksum_cancel.out
Normal file
@ -0,0 +1,27 @@
|
||||
Parsed test spec with 2 sessions
|
||||
|
||||
starting permutation: c_verify_checksums_off r_seqread c_enable_checksums c_verify_checksums_inprogress c_disable_checksums c_wait_checksums_off
|
||||
step c_verify_checksums_off: SELECT setting = 'off' FROM pg_catalog.pg_settings WHERE name = 'data_checksums';
|
||||
?column?
|
||||
|
||||
t
|
||||
step r_seqread: SELECT * FROM reader_loop();
|
||||
reader_loop
|
||||
|
||||
t
|
||||
step c_enable_checksums: SELECT pg_enable_data_checksums(1000);
|
||||
pg_enable_data_checksums
|
||||
|
||||
|
||||
step c_verify_checksums_inprogress: SELECT setting = 'inprogress' FROM pg_catalog.pg_settings WHERE name = 'data_checksums';
|
||||
?column?
|
||||
|
||||
t
|
||||
step c_disable_checksums: SELECT pg_disable_data_checksums();
|
||||
pg_disable_data_checksums
|
||||
|
||||
|
||||
step c_wait_checksums_off: SELECT test_checksums_off();
|
||||
test_checksums_off
|
||||
|
||||
t
|
27
src/test/isolation/expected/checksum_enable.out
Normal file
27
src/test/isolation/expected/checksum_enable.out
Normal file
@ -0,0 +1,27 @@
|
||||
Parsed test spec with 3 sessions
|
||||
|
||||
starting permutation: c_verify_checksums_off w_insert100k r_seqread c_enable_checksums c_wait_for_checksums c_verify_checksums_on
|
||||
step c_verify_checksums_off: SELECT setting = 'off' FROM pg_catalog.pg_settings WHERE name = 'data_checksums';
|
||||
?column?
|
||||
|
||||
t
|
||||
step w_insert100k: SELECT insert_1k(100);
|
||||
insert_1k
|
||||
|
||||
t
|
||||
step r_seqread: SELECT * FROM reader_loop();
|
||||
reader_loop
|
||||
|
||||
t
|
||||
step c_enable_checksums: SELECT pg_enable_data_checksums();
|
||||
pg_enable_data_checksums
|
||||
|
||||
|
||||
step c_wait_for_checksums: SELECT test_checksums_on();
|
||||
test_checksums_on
|
||||
|
||||
t
|
||||
step c_verify_checksums_on: SELECT setting = 'on' FROM pg_catalog.pg_settings WHERE name = 'data_checksums';
|
||||
?column?
|
||||
|
||||
t
|
@ -72,3 +72,7 @@ test: timeouts
|
||||
test: vacuum-concurrent-drop
|
||||
test: predicate-gist
|
||||
test: predicate-gin
|
||||
# The checksum_enable suite will enable checksums for the cluster so should
|
||||
# not run before anything expecting the cluster to have checksums turned off
|
||||
test: checksum_cancel
|
||||
test: checksum_enable
|
||||
|
47
src/test/isolation/specs/checksum_cancel.spec
Normal file
47
src/test/isolation/specs/checksum_cancel.spec
Normal file
@ -0,0 +1,47 @@
|
||||
setup
|
||||
{
|
||||
CREATE TABLE t1 (a serial, b integer, c text);
|
||||
INSERT INTO t1 (b, c) VALUES (generate_series(1,10000), 'starting values');
|
||||
|
||||
CREATE OR REPLACE FUNCTION test_checksums_off() RETURNS boolean AS $$
|
||||
DECLARE
|
||||
enabled boolean;
|
||||
BEGIN
|
||||
PERFORM pg_sleep(1);
|
||||
SELECT setting = 'off' INTO enabled FROM pg_catalog.pg_settings WHERE name = 'data_checksums';
|
||||
RETURN enabled;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION reader_loop() RETURNS boolean AS $$
|
||||
DECLARE
|
||||
counter integer;
|
||||
enabled boolean;
|
||||
BEGIN
|
||||
FOR counter IN 1..100 LOOP
|
||||
PERFORM count(a) FROM t1;
|
||||
END LOOP;
|
||||
RETURN True;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP FUNCTION reader_loop();
|
||||
DROP FUNCTION test_checksums_off();
|
||||
|
||||
DROP TABLE t1;
|
||||
}
|
||||
|
||||
session "reader"
|
||||
step "r_seqread" { SELECT * FROM reader_loop(); }
|
||||
|
||||
session "checksums"
|
||||
step "c_verify_checksums_off" { SELECT setting = 'off' FROM pg_catalog.pg_settings WHERE name = 'data_checksums'; }
|
||||
step "c_enable_checksums" { SELECT pg_enable_data_checksums(1000); }
|
||||
step "c_disable_checksums" { SELECT pg_disable_data_checksums(); }
|
||||
step "c_verify_checksums_inprogress" { SELECT setting = 'inprogress' FROM pg_catalog.pg_settings WHERE name = 'data_checksums'; }
|
||||
step "c_wait_checksums_off" { SELECT test_checksums_off(); }
|
||||
|
||||
permutation "c_verify_checksums_off" "r_seqread" "c_enable_checksums" "c_verify_checksums_inprogress" "c_disable_checksums" "c_wait_checksums_off"
|
70
src/test/isolation/specs/checksum_enable.spec
Normal file
70
src/test/isolation/specs/checksum_enable.spec
Normal file
@ -0,0 +1,70 @@
|
||||
setup
|
||||
{
|
||||
CREATE TABLE t1 (a serial, b integer, c text);
|
||||
INSERT INTO t1 (b, c) VALUES (generate_series(1,10000), 'starting values');
|
||||
|
||||
CREATE OR REPLACE FUNCTION insert_1k(iterations int) RETURNS boolean AS $$
|
||||
DECLARE
|
||||
counter integer;
|
||||
BEGIN
|
||||
FOR counter IN 1..$1 LOOP
|
||||
INSERT INTO t1 (b, c) VALUES (
|
||||
generate_series(1, 1000),
|
||||
array_to_string(array(select chr(97 + (random() * 25)::int) from generate_series(1,250)), '')
|
||||
);
|
||||
PERFORM pg_sleep(0.1);
|
||||
END LOOP;
|
||||
RETURN True;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION test_checksums_on() RETURNS boolean AS $$
|
||||
DECLARE
|
||||
enabled boolean;
|
||||
BEGIN
|
||||
LOOP
|
||||
SELECT setting = 'on' INTO enabled FROM pg_catalog.pg_settings WHERE name = 'data_checksums';
|
||||
IF enabled THEN
|
||||
EXIT;
|
||||
END IF;
|
||||
PERFORM pg_sleep(1);
|
||||
END LOOP;
|
||||
RETURN enabled;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION reader_loop() RETURNS boolean AS $$
|
||||
DECLARE
|
||||
counter integer;
|
||||
BEGIN
|
||||
FOR counter IN 1..30 LOOP
|
||||
PERFORM count(a) FROM t1;
|
||||
PERFORM pg_sleep(0.2);
|
||||
END LOOP;
|
||||
RETURN True;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP FUNCTION reader_loop();
|
||||
DROP FUNCTION test_checksums_on();
|
||||
DROP FUNCTION insert_1k(int);
|
||||
|
||||
DROP TABLE t1;
|
||||
}
|
||||
|
||||
session "writer"
|
||||
step "w_insert100k" { SELECT insert_1k(100); }
|
||||
|
||||
session "reader"
|
||||
step "r_seqread" { SELECT * FROM reader_loop(); }
|
||||
|
||||
session "checksums"
|
||||
step "c_verify_checksums_off" { SELECT setting = 'off' FROM pg_catalog.pg_settings WHERE name = 'data_checksums'; }
|
||||
step "c_enable_checksums" { SELECT pg_enable_data_checksums(); }
|
||||
step "c_wait_for_checksums" { SELECT test_checksums_on(); }
|
||||
step "c_verify_checksums_on" { SELECT setting = 'on' FROM pg_catalog.pg_settings WHERE name = 'data_checksums'; }
|
||||
|
||||
permutation "c_verify_checksums_off" "w_insert100k" "r_seqread" "c_enable_checksums" "c_wait_for_checksums" "c_verify_checksums_on"
|
Reference in New Issue
Block a user