LCOV - code coverage report
Current view: top level - src/backend/access/transam - subtrans.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 80 87 92.0 %
Date: 2019-11-15 23:07:02 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          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-2019, 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        5346 : SubTransSetParent(TransactionId xid, TransactionId parent)
      75             : {
      76        5346 :     int         pageno = TransactionIdToPage(xid);
      77        5346 :     int         entryno = TransactionIdToEntry(xid);
      78             :     int         slotno;
      79             :     TransactionId *ptr;
      80             : 
      81             :     Assert(TransactionIdIsValid(parent));
      82             :     Assert(TransactionIdFollows(xid, parent));
      83             : 
      84        5346 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
      85             : 
      86        5346 :     slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
      87        5346 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
      88        5346 :     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        5346 :     if (*ptr != parent)
      96             :     {
      97             :         Assert(*ptr == InvalidTransactionId);
      98        4834 :         *ptr = parent;
      99        4834 :         SubTransCtl->shared->page_dirty[slotno] = true;
     100             :     }
     101             : 
     102        5346 :     LWLockRelease(SubtransControlLock);
     103        5346 : }
     104             : 
     105             : /*
     106             :  * Interrogate the parent of a transaction in the subtrans log.
     107             :  */
     108             : TransactionId
     109        3022 : SubTransGetParent(TransactionId xid)
     110             : {
     111        3022 :     int         pageno = TransactionIdToPage(xid);
     112        3022 :     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             :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
     119             : 
     120             :     /* Bootstrap and frozen XIDs have no parent */
     121        3022 :     if (!TransactionIdIsNormal(xid))
     122           0 :         return InvalidTransactionId;
     123             : 
     124             :     /* lock is acquired by SimpleLruReadPage_ReadOnly */
     125             : 
     126        3022 :     slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
     127        3022 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
     128        3022 :     ptr += entryno;
     129             : 
     130        3022 :     parent = *ptr;
     131             : 
     132        3022 :     LWLockRelease(SubtransControlLock);
     133             : 
     134        3022 :     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        2056 : SubTransGetTopmostTransaction(TransactionId xid)
     151             : {
     152        2056 :     TransactionId parentXid = xid,
     153        2056 :                 previousXid = xid;
     154             : 
     155             :     /* Can't ask about stuff that might not be around anymore */
     156             :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
     157             : 
     158        7134 :     while (TransactionIdIsValid(parentXid))
     159             :     {
     160        3022 :         previousXid = parentXid;
     161        3022 :         if (TransactionIdPrecedes(parentXid, TransactionXmin))
     162           0 :             break;
     163        3022 :         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        3022 :         if (!TransactionIdPrecedes(parentXid, previousXid))
     171           0 :             elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
     172             :                  previousXid, parentXid);
     173             :     }
     174             : 
     175             :     Assert(TransactionIdIsValid(previousXid));
     176             : 
     177        2056 :     return previousXid;
     178             : }
     179             : 
     180             : 
     181             : /*
     182             :  * Initialization of shared memory for SUBTRANS
     183             :  */
     184             : Size
     185        1894 : SUBTRANSShmemSize(void)
     186             : {
     187        1894 :     return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
     188             : }
     189             : 
     190             : void
     191        1890 : SUBTRANSShmemInit(void)
     192             : {
     193        1890 :     SubTransCtl->PagePrecedes = SubTransPagePrecedes;
     194        1890 :     SimpleLruInit(SubTransCtl, "subtrans", NUM_SUBTRANS_BUFFERS, 0,
     195        1890 :                   SubtransControlLock, "pg_subtrans",
     196             :                   LWTRANCHE_SUBTRANS_BUFFERS);
     197             :     /* Override default assumption that writes should be fsync'd */
     198        1890 :     SubTransCtl->do_fsync = false;
     199        1890 : }
     200             : 
     201             : /*
     202             :  * This func must be called ONCE on system install.  It creates
     203             :  * the initial SUBTRANS segment.  (The SUBTRANS directory is assumed to
     204             :  * have been created by the initdb shell script, and SUBTRANSShmemInit
     205             :  * must have been called already.)
     206             :  *
     207             :  * Note: it's not really necessary to create the initial segment now,
     208             :  * since slru.c would create it on first write anyway.  But we may as well
     209             :  * do it to be sure the directory is set up correctly.
     210             :  */
     211             : void
     212         322 : BootStrapSUBTRANS(void)
     213             : {
     214             :     int         slotno;
     215             : 
     216         322 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
     217             : 
     218             :     /* Create and zero the first page of the subtrans log */
     219         322 :     slotno = ZeroSUBTRANSPage(0);
     220             : 
     221             :     /* Make sure it's written out */
     222         322 :     SimpleLruWritePage(SubTransCtl, slotno);
     223             :     Assert(!SubTransCtl->shared->page_dirty[slotno]);
     224             : 
     225         322 :     LWLockRelease(SubtransControlLock);
     226         322 : }
     227             : 
     228             : /*
     229             :  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
     230             :  *
     231             :  * The page is not actually written, just set up in shared memory.
     232             :  * The slot number of the new page is returned.
     233             :  *
     234             :  * Control lock must be held at entry, and will be held at exit.
     235             :  */
     236             : static int
     237        1870 : ZeroSUBTRANSPage(int pageno)
     238             : {
     239        1870 :     return SimpleLruZeroPage(SubTransCtl, pageno);
     240             : }
     241             : 
     242             : /*
     243             :  * This must be called ONCE during postmaster or standalone-backend startup,
     244             :  * after StartupXLOG has initialized ShmemVariableCache->nextFullXid.
     245             :  *
     246             :  * oldestActiveXID is the oldest XID of any prepared transaction, or nextFullXid
     247             :  * if there are none.
     248             :  */
     249             : void
     250        1192 : StartupSUBTRANS(TransactionId oldestActiveXID)
     251             : {
     252             :     FullTransactionId nextFullXid;
     253             :     int         startPage;
     254             :     int         endPage;
     255             : 
     256             :     /*
     257             :      * Since we don't expect pg_subtrans to be valid across crashes, we
     258             :      * initialize the currently-active page(s) to zeroes during startup.
     259             :      * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
     260             :      * the new page without regard to whatever was previously on disk.
     261             :      */
     262        1192 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
     263             : 
     264        1192 :     startPage = TransactionIdToPage(oldestActiveXID);
     265        1192 :     nextFullXid = ShmemVariableCache->nextFullXid;
     266        1192 :     endPage = TransactionIdToPage(XidFromFullTransactionId(nextFullXid));
     267             : 
     268        2384 :     while (startPage != endPage)
     269             :     {
     270           0 :         (void) ZeroSUBTRANSPage(startPage);
     271           0 :         startPage++;
     272             :         /* must account for wraparound */
     273           0 :         if (startPage > TransactionIdToPage(MaxTransactionId))
     274           0 :             startPage = 0;
     275             :     }
     276        1192 :     (void) ZeroSUBTRANSPage(startPage);
     277             : 
     278        1192 :     LWLockRelease(SubtransControlLock);
     279        1192 : }
     280             : 
     281             : /*
     282             :  * This must be called ONCE during postmaster or standalone-backend shutdown
     283             :  */
     284             : void
     285        1024 : ShutdownSUBTRANS(void)
     286             : {
     287             :     /*
     288             :      * Flush dirty SUBTRANS pages to disk
     289             :      *
     290             :      * This is not actually necessary from a correctness point of view. We do
     291             :      * it merely as a debugging aid.
     292             :      */
     293             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(false);
     294        1024 :     SimpleLruFlush(SubTransCtl, false);
     295             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(false);
     296        1024 : }
     297             : 
     298             : /*
     299             :  * Perform a checkpoint --- either during shutdown, or on-the-fly
     300             :  */
     301             : void
     302        2832 : CheckPointSUBTRANS(void)
     303             : {
     304             :     /*
     305             :      * Flush dirty SUBTRANS pages to disk
     306             :      *
     307             :      * This is not actually necessary from a correctness point of view. We do
     308             :      * it merely to improve the odds that writing of dirty pages is done by
     309             :      * the checkpoint process and not by backends.
     310             :      */
     311             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
     312        2832 :     SimpleLruFlush(SubTransCtl, true);
     313             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
     314        2832 : }
     315             : 
     316             : 
     317             : /*
     318             :  * Make sure that SUBTRANS has room for a newly-allocated XID.
     319             :  *
     320             :  * NB: this is called while holding XidGenLock.  We want it to be very fast
     321             :  * most of the time; even when it's not so fast, no actual I/O need happen
     322             :  * unless we're forced to write out a dirty subtrans page to make room
     323             :  * in shared memory.
     324             :  */
     325             : void
     326      256402 : ExtendSUBTRANS(TransactionId newestXact)
     327             : {
     328             :     int         pageno;
     329             : 
     330             :     /*
     331             :      * No work except at first XID of a page.  But beware: just after
     332             :      * wraparound, the first XID of page zero is FirstNormalTransactionId.
     333             :      */
     334      256402 :     if (TransactionIdToEntry(newestXact) != 0 &&
     335             :         !TransactionIdEquals(newestXact, FirstNormalTransactionId))
     336      256046 :         return;
     337             : 
     338         356 :     pageno = TransactionIdToPage(newestXact);
     339             : 
     340         356 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
     341             : 
     342             :     /* Zero the page */
     343         356 :     ZeroSUBTRANSPage(pageno);
     344             : 
     345         356 :     LWLockRelease(SubtransControlLock);
     346             : }
     347             : 
     348             : 
     349             : /*
     350             :  * Remove all SUBTRANS segments before the one holding the passed transaction ID
     351             :  *
     352             :  * This is normally called during checkpoint, with oldestXact being the
     353             :  * oldest TransactionXmin of any running transaction.
     354             :  */
     355             : void
     356        3750 : TruncateSUBTRANS(TransactionId oldestXact)
     357             : {
     358             :     int         cutoffPage;
     359             : 
     360             :     /*
     361             :      * The cutoff point is the start of the segment containing oldestXact. We
     362             :      * pass the *page* containing oldestXact to SimpleLruTruncate.  We step
     363             :      * back one transaction to avoid passing a cutoff page that hasn't been
     364             :      * created yet in the rare case that oldestXact would be the first item on
     365             :      * a page and oldestXact == next XID.  In that case, if we didn't subtract
     366             :      * one, we'd trigger SimpleLruTruncate's wraparound detection.
     367             :      */
     368        3750 :     TransactionIdRetreat(oldestXact);
     369        2784 :     cutoffPage = TransactionIdToPage(oldestXact);
     370             : 
     371        2784 :     SimpleLruTruncate(SubTransCtl, cutoffPage);
     372        2784 : }
     373             : 
     374             : 
     375             : /*
     376             :  * Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
     377             :  *
     378             :  * We need to use comparison of TransactionIds here in order to do the right
     379             :  * thing with wraparound XID arithmetic.  However, if we are asked about
     380             :  * page number zero, we don't want to hand InvalidTransactionId to
     381             :  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
     382             :  * offset both xids by FirstNormalTransactionId to avoid that.
     383             :  */
     384             : static bool
     385        8436 : SubTransPagePrecedes(int page1, int page2)
     386             : {
     387             :     TransactionId xid1;
     388             :     TransactionId xid2;
     389             : 
     390        8436 :     xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
     391        8436 :     xid1 += FirstNormalTransactionId;
     392        8436 :     xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
     393        8436 :     xid2 += FirstNormalTransactionId;
     394             : 
     395        8436 :     return TransactionIdPrecedes(xid1, xid2);
     396             : }

Generated by: LCOV version 1.13