1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Add the "snapshot too old" feature

This feature is controlled by a new old_snapshot_threshold GUC.  A
value of -1 disables the feature, and that is the default.  The
value of 0 is just intended for testing.  Above that it is the
number of minutes a snapshot can reach before pruning and vacuum
are allowed to remove dead tuples which the snapshot would
otherwise protect.  The xmin associated with a transaction ID does
still protect dead tuples.  A connection which is using an "old"
snapshot does not get an error unless it accesses a page modified
recently enough that it might not be able to produce accurate
results.

This is similar to the Oracle feature, and we use the same SQLSTATE
and error message for compatibility.
This commit is contained in:
Kevin Grittner
2016-04-08 14:36:30 -05:00
parent 8b65cf4c5e
commit 848ef42bb8
41 changed files with 942 additions and 85 deletions

View File

@ -8,6 +8,7 @@ SUBDIRS = \
brin \
commit_ts \
dummy_seclabel \
snapshot_too_old \
test_ddl_deparse \
test_extensions \
test_parser \

View File

@ -0,0 +1,47 @@
# src/test/modules/snapshot_too_old/Makefile
EXTRA_CLEAN = ./isolation_output
ISOLATIONCHECKS=sto_using_cursor sto_using_select
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = src/test/modules/snapshot_too_old
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
# Disabled because these tests require "old_snapshot_threshold" >= 0, which
# typical installcheck users do not have (e.g. buildfarm clients).
installcheck:;
# But it can nonetheless be very helpful to run tests on preexisting
# installation, allow to do so, but only if requested explicitly.
installcheck-force: isolationcheck-install-force
check: isolationcheck
submake-isolation:
$(MAKE) -C $(top_builddir)/src/test/isolation all
submake-test_snapshot_too_old:
$(MAKE) -C $(top_builddir)/src/test/modules/snapshot_too_old
isolationcheck: | submake-isolation submake-test_snapshot_too_old temp-install
$(MKDIR_P) isolation_output
$(pg_isolation_regress_check) \
--temp-config $(top_srcdir)/src/test/modules/snapshot_too_old/sto.conf \
--outputdir=./isolation_output \
$(ISOLATIONCHECKS)
isolationcheck-install-force: all | submake-isolation submake-test_snapshot_too_old temp-install
$(pg_isolation_regress_installcheck) \
$(ISOLATIONCHECKS)
.PHONY: check submake-test_snapshot_too_old isolationcheck isolationcheck-install-force
temp-install: EXTRA_INSTALL=src/test/modules/snapshot_too_old

View File

@ -0,0 +1,73 @@
Parsed test spec with 2 sessions
starting permutation: s1decl s1f1 s1sleep s1f2 s2u
step s1decl: DECLARE cursor1 CURSOR FOR SELECT c FROM sto1;
step s1f1: FETCH FIRST FROM cursor1;
c
1
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: FETCH FIRST FROM cursor1;
c
1
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
starting permutation: s1decl s1f1 s1sleep s2u s1f2
step s1decl: DECLARE cursor1 CURSOR FOR SELECT c FROM sto1;
step s1f1: FETCH FIRST FROM cursor1;
c
1
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1f2: FETCH FIRST FROM cursor1;
ERROR: snapshot too old
starting permutation: s1decl s1f1 s2u s1sleep s1f2
step s1decl: DECLARE cursor1 CURSOR FOR SELECT c FROM sto1;
step s1f1: FETCH FIRST FROM cursor1;
c
1
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: FETCH FIRST FROM cursor1;
ERROR: snapshot too old
starting permutation: s1decl s2u s1f1 s1sleep s1f2
step s1decl: DECLARE cursor1 CURSOR FOR SELECT c FROM sto1;
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1f1: FETCH FIRST FROM cursor1;
c
1
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: FETCH FIRST FROM cursor1;
ERROR: snapshot too old
starting permutation: s2u s1decl s1f1 s1sleep s1f2
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1decl: DECLARE cursor1 CURSOR FOR SELECT c FROM sto1;
step s1f1: FETCH FIRST FROM cursor1;
c
2
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: FETCH FIRST FROM cursor1;
ERROR: snapshot too old

View File

@ -0,0 +1,55 @@
Parsed test spec with 2 sessions
starting permutation: s1f1 s1sleep s1f2 s2u
step s1f1: SELECT c FROM sto1 ORDER BY c LIMIT 1;
c
1
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: SELECT c FROM sto1 ORDER BY c LIMIT 1;
c
1
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
starting permutation: s1f1 s1sleep s2u s1f2
step s1f1: SELECT c FROM sto1 ORDER BY c LIMIT 1;
c
1
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1f2: SELECT c FROM sto1 ORDER BY c LIMIT 1;
ERROR: snapshot too old
starting permutation: s1f1 s2u s1sleep s1f2
step s1f1: SELECT c FROM sto1 ORDER BY c LIMIT 1;
c
1
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: SELECT c FROM sto1 ORDER BY c LIMIT 1;
ERROR: snapshot too old
starting permutation: s2u s1f1 s1sleep s1f2
step s2u: UPDATE sto1 SET c = 1001 WHERE c = 1;
step s1f1: SELECT c FROM sto1 ORDER BY c LIMIT 1;
c
2
step s1sleep: SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold';
setting pg_sleep
0
step s1f2: SELECT c FROM sto1 ORDER BY c LIMIT 1;
ERROR: snapshot too old

View File

@ -0,0 +1,37 @@
# This test provokes a "snapshot too old" error using a cursor.
#
# The sleep is needed because with a threshold of zero a statement could error
# on changes it made. With more normal settings no external delay is needed,
# but we don't want these tests to run long enough to see that, since
# granularity is in minutes.
#
# Since results depend on the value of old_snapshot_threshold, sneak that into
# the line generated by the sleep, so that a surprising values isn't so hard
# to identify.
setup
{
CREATE TABLE sto1 (c int NOT NULL);
INSERT INTO sto1 SELECT generate_series(1, 1000);
CREATE TABLE sto2 (c int NOT NULL);
}
setup
{
VACUUM ANALYZE sto1;
}
teardown
{
DROP TABLE sto1, sto2;
}
session "s1"
setup { BEGIN ISOLATION LEVEL REPEATABLE READ; }
step "s1decl" { DECLARE cursor1 CURSOR FOR SELECT c FROM sto1; }
step "s1f1" { FETCH FIRST FROM cursor1; }
step "s1sleep" { SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold'; }
step "s1f2" { FETCH FIRST FROM cursor1; }
teardown { COMMIT; }
session "s2"
step "s2u" { UPDATE sto1 SET c = 1001 WHERE c = 1; }

View File

@ -0,0 +1,36 @@
# This test provokes a "snapshot too old" error using SELECT statements.
#
# The sleep is needed because with a threshold of zero a statement could error
# on changes it made. With more normal settings no external delay is needed,
# but we don't want these tests to run long enough to see that, since
# granularity is in minutes.
#
# Since results depend on the value of old_snapshot_threshold, sneak that into
# the line generated by the sleep, so that a surprising values isn't so hard
# to identify.
setup
{
CREATE TABLE sto1 (c int NOT NULL);
INSERT INTO sto1 SELECT generate_series(1, 1000);
CREATE TABLE sto2 (c int NOT NULL);
}
setup
{
VACUUM ANALYZE sto1;
}
teardown
{
DROP TABLE sto1, sto2;
}
session "s1"
setup { BEGIN ISOLATION LEVEL REPEATABLE READ; }
step "s1f1" { SELECT c FROM sto1 ORDER BY c LIMIT 1; }
step "s1sleep" { SELECT setting, pg_sleep(6) FROM pg_settings WHERE name = 'old_snapshot_threshold'; }
step "s1f2" { SELECT c FROM sto1 ORDER BY c LIMIT 1; }
teardown { COMMIT; }
session "s2"
step "s2u" { UPDATE sto1 SET c = 1001 WHERE c = 1; }

View File

@ -0,0 +1,3 @@
autovacuum = off
old_snapshot_threshold = 0