From 9f686cb3d7edfc5b214c2eddbc20f0ccd6bcda7f Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 27 Jun 2022 16:44:41 +0900 Subject: [PATCH v1 1/2] Add regression tests for emergency vacuums. --- src/test/modules/Makefile | 1 + src/test/modules/heap/.gitignore | 4 + src/test/modules/heap/Makefile | 20 +++ .../modules/heap/t/001_emergency_vacuum.pl | 116 ++++++++++++++++++ src/test/modules/heap/test_heap--1.0.sql | 9 ++ src/test/modules/heap/test_heap.c | 72 +++++++++++ src/test/modules/heap/test_heap.control | 4 + src/tools/msvc/Mkvcbuild.pm | 2 +- 8 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 src/test/modules/heap/.gitignore create mode 100644 src/test/modules/heap/Makefile create mode 100644 src/test/modules/heap/t/001_emergency_vacuum.pl create mode 100644 src/test/modules/heap/test_heap--1.0.sql create mode 100644 src/test/modules/heap/test_heap.c create mode 100644 src/test/modules/heap/test_heap.control diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 9090226daa..3d53edc1d2 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -10,6 +10,7 @@ SUBDIRS = \ delay_execution \ dummy_index_am \ dummy_seclabel \ + heap \ libpq_pipeline \ plsample \ snapshot_too_old \ diff --git a/src/test/modules/heap/.gitignore b/src/test/modules/heap/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/src/test/modules/heap/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/heap/Makefile b/src/test/modules/heap/Makefile new file mode 100644 index 0000000000..aeae3f938e --- /dev/null +++ b/src/test/modules/heap/Makefile @@ -0,0 +1,20 @@ +# src/test/modules/heap/Makefile + +MODULES = test_heap +PGFILEDESC = "test_heap - regression test for heap" + +EXTENSION = test_heap +DATA = test_heap--1.0.sql + +TAP_TESTS = 1 + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/heap +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/heap/t/001_emergency_vacuum.pl b/src/test/modules/heap/t/001_emergency_vacuum.pl new file mode 100644 index 0000000000..791b97f217 --- /dev/null +++ b/src/test/modules/heap/t/001_emergency_vacuum.pl @@ -0,0 +1,116 @@ +# Copyright (c) 2022, PostgreSQL Global Development Group + +# Test for wraparound emergency situation + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +# Initialize node +my $node = PostgreSQL::Test::Cluster->new('main'); + +$node->init; +$node->append_conf('postgresql.conf', qq[ +autovacuum = off # run autovacuum only when to anti wraparound +max_prepared_transactions = 10 +autovacuum_naptime = 1s +# so it's easier to verify the order of operations +autovacuum_max_workers = 1 +log_autovacuum_min_duration = 0 +]); +$node->start; + +$node->safe_psql('postgres', 'CREATE EXTENSION test_heap'); + +# Create tables for a few different test scenarios +$node->safe_psql('postgres', qq[ +CREATE TABLE large(id serial primary key, data text, filler text default repeat(random()::text, 10)); +INSERT INTO large(data) SELECT generate_series(1,30000); + +CREATE TABLE large_trunc(id serial primary key, data text, filler text default repeat(random()::text, 10)); +INSERT INTO large_trunc(data) SELECT generate_series(1,30000); + +CREATE TABLE small(id serial primary key, data text, filler text default repeat(random()::text, 10)); +INSERT INTO small(data) SELECT generate_series(1,15000); + +CREATE TABLE small_trunc(id serial primary key, data text, filler text default repeat(random()::text, 10)); +INSERT INTO small_trunc(data) SELECT generate_series(1,15000); + +CREATE TABLE autovacuum_disabled(id serial primary key, data text) WITH (autovacuum_enabled=false); +INSERT INTO autovacuum_disabled(data) SELECT generate_series(1,1000); +]); + +# To prevent autovacuum from handling the tables immediately after +# restart, acquire locks in a 2PC transaction. That allows us to test +# interactions with running commands. +$node->safe_psql('postgres', qq[ +BEGIN; +LOCK TABLE large IN SHARE UPDATE EXCLUSIVE MODE; +LOCK TABLE large_trunc IN SHARE UPDATE EXCLUSIVE MODE; +LOCK TABLE small IN SHARE UPDATE EXCLUSIVE MODE; +LOCK TABLE small_trunc IN SHARE UPDATE EXCLUSIVE MODE; +LOCK TABLE autovacuum_disabled IN SHARE UPDATE EXCLUSIVE MODE; +PREPARE TRANSACTION 'prevent-vacuum'; +]); + +# Delete a few rows to ensure that vacuum has work to do. +$node->safe_psql('postgres', qq[ +DELETE FROM large WHERE id % 2 = 0; +DELETE FROM large_trunc WHERE id > 10000; +DELETE FROM small WHERE id % 2 = 0; +DELETE FROM small_trunc WHERE id > 1000; +DELETE FROM autovacuum_disabled WHERE id % 2 = 0; +]); + +# New XID needs to be a clog page boundary, otherwise we'll get errors about +# the file not exisitng error. With default compilation settings +# CLOG_XACTS_PER_PAGE is 32768. The value below is 32768 * +# (2000000000/32768 + 1), with 2000000000 being the max value for +# autovacuum_freeze_max_age. Since the prepared transaction keeps holding the +# lock on tables above, autovacuum won't run +$node->safe_psql('postgres', qq[SELECT set_next_xid('2000027648'::xid)]); + +# Make sure updating the latest completed with the advanced XID. +$node->safe_psql('postgres', qq[INSERT INTO small(data) SELECT 1]); + +# Check if all databases became old now. +my $ret = $node->safe_psql('postgres', + qq[ +SELECT datname, + age(datfrozenxid) > current_setting('autovacuum_freeze_max_age')::int as old +FROM pg_database ORDER BY 1 +]); +is($ret, "postgres|t +template0|t +template1|t", "all tables became old"); + +# Allow autovacuum to start working on these tables. +$node->safe_psql('postgres', qq[COMMIT PREPARED 'prevent-vacuum']); + +$node->poll_query_until('postgres', + qq[ +SELECT NOT EXISTS ( + SELECT * + FROM pg_database + WHERE age(datfrozenxid) > current_setting('autovacuum_freeze_max_age')::int) +]) or die "timeout waiting all database are vacuumed"; + +# Check if these tables are vacuumed. +$ret = $node->safe_psql('postgres', qq[ +SELECT relname, age(relfrozenxid) > current_setting('autovacuum_freeze_max_age')::int +FROM pg_class +WHERE oid = ANY(ARRAY['large'::regclass, 'large_trunc', 'small', 'small_trunc', 'autovacuum_disabled']) +ORDER BY 1 +]); + +is($ret, "autovacuum_disabled|f +large|f +large_trunc|f +small|f +small_trunc|f", "all tables are vacuumed"); + +$node->stop; + +done_testing(); diff --git a/src/test/modules/heap/test_heap--1.0.sql b/src/test/modules/heap/test_heap--1.0.sql new file mode 100644 index 0000000000..b7be733bfe --- /dev/null +++ b/src/test/modules/heap/test_heap--1.0.sql @@ -0,0 +1,9 @@ +/* src/test/modules/heap/test_heap--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_heap" to load this file. \quit + +CREATE FUNCTION set_next_xid(xid) + RETURNS void + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT VOLATILE; diff --git a/src/test/modules/heap/test_heap.c b/src/test/modules/heap/test_heap.c new file mode 100644 index 0000000000..66f73edb76 --- /dev/null +++ b/src/test/modules/heap/test_heap.c @@ -0,0 +1,72 @@ +/*---------------------------------------------------------------------- + * test_heap.c + * Support test functions for the heap + * + * Copyright (c) 2014-2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/heap/test_heap.c + *---------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/clog.h" +#include "access/commit_ts.h" +#include "access/subtrans.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "storage/lwlock.h" +#include "storage/pmsignal.h" +#include "utils/builtins.h" + +PG_MODULE_MAGIC; + +/* + * Set the given XID in the current epoch to the next XID + */ +PG_FUNCTION_INFO_V1(set_next_xid); +Datum +set_next_xid(PG_FUNCTION_ARGS) +{ + TransactionId next_xid = PG_GETARG_TRANSACTIONID(0); + uint32 epoch; + + if (!TransactionIdIsNormal(next_xid)) + elog(ERROR, "cannot set invalid transaction id"); + + LWLockAcquire(XidGenLock, LW_EXCLUSIVE); + + if (TransactionIdPrecedes(next_xid, + XidFromFullTransactionId(ShmemVariableCache->nextXid))) + { + LWLockRelease(XidGenLock); + elog(ERROR, "cannot set transaction id older than the current transaction id"); + } + + /* + * If the new XID is past xidVacLimit, start trying to force autovacuum + * cycles. + */ + if (TransactionIdFollowsOrEquals(next_xid, ShmemVariableCache->xidVacLimit)) + { + /* For safety, we release XidGenLock while sending signal */ + LWLockRelease(XidGenLock); + SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); + LWLockAcquire(XidGenLock, LW_EXCLUSIVE); + } + + /* Construct the new XID in the current epoch */ + epoch = EpochFromFullTransactionId(ShmemVariableCache->nextXid); + ShmemVariableCache->nextXid = + FullTransactionIdFromEpochAndXid(epoch, next_xid); + + ExtendCLOG(next_xid); + ExtendCommitTs(next_xid); + ExtendSUBTRANS(next_xid); + + LWLockRelease(XidGenLock); + + PG_RETURN_VOID(); +} + diff --git a/src/test/modules/heap/test_heap.control b/src/test/modules/heap/test_heap.control new file mode 100644 index 0000000000..7d089bb6d1 --- /dev/null +++ b/src/test/modules/heap/test_heap.control @@ -0,0 +1,4 @@ +comment = 'Test code for heap' +default_version = '1.0' +module_pathname = '$libdir/test_heap' +relocatable = true diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index e4feda10fd..022a2fa5f7 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -50,7 +50,7 @@ my @contrib_excludes = ( 'sepgsql', 'brin', 'test_extensions', 'test_misc', 'test_pg_dump', 'snapshot_too_old', - 'unsafe_tests'); + 'unsafe_tests', 'heap'); # Set of variables for frontend modules my $frontend_defines = { 'initdb' => 'FRONTEND' }; -- 2.24.3 (Apple Git-128)