LCOV - code coverage report
Current view: top level - src/test/modules/xid_wraparound - xid_wraparound.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 68 72 94.4 %
Date: 2024-11-21 08:14:44 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*--------------------------------------------------------------------------
       2             :  *
       3             :  * xid_wraparound.c
       4             :  *      Utilities for testing XID wraparound
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *      src/test/modules/xid_wraparound/xid_wraparound.c
      12             :  *
      13             :  * -------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/xact.h"
      18             : #include "miscadmin.h"
      19             : #include "storage/proc.h"
      20             : #include "utils/xid8.h"
      21             : 
      22         236 : PG_MODULE_MAGIC;
      23             : 
      24             : static int64 consume_xids_shortcut(void);
      25             : static FullTransactionId consume_xids_common(FullTransactionId untilxid, uint64 nxids);
      26             : 
      27             : /*
      28             :  * Consume the specified number of XIDs.
      29             :  */
      30         234 : PG_FUNCTION_INFO_V1(consume_xids);
      31             : Datum
      32         228 : consume_xids(PG_FUNCTION_ARGS)
      33             : {
      34         228 :     int64       nxids = PG_GETARG_INT64(0);
      35             :     FullTransactionId lastxid;
      36             : 
      37         228 :     if (nxids < 0)
      38           0 :         elog(ERROR, "invalid nxids argument: %lld", (long long) nxids);
      39             : 
      40         228 :     if (nxids == 0)
      41           0 :         lastxid = ReadNextFullTransactionId();
      42             :     else
      43         228 :         lastxid = consume_xids_common(InvalidFullTransactionId, (uint64) nxids);
      44             : 
      45         226 :     PG_RETURN_FULLTRANSACTIONID(lastxid);
      46             : }
      47             : 
      48             : /*
      49             :  * Consume XIDs, up to the given XID.
      50             :  */
      51           8 : PG_FUNCTION_INFO_V1(consume_xids_until);
      52             : Datum
      53           2 : consume_xids_until(PG_FUNCTION_ARGS)
      54             : {
      55           2 :     FullTransactionId targetxid = PG_GETARG_FULLTRANSACTIONID(0);
      56             :     FullTransactionId lastxid;
      57             : 
      58           2 :     if (!FullTransactionIdIsNormal(targetxid))
      59           0 :         elog(ERROR, "targetxid %llu is not normal",
      60             :              (unsigned long long) U64FromFullTransactionId(targetxid));
      61             : 
      62           2 :     lastxid = consume_xids_common(targetxid, 0);
      63             : 
      64           2 :     PG_RETURN_FULLTRANSACTIONID(lastxid);
      65             : }
      66             : 
      67             : /*
      68             :  * Common functionality between the two public functions.
      69             :  */
      70             : static FullTransactionId
      71         230 : consume_xids_common(FullTransactionId untilxid, uint64 nxids)
      72             : {
      73             :     FullTransactionId lastxid;
      74         230 :     uint64      last_reported_at = 0;
      75         230 :     uint64      consumed = 0;
      76             : 
      77             :     /* Print a NOTICE every REPORT_INTERVAL xids */
      78             : #define REPORT_INTERVAL (10 * 1000000)
      79             : 
      80             :     /* initialize 'lastxid' with the system's current next XID */
      81         230 :     lastxid = ReadNextFullTransactionId();
      82             : 
      83             :     /*
      84             :      * We consume XIDs by calling GetNewTransactionId(true), which marks the
      85             :      * consumed XIDs as subtransactions of the current top-level transaction.
      86             :      * For that to work, this transaction must have a top-level XID.
      87             :      *
      88             :      * GetNewTransactionId registers them in the subxid cache in PGPROC, until
      89             :      * the cache overflows, but beyond that, we don't keep track of the
      90             :      * consumed XIDs.
      91             :      */
      92         230 :     (void) GetTopTransactionId();
      93             : 
      94             :     for (;;)
      95    97027396 :     {
      96             :         uint64      xids_left;
      97             : 
      98    97027626 :         CHECK_FOR_INTERRUPTS();
      99             : 
     100             :         /* How many XIDs do we have left to consume? */
     101    97027626 :         if (nxids > 0)
     102             :         {
     103    83358792 :             if (consumed >= nxids)
     104         226 :                 break;
     105    83358566 :             xids_left = nxids - consumed;
     106             :         }
     107             :         else
     108             :         {
     109    13668834 :             if (FullTransactionIdFollowsOrEquals(lastxid, untilxid))
     110           2 :                 break;
     111    13668832 :             xids_left = U64FromFullTransactionId(untilxid) - U64FromFullTransactionId(lastxid);
     112             :         }
     113             : 
     114             :         /*
     115             :          * If we still have plenty of XIDs to consume, try to take a shortcut
     116             :          * and bump up the nextXid counter directly.
     117             :          */
     118    97027398 :         if (xids_left > 2000 &&
     119    96653812 :             consumed - last_reported_at < REPORT_INTERVAL &&
     120    96651218 :             MyProc->subxidStatus.overflowed)
     121             :         {
     122    96636268 :             int64       consumed_by_shortcut = consume_xids_shortcut();
     123             : 
     124    96636268 :             if (consumed_by_shortcut > 0)
     125             :             {
     126    48302666 :                 consumed += consumed_by_shortcut;
     127    48302666 :                 continue;
     128             :             }
     129             :         }
     130             : 
     131             :         /* Slow path: Call GetNewTransactionId to allocate a new XID. */
     132    48724732 :         lastxid = GetNewTransactionId(true);
     133    48724730 :         consumed++;
     134             : 
     135             :         /* Report progress */
     136    48724730 :         if (consumed - last_reported_at >= REPORT_INTERVAL)
     137             :         {
     138        2622 :             if (nxids > 0)
     139        2224 :                 elog(NOTICE, "consumed %llu / %llu XIDs, latest %u:%u",
     140             :                      (unsigned long long) consumed, (unsigned long long) nxids,
     141             :                      EpochFromFullTransactionId(lastxid),
     142             :                      XidFromFullTransactionId(lastxid));
     143             :             else
     144         398 :                 elog(NOTICE, "consumed up to %u:%u / %u:%u",
     145             :                      EpochFromFullTransactionId(lastxid),
     146             :                      XidFromFullTransactionId(lastxid),
     147             :                      EpochFromFullTransactionId(untilxid),
     148             :                      XidFromFullTransactionId(untilxid));
     149        2622 :             last_reported_at = consumed;
     150             :         }
     151             :     }
     152             : 
     153         228 :     return lastxid;
     154             : }
     155             : 
     156             : /*
     157             :  * These constants copied from .c files, because they're private.
     158             :  */
     159             : #define COMMIT_TS_XACTS_PER_PAGE (BLCKSZ / 10)
     160             : #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
     161             : #define CLOG_XACTS_PER_BYTE 4
     162             : #define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
     163             : 
     164             : /*
     165             :  * All the interesting action in GetNewTransactionId happens when we extend
     166             :  * the SLRUs, or at the uint32 wraparound. If the nextXid counter is not close
     167             :  * to any of those interesting values, take a shortcut and bump nextXID
     168             :  * directly, close to the next "interesting" value.
     169             :  */
     170             : static inline uint32
     171    96636268 : XidSkip(FullTransactionId fullxid)
     172             : {
     173    96636268 :     uint32      low = XidFromFullTransactionId(fullxid);
     174             :     uint32      rem;
     175             :     uint32      distance;
     176             : 
     177    96636268 :     if (low < 5 || low >= UINT32_MAX - 5)
     178          32 :         return 0;
     179    96636236 :     distance = UINT32_MAX - 5 - low;
     180             : 
     181    96636236 :     rem = low % COMMIT_TS_XACTS_PER_PAGE;
     182    96636236 :     if (rem == 0)
     183    34538934 :         return 0;
     184    62097302 :     distance = Min(distance, COMMIT_TS_XACTS_PER_PAGE - rem);
     185             : 
     186    62097302 :     rem = low % SUBTRANS_XACTS_PER_PAGE;
     187    62097302 :     if (rem == 0)
     188    13794636 :         return 0;
     189    48302666 :     distance = Min(distance, SUBTRANS_XACTS_PER_PAGE - rem);
     190             : 
     191    48302666 :     rem = low % CLOG_XACTS_PER_PAGE;
     192    48302666 :     if (rem == 0)
     193           0 :         return 0;
     194    48302666 :     distance = Min(distance, CLOG_XACTS_PER_PAGE - rem);
     195             : 
     196    48302666 :     return distance;
     197             : }
     198             : 
     199             : static int64
     200    96636268 : consume_xids_shortcut(void)
     201             : {
     202             :     FullTransactionId nextXid;
     203             :     uint32      consumed;
     204             : 
     205    96636268 :     LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
     206    96636268 :     nextXid = TransamVariables->nextXid;
     207             : 
     208             :     /*
     209             :      * Go slow near the "interesting values". The interesting zones include 5
     210             :      * transactions before and after SLRU page switches.
     211             :      */
     212    96636268 :     consumed = XidSkip(nextXid);
     213    96636268 :     if (consumed > 0)
     214    48302666 :         TransamVariables->nextXid.value += (uint64) consumed;
     215             : 
     216    96636268 :     LWLockRelease(XidGenLock);
     217             : 
     218    96636268 :     return consumed;
     219             : }

Generated by: LCOV version 1.14