Header And Logo

PostgreSQL
| The world's most advanced open source database.

subtrans.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * subtrans.c
00004  *      PostgreSQL subtransaction-log manager
00005  *
00006  * The pg_subtrans manager is a pg_clog-like manager that stores the parent
00007  * transaction Id for each transaction.  It is a fundamental part of the
00008  * nested transactions implementation.  A main transaction has a parent
00009  * of InvalidTransactionId, and each subtransaction has its immediate parent.
00010  * The tree can easily be walked from child to parent, but not in the
00011  * opposite direction.
00012  *
00013  * This code is based on clog.c, but the robustness requirements
00014  * are completely different from pg_clog, because we only need to remember
00015  * pg_subtrans information for currently-open transactions.  Thus, there is
00016  * no need to preserve data over a crash and restart.
00017  *
00018  * There are no XLOG interactions since we do not care about preserving
00019  * data across crashes.  During database startup, we simply force the
00020  * currently-active page of SUBTRANS to zeroes.
00021  *
00022  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00023  * Portions Copyright (c) 1994, Regents of the University of California
00024  *
00025  * src/backend/access/transam/subtrans.c
00026  *
00027  *-------------------------------------------------------------------------
00028  */
00029 #include "postgres.h"
00030 
00031 #include "access/slru.h"
00032 #include "access/subtrans.h"
00033 #include "access/transam.h"
00034 #include "pg_trace.h"
00035 #include "utils/snapmgr.h"
00036 
00037 
00038 /*
00039  * Defines for SubTrans page sizes.  A page is the same BLCKSZ as is used
00040  * everywhere else in Postgres.
00041  *
00042  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
00043  * SubTrans page numbering also wraps around at
00044  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
00045  * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_SEGMENTS_PER_PAGE.  We need take no
00046  * explicit notice of that fact in this module, except when comparing segment
00047  * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes).
00048  */
00049 
00050 /* We need four bytes per xact */
00051 #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
00052 
00053 #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
00054 #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
00055 
00056 
00057 /*
00058  * Link to shared-memory data structures for SUBTRANS control
00059  */
00060 static SlruCtlData SubTransCtlData;
00061 
00062 #define SubTransCtl  (&SubTransCtlData)
00063 
00064 
00065 static int  ZeroSUBTRANSPage(int pageno);
00066 static bool SubTransPagePrecedes(int page1, int page2);
00067 
00068 
00069 /*
00070  * Record the parent of a subtransaction in the subtrans log.
00071  *
00072  * In some cases we may need to overwrite an existing value.
00073  */
00074 void
00075 SubTransSetParent(TransactionId xid, TransactionId parent, bool overwriteOK)
00076 {
00077     int         pageno = TransactionIdToPage(xid);
00078     int         entryno = TransactionIdToEntry(xid);
00079     int         slotno;
00080     TransactionId *ptr;
00081 
00082     Assert(TransactionIdIsValid(parent));
00083 
00084     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
00085 
00086     slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
00087     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
00088     ptr += entryno;
00089 
00090     /* Current state should be 0 */
00091     Assert(*ptr == InvalidTransactionId ||
00092            (*ptr == parent && overwriteOK));
00093 
00094     *ptr = parent;
00095 
00096     SubTransCtl->shared->page_dirty[slotno] = true;
00097 
00098     LWLockRelease(SubtransControlLock);
00099 }
00100 
00101 /*
00102  * Interrogate the parent of a transaction in the subtrans log.
00103  */
00104 TransactionId
00105 SubTransGetParent(TransactionId xid)
00106 {
00107     int         pageno = TransactionIdToPage(xid);
00108     int         entryno = TransactionIdToEntry(xid);
00109     int         slotno;
00110     TransactionId *ptr;
00111     TransactionId parent;
00112 
00113     /* Can't ask about stuff that might not be around anymore */
00114     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
00115 
00116     /* Bootstrap and frozen XIDs have no parent */
00117     if (!TransactionIdIsNormal(xid))
00118         return InvalidTransactionId;
00119 
00120     /* lock is acquired by SimpleLruReadPage_ReadOnly */
00121 
00122     slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
00123     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
00124     ptr += entryno;
00125 
00126     parent = *ptr;
00127 
00128     LWLockRelease(SubtransControlLock);
00129 
00130     return parent;
00131 }
00132 
00133 /*
00134  * SubTransGetTopmostTransaction
00135  *
00136  * Returns the topmost transaction of the given transaction id.
00137  *
00138  * Because we cannot look back further than TransactionXmin, it is possible
00139  * that this function will lie and return an intermediate subtransaction ID
00140  * instead of the true topmost parent ID.  This is OK, because in practice
00141  * we only care about detecting whether the topmost parent is still running
00142  * or is part of a current snapshot's list of still-running transactions.
00143  * Therefore, any XID before TransactionXmin is as good as any other.
00144  */
00145 TransactionId
00146 SubTransGetTopmostTransaction(TransactionId xid)
00147 {
00148     TransactionId parentXid = xid,
00149                 previousXid = xid;
00150 
00151     /* Can't ask about stuff that might not be around anymore */
00152     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
00153 
00154     while (TransactionIdIsValid(parentXid))
00155     {
00156         previousXid = parentXid;
00157         if (TransactionIdPrecedes(parentXid, TransactionXmin))
00158             break;
00159         parentXid = SubTransGetParent(parentXid);
00160     }
00161 
00162     Assert(TransactionIdIsValid(previousXid));
00163 
00164     return previousXid;
00165 }
00166 
00167 
00168 /*
00169  * Initialization of shared memory for SUBTRANS
00170  */
00171 Size
00172 SUBTRANSShmemSize(void)
00173 {
00174     return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
00175 }
00176 
00177 void
00178 SUBTRANSShmemInit(void)
00179 {
00180     SubTransCtl->PagePrecedes = SubTransPagePrecedes;
00181     SimpleLruInit(SubTransCtl, "SUBTRANS Ctl", NUM_SUBTRANS_BUFFERS, 0,
00182                   SubtransControlLock, "pg_subtrans");
00183     /* Override default assumption that writes should be fsync'd */
00184     SubTransCtl->do_fsync = false;
00185 }
00186 
00187 /*
00188  * This func must be called ONCE on system install.  It creates
00189  * the initial SUBTRANS segment.  (The SUBTRANS directory is assumed to
00190  * have been created by the initdb shell script, and SUBTRANSShmemInit
00191  * must have been called already.)
00192  *
00193  * Note: it's not really necessary to create the initial segment now,
00194  * since slru.c would create it on first write anyway.  But we may as well
00195  * do it to be sure the directory is set up correctly.
00196  */
00197 void
00198 BootStrapSUBTRANS(void)
00199 {
00200     int         slotno;
00201 
00202     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
00203 
00204     /* Create and zero the first page of the subtrans log */
00205     slotno = ZeroSUBTRANSPage(0);
00206 
00207     /* Make sure it's written out */
00208     SimpleLruWritePage(SubTransCtl, slotno);
00209     Assert(!SubTransCtl->shared->page_dirty[slotno]);
00210 
00211     LWLockRelease(SubtransControlLock);
00212 }
00213 
00214 /*
00215  * Initialize (or reinitialize) a page of SUBTRANS to zeroes.
00216  *
00217  * The page is not actually written, just set up in shared memory.
00218  * The slot number of the new page is returned.
00219  *
00220  * Control lock must be held at entry, and will be held at exit.
00221  */
00222 static int
00223 ZeroSUBTRANSPage(int pageno)
00224 {
00225     return SimpleLruZeroPage(SubTransCtl, pageno);
00226 }
00227 
00228 /*
00229  * This must be called ONCE during postmaster or standalone-backend startup,
00230  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
00231  *
00232  * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
00233  * if there are none.
00234  */
00235 void
00236 StartupSUBTRANS(TransactionId oldestActiveXID)
00237 {
00238     int         startPage;
00239     int         endPage;
00240 
00241     /*
00242      * Since we don't expect pg_subtrans to be valid across crashes, we
00243      * initialize the currently-active page(s) to zeroes during startup.
00244      * Whenever we advance into a new page, ExtendSUBTRANS will likewise zero
00245      * the new page without regard to whatever was previously on disk.
00246      */
00247     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
00248 
00249     startPage = TransactionIdToPage(oldestActiveXID);
00250     endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
00251 
00252     while (startPage != endPage)
00253     {
00254         (void) ZeroSUBTRANSPage(startPage);
00255         startPage++;
00256     }
00257     (void) ZeroSUBTRANSPage(startPage);
00258 
00259     LWLockRelease(SubtransControlLock);
00260 }
00261 
00262 /*
00263  * This must be called ONCE during postmaster or standalone-backend shutdown
00264  */
00265 void
00266 ShutdownSUBTRANS(void)
00267 {
00268     /*
00269      * Flush dirty SUBTRANS pages to disk
00270      *
00271      * This is not actually necessary from a correctness point of view. We do
00272      * it merely as a debugging aid.
00273      */
00274     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(false);
00275     SimpleLruFlush(SubTransCtl, false);
00276     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(false);
00277 }
00278 
00279 /*
00280  * Perform a checkpoint --- either during shutdown, or on-the-fly
00281  */
00282 void
00283 CheckPointSUBTRANS(void)
00284 {
00285     /*
00286      * Flush dirty SUBTRANS pages to disk
00287      *
00288      * This is not actually necessary from a correctness point of view. We do
00289      * it merely to improve the odds that writing of dirty pages is done by
00290      * the checkpoint process and not by backends.
00291      */
00292     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_START(true);
00293     SimpleLruFlush(SubTransCtl, true);
00294     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
00295 }
00296 
00297 
00298 /*
00299  * Make sure that SUBTRANS has room for a newly-allocated XID.
00300  *
00301  * NB: this is called while holding XidGenLock.  We want it to be very fast
00302  * most of the time; even when it's not so fast, no actual I/O need happen
00303  * unless we're forced to write out a dirty subtrans page to make room
00304  * in shared memory.
00305  */
00306 void
00307 ExtendSUBTRANS(TransactionId newestXact)
00308 {
00309     int         pageno;
00310 
00311     /*
00312      * No work except at first XID of a page.  But beware: just after
00313      * wraparound, the first XID of page zero is FirstNormalTransactionId.
00314      */
00315     if (TransactionIdToEntry(newestXact) != 0 &&
00316         !TransactionIdEquals(newestXact, FirstNormalTransactionId))
00317         return;
00318 
00319     pageno = TransactionIdToPage(newestXact);
00320 
00321     LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
00322 
00323     /* Zero the page */
00324     ZeroSUBTRANSPage(pageno);
00325 
00326     LWLockRelease(SubtransControlLock);
00327 }
00328 
00329 
00330 /*
00331  * Remove all SUBTRANS segments before the one holding the passed transaction ID
00332  *
00333  * This is normally called during checkpoint, with oldestXact being the
00334  * oldest TransactionXmin of any running transaction.
00335  */
00336 void
00337 TruncateSUBTRANS(TransactionId oldestXact)
00338 {
00339     int         cutoffPage;
00340 
00341     /*
00342      * The cutoff point is the start of the segment containing oldestXact. We
00343      * pass the *page* containing oldestXact to SimpleLruTruncate.
00344      */
00345     cutoffPage = TransactionIdToPage(oldestXact);
00346 
00347     SimpleLruTruncate(SubTransCtl, cutoffPage);
00348 }
00349 
00350 
00351 /*
00352  * Decide which of two SUBTRANS page numbers is "older" for truncation purposes.
00353  *
00354  * We need to use comparison of TransactionIds here in order to do the right
00355  * thing with wraparound XID arithmetic.  However, if we are asked about
00356  * page number zero, we don't want to hand InvalidTransactionId to
00357  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
00358  * offset both xids by FirstNormalTransactionId to avoid that.
00359  */
00360 static bool
00361 SubTransPagePrecedes(int page1, int page2)
00362 {
00363     TransactionId xid1;
00364     TransactionId xid2;
00365 
00366     xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
00367     xid1 += FirstNormalTransactionId;
00368     xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
00369     xid2 += FirstNormalTransactionId;
00370 
00371     return TransactionIdPrecedes(xid1, xid2);
00372 }