LCOV - code coverage report
Current view: top level - src/test/modules/xid_wraparound - xid_wraparound.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 94.4 % 72 68
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 76.1 % 46 35

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

Generated by: LCOV version 2.0-1