Hackers,

While working on the problem of XID wraparound within the LISTEN/NOTIFY
system, I tried to increment XIDs by more than one per transaction. This leads to a number of test failures, many which look like:

+ERROR:  could not access status of transaction 7485
+DETAIL: Could not read from file "pg_subtrans/0000" at offset 24576: read too few bytes.

I might not have read the right documentation, but....

I do not see anything in src/backend/access/transam/README nor elsewhere
documenting a design decision or assumption that transaction IDs must
be assigned contiguously.  I suppose this is such a fundamental
assumption that it is completely implicit and nobody thought to document
it, but I'd like to check for two reasons:

First, I'd like a good method of burning through transaction ids in
tests designed to check for problems in XID wrap-around.

Second, I'd like to add Asserts where appropriate regarding this
assumption.  It seems strange to me that I should have gotten as far
as a failing read() without having tripped an Assert somewhere along the
way.

To duplicate the errors I hit, you can either apply this simple change:


diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156..360b7335bb 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -83,7 +83,7 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 static inline void
 FullTransactionIdAdvance(FullTransactionId *dest)
 {
-       dest->value++;
+       dest->value += 2;
        while (XidFromFullTransactionId(*dest) < FirstNormalTransactionId)
                dest->value++;
 }


or apply the much larger WIP patch, attached, and then be sure to
provide the --enable-xidcheck flag to configure before building.

--
Mark Dilger
diff --git a/configure b/configure
index 1d88983b34..909907127a 100755
--- a/configure
+++ b/configure
@@ -841,6 +841,7 @@ with_CC
 with_llvm
 enable_depend
 enable_cassert
+enable_xidcheck
 enable_thread_safety
 with_icu
 with_tcl
@@ -1521,6 +1522,7 @@ Optional Features:
   --enable-tap-tests      enable TAP tests (requires Perl and IPC::Run)
   --enable-depend         turn on automatic dependency tracking
   --enable-cassert        enable assertion checks (for debugging)
+  --enable-xidcheck       enable transactionid checks (for debugging)
   --disable-thread-safety disable thread-safety in client libraries
   --disable-largefile     omit support for large files
 
@@ -7197,6 +7199,36 @@ fi
 
 
 
+#
+# Enable transactionid checks
+#
+
+
+# Check whether --enable-xidcheck was given.
+if test "${enable_xidcheck+set}" = set; then :
+  enableval=$enable_xidcheck;
+  case $enableval in
+    yes)
+
+$as_echo "#define USE_XID_CHECKING 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --enable-xidcheck option" 
"$LINENO" 5
+      ;;
+  esac
+
+else
+  enable_xidcheck=no
+
+fi
+
+
+
+
 #
 # Include directories
 #
diff --git a/configure.in b/configure.in
index a2cb20b5e3..5aac6c24f1 100644
--- a/configure.in
+++ b/configure.in
@@ -680,6 +680,14 @@ PGAC_ARG_BOOL(enable, cassert, no, [enable assertion 
checks (for debugging)],
                          [Define to 1 to build with assertion checks. 
(--enable-cassert)])])
 
 
+#
+# Enable transactionid checks
+#
+PGAC_ARG_BOOL(enable, xidcheck, no, [enable transactionid checks (for 
debugging)],
+              [AC_DEFINE([USE_XID_CHECKING], 1,
+                         [Define to 1 to build with transactionid checks. 
(--enable-xidcheck)])])
+
+
 #
 # Include directories
 #
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d4d1fe45cc..a3dbe27640 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9246,6 +9246,26 @@ dynamic_library_path = 
'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-debug-xidcheck" xreflabel="debug_xidcheck">
+      <term><varname>debug_xidcheck</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>debug_xidcheck</varname> configuration 
parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Reports whether <productname>PostgreSQL</productname> has been built
+        with transaction id checking enabled. That is the case if the
+        macro <symbol>USE_XID_CHECKING</symbol> is defined
+        when <productname>PostgreSQL</productname> is built (accomplished
+        e.g. by the <command>configure</command> option
+        <option>--enable-xidcheck</option>). By
+        default <productname>PostgreSQL</productname> is built without
+        transaction id checking.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-integer-datetimes" xreflabel="integer_datetimes">
       <term><varname>integer_datetimes</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 9c10a897f1..eedfd021a0 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1524,6 +1524,19 @@ build-postgresql:
        </listitem>
       </varlistentry>
 
+      <varlistentry>
+       <term><option>--enable-xidcheck</option></term>
+       <listitem>
+        <para>
+         Enables <firstterm>transaction ID</firstterm> checks in the server, 
which test
+         the 64-bit transaction ID management.  This may prove valuable for
+         code development purposes, but enabling this feature will consume a 
small
+         amount of shared memory and might slow down the server.  Enabling 
this feature
+         will not improve the stability of your server.
+        </para>
+       </listitem>
+      </varlistentry>
+
       <varlistentry>
        <term><option>--enable-tap-tests</option></term>
        <listitem>
diff --git a/src/backend/access/transam/varsup.c 
b/src/backend/access/transam/varsup.c
index b18eee42d4..d26ad71e8a 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -50,6 +50,9 @@ GetNewTransactionId(bool isSubXact)
 {
        FullTransactionId full_xid;
        TransactionId xid;
+#if USE_XID_CHECKING
+       int64 xidstep;
+#endif
 
        /*
         * Workers synchronize transaction state at the beginning of each 
parallel
@@ -182,7 +185,18 @@ GetNewTransactionId(bool isSubXact)
         * we want the next incoming transaction to try it again.  We cannot
         * assign more XIDs until there is CLOG space for them.
         */
+#if USE_XID_CHECKING
+       xidstep = ShmemVariableCache->xidAdvanceBy;
+       if (ShmemVariableCache->xidAdvanceRandom)
+       {
+               xidstep *= pg_erand48(0);
+               if (!xidstep)
+                       xidstep = 1;
+       }
+       FullTransactionIdAdvance(&ShmemVariableCache->nextFullXid, xidstep);
+#else
        FullTransactionIdAdvance(&ShmemVariableCache->nextFullXid);
+#endif
 
        /*
         * We must store the new XID into the shared ProcArray before releasing
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 6b104695c0..08287bebec 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -536,3 +536,68 @@ pg_nextoid(PG_FUNCTION_ARGS)
 
        return newoid;
 }
+
+/*
+ * SQL callable interface.
+ *
+ * Function is intentionally not documented in the user facing docs.
+ */
+Datum
+getxidstep(PG_FUNCTION_ARGS)
+{
+#if USE_XID_CHECKING
+       int64 result;
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to call 
getxidstep()")));
+
+       /*
+        * This lock probably does not need to be exclusive, and might not
+        * be necessary at all.  But for sanity, and since this is just for
+        * regression testing anyway, play it safe and use a strong lock.
+        */
+       LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
+       result = ShmemVariableCache->xidAdvanceBy;
+       LWLockRelease(XidGenLock);
+
+       PG_RETURN_INT64(result);
+#else
+       PG_RETURN_INT64(1);
+#endif
+}
+
+/*
+ * SQL callable interface.
+ *
+ * Function is intentionally not documented in the user facing docs.
+ */
+Datum
+setxidstep(PG_FUNCTION_ARGS)
+{
+#if USE_XID_CHECKING
+       int64   oldstep;
+       int64   newstep = PG_GETARG_INT64(0);
+       bool    newrand = PG_GETARG_BOOL(1);
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to call 
setxidstep()")));
+       if (newstep < 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("xid step value must be positive")));
+
+       LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
+       oldstep = ShmemVariableCache->xidAdvanceBy;
+       ShmemVariableCache->xidAdvanceBy = newstep;
+       ShmemVariableCache->xidAdvanceRandom = newrand;
+       LWLockRelease(XidGenLock);
+
+       PG_RETURN_INT64(oldstep);
+#else
+       PG_RETURN_INT64(1);
+#endif
+}
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index a1567d3559..b24770a2ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -144,6 +144,10 @@ InitShmemAllocation(void)
        ShmemVariableCache = (VariableCache)
                ShmemAlloc(sizeof(*ShmemVariableCache));
        memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
+#if USE_XID_CHECKING
+       ShmemVariableCache->xidAdvanceBy = 1;
+       ShmemVariableCache->xidAdvanceRandom = false;
+#endif
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ba4edde71a..aed2d7b9a4 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -582,6 +582,7 @@ static int  wal_block_size;
 static bool data_checksums;
 static bool integer_datetimes;
 static bool assert_enabled;
+static bool xidcheck_enabled;
 static char *recovery_target_timeline_string;
 static char *recovery_target_string;
 static char *recovery_target_xid_string;
@@ -1271,6 +1272,20 @@ static struct config_bool ConfigureNamesBool[] =
 #endif
                NULL, NULL, NULL
        },
+       {
+               {"debug_xidcheck", PGC_INTERNAL, PRESET_OPTIONS,
+                       gettext_noop("Shows whether the running server has 
transaction id checks enabled."),
+                       NULL,
+                       GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+               },
+               &xidcheck_enabled,
+#ifdef USE_XID_CHECKING
+               true,
+#else
+               false,
+#endif
+               NULL, NULL, NULL
+       },
 
        {
                {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156..93e1b8f6c0 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -81,9 +81,15 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId 
xid)
 
 /* advance a FullTransactionId variable, stepping over special XIDs */
 static inline void
+#if USE_XID_CHECKING
+FullTransactionIdAdvance(FullTransactionId *dest, int64 step)
+{
+       dest->value += step;
+#else
 FullTransactionIdAdvance(FullTransactionId *dest)
 {
        dest->value++;
+#endif
        while (XidFromFullTransactionId(*dest) < FirstNormalTransactionId)
                dest->value++;
 }
@@ -163,6 +169,10 @@ typedef struct VariableCacheData
         */
        FullTransactionId nextFullXid;  /* next full XID to assign */
 
+#if USE_XID_CHECKING
+       int64           xidAdvanceBy;   /* step value for advancing; typically 
just 1 */
+       bool            xidAdvanceRandom;       /* whether advancement should 
be randomized */
+#endif
        TransactionId oldestXid;        /* cluster-wide minimum datfrozenxid */
        TransactionId xidVacLimit;      /* start forcing autovacuums here */
        TransactionId xidWarnLimit; /* start complaining here */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index ac8f64b219..c93376418a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -3210,6 +3210,16 @@
   prorettype => 'oid', proargtypes => 'regclass name regclass',
   prosrc => 'pg_nextoid' },
 
+{ oid => '9816', descr => 'return the step value for incrementing 
transactionid values',
+  proname => 'getxidstep', provolatile => 'v', proparallel => 'u',
+  prorettype => 'int8', proargtypes => '',
+  prosrc => 'getxidstep' },
+
+{ oid => '9817', descr => 'set the new step value and return the old step 
value for incrementing transactionid values',
+  proname => 'setxidstep', provolatile => 'v', proparallel => 'u',
+  prorettype => 'int8', proargtypes => 'int8 bool',
+  prosrc => 'setxidstep' },
+
 { oid => '1579', descr => 'I/O',
   proname => 'varbit_in', prorettype => 'varbit',
   proargtypes => 'cstring oid int4', prosrc => 'varbit_in' },
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c208dcdfc7..cd3a7cb1c7 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -885,6 +885,9 @@
 /* Define to 1 to build with assertion checks. (--enable-cassert) */
 #undef USE_ASSERT_CHECKING
 
+/* Define to 1 to build with transactionid checks. (--enable-xidcheck) */
+#undef USE_XID_CHECKING
+
 /* Define to 1 to build with Bonjour support. (--with-bonjour) */
 #undef USE_BONJOUR
 
diff --git a/src/test/regress/expected/xidtestsetup.out 
b/src/test/regress/expected/xidtestsetup.out
new file mode 100644
index 0000000000..f546beeb8e
--- /dev/null
+++ b/src/test/regress/expected/xidtestsetup.out
@@ -0,0 +1,9 @@
+-- If configured with xidcheck, henceforth, advance the next TransactionId by a
+-- random fraction of 1 billion each time the transaction id counter is 
advanced,
+-- otherwise do nothing special.
+SELECT setxidstep(1000000000, true);
+ setxidstep 
+------------
+          1
+(1 row)
+
diff --git a/src/test/regress/parallel_schedule 
b/src/test/regress/parallel_schedule
index d33a4e143d..ceceb333fa 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -10,6 +10,12 @@
 # interferes with crash-recovery testing.
 test: tablespace
 
+# ----------
+# Set up transactionid checking, if --enable-xidtest was given, otherwise
+# do not much of anything
+# ----------
+test: xidtestsetup
+
 # ----------
 # The first group of parallel tests
 # ----------
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index f86f5c5682..af709c6390 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -1,6 +1,7 @@
 # src/test/regress/serial_schedule
 # This should probably be in an order similar to parallel_schedule.
 test: tablespace
+test: xidtestsetup
 test: boolean
 test: char
 test: name
diff --git a/src/test/regress/sql/xidtestsetup.sql 
b/src/test/regress/sql/xidtestsetup.sql
new file mode 100644
index 0000000000..2b15db3509
--- /dev/null
+++ b/src/test/regress/sql/xidtestsetup.sql
@@ -0,0 +1,4 @@
+-- If configured with xidcheck, henceforth, advance the next TransactionId by a
+-- random fraction of 1 billion each time the transaction id counter is 
advanced,
+-- otherwise do nothing special.
+SELECT setxidstep(1000000000, true);
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 5f72530c72..631dc50f32 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -842,6 +842,7 @@ sub GetFakeConfigure
        my $cfg = '--enable-thread-safety';
        $cfg .= ' --enable-cassert'   if ($self->{options}->{asserts});
        $cfg .= ' --enable-nls'       if ($self->{options}->{nls});
+       $cfg .= ' --enable-xidcheck'  if ($self->{options}->{xidcheck});
        $cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests});
        $cfg .= ' --with-ldap'        if ($self->{options}->{ldap});
        $cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index 2ef2cfc4e9..cb62e09982 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -12,6 +12,7 @@ our $config = {
        gss       => undef,    # --with-gssapi=<path>
        icu       => undef,    # --with-icu=<path>
        nls       => undef,    # --enable-nls=<path>
+       xidchecks => undef,    # --enable-xidcheck
        tap_tests => undef,    # --enable-tap-tests
        tcl       => undef,    # --with-tcl=<path>
        perl      => undef,    # --with-perl=<path>

Reply via email to