1
0
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:
Magnus Hagander
2018-04-05 21:57:26 +02:00
parent c39e903d51
commit 1fde38beaa
45 changed files with 2118 additions and 34 deletions

View File

@ -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
View File

@ -0,0 +1,2 @@
# Generated by test suite
/tmp_check/

View 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
View 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.

View 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');

View 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

View 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

View File

@ -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

View 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"

View 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"