LCOV - code coverage report
Current view: top level - src/backend/access/transam - subtrans.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 86 93 92.5 %
Date: 2017-09-29 13:40:31 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-2017, 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          64 : SubTransSetParent(TransactionId xid, TransactionId parent)
      75             : {
      76          64 :     int         pageno = TransactionIdToPage(xid);
      77          64 :     int         entryno = TransactionIdToEntry(xid);
      78             :     int         slotno;
      79             :     TransactionId *ptr;
      80             : 
      81          64 :     Assert(TransactionIdIsValid(parent));
      82          64 :     Assert(TransactionIdFollows(xid, parent));
      83             : 
      84          64 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
      85             : 
      86          64 :     slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
      87          64 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
      88          64 :     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          64 :     if (*ptr != parent)
      96             :     {
      97          64 :         Assert(*ptr == InvalidTransactionId);
      98          64 :         *ptr = parent;
      99          64 :         SubTransCtl->shared->page_dirty[slotno] = true;
     100             :     }
     101             : 
     102          64 :     LWLockRelease(SubtransControlLock);
     103          64 : }
     104             : 
     105             : /*
     106             :  * Interrogate the parent of a transaction in the subtrans log.
     107             :  */
     108             : TransactionId
     109           2 : SubTransGetParent(TransactionId xid)
     110             : {
     111           2 :     int         pageno = TransactionIdToPage(xid);
     112           2 :     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           2 :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
     119             : 
     120             :     /* Bootstrap and frozen XIDs have no parent */
     121           2 :     if (!TransactionIdIsNormal(xid))
     122           0 :         return InvalidTransactionId;
     123             : 
     124             :     /* lock is acquired by SimpleLruReadPage_ReadOnly */
     125             : 
     126           2 :     slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
     127           2 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
     128           2 :     ptr += entryno;
     129             : 
     130           2 :     parent = *ptr;
     131             : 
     132           2 :     LWLockRelease(SubtransControlLock);
     133             : 
     134           2 :     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           2 : SubTransGetTopmostTransaction(TransactionId xid)
     151             : {
     152           2 :     TransactionId parentXid = xid,
     153           2 :                 previousXid = xid;
     154             : 
     155             :     /* Can't ask about stuff that might not be around anymore */
     156           2 :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
     157             : 
     158           6 :     while (TransactionIdIsValid(parentXid))
     159             :     {
     160           2 :         previousXid = parentXid;
     161           2 :         if (TransactionIdPrecedes(parentXid, TransactionXmin))
     162           0 :             break;
     163           2 :         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           2 :         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           2 :     Assert(TransactionIdIsValid(previousXid));
     176             : 
     177           2 :     return previousXid;
     178             : }
     179             : 
     180             : 
     181             : /*
     182             :  * Initialization of shared memory for SUBTRANS
     183             :  */
     184             : Size
     185           5 : SUBTRANSShmemSize(void)
     186             : {
     187           5 :     return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
     188             : }
     189             : 
     190             : void
     191           5 : SUBTRANSShmemInit(void)
     192             : {
     193           5 :     SubTransCtl->PagePrecedes = SubTransPagePrecedes;
     194           5 :     SimpleLruInit(SubTransCtl, "subtrans", NUM_SUBTRANS_BUFFERS, 0,
     195           5 :                   SubtransControlLock, "pg_subtrans",
     196             :                   LWTRANCHE_SUBTRANS_BUFFERS);
     197             :     /* Override default assumption that writes should be fsync'd */
     198           5 :     SubTransCtl->do_fsync = false;
     199           5 : }
     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           1 : BootStrapSUBTRANS(void)
     213             : {
     214             :     int         slotno;
     215             : 
     216           1 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
     217             : 
     218             :     /* Create and zero the first page of the subtrans log */
     219           1 :     slotno = ZeroSUBTRANSPage(0);
     220             : 
     221             :     /* Make sure it's written out */
     222           1 :     SimpleLruWritePage(SubTransCtl, slotno);
     223           1 :     Assert(!SubTransCtl->shared->page_dirty[slotno]);
     224             : 
     225           1 :     LWLockRelease(SubtransControlLock);
     226           1 : }
     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          10 : ZeroSUBTRANSPage(int pageno)
     238             : {
     239          10 :     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->nextXid.
     245             :  *
     246             :  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
     247             :  * if there are none.
     248             :  */
     249             : void
     250           3 : StartupSUBTRANS(TransactionId oldestActiveXID)
     251             : {
     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           3 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
     262             : 
     263           3 :     startPage = TransactionIdToPage(oldestActiveXID);
     264           3 :     endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
     265             : 
     266           6 :     while (startPage != endPage)
     267             :     {
     268           0 :         (void) ZeroSUBTRANSPage(startPage);
     269           0 :         startPage++;
     270             :         /* must account for wraparound */
     271           0 :         if (startPage > TransactionIdToPage(MaxTransactionId))
     272           0 :             startPage = 0;
     273             :     }
     274           3 :     (void) ZeroSUBTRANSPage(startPage);
     275             : 
     276           3 :     LWLockRelease(SubtransControlLock);
     277           3 : }
     278             : 
     279             : /*
     280             :  * This must be called ONCE during postmaster or standalone-backend shutdown
     281             :  */
     282             : void
     283           3 : ShutdownSUBTRANS(void)
     284             : {
     285             :     /*
     286             :      * Flush dirty SUBTRANS pages to disk
     287             :      *
     288             :      * This is not actually necessary from a correctness point of view. We do
     289             :      * it merely as a debugging aid.
     290             :      */
     291             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(false);
     292           3 :     SimpleLruFlush(SubTransCtl, false);
     293             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(false);
     294           3 : }
     295             : 
     296             : /*
     297             :  * Perform a checkpoint --- either during shutdown, or on-the-fly
     298             :  */
     299             : void
     300          11 : CheckPointSUBTRANS(void)
     301             : {
     302             :     /*
     303             :      * Flush dirty SUBTRANS pages to disk
     304             :      *
     305             :      * This is not actually necessary from a correctness point of view. We do
     306             :      * it merely to improve the odds that writing of dirty pages is done by
     307             :      * the checkpoint process and not by backends.
     308             :      */
     309             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
     310          11 :     SimpleLruFlush(SubTransCtl, true);
     311             :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
     312          11 : }
     313             : 
     314             : 
     315             : /*
     316             :  * Make sure that SUBTRANS has room for a newly-allocated XID.
     317             :  *
     318             :  * NB: this is called while holding XidGenLock.  We want it to be very fast
     319             :  * most of the time; even when it's not so fast, no actual I/O need happen
     320             :  * unless we're forced to write out a dirty subtrans page to make room
     321             :  * in shared memory.
     322             :  */
     323             : void
     324       10625 : ExtendSUBTRANS(TransactionId newestXact)
     325             : {
     326             :     int         pageno;
     327             : 
     328             :     /*
     329             :      * No work except at first XID of a page.  But beware: just after
     330             :      * wraparound, the first XID of page zero is FirstNormalTransactionId.
     331             :      */
     332       10625 :     if (TransactionIdToEntry(newestXact) != 0 &&
     333             :         !TransactionIdEquals(newestXact, FirstNormalTransactionId))
     334       21244 :         return;
     335             : 
     336           6 :     pageno = TransactionIdToPage(newestXact);
     337             : 
     338           6 :     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
     339             : 
     340             :     /* Zero the page */
     341           6 :     ZeroSUBTRANSPage(pageno);
     342             : 
     343           6 :     LWLockRelease(SubtransControlLock);
     344             : }
     345             : 
     346             : 
     347             : /*
     348             :  * Remove all SUBTRANS segments before the one holding the passed transaction ID
     349             :  *
     350             :  * This is normally called during checkpoint, with oldestXact being the
     351             :  * oldest TransactionXmin of any running transaction.
     352             :  */
     353             : void
     354          14 : TruncateSUBTRANS(TransactionId oldestXact)
     355             : {
     356             :     int         cutoffPage;
     357             : 
     358             :     /*
     359             :      * The cutoff point is the start of the segment containing oldestXact. We
     360             :      * pass the *page* containing oldestXact to SimpleLruTruncate.  We step
     361             :      * back one transaction to avoid passing a cutoff page that hasn't been
     362             :      * created yet in the rare case that oldestXact would be the first item on
     363             :      * a page and oldestXact == next XID.  In that case, if we didn't subtract
     364             :      * one, we'd trigger SimpleLruTruncate's wraparound detection.
     365             :      */
     366          14 :     TransactionIdRetreat(oldestXact);
     367          11 :     cutoffPage = TransactionIdToPage(oldestXact);
     368             : 
     369          11 :     SimpleLruTruncate(SubTransCtl, cutoffPage);
     370          11 : }
     371             : 
     372             : 
     373             : /*
     374             :  * Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
     375             :  *
     376             :  * We need to use comparison of TransactionIds here in order to do the right
     377             :  * thing with wraparound XID arithmetic.  However, if we are asked about
     378             :  * page number zero, we don't want to hand InvalidTransactionId to
     379             :  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
     380             :  * offset both xids by FirstNormalTransactionId to avoid that.
     381             :  */
     382             : static bool
     383          38 : SubTransPagePrecedes(int page1, int page2)
     384             : {
     385             :     TransactionId xid1;
     386             :     TransactionId xid2;
     387             : 
     388          38 :     xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
     389          38 :     xid1 += FirstNormalTransactionId;
     390          38 :     xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
     391          38 :     xid2 += FirstNormalTransactionId;
     392             : 
     393          38 :     return TransactionIdPrecedes(xid1, xid2);
     394             : }

Generated by: LCOV version 1.11