LCOV - code coverage report
Current view: top level - src/test/modules/xid_wraparound - xid_wraparound.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 87 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 8 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-2026, 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            0 : 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            0 : PG_FUNCTION_INFO_V1(consume_xids);
      31              : Datum
      32            0 : consume_xids(PG_FUNCTION_ARGS)
      33              : {
      34            0 :         int64           nxids = PG_GETARG_INT64(0);
      35            0 :         FullTransactionId lastxid;
      36              : 
      37            0 :         if (nxids < 0)
      38            0 :                 elog(ERROR, "invalid nxids argument: %" PRId64, nxids);
      39              : 
      40            0 :         if (nxids == 0)
      41            0 :                 lastxid = ReadNextFullTransactionId();
      42              :         else
      43            0 :                 lastxid = consume_xids_common(InvalidFullTransactionId, (uint64) nxids);
      44              : 
      45            0 :         PG_RETURN_FULLTRANSACTIONID(lastxid);
      46            0 : }
      47              : 
      48              : /*
      49              :  * Consume XIDs, up to the given XID.
      50              :  */
      51            0 : PG_FUNCTION_INFO_V1(consume_xids_until);
      52              : Datum
      53            0 : consume_xids_until(PG_FUNCTION_ARGS)
      54              : {
      55            0 :         FullTransactionId targetxid = PG_GETARG_FULLTRANSACTIONID(0);
      56            0 :         FullTransactionId lastxid;
      57              : 
      58            0 :         if (!FullTransactionIdIsNormal(targetxid))
      59            0 :                 elog(ERROR, "targetxid %" PRIu64 " is not normal",
      60              :                          U64FromFullTransactionId(targetxid));
      61              : 
      62            0 :         lastxid = consume_xids_common(targetxid, 0);
      63              : 
      64            0 :         PG_RETURN_FULLTRANSACTIONID(lastxid);
      65            0 : }
      66              : 
      67              : /*
      68              :  * Common functionality between the two public functions.
      69              :  */
      70              : static FullTransactionId
      71            0 : consume_xids_common(FullTransactionId untilxid, uint64 nxids)
      72              : {
      73              :         FullTransactionId lastxid;
      74            0 :         uint64          last_reported_at = 0;
      75            0 :         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            0 :         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            0 :         (void) GetTopTransactionId();
      93              : 
      94            0 :         for (;;)
      95              :         {
      96            0 :                 uint64          xids_left;
      97              : 
      98            0 :                 CHECK_FOR_INTERRUPTS();
      99              : 
     100              :                 /* How many XIDs do we have left to consume? */
     101            0 :                 if (nxids > 0)
     102              :                 {
     103            0 :                         if (consumed >= nxids)
     104            0 :                                 break;
     105            0 :                         xids_left = nxids - consumed;
     106            0 :                 }
     107              :                 else
     108              :                 {
     109            0 :                         if (FullTransactionIdFollowsOrEquals(lastxid, untilxid))
     110            0 :                                 break;
     111            0 :                         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            0 :                 if (xids_left > 2000 &&
     119            0 :                         consumed - last_reported_at < REPORT_INTERVAL &&
     120            0 :                         MyProc->subxidStatus.overflowed)
     121              :                 {
     122            0 :                         int64           consumed_by_shortcut = consume_xids_shortcut();
     123              : 
     124            0 :                         if (consumed_by_shortcut > 0)
     125              :                         {
     126            0 :                                 consumed += consumed_by_shortcut;
     127            0 :                                 continue;
     128              :                         }
     129            0 :                 }
     130              : 
     131              :                 /* Slow path: Call GetNewTransactionId to allocate a new XID. */
     132            0 :                 lastxid = GetNewTransactionId(true);
     133            0 :                 consumed++;
     134              : 
     135              :                 /* Report progress */
     136            0 :                 if (consumed - last_reported_at >= REPORT_INTERVAL)
     137              :                 {
     138            0 :                         if (nxids > 0)
     139            0 :                                 elog(NOTICE, "consumed %" PRIu64 " / %" PRIu64 " XIDs, latest %u:%u",
     140              :                                          consumed, nxids,
     141              :                                          EpochFromFullTransactionId(lastxid),
     142              :                                          XidFromFullTransactionId(lastxid));
     143              :                         else
     144            0 :                                 elog(NOTICE, "consumed up to %u:%u / %u:%u",
     145              :                                          EpochFromFullTransactionId(lastxid),
     146              :                                          XidFromFullTransactionId(lastxid),
     147              :                                          EpochFromFullTransactionId(untilxid),
     148              :                                          XidFromFullTransactionId(untilxid));
     149            0 :                         last_reported_at = consumed;
     150            0 :                 }
     151            0 :         }
     152              : 
     153              :         return lastxid;
     154            0 : }
     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            0 : XidSkip(FullTransactionId fullxid)
     172              : {
     173            0 :         uint32          low = XidFromFullTransactionId(fullxid);
     174            0 :         uint32          rem;
     175            0 :         uint32          distance;
     176              : 
     177            0 :         if (low < 5 || low >= UINT32_MAX - 5)
     178            0 :                 return 0;
     179            0 :         distance = UINT32_MAX - 5 - low;
     180              : 
     181            0 :         rem = low % COMMIT_TS_XACTS_PER_PAGE;
     182            0 :         if (rem == 0)
     183            0 :                 return 0;
     184            0 :         distance = Min(distance, COMMIT_TS_XACTS_PER_PAGE - rem);
     185              : 
     186            0 :         rem = low % SUBTRANS_XACTS_PER_PAGE;
     187            0 :         if (rem == 0)
     188            0 :                 return 0;
     189            0 :         distance = Min(distance, SUBTRANS_XACTS_PER_PAGE - rem);
     190              : 
     191            0 :         rem = low % CLOG_XACTS_PER_PAGE;
     192            0 :         if (rem == 0)
     193            0 :                 return 0;
     194            0 :         distance = Min(distance, CLOG_XACTS_PER_PAGE - rem);
     195              : 
     196            0 :         return distance;
     197            0 : }
     198              : 
     199              : static int64
     200            0 : consume_xids_shortcut(void)
     201              : {
     202            0 :         FullTransactionId nextXid;
     203            0 :         uint32          consumed;
     204              : 
     205            0 :         LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
     206            0 :         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            0 :         consumed = XidSkip(nextXid);
     213            0 :         if (consumed > 0)
     214            0 :                 TransamVariables->nextXid.value += (uint64) consumed;
     215              : 
     216            0 :         LWLockRelease(XidGenLock);
     217              : 
     218            0 :         return consumed;
     219            0 : }
        

Generated by: LCOV version 2.3.2-1