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 17:13:01 Functions: 100.0 % 12 12 12
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 92.4 % 92 85 7 85
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 100.0 % 12 12 12

 Age         Owner                  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
 2173 simon                      74 CBC        5103 : SubTransSetParent(TransactionId xid, TransactionId parent)
                                 75                 : {
 6856 tgl                        76            5103 :     int         pageno = TransactionIdToPage(xid);
                                 77            5103 :     int         entryno = TransactionIdToEntry(xid);
                                 78                 :     int         slotno;
                                 79                 :     TransactionId *ptr;
                                 80                 : 
 4859 simon                      81            5103 :     Assert(TransactionIdIsValid(parent));
 2173                            82            5103 :     Assert(TransactionIdFollows(xid, parent));
                                 83                 : 
 1059 tgl                        84            5103 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
                                 85                 : 
 5730                            86            5103 :     slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
 6803                            87            5103 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
 6856                            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                 :      */
 2173 simon                      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                 : 
 1059 tgl                       102            5103 :     LWLockRelease(SubtransSLRULock);
 6856                           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 */
 6779                           118            3079 :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
                                119                 : 
                                120                 :     /* Bootstrap and frozen XIDs have no parent */
 6856                           121            3079 :     if (!TransactionIdIsNormal(xid))
 6856 tgl                       122 UBC           0 :         return InvalidTransactionId;
                                123                 : 
                                124                 :     /* lock is acquired by SimpleLruReadPage_ReadOnly */
                                125                 : 
 6333 tgl                       126 CBC        3079 :     slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
 6803                           127            3079 :     ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
 6856                           128            3079 :     ptr += entryno;
                                129                 : 
                                130            3079 :     parent = *ptr;
                                131                 : 
 1059                           132            3079 :     LWLockRelease(SubtransSLRULock);
                                133                 : 
 6856                           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,
 6797 bruce                     153            1076 :                 previousXid = xid;
                                154                 : 
                                155                 :     /* Can't ask about stuff that might not be around anymore */
 6779 tgl                       156            1076 :     Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
                                157                 : 
 6856                           158            4155 :     while (TransactionIdIsValid(parentXid))
                                159                 :     {
                                160            3079 :         previousXid = parentXid;
 6779                           161            3079 :         if (TransactionIdPrecedes(parentXid, TransactionXmin))
 6804 tgl                       162 UBC           0 :             break;
 6856 tgl                       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                 :          */
 2173 simon                     170            3079 :         if (!TransactionIdPrecedes(parentXid, previousXid))
 2173 simon                     171 UBC           0 :             elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
                                172                 :                  previousXid, parentXid);
                                173                 :     }
                                174                 : 
 6856 tgl                       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                 : {
 5730                           187            2738 :     return SimpleLruShmemSize(NUM_SUBTRANS_BUFFERS, 0);
                                188                 : }
                                189                 : 
                                190                 : void
 6856                           191            1826 : SUBTRANSShmemInit(void)
                                192                 : {
                                193            1826 :     SubTransCtl->PagePrecedes = SubTransPagePrecedes;
 1059                           194            1826 :     SimpleLruInit(SubTransCtl, "Subtrans", NUM_SUBTRANS_BUFFERS, 0,
                                195            1826 :                   SubtransSLRULock, "pg_subtrans",
                                196                 :                   LWTRANCHE_SUBTRANS_BUFFER, SYNC_HANDLER_NONE);
  813 noah                      197            1826 :     SlruPagePrecedesUnitTests(SubTransCtl, SUBTRANS_XACTS_PER_PAGE);
 6856 tgl                       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                 : 
 1059                           215             305 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
                                216                 : 
                                217                 :     /* Create and zero the first page of the subtrans log */
 6803                           218             305 :     slotno = ZeroSUBTRANSPage(0);
                                219                 : 
                                220                 :     /* Make sure it's written out */
 4483 alvherre                  221             305 :     SimpleLruWritePage(SubTransCtl, slotno);
 6364 tgl                       222             305 :     Assert(!SubTransCtl->shared->page_dirty[slotno]);
                                223                 : 
 1059                           224             305 :     LWLockRelease(SubtransSLRULock);
 6856                           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
 6803                           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
 6505                           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                 :      */
 1059                           261            1174 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
                                262                 : 
 6505                           263            1174 :     startPage = TransactionIdToPage(oldestActiveXID);
  971 andres                    264            1174 :     nextXid = ShmemVariableCache->nextXid;
                                265            1174 :     endPage = TransactionIdToPage(XidFromFullTransactionId(nextXid));
                                266                 : 
 6505 tgl                       267            1174 :     while (startPage != endPage)
                                268                 :     {
 6505 tgl                       269 UBC           0 :         (void) ZeroSUBTRANSPage(startPage);
                                270               0 :         startPage++;
                                271                 :         /* must account for wraparound */
 2606 simon                     272               0 :         if (startPage > TransactionIdToPage(MaxTransactionId))
 2495 rhaas                     273               0 :             startPage = 0;
                                274                 :     }
 6803 tgl                       275 CBC        1174 :     (void) ZeroSUBTRANSPage(startPage);
                                276                 : 
 1059                           277            1174 :     LWLockRelease(SubtransSLRULock);
 6856                           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);
  926 tmunro                    294            2363 :     SimpleLruWriteAll(SubTransCtl, true);
                                295                 :     TRACE_POSTGRESQL_SUBTRANS_CHECKPOINT_DONE(true);
 6856 tgl                       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                 : 
 1059                           322             343 :     LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
                                323                 : 
                                324                 :     /* Zero the page */
 6803                           325             343 :     ZeroSUBTRANSPage(pageno);
                                326                 : 
 1059                           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
 6856                           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                 :      */
 2817 heikki.linnakangas        350            3257 :     TransactionIdRetreat(oldestXact);
 6856 tgl                       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;
  813 noah                      368           78322 :     xid1 += FirstNormalTransactionId + 1;
 6856 tgl                       369           78322 :     xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
  813 noah                      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