Rework redundant code in subtrans.c
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 5 Mar 2024 11:09:18 +0000 (12:09 +0100)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 5 Mar 2024 11:09:18 +0000 (12:09 +0100)
When this code was written the duplicity didn't matter, but with all the
SLRU-bank stuff we just added, it has become excessive.  Turn it into a
simpler loop with no code duplication.  Also add a test so that this
code becomes covered.

Discussion: https://postgr.es/m/202403041517.3a35jw53os65@alvherre.pgsql

src/backend/access/transam/subtrans.c
src/test/recovery/t/009_twophase.pl

index dc9566fb51b5626e3f2cb41a4d31d578b272cd5b..50bb1d8cfc5f0950fd797bb3f84e40c792e38f90 100644 (file)
@@ -311,7 +311,7 @@ StartupSUBTRANS(TransactionId oldestActiveXID)
    FullTransactionId nextXid;
    int64       startPage;
    int64       endPage;
-   LWLock     *prevlock;
+   LWLock     *prevlock = NULL;
    LWLock     *lock;
 
    /*
@@ -324,42 +324,27 @@ StartupSUBTRANS(TransactionId oldestActiveXID)
    nextXid = TransamVariables->nextXid;
    endPage = TransactionIdToPage(XidFromFullTransactionId(nextXid));
 
-   prevlock = SimpleLruGetBankLock(SubTransCtl, startPage);
-   LWLockAcquire(prevlock, LW_EXCLUSIVE);
-   while (startPage != endPage)
+   for (;;)
    {
        lock = SimpleLruGetBankLock(SubTransCtl, startPage);
-
-       /*
-        * Check if we need to acquire the lock on the new bank then release
-        * the lock on the old bank and acquire on the new bank.
-        */
        if (prevlock != lock)
        {
-           LWLockRelease(prevlock);
+           if (prevlock)
+               LWLockRelease(prevlock);
            LWLockAcquire(lock, LW_EXCLUSIVE);
            prevlock = lock;
        }
 
        (void) ZeroSUBTRANSPage(startPage);
+       if (startPage == endPage)
+           break;
+
        startPage++;
        /* must account for wraparound */
        if (startPage > TransactionIdToPage(MaxTransactionId))
            startPage = 0;
    }
 
-   lock = SimpleLruGetBankLock(SubTransCtl, startPage);
-
-   /*
-    * Check if we need to acquire the lock on the new bank then release the
-    * lock on the old bank and acquire on the new bank.
-    */
-   if (prevlock != lock)
-   {
-       LWLockRelease(prevlock);
-       LWLockAcquire(lock, LW_EXCLUSIVE);
-   }
-   (void) ZeroSUBTRANSPage(startPage);
    LWLockRelease(lock);
 }
 
index a3cdcf3240823ceadfe1006bf065137f96074345..701f9cc20f8411dc49e456dcc8dc3075cf78eb24 100644 (file)
@@ -45,6 +45,10 @@ $node_london->backup('london_backup');
 my $node_paris = PostgreSQL::Test::Cluster->new('paris');
 $node_paris->init_from_backup($node_london, 'london_backup',
    has_streaming => 1);
+$node_paris->append_conf(
+   'postgresql.conf', qq(
+   subtransaction_buffers = 32
+));
 $node_paris->start;
 
 # Switch to synchronous replication in both directions
@@ -481,4 +485,42 @@ is( $psql_out,
    qq{27|issued to paris},
    "Check expected t_009_tbl2 data on standby");
 
+
+# Exercise the 2PC recovery code in StartupSUBTRANS, which is concerned with
+# ensuring that enough pg_subtrans pages exist on disk to cover the range of
+# prepared transactions at server start time.  There's not much we can verify
+# directly, but let's at least get the code to run.
+$cur_standby->stop();
+configure_and_reload($cur_primary, "synchronous_standby_names = ''");
+
+$cur_primary->safe_psql('postgres', "CHECKPOINT");
+
+my $start_lsn =
+  $cur_primary->safe_psql('postgres', 'select pg_current_wal_insert_lsn()');
+$cur_primary->safe_psql('postgres',
+   "CREATE TABLE test(); BEGIN; CREATE TABLE test1(); PREPARE TRANSACTION 'foo';"
+);
+my $osubtrans = $cur_primary->safe_psql('postgres',
+   "select 'pg_subtrans/'||f, s.size from pg_ls_dir('pg_subtrans') f, pg_stat_file('pg_subtrans/'||f) s"
+);
+$cur_primary->pgbench(
+   '--no-vacuum --client=5 --transactions=1000',
+   0,
+   [],
+   [],
+   'pgbench run to cause pg_subtrans traffic',
+   {
+       '009_twophase.pgb' => 'insert into test default values'
+   });
+# StartupSUBTRANS is exercised with a wide range of visible XIDs in this
+# stop/start sequence, because we left a prepared transaction open above.
+# Also, setting subtransaction_buffers to 32 above causes to switch SLRU
+# bank, for additional code coverage.
+$cur_primary->stop;
+$cur_primary->start;
+my $nsubtrans = $cur_primary->safe_psql('postgres',
+   "select 'pg_subtrans/'||f, s.size from pg_ls_dir('pg_subtrans') f, pg_stat_file('pg_subtrans/'||f) s"
+);
+isnt($osubtrans, $nsubtrans, "contents of pg_subtrans/ have changed");
+
 done_testing();