LCOV - differential code coverage report
Current view: top level - src/backend/access/transam - subtrans.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 92.4 % 92 85 7 85
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 12 12 12
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * subtrans.c
       4                 :  *      PostgreSQL subtransaction-log manager
       5                 :  *
       6                 :  * The pg_subtrans manager is a pg_xact-like manager that stores the parent
       7                 :  * transaction Id for each transaction.  It is a fundamental part of the
       8                 :  * nested transactions implementation.  A main transaction has a parent
       9                 :  * of InvalidTransactionId, and each subtransaction has its immediate parent.
      10                 :  * The tree can easily be walked from child to parent, but not in the
      11                 :  * opposite direction.
      12                 :  *
      13                 :  * This code is based on xact.c, but the robustness requirements
      14                 :  * are completely different from pg_xact, because we only need to remember
      15                 :  * pg_subtrans information for currently-open transactions.  Thus, there is
      16                 :  * no need to preserve data over a crash and restart.
      17                 :  *
      18                 :  * There are no XLOG interactions since we do not care about preserving
      19                 :  * data across crashes.  During database startup, we simply force the
      20                 :  * currently-active page of SUBTRANS to zeroes.
      21                 :  *
      22                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
      23                 :  * Portions Copyright (c) 1994, Regents of the University of California
      24                 :  *
      25                 :  * src/backend/access/transam/subtrans.c
      26                 :  *
      27                 :  *-------------------------------------------------------------------------
      28                 :  */
      29                 : #include "postgres.h"
      30                 : 
      31                 : #include "access/slru.h"
      32                 : #include "access/subtrans.h"
      33                 : #include "access/transam.h"
      34                 : #include "pg_trace.h"
      35                 : #include "utils/snapmgr.h"
      36                 : 
      37                 : 
      38                 : /*
      39                 :  * Defines for SubTrans page sizes.  A page is the same BLCKSZ as is used
      40                 :  * everywhere else in Postgres.
      41                 :  *
      42                 :  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
      43                 :  * SubTrans page numbering also wraps around at
      44                 :  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
      45                 :  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need take no
      46                 :  * explicit notice of that fact in this module, except when comparing segment
      47                 :  * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes) and zeroing
      48                 :  * them in StartupSUBTRANS.
      49                 :  */
      50                 : 
      51                 : /* We need four bytes per xact */
      52                 : #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
      53                 : 
      54                 : #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
      55                 : #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
      56                 : 
      57                 : 
      58                 : /*
      59                 :  * Link to shared-memory data structures for SUBTRANS control
      60                 :  */
      61                 : static SlruCtlData SubTransCtlData;
      62                 : 
      63                 : #define SubTransCtl  (&SubTransCtlData)
      64                 : 
      65                 : 
      66                 : static int  ZeroSUBTRANSPage(int pageno);
      67                 : static bool SubTransPagePrecedes(int page1, int page2);
      68                 : 
      69                 : 
      70                 : /*
      71                 :  * Record the parent of a subtransaction in the subtrans log.
      72                 :  */
      73                 : void
      74 CBC        5103 : SubTransSetParent(TransactionId xid, TransactionId parent)
      75                 : {
      76            5103 :     int         pageno = TransactionIdToPage(xid);
      77            5103 :     int         entryno = TransactionIdToEntry(xid);
      78                 :     int         slotno;
      79                 :     TransactionId *ptr;
      80                 : 
      81            5103 :     Assert(TransactionIdIsValid(parent));
      82            5103 :     Assert(TransactionIdFollows(xid, parent));
      83                 : 
      84            5103 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
      85                 : 
      86            5103 :     slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
      87            5103 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
      88            5103 :     ptr += entryno;
      89                 : 
      90                 :     /*
      91                 :      * It's possible we'll try to set the parent xid multiple times but we
      92                 :      * shouldn't ever be changing the xid from one valid xid to another valid
      93                 :      * xid, which would corrupt the data structure.
      94                 :      */
      95            5103 :     if (*ptr != parent)
      96                 :     {
      97            4847 :         Assert(*ptr == InvalidTransactionId);
      98            4847 :         *ptr = parent;
      99            4847 :         SubTransCtl->shared->page_dirty[slotno] = true;
     100                 :     }
     101                 : 
     102            5103 :     LWLockRelease(SubtransSLRULock);
     103            5103 : }
     104                 : 
     105                 : /*
     106                 :  * Interrogate the parent of a transaction in the subtrans log.
     107                 :  */
     108                 : TransactionId
     109            3079 : SubTransGetParent(TransactionId xid)
     110                 : {
     111            3079 :     int         pageno = TransactionIdToPage(xid);
     112            3079 :     int         entryno = TransactionIdToEntry(xid);
     113                 :     int         slotno;
     114                 :     TransactionId *ptr;
     115                 :     TransactionId parent;
     116                 : 
     117                 :     /* Can't ask about stuff that might not be around anymore */
     118            3079 :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
     119                 : 
     120                 :     /* Bootstrap and frozen XIDs have no parent */
     121            3079 :     if (!TransactionIdIsNormal(xid))
     122 UBC           0 :         return InvalidTransactionId;
     123                 : 
     124                 :     /* lock is acquired by SimpleLruReadPage_ReadOnly */
     125                 : 
     126 CBC        3079 :     slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
     127            3079 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
     128            3079 :     ptr += entryno;
     129                 : 
     130            3079 :     parent = *ptr;
     131                 : 
     132            3079 :     LWLockRelease(SubtransSLRULock);
     133                 : 
     134            3079 :     return parent;
     135                 : }
     136                 : 
     137                 : /*
     138                 :  * SubTransGetTopmostTransaction
     139                 :  *
     140                 :  * Returns the topmost transaction of the given transaction id.
     141                 :  *
     142                 :  * Because we cannot look back further than TransactionXmin, it is possible
     143                 :  * that this function will lie and return an intermediate subtransaction ID
     144                 :  * instead of the true topmost parent ID.  This is OK, because in practice
     145                 :  * we only care about detecting whether the topmost parent is still running
     146                 :  * or is part of a current snapshot's list of still-running transactions.
     147                 :  * Therefore, any XID before TransactionXmin is as good as any other.
     148                 :  */
     149                 : TransactionId
     150            1076 : SubTransGetTopmostTransaction(TransactionId xid)
     151                 : {
     152            1076 :     TransactionId parentXid = xid,
     153            1076 :                 previousXid = xid;
     154                 : 
     155                 :     /* Can't ask about stuff that might not be around anymore */
     156            1076 :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
     157                 : 
     158            4155 :     while (TransactionIdIsValid(parentXid))
     159                 :     {
     160            3079 :         previousXid = parentXid;
     161            3079 :         if (TransactionIdPrecedes(parentXid, TransactionXmin))
     162 UBC           0 :             break;
     163 CBC        3079 :         parentXid = SubTransGetParent(parentXid);
     164                 : 
     165                 :         /*
     166                 :          * By convention the parent xid gets allocated first, so should always
     167                 :          * precede the child xid. Anything else points to a corrupted data
     168                 :          * structure that could lead to an infinite loop, so exit.
     169                 :          */
     170            3079 :         if (!TransactionIdPrecedes(parentXid, previousXid))
     171 UBC           0 :             elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
     172                 :                  previousXid, parentXid);
     173                 :     }
     174                 : 
     175 CBC        1076 :     Assert(TransactionIdIsValid(previousXid));
     176                 : 
     177            1076 :     return previousXid;
     178                 : }
     179                 : 
     180                 : 
     181                 : /*
     182                 :  * Initialization of shared memory for SUBTRANS
     183                 :  */
     184                 : Size
     185            2738 : SUBTRANSShmemSize(void)
     186                 : {
     187            2738 :     return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
     188                 : }
     189                 : 
     190                 : void
     191            1826 : SUBTRANSShmemInit(void)
     192                 : {
     193            1826 :     SubTransCtl->PagePrecedes = SubTransPagePrecedes;
     194            1826 :     SimpleLruInit(SubTransCtl, "Subtrans", NUM_SUBTRANS_BUFFERS, 0,
     195            1826 :                   SubtransSLRULock, "pg_subtrans",
     196                 :                   LWTRANCHE_SUBTRANS_BUFFER, SYNC_HANDLER_NONE);
     197            1826 :     SlruPagePrecedesUnitTests(SubTransCtl, SUBTRANS_XACTS_PER_PAGE);
     198            1826 : }
     199                 : 
     200                 : /*
     201                 :  * This func must be called ONCE on system install.  It creates
     202                 :  * the initial SUBTRANS segment.  (The SUBTRANS directory is assumed to
     203                 :  * have been created by the initdb shell script, and SUBTRANSShmemInit
     204                 :  * must have been called already.)
     205                 :  *
     206                 :  * Note: it's not really necessary to create the initial segment now,
     207                 :  * since slru.c would create it on first write anyway.  But we may as well
     208                 :  * do it to be sure the directory is set up correctly.
     209                 :  */
     210                 : void
     211             305 : BootStrapSUBTRANS(void)
     212                 : {
     213                 :     int         slotno;
     214                 : 
     215             305 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
     216                 : 
     217                 :     /* Create and zero the first page of the subtrans log */
     218             305 :     slotno = ZeroSUBTRANSPage(0);
     219                 : 
     220                 :     /* Make sure it's written out */
     221             305 :     SimpleLruWritePage(SubTransCtl, slotno);
     222             305 :     Assert(!SubTransCtl->shared->page_dirty[slotno]);
     223                 : 
     224             305 :     LWLockRelease(SubtransSLRULock);
     225             305 : }
     226                 : 
     227                 : /*
     228                 :  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
     229                 :  *
     230                 :  * The page is not actually written, just set up in shared memory.
     231                 :  * The slot number of the new page is returned.
     232                 :  *
     233                 :  * Control lock must be held at entry, and will be held at exit.
     234                 :  */
     235                 : static int
     236            1822 : ZeroSUBTRANSPage(int pageno)
     237                 : {
     238            1822 :     return SimpleLruZeroPage(SubTransCtl, pageno);
     239                 : }
     240                 : 
     241                 : /*
     242                 :  * This must be called ONCE during postmaster or standalone-backend startup,
     243                 :  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
     244                 :  *
     245                 :  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
     246                 :  * if there are none.
     247                 :  */
     248                 : void
     249            1174 : StartupSUBTRANS(TransactionId oldestActiveXID)
     250                 : {
     251                 :     FullTransactionId nextXid;
     252                 :     int         startPage;
     253                 :     int         endPage;
     254                 : 
     255                 :     /*
     256                 :      * Since we don't expect pg_subtrans to be valid across crashes, we
     257                 :      * initialize the currently-active page(s) to zeroes during startup.
     258                 :      * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
     259                 :      * the new page without regard to whatever was previously on disk.
     260                 :      */
     261            1174 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
     262                 : 
     263            1174 :     startPage = TransactionIdToPage(oldestActiveXID);
     264            1174 :     nextXid = ShmemVariableCache->nextXid;
     265            1174 :     endPage = TransactionIdToPage(XidFromFullTransactionId(nextXid));
     266                 : 
     267            1174 :     while (startPage != endPage)
     268                 :     {
     269 UBC           0 :         (void) ZeroSUBTRANSPage(startPage);
     270               0 :         startPage++;
     271                 :         /* must account for wraparound */
     272               0 :         if (startPage > TransactionIdToPage(MaxTransactionId))
     273               0 :             startPage = 0;
     274                 :     }
     275 CBC        1174 :     (void) ZeroSUBTRANSPage(startPage);
     276                 : 
     277            1174 :     LWLockRelease(SubtransSLRULock);
     278            1174 : }
     279                 : 
     280                 : /*
     281                 :  * Perform a checkpoint --- either during shutdown, or on-the-fly
     282                 :  */
     283                 : void
     284            2363 : CheckPointSUBTRANS(void)
     285                 : {
     286                 :     /*
     287                 :      * Write dirty SUBTRANS pages to disk
     288                 :      *
     289                 :      * This is not actually necessary from a correctness point of view. We do
     290                 :      * it merely to improve the odds that writing of dirty pages is done by
     291                 :      * the checkpoint process and not by backends.
     292                 :      */
     293                 :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
     294            2363 :     SimpleLruWriteAll(SubTransCtl, true);
     295                 :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
     296            2363 : }
     297                 : 
     298                 : 
     299                 : /*
     300                 :  * Make sure that SUBTRANS has room for a newly-allocated XID.
     301                 :  *
     302                 :  * NB: this is called while holding XidGenLock.  We want it to be very fast
     303                 :  * most of the time; even when it's not so fast, no actual I/O need happen
     304                 :  * unless we're forced to write out a dirty subtrans page to make room
     305                 :  * in shared memory.
     306                 :  */
     307                 : void
     308          320834 : ExtendSUBTRANS(TransactionId newestXact)
     309                 : {
     310                 :     int         pageno;
     311                 : 
     312                 :     /*
     313                 :      * No work except at first XID of a page.  But beware: just after
     314                 :      * wraparound, the first XID of page zero is FirstNormalTransactionId.
     315                 :      */
     316          320834 :     if (TransactionIdToEntry(newestXact) != 0 &&
     317                 :         !TransactionIdEquals(newestXact, FirstNormalTransactionId))
     318          320491 :         return;
     319                 : 
     320             343 :     pageno = TransactionIdToPage(newestXact);
     321                 : 
     322             343 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
     323                 : 
     324                 :     /* Zero the page */
     325             343 :     ZeroSUBTRANSPage(pageno);
     326                 : 
     327             343 :     LWLockRelease(SubtransSLRULock);
     328                 : }
     329                 : 
     330                 : 
     331                 : /*
     332                 :  * Remove all SUBTRANS segments before the one holding the passed transaction ID
     333                 :  *
     334                 :  * oldestXact is the oldest TransactionXmin of any running transaction.  This
     335                 :  * is called only during checkpoint.
     336                 :  */
     337                 : void
     338            2336 : TruncateSUBTRANS(TransactionId oldestXact)
     339                 : {
     340                 :     int         cutoffPage;
     341                 : 
     342                 :     /*
     343                 :      * The cutoff point is the start of the segment containing oldestXact. We
     344                 :      * pass the *page* containing oldestXact to SimpleLruTruncate.  We step
     345                 :      * back one transaction to avoid passing a cutoff page that hasn't been
     346                 :      * created yet in the rare case that oldestXact would be the first item on
     347                 :      * a page and oldestXact == next XID.  In that case, if we didn't subtract
     348                 :      * one, we'd trigger SimpleLruTruncate's wraparound detection.
     349                 :      */
     350            3257 :     TransactionIdRetreat(oldestXact);
     351            2336 :     cutoffPage = TransactionIdToPage(oldestXact);
     352                 : 
     353            2336 :     SimpleLruTruncate(SubTransCtl, cutoffPage);
     354            2336 : }
     355                 : 
     356                 : 
     357                 : /*
     358                 :  * Decide whether a SUBTRANS page number is "older" for truncation purposes.
     359                 :  * Analogous to CLOGPagePrecedes().
     360                 :  */
     361                 : static bool
     362           78322 : SubTransPagePrecedes(int page1, int page2)
     363                 : {
     364                 :     TransactionId xid1;
     365                 :     TransactionId xid2;
     366                 : 
     367           78322 :     xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
     368           78322 :     xid1 += FirstNormalTransactionId + 1;
     369           78322 :     xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
     370           78322 :     xid2 += FirstNormalTransactionId + 1;
     371                 : 
     372          125889 :     return (TransactionIdPrecedes(xid1, xid2) &&
     373           47567 :             TransactionIdPrecedes(xid1, xid2 + SUBTRANS_XACTS_PER_PAGE - 1));
     374                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a