test-inplace-inconsistent-read-v0.patch
text/plain
Filename: test-inplace-inconsistent-read-v0.patch
Type: text/plain
Part: 0
Patch
Format: unified
Series: patch v0
| File | + | − |
|---|---|---|
| src/backend/commands/vacuum.c | 14 | 0 |
| src/backend/utils/adt/lockfuncs.c | 20 | 2 |
| src/test/modules/injection_points/expected/inplace_update_reader.out | 292 | 0 |
| src/test/modules/injection_points/injection_points.c | 25 | 5 |
| src/test/modules/injection_points/Makefile | 2 | 0 |
| src/test/modules/injection_points/specs/inplace_update_reader.spec | 75 | 0 |
Author: Noah Misch <noah@leadboat.com>
Commit: Noah Misch <noah@leadboat.com>
Test datfrozenxid/relfrozenxid update race condition
Not for commit, mostly because the injection point won't make sense
after the fix; it would be obvious that the test is unlikely to detect
similar future bugs. (A commit-ready test would also need to remove
temporary WARNING messages added to clarify what's happening, and it
would need to hide outputs sensitive to exact XID consumption.)
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index b589279..73f0245 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -53,9 +53,11 @@
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
+#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/guc_hooks.h"
+#include "utils/injection_point.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
@@ -1620,6 +1622,8 @@ vac_update_datfrozenxid(void)
while ((classTup = systable_getnext(scan)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
+ TransactionId before, after;
+ before = classForm->relfrozenxid;
/*
* Only consider relations able to hold unfrozen XIDs (anything else
@@ -1662,7 +1666,11 @@ vac_update_datfrozenxid(void)
/* determine new horizon */
if (TransactionIdPrecedes(classForm->relfrozenxid, newFrozenXid))
+ {
+ if (namestrcmp(&classForm->relname, "pg_test_last_table") == 0)
+ INJECTION_POINT("between-relfrozenxid-reads");
newFrozenXid = classForm->relfrozenxid;
+ }
}
if (MultiXactIdIsValid(classForm->relminmxid))
@@ -1678,6 +1686,12 @@ vac_update_datfrozenxid(void)
if (MultiXactIdPrecedes(classForm->relminmxid, newMinMulti))
newMinMulti = classForm->relminmxid;
}
+
+ after = classForm->relfrozenxid;
+ elog(WARNING, "done reading %s before=%u after=%u myxid=%u myxmin=%u",
+ NameStr(classForm->relname),
+ before, after,
+ MyProc->xid, MyProc->xmin);
}
/* we're done with pg_class */
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 13009cc..bb2db58 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -17,8 +17,11 @@
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/predicate_internals.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/wait_event.h"
/*
@@ -606,8 +609,9 @@ pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
*
* Check if specified PID is blocked by any of the PIDs listed in the second
* argument. Currently, this looks for blocking caused by waiting for
- * heavyweight locks or safe snapshots. We ignore blockage caused by PIDs
- * not directly under the isolationtester's control, eg autovacuum.
+ * injection points, heavyweight locks, or safe snapshots. We ignore blockage
+ * caused by PIDs not directly under the isolationtester's control, eg
+ * autovacuum.
*
* This is an undocumented function intended for use by the isolation tester,
* and may change in future releases as required for testing purposes.
@@ -615,8 +619,11 @@ pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
Datum
pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
{
+#define UINT32_ACCESS_ONCE(var) ((uint32)(*((volatile uint32 *)&(var))))
int blocked_pid = PG_GETARG_INT32(0);
ArrayType *interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1);
+ PGPROC *proc;
+ const char *wait_event;
ArrayType *blocking_pids_a;
int32 *interesting_pids;
int32 *blocking_pids;
@@ -626,6 +633,17 @@ pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
int i,
j;
+ /* Check if blocked_pid is in injection_wait(). */
+ proc = BackendPidGetProc(blocked_pid);
+ if (proc == NULL)
+ PG_RETURN_BOOL(false); /* session gone: definitely unblocked */
+ wait_event =
+ pgstat_get_wait_event(UINT32_ACCESS_ONCE(proc->wait_event_info));
+ if (wait_event && strncmp("INJECTION_POINT(",
+ wait_event,
+ strlen("INJECTION_POINT(")) == 0)
+ PG_RETURN_BOOL(true);
+
/* Validate the passed-in array */
Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID);
if (array_contains_nulls(interesting_pids_a))
diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile
index 31bd787..7ee6135d 100644
--- a/src/test/modules/injection_points/Makefile
+++ b/src/test/modules/injection_points/Makefile
@@ -9,6 +9,8 @@ PGFILEDESC = "injection_points - facility for injection points"
REGRESS = injection_points
REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
+ISOLATION = inplace_update_reader
+
# The injection points are cluster-wide, so disable installcheck
NO_INSTALLCHECK = 1
diff --git a/src/test/modules/injection_points/expected/inplace_update_reader.out b/src/test/modules/injection_points/expected/inplace_update_reader.out
new file mode 100644
index 0000000..799100e
--- /dev/null
+++ b/src/test/modules/injection_points/expected/inplace_update_reader.out
@@ -0,0 +1,292 @@
+Parsed test spec with 5 sessions
+
+starting permutation: assign1 lock2 vac4 assign1 lock3 attach4 vac4 r2 vac5 detach1 read1 r3
+step assign1: SELECT txid_current();
+txid_current
+------------
+ 743
+(1 row)
+
+step lock2: BEGIN; LOCK pg_test_last_table IN SHARE UPDATE EXCLUSIVE MODE;
+s4: WARNING: done reading pg_statistic before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_type before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1255 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1247 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2604 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2606 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2612 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2600 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2619 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3381 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3429 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2618 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2620 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3466 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2609 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_foreign_table before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2615 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1262 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2964 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1213 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1260 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2396 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3600 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3079 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_2328 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1417 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_1418 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3118 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3256 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_6000 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_826 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3394 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3596 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3592 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3456 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_6243 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_3350 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_6106 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_6100 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_authid before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_statistic_ext_data before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_user_mapping before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_subscription before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_attribute before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_proc before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_attrdef before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_constraint before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_inherits before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_index before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_operator before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_opfamily before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_opclass before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_am before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_amop before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_amproc before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_language before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_largeobject_metadata before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_aggregate before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_statistic_ext before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_rewrite before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_trigger before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_event_trigger before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_description before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_cast before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_enum before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_namespace before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_conversion before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_depend before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_database before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_db_role_setting before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_tablespace before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_auth_members before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_shdepend before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_shdescription before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_ts_config before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_ts_config_map before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_ts_dict before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_ts_parser before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_ts_template before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_extension before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_foreign_data_wrapper before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_foreign_server before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_policy before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_replication_origin before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_default_acl before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_init_privs before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_seclabel before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_shseclabel before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_collation before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_parameter_acl before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_partitioned_table before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_range before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_transform before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_sequence before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_publication before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_publication_namespace before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_publication_rel before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_subscription_rel before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_largeobject before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_14108 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading sql_features before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_14113 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading sql_implementation_info before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_14118 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading sql_parts before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_toast_14123 before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading sql_sizing before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_class before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading second_oldest before=744 after=744 myxid=0 myxmin=744
+s4: WARNING: done reading pg_test_last_table before=742 after=742 myxid=0 myxmin=744
+step vac4: VACUUM (FREEZE, SKIP_LOCKED);
+step assign1: SELECT txid_current();
+txid_current
+------------
+ 744
+(1 row)
+
+step lock3: BEGIN; LOCK second_oldest IN SHARE UPDATE EXCLUSIVE MODE;
+step attach4:
+ SELECT injection_points_set_local();
+ SELECT injection_points_attach('between-relfrozenxid-reads', 'wait');
+
+injection_points_set_local
+--------------------------
+
+(1 row)
+
+injection_points_attach
+-----------------------
+
+(1 row)
+
+s4: WARNING: done reading pg_statistic before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_type before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1255 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1247 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2604 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2606 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2612 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2600 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2619 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3381 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3429 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2618 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2620 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3466 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2609 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_foreign_table before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2615 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1262 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2964 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1213 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1260 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2396 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3600 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3079 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_2328 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1417 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_1418 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3118 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3256 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_6000 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_826 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3394 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3596 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3592 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3456 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_6243 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_3350 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_6106 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_6100 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_authid before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_statistic_ext_data before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_user_mapping before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_subscription before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_attribute before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_proc before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_attrdef before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_constraint before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_inherits before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_index before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_operator before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_opfamily before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_opclass before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_am before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_amop before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_amproc before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_language before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_largeobject_metadata before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_aggregate before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_statistic_ext before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_rewrite before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_trigger before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_event_trigger before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_description before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_cast before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_enum before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_namespace before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_conversion before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_depend before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_database before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_db_role_setting before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_tablespace before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_auth_members before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_shdepend before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_shdescription before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_ts_config before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_ts_config_map before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_ts_dict before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_ts_parser before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_ts_template before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_extension before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_foreign_data_wrapper before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_foreign_server before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_policy before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_replication_origin before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_default_acl before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_init_privs before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_seclabel before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_shseclabel before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_collation before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_parameter_acl before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_partitioned_table before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_range before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_transform before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_sequence before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_publication before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_publication_namespace before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_publication_rel before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_subscription_rel before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_largeobject before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_14108 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading sql_features before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_14113 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading sql_implementation_info before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_14118 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading sql_parts before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_toast_14123 before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading sql_sizing before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading pg_class before=745 after=745 myxid=0 myxmin=745
+s4: WARNING: done reading second_oldest before=744 after=744 myxid=0 myxmin=745
+step vac4: VACUUM (FREEZE, SKIP_LOCKED); <waiting ...>
+step r2: ROLLBACK;
+step vac5: VACUUM (FREEZE, SKIP_DATABASE_STATS) pg_test_last_table;
+step detach1:
+ SELECT injection_points_detach('between-relfrozenxid-reads');
+
+injection_points_detach
+-----------------------
+
+(1 row)
+
+s4: WARNING: done reading pg_test_last_table before=742 after=745 myxid=0 myxmin=745
+step vac4: <... completed>
+step read1:
+ -- should never return rows, but it does in this test case
+ select c.oid::regclass
+ from pg_database d, pg_class c
+ where relkind = 'r' and age(relfrozenxid) > age(datfrozenxid);
+
+ select age(datfrozenxid) from pg_database where datname = current_catalog;
+
+ select min(age(relfrozenxid)), max(age(relfrozenxid))
+ from pg_class c
+ where relkind = 'r';
+
+oid
+-------------
+second_oldest
+(1 row)
+
+age
+---
+ 0
+(1 row)
+
+min|max
+---+---
+ 0| 1
+(1 row)
+
+step r3: ROLLBACK;
diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index ace386d..f4fe678 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -205,6 +205,7 @@ injection_notice(const char *name)
void
injection_wait(const char *name)
{
+ char event_name[NAMEDATALEN];
uint32 old_wait_counts = 0;
int index = -1;
uint32 injection_wait_event = 0;
@@ -216,11 +217,11 @@ injection_wait(const char *name)
return;
/*
- * Use the injection point name for this custom wait event. Note that
- * this custom wait event name is not released, but we don't care much for
- * testing as this should be short-lived.
+ * Note that this custom wait event name is not released, but we don't
+ * care much for testing as this should be short-lived.
*/
- injection_wait_event = WaitEventExtensionNew(name);
+ snprintf(event_name, sizeof(event_name), "INJECTION_POINT(%s)", name);
+ injection_wait_event = WaitEventExtensionNew(event_name);
/*
* Find a free slot to wait for, and register this injection point's name.
@@ -402,14 +403,28 @@ Datum
injection_points_detach(PG_FUNCTION_ARGS)
{
char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ int wait_index = -1;
InjectionPointDetach(name);
if (inj_state == NULL)
injection_init_shmem();
- /* Clean up any conditions associated to this injection point */
SpinLockAcquire(&inj_state->lock);
+
+ /* First bump any wait counter for the injection point to wake up */
+ for (int i = 0; i < INJ_MAX_WAIT; i++)
+ {
+ if (strcmp(name, inj_state->name[i]) == 0)
+ {
+ wait_index = i;
+ break;
+ }
+ }
+ if (wait_index >= 0)
+ inj_state->wait_counts[wait_index]++;
+
+ /* Clean up any conditions associated to this injection point */
for (int i = 0; i < INJ_MAX_CONDITION; i++)
{
InjectionPointCondition *condition = &inj_state->conditions[i];
@@ -420,7 +435,12 @@ injection_points_detach(PG_FUNCTION_ARGS)
condition->name[0] = '\0';
}
}
+
SpinLockRelease(&inj_state->lock);
+ /* And broadcast the change to any waiters */
+ if (wait_index >= 0)
+ ConditionVariableBroadcast(&inj_state->wait_point);
+
PG_RETURN_VOID();
}
diff --git a/src/test/modules/injection_points/specs/inplace_update_reader.spec b/src/test/modules/injection_points/specs/inplace_update_reader.spec
new file mode 100644
index 0000000..325ba12
--- /dev/null
+++ b/src/test/modules/injection_points/specs/inplace_update_reader.spec
@@ -0,0 +1,75 @@
+# Test race between vac_update_relstats() and vac_update_datfrozenxid()
+
+# damage is hard to reach because:
+# - When v_u_d() first reads the value, the value must be a new minimum.
+# - The same v_u_d() must not read any later tuple that wipes out the damage.
+
+setup
+{
+ CREATE EXTENSION injection_points;
+}
+setup
+{
+ VACUUM FULL pg_class; -- help CREATE TABLE place at end
+}
+setup
+{
+ CREATE TABLE second_oldest ();
+ CREATE TABLE pg_test_last_table ();
+}
+
+teardown
+{
+ DROP EXTENSION injection_points;
+}
+
+
+# s1 does non-blocking, no-transaction-block activities
+session s1
+step assign1 { SELECT txid_current(); }
+step read1
+{
+ -- should never return rows, but it does in this test case
+ select c.oid::regclass
+ from pg_database d, pg_class c
+ where relkind = 'r' and age(relfrozenxid) > age(datfrozenxid);
+
+ select age(datfrozenxid) from pg_database where datname = current_catalog;
+
+ select min(age(relfrozenxid)), max(age(relfrozenxid))
+ from pg_class c
+ where relkind = 'r';
+}
+step detach1
+{
+ SELECT injection_points_detach('between-relfrozenxid-reads');
+}
+
+# s2 holds the lock on pg_test_last_table
+session s2
+step lock2 { BEGIN; LOCK pg_test_last_table IN SHARE UPDATE EXCLUSIVE MODE; }
+step r2 { ROLLBACK; }
+
+# s3 holds the lock on second_oldest
+session s3
+step lock3 { BEGIN; LOCK second_oldest IN SHARE UPDATE EXCLUSIVE MODE; }
+step r3 { ROLLBACK; }
+
+# s4 does database-wide VACUUM w/ SKIP_LOCKED
+session s4
+step attach4
+{
+ SELECT injection_points_set_local();
+ SELECT injection_points_attach('between-relfrozenxid-reads', 'wait');
+}
+step vac4 { VACUUM (FREEZE, SKIP_LOCKED); }
+
+# s5 does single-table VACUUM
+session s5
+step vac5 { VACUUM (FREEZE, SKIP_DATABASE_STATS) pg_test_last_table; }
+
+
+# 1st vac4 updates every relfrozenxid except pg_test_last_table
+# 2nd vac4 updates every relfrozenxid except pg_test_last_table and second_oldest
+# vac5 updates pg_test_last_table relfrozenxid between the two vac_update_datfrozenxid reads
+permutation assign1 lock2 vac4 assign1 lock3 attach4 vac4(detach1) r2 vac5 detach1 read1 r3