LCOV - differential code coverage report
Current view: top level - src/backend/access/transam - xlogwait.c (source / functions) Coverage Total Hit UBC CBC
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 98.1 % 155 152 3 152
Current Date: 2026-08-27 14:31:44 +0300 Functions: 100.0 % 14 14 14
Baseline: lcov-20260827-baseline Branches: 74.3 % 105 78 27 78
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 17 17 17
(30,360] days: 97.8 % 138 135 3 135
Function coverage date bins:
(7,30] days: 100.0 % 2 2 2
(30,360] days: 100.0 % 12 12 12
Branch coverage date bins:
(7,30] days: 100.0 % 8 8 8
(30,360] days: 72.2 % 97 70 27 70

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * xlogwait.c
                                  4                 :                :  *    Implements waiting for WAL operations to reach specific LSNs.
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2025-2026, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  * IDENTIFICATION
                                  9                 :                :  *    src/backend/access/transam/xlogwait.c
                                 10                 :                :  *
                                 11                 :                :  * NOTES
                                 12                 :                :  *      This file implements waiting for WAL operations to reach specific LSNs
                                 13                 :                :  *      on both physical standby and primary servers. The core idea is simple:
                                 14                 :                :  *      every process that wants to wait publishes the LSN it needs to the
                                 15                 :                :  *      shared memory, and the appropriate process (startup on standby,
                                 16                 :                :  *      walreceiver on standby, or WAL writer/backend on primary) wakes it
                                 17                 :                :  *      once that LSN has been reached.
                                 18                 :                :  *
                                 19                 :                :  *      The shared memory used by this module comprises a procInfos
                                 20                 :                :  *      per-backend array with the information of the awaited LSN for each
                                 21                 :                :  *      of the backend processes.  The elements of that array are organized
                                 22                 :                :  *      into pairing heaps (waitersHeap), one for each WaitLSNType, which
                                 23                 :                :  *      allows for very fast finding of the least awaited LSN for each type.
                                 24                 :                :  *
                                 25                 :                :  *      In addition, the least-awaited LSN for each type is cached in the
                                 26                 :                :  *      minWaitedLSN array.  The waiter process publishes information about
                                 27                 :                :  *      itself to the shared memory and waits on the latch until it is woken
                                 28                 :                :  *      up by the appropriate process, standby is promoted, or the postmaster
                                 29                 :                :  *      dies.  Then, it cleans information about itself in the shared memory.
                                 30                 :                :  *
                                 31                 :                :  *      On standby servers:
                                 32                 :                :  *      - After replaying a WAL record, the startup process performs a fast
                                 33                 :                :  *        path check minWaitedLSN[REPLAY] > replayLSN.  If this check is
                                 34                 :                :  *        negative, it checks waitersHeap[REPLAY] and wakes up the backends
                                 35                 :                :  *        whose awaited LSNs are reached.
                                 36                 :                :  *      - After receiving WAL, the walreceiver process performs similar checks
                                 37                 :                :  *        against the flush and write LSNs, waking up waiters in the FLUSH
                                 38                 :                :  *        and WRITE heaps, respectively.
                                 39                 :                :  *
                                 40                 :                :  *      On primary servers: After flushing WAL, the WAL writer or backend
                                 41                 :                :  *      process performs a similar check against the flush LSN and wakes up
                                 42                 :                :  *      waiters whose target flush LSNs have been reached.
                                 43                 :                :  *
                                 44                 :                :  *-------------------------------------------------------------------------
                                 45                 :                :  */
                                 46                 :                : 
                                 47                 :                : #include "postgres.h"
                                 48                 :                : 
                                 49                 :                : #include <float.h>
                                 50                 :                : 
                                 51                 :                : #include "access/xlog.h"
                                 52                 :                : #include "access/xlogrecovery.h"
                                 53                 :                : #include "access/xlogwait.h"
                                 54                 :                : #include "miscadmin.h"
                                 55                 :                : #include "pgstat.h"
                                 56                 :                : #include "replication/walreceiver.h"
                                 57                 :                : #include "storage/ipc.h"
                                 58                 :                : #include "storage/latch.h"
                                 59                 :                : #include "storage/proc.h"
                                 60                 :                : #include "storage/shmem.h"
                                 61                 :                : #include "storage/subsystems.h"
                                 62                 :                : #include "utils/fmgrprotos.h"
                                 63                 :                : #include "utils/pg_lsn.h"
                                 64                 :                : #include "utils/snapmgr.h"
                                 65                 :                : #include "utils/wait_event.h"
                                 66                 :                : 
                                 67                 :                : 
                                 68                 :                : static int  waitlsn_cmp(const pairingheap_node *a, const pairingheap_node *b,
                                 69                 :                :                         void *arg);
                                 70                 :                : 
                                 71                 :                : struct WaitLSNState *waitLSNState = NULL;
                                 72                 :                : 
                                 73                 :                : static bool waitLSNShmemExitRegistered = false;
                                 74                 :                : 
                                 75                 :                : static void WaitLSNShmemRequest(void *arg);
                                 76                 :                : static void WaitLSNShmemInit(void *arg);
                                 77                 :                : static void WaitLSNShmemExit(int code, Datum arg);
                                 78                 :                : static void RegisterWaitLSNShmemExit(void);
                                 79                 :                : 
                                 80                 :                : const ShmemCallbacks WaitLSNShmemCallbacks = {
                                 81                 :                :     .request_fn = WaitLSNShmemRequest,
                                 82                 :                :     .init_fn = WaitLSNShmemInit,
                                 83                 :                : };
                                 84                 :                : 
                                 85                 :                : /*
                                 86                 :                :  * Wait event for each WaitLSNType, used with WaitLatch() to report
                                 87                 :                :  * the wait in pg_stat_activity.
                                 88                 :                :  */
                                 89                 :                : static const uint32 WaitLSNWaitEvents[] = {
                                 90                 :                :     [WAIT_LSN_TYPE_STANDBY_REPLAY] = WAIT_EVENT_WAIT_FOR_WAL_REPLAY,
                                 91                 :                :     [WAIT_LSN_TYPE_STANDBY_WRITE] = WAIT_EVENT_WAIT_FOR_WAL_WRITE,
                                 92                 :                :     [WAIT_LSN_TYPE_STANDBY_FLUSH] = WAIT_EVENT_WAIT_FOR_WAL_FLUSH,
                                 93                 :                :     [WAIT_LSN_TYPE_PRIMARY_FLUSH] = WAIT_EVENT_WAIT_FOR_WAL_FLUSH,
                                 94                 :                : };
                                 95                 :                : 
                                 96                 :                : StaticAssertDecl(lengthof(WaitLSNWaitEvents) == WAIT_LSN_TYPE_COUNT,
                                 97                 :                :                  "WaitLSNWaitEvents must match WaitLSNType enum");
                                 98                 :                : 
                                 99                 :                : /*
                                100                 :                :  * Get the current LSN for the specified wait type.  Provide memory
                                101                 :                :  * barrier semantics before getting the value.
                                102                 :                :  */
                                103                 :                : XLogRecPtr
  234 akorotkov@postgresql      104                 :CBC         847 : GetCurrentLSNForWaitType(WaitLSNType lsnType)
                                105                 :                : {
                                106         [ -  + ]:            847 :     Assert(lsnType >= 0 && lsnType < WAIT_LSN_TYPE_COUNT);
                                107                 :                : 
                                108                 :                :     /*
                                109                 :                :      * All of the cases below provide memory barrier semantics:
                                110                 :                :      * GetWalRcvWriteRecPtr() and GetFlushRecPtr() have explicit barriers,
                                111                 :                :      * while GetXLogReplayRecPtr() and GetWalRcvFlushRecPtr() use spinlocks.
                                112                 :                :      */
                                113   [ +  +  +  +  :            847 :     switch (lsnType)
                                                 - ]
                                114                 :                :     {
                                115                 :            232 :         case WAIT_LSN_TYPE_STANDBY_REPLAY:
                                116                 :            232 :             return GetXLogReplayRecPtr(NULL);
                                117                 :                : 
                                118                 :             52 :         case WAIT_LSN_TYPE_STANDBY_WRITE:
                                119                 :                :             {
  116                           120                 :             52 :                 XLogRecPtr  recptr = GetWalRcvWriteRecPtr();
                                121                 :             52 :                 XLogRecPtr  replay = GetXLogReplayRecPtr(NULL);
                                122                 :                : 
                                123                 :                :                 /*
                                124                 :                :                  * Use the replay position as a floor.  WAL up to the replay
                                125                 :                :                  * point is already on disk from a base backup, archive
                                126                 :                :                  * restore, or prior streaming, so there is no reason to wait
                                127                 :                :                  * for the walreceiver to re-receive it.
                                128                 :                :                  */
                                129                 :             52 :                 return Max(recptr, replay);
                                130                 :                :             }
                                131                 :                : 
  234                           132                 :             37 :         case WAIT_LSN_TYPE_STANDBY_FLUSH:
                                133                 :                :             {
  116                           134                 :             37 :                 XLogRecPtr  recptr = GetWalRcvFlushRecPtr(NULL, NULL);
                                135                 :             37 :                 XLogRecPtr  replay = GetXLogReplayRecPtr(NULL);
                                136                 :                : 
                                137                 :                :                 /* Same floor as standby_write; see comment above. */
                                138                 :             37 :                 return Max(recptr, replay);
                                139                 :                :             }
                                140                 :                : 
  234                           141                 :            526 :         case WAIT_LSN_TYPE_PRIMARY_FLUSH:
                                142                 :            526 :             return GetFlushRecPtr(NULL);
                                143                 :                :     }
                                144                 :                : 
  234 akorotkov@postgresql      145         [ #  # ]:UBC           0 :     elog(ERROR, "invalid LSN wait type: %d", lsnType);
                                146                 :                :     pg_unreachable();
                                147                 :                : }
                                148                 :                : 
                                149                 :                : /* Register the shared memory space needed for WaitLSNState. */
                                150                 :                : static void
  143 heikki.linnakangas@i      151                 :CBC        1239 : WaitLSNShmemRequest(void *arg)
                                152                 :                : {
                                153                 :                :     Size        size;
                                154                 :                : 
  297 akorotkov@postgresql      155                 :           1239 :     size = offsetof(WaitLSNState, procInfos);
                                156                 :           1239 :     size = add_size(size, mul_size(MaxBackends + NUM_AUXILIARY_PROCS, sizeof(WaitLSNProcInfo)));
  143 heikki.linnakangas@i      157                 :           1239 :     ShmemRequestStruct(.name = "WaitLSNState",
                                158                 :                :                        .size = size,
                                159                 :                :                        .ptr = (void **) &waitLSNState,
                                160                 :                :         );
  297 akorotkov@postgresql      161                 :           1239 : }
                                162                 :                : 
                                163                 :                : /* Initialize the WaitLSNState in the shared memory. */
                                164                 :                : static void
  143 heikki.linnakangas@i      165                 :           1236 : WaitLSNShmemInit(void *arg)
                                166                 :                : {
                                167                 :                :     /* Initialize heaps and tracking */
                                168         [ +  + ]:           6180 :     for (int i = 0; i < WAIT_LSN_TYPE_COUNT; i++)
                                169                 :                :     {
                                170                 :           4944 :         pg_atomic_init_u64(&waitLSNState->minWaitedLSN[i], PG_UINT64_MAX);
                                171                 :           4944 :         pairingheap_initialize(&waitLSNState->waitersHeap[i], waitlsn_cmp, NULL);
                                172                 :                :     }
                                173                 :                : 
                                174                 :                :     /* Initialize process info array */
                                175                 :           1236 :     memset(&waitLSNState->procInfos, 0,
                                176                 :           1236 :            (MaxBackends + NUM_AUXILIARY_PROCS) * sizeof(WaitLSNProcInfo));
  297 akorotkov@postgresql      177                 :           1236 : }
                                178                 :                : 
                                179                 :                : /*
                                180                 :                :  * Comparison function for LSN waiters heaps. Waiting processes are ordered by
                                181                 :                :  * LSN, so that the waiter with smallest LSN is at the top.
                                182                 :                :  */
                                183                 :                : static int
                                184                 :             28 : waitlsn_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
                                185                 :                : {
  282                           186                 :             28 :     const WaitLSNProcInfo *aproc = pairingheap_const_container(WaitLSNProcInfo, heapNode, a);
                                187                 :             28 :     const WaitLSNProcInfo *bproc = pairingheap_const_container(WaitLSNProcInfo, heapNode, b);
                                188                 :                : 
  297                           189         [ +  + ]:             28 :     if (aproc->waitLSN < bproc->waitLSN)
                                190                 :             15 :         return 1;
                                191         [ +  + ]:             13 :     else if (aproc->waitLSN > bproc->waitLSN)
                                192                 :             10 :         return -1;
                                193                 :                :     else
                                194                 :              3 :         return 0;
                                195                 :                : }
                                196                 :                : 
                                197                 :                : /*
                                198                 :                :  * Update minimum waited LSN for the specified LSN type
                                199                 :                :  */
                                200                 :                : static void
                                201                 :           4466 : updateMinWaitedLSN(WaitLSNType lsnType)
                                202                 :                : {
                                203                 :           4466 :     XLogRecPtr  minWaitedLSN = PG_UINT64_MAX;
                                204                 :           4466 :     int         i = (int) lsnType;
                                205                 :                : 
  256                           206   [ +  -  -  + ]:           4466 :     Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
                                207                 :                : 
  297                           208         [ +  + ]:           4466 :     if (!pairingheap_is_empty(&waitLSNState->waitersHeap[i]))
                                209                 :                :     {
                                210                 :            745 :         pairingheap_node *node = pairingheap_first(&waitLSNState->waitersHeap[i]);
  282                           211                 :            745 :         WaitLSNProcInfo *procInfo = pairingheap_container(WaitLSNProcInfo, heapNode, node);
                                212                 :                : 
  297                           213                 :            745 :         minWaitedLSN = procInfo->waitLSN;
                                214                 :                :     }
                                215                 :                :     /* Pairs with pg_atomic_read_membarrier_u64() in WaitLSNWakeup(). */
  116                           216                 :           4466 :     pg_atomic_write_membarrier_u64(&waitLSNState->minWaitedLSN[i], minWaitedLSN);
  297                           217                 :           4466 : }
                                218                 :                : 
                                219                 :                : /*
                                220                 :                :  * Add current process to appropriate waiters heap based on LSN type
                                221                 :                :  */
                                222                 :                : static void
                                223                 :            740 : addLSNWaiter(XLogRecPtr lsn, WaitLSNType lsnType)
                                224                 :                : {
                                225                 :            740 :     WaitLSNProcInfo *procInfo = &waitLSNState->procInfos[MyProcNumber];
                                226                 :            740 :     int         i = (int) lsnType;
                                227                 :                : 
  256                           228   [ +  -  -  + ]:            740 :     Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
                                229                 :                : 
  297                           230                 :            740 :     LWLockAcquire(WaitLSNLock, LW_EXCLUSIVE);
                                231                 :                : 
                                232                 :            740 :     procInfo->procno = MyProcNumber;
                                233                 :            740 :     procInfo->waitLSN = lsn;
  282                           234                 :            740 :     procInfo->lsnType = lsnType;
                                235                 :                : 
                                236         [ -  + ]:            740 :     Assert(!procInfo->inHeap);
                                237                 :            740 :     pairingheap_add(&waitLSNState->waitersHeap[i], &procInfo->heapNode);
                                238                 :            740 :     procInfo->inHeap = true;
  297                           239                 :            740 :     updateMinWaitedLSN(lsnType);
                                240                 :                : 
                                241                 :            740 :     LWLockRelease(WaitLSNLock);
                                242                 :            740 : }
                                243                 :                : 
                                244                 :                : /*
                                245                 :                :  * Remove current process from appropriate waiters heap based on LSN type
                                246                 :                :  */
                                247                 :                : static void
                                248                 :          42260 : deleteLSNWaiter(WaitLSNType lsnType)
                                249                 :                : {
                                250                 :          42260 :     WaitLSNProcInfo *procInfo = &waitLSNState->procInfos[MyProcNumber];
                                251                 :          42260 :     int         i = (int) lsnType;
                                252                 :                : 
  256                           253   [ +  -  -  + ]:          42260 :     Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
                                254                 :                : 
                                255                 :                :     /*
                                256                 :                :      * Avoid taking WaitLSNLock if a waker has already removed us.  Only this
                                257                 :                :      * backend can set inHeap; other processes can only clear it.  Therefore
                                258                 :                :      * false is conclusive, while a stale true is harmless because it is
                                259                 :                :      * rechecked under WaitLSNLock below.
                                260                 :                :      */
    9                           261         [ +  + ]:          42260 :     if (!procInfo->inHeap)
                                262                 :          41578 :         return;
                                263                 :                : 
  297                           264                 :            682 :     LWLockAcquire(WaitLSNLock, LW_EXCLUSIVE);
                                265                 :                : 
  282                           266         [ -  + ]:            682 :     Assert(procInfo->lsnType == lsnType);
                                267                 :                : 
                                268         [ +  - ]:            682 :     if (procInfo->inHeap)
                                269                 :                :     {
                                270                 :            682 :         pairingheap_remove(&waitLSNState->waitersHeap[i], &procInfo->heapNode);
                                271                 :            682 :         procInfo->inHeap = false;
  297                           272                 :            682 :         updateMinWaitedLSN(lsnType);
                                273                 :                :     }
                                274                 :                : 
                                275                 :            682 :     LWLockRelease(WaitLSNLock);
                                276                 :                : }
                                277                 :                : 
                                278                 :                : /*
                                279                 :                :  * Size of a static array of procs to wakeup by WaitLSNWakeup() allocated
                                280                 :                :  * on the stack.  It should be enough to take single iteration for most cases.
                                281                 :                :  */
                                282                 :                : #define WAKEUP_PROC_STATIC_ARRAY_SIZE (16)
                                283                 :                : 
                                284                 :                : /*
                                285                 :                :  * Remove waiters whose LSN has been reached from the heap and set their
                                286                 :                :  * latches.  If InvalidXLogRecPtr is given, remove all waiters from the heap
                                287                 :                :  * and set latches for all waiters.
                                288                 :                :  *
                                289                 :                :  * This function first accumulates waiters to wake up into an array, then
                                290                 :                :  * wakes them up without holding a WaitLSNLock.  The array size is static and
                                291                 :                :  * equal to WAKEUP_PROC_STATIC_ARRAY_SIZE.  That should be more than enough
                                292                 :                :  * to wake up all the waiters at once in the vast majority of cases.  However,
                                293                 :                :  * if there are more waiters, this function will loop to process them in
                                294                 :                :  * multiple chunks.
                                295                 :                :  */
                                296                 :                : static void
                                297                 :           3044 : wakeupWaiters(WaitLSNType lsnType, XLogRecPtr currentLSN)
                                298                 :                : {
                                299                 :                :     ProcNumber  wakeUpProcs[WAKEUP_PROC_STATIC_ARRAY_SIZE];
                                300                 :                :     int         numWakeUpProcs;
                                301                 :           3044 :     int         i = (int) lsnType;
                                302                 :                : 
  256                           303   [ +  -  -  + ]:           3044 :     Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
                                304                 :                : 
                                305                 :                :     do
                                306                 :                :     {
                                307                 :                :         int         j;
                                308                 :                : 
  297                           309                 :           3044 :         numWakeUpProcs = 0;
                                310                 :           3044 :         LWLockAcquire(WaitLSNLock, LW_EXCLUSIVE);
                                311                 :                : 
                                312                 :                :         /*
                                313                 :                :          * Iterate the waiters heap until we find LSN not yet reached. Record
                                314                 :                :          * process numbers to wake up, but send wakeups after releasing lock.
                                315                 :                :          */
                                316         [ +  + ]:           3099 :         while (!pairingheap_is_empty(&waitLSNState->waitersHeap[i]))
                                317                 :                :         {
                                318                 :             59 :             pairingheap_node *node = pairingheap_first(&waitLSNState->waitersHeap[i]);
                                319                 :                :             WaitLSNProcInfo *procInfo;
                                320                 :                : 
                                321                 :                :             /* Get procInfo using appropriate heap node */
  282                           322                 :             59 :             procInfo = pairingheap_container(WaitLSNProcInfo, heapNode, node);
                                323                 :                : 
  294 alvherre@kurilemu.de      324   [ +  +  +  + ]:             59 :             if (XLogRecPtrIsValid(currentLSN) && procInfo->waitLSN > currentLSN)
  297 akorotkov@postgresql      325                 :              4 :                 break;
                                326                 :                : 
                                327         [ -  + ]:             55 :             Assert(numWakeUpProcs < WAKEUP_PROC_STATIC_ARRAY_SIZE);
                                328                 :             55 :             wakeUpProcs[numWakeUpProcs++] = procInfo->procno;
                                329                 :             55 :             (void) pairingheap_remove_first(&waitLSNState->waitersHeap[i]);
                                330                 :                : 
                                331                 :                :             /* Update appropriate flag */
  282                           332                 :             55 :             procInfo->inHeap = false;
                                333                 :                : 
  297                           334         [ -  + ]:             55 :             if (numWakeUpProcs == WAKEUP_PROC_STATIC_ARRAY_SIZE)
  297 akorotkov@postgresql      335                 :UBC           0 :                 break;
                                336                 :                :         }
                                337                 :                : 
  297 akorotkov@postgresql      338                 :CBC        3044 :         updateMinWaitedLSN(lsnType);
                                339                 :           3044 :         LWLockRelease(WaitLSNLock);
                                340                 :                : 
                                341                 :                :         /*
                                342                 :                :          * Set latches for processes whose waited LSNs have been reached.
                                343                 :                :          * Since SetLatch() is a time-consuming operation, we do this outside
                                344                 :                :          * of WaitLSNLock. This is safe because procLatch is never freed, so
                                345                 :                :          * at worst we may set a latch for the wrong process or for no process
                                346                 :                :          * at all, which is harmless.
                                347                 :                :          */
  233                           348         [ +  + ]:           3099 :         for (j = 0; j < numWakeUpProcs; j++)
                                349                 :             55 :             SetLatch(&GetPGProcByNumber(wakeUpProcs[j])->procLatch);
                                350                 :                : 
  297                           351         [ -  + ]:           3044 :     } while (numWakeUpProcs == WAKEUP_PROC_STATIC_ARRAY_SIZE);
                                352                 :           3044 : }
                                353                 :                : 
                                354                 :                : /*
                                355                 :                :  * Wake up processes waiting for LSN to reach currentLSN
                                356                 :                :  */
                                357                 :                : void
                                358                 :        9184118 : WaitLSNWakeup(WaitLSNType lsnType, XLogRecPtr currentLSN)
                                359                 :                : {
                                360                 :        9184118 :     int         i = (int) lsnType;
                                361                 :                : 
  256                           362   [ +  -  -  + ]:        9184118 :     Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
                                363                 :                : 
                                364                 :                :     /*
                                365                 :                :      * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
                                366                 :                :      * "wake all waiters" (e.g., during promotion when recovery ends). Pairs
                                367                 :                :      * with pg_atomic_write_membarrier_u64() in updateMinWaitedLSN().
                                368                 :                :      */
  285                           369   [ +  +  +  + ]:       18365233 :     if (XLogRecPtrIsValid(currentLSN) &&
  116                           370                 :        9181115 :         pg_atomic_read_membarrier_u64(&waitLSNState->minWaitedLSN[i]) > currentLSN)
  297                           371                 :        9181074 :         return;
                                372                 :                : 
                                373                 :           3044 :     wakeupWaiters(lsnType, currentLSN);
                                374                 :                : }
                                375                 :                : 
                                376                 :                : /*
                                377                 :                :  * Clean up any LSN wait state for the current process.
                                378                 :                :  */
                                379                 :                : void
                                380                 :          41522 : WaitLSNCleanup(void)
                                381                 :                : {
                                382                 :                :     /*
                                383                 :                :      * deleteLSNWaiter() starts with the same lockless inHeap check, so
                                384                 :                :      * calling it unconditionally costs nothing when this process isn't
                                385                 :                :      * waiting.  Its lsnType is then unused, and reading it is harmless in any
                                386                 :                :      * case: an entry that was never used is zeroed, which is a valid
                                387                 :                :      * WaitLSNType.
                                388                 :                :      */
                                389         [ +  - ]:          41522 :     if (waitLSNState)
    9                           390                 :          41522 :         deleteLSNWaiter(waitLSNState->procInfos[MyProcNumber].lsnType);
  297                           391                 :          41522 : }
                                392                 :                : 
                                393                 :                : /*
                                394                 :                :  * Exit callback to clean up any LSN wait state left behind if this process
                                395                 :                :  * exits while waiting.  Transaction abort paths call WaitLSNCleanup()
                                396                 :                :  * directly.
                                397                 :                :  */
                                398                 :                : static void
    9                           399                 :            259 : WaitLSNShmemExit(int code, Datum arg)
                                400                 :                : {
                                401                 :            259 :     WaitLSNCleanup();
                                402                 :            259 : }
                                403                 :                : 
                                404                 :                : /*
                                405                 :                :  * Register shared-memory exit cleanup once per process.  A backend may
                                406                 :                :  * execute WAIT FOR LSN more than once.
                                407                 :                :  */
                                408                 :                : static void
                                409                 :            739 : RegisterWaitLSNShmemExit(void)
                                410                 :                : {
                                411         [ +  + ]:            739 :     if (!waitLSNShmemExitRegistered)
                                412                 :                :     {
                                413                 :            259 :         on_shmem_exit(WaitLSNShmemExit, 0);
                                414                 :            259 :         waitLSNShmemExitRegistered = true;
                                415                 :                :     }
                                416                 :            739 : }
                                417                 :                : 
                                418                 :                : /*
                                419                 :                :  * Check if the given LSN type requires recovery to be in progress.
                                420                 :                :  * Standby wait types (replay, write, flush) require recovery;
                                421                 :                :  * primary wait types (flush) do not.
                                422                 :                :  */
                                423                 :                : static inline bool
  234                           424                 :            843 : WaitLSNTypeRequiresRecovery(WaitLSNType t)
                                425                 :                : {
                                426         [ +  + ]:            613 :     return t == WAIT_LSN_TYPE_STANDBY_REPLAY ||
                                427   [ +  +  +  + ]:           1456 :         t == WAIT_LSN_TYPE_STANDBY_WRITE ||
                                428                 :                :         t == WAIT_LSN_TYPE_STANDBY_FLUSH;
                                429                 :                : }
                                430                 :                : 
                                431                 :                : /*
                                432                 :                :  * Wait using MyLatch till the given LSN is reached, the replica gets
                                433                 :                :  * promoted, or the postmaster dies.
                                434                 :                :  *
                                435                 :                :  * Returns WAIT_LSN_RESULT_SUCCESS if target LSN was reached.
                                436                 :                :  * Returns WAIT_LSN_RESULT_NOT_IN_RECOVERY if run not in recovery,
                                437                 :                :  * or replica got promoted before the target LSN reached.
                                438                 :                :  */
                                439                 :                : WaitLSNResult
  297                           440                 :            739 : WaitForLSN(WaitLSNType lsnType, XLogRecPtr targetLSN, int64 timeout)
                                441                 :                : {
                                442                 :                :     XLogRecPtr  currentLSN;
                                443                 :                :     WaitLSNProcInfo *procInfo;
                                444                 :            739 :     TimestampTz endtime = 0;
                                445                 :            739 :     int         wake_events = WL_LATCH_SET | WL_POSTMASTER_DEATH;
                                446                 :                : 
                                447                 :                :     /* Shouldn't be called when shmem isn't initialized */
                                448         [ -  + ]:            739 :     Assert(waitLSNState);
                                449                 :                : 
                                450                 :                :     /* Should have a valid proc number */
  266                           451   [ +  -  -  + ]:            739 :     Assert(MyProcNumber >= 0 && MyProcNumber < MaxBackends + NUM_AUXILIARY_PROCS);
                                452                 :                : 
    9                           453                 :            739 :     procInfo = &waitLSNState->procInfos[MyProcNumber];
                                454                 :                : 
                                455                 :                :     /*
                                456                 :                :      * Ensure cleanup is registered before publishing our waiter entry.
                                457                 :                :      * on_shmem_exit callbacks run in reverse registration order, so this
                                458                 :                :      * callback runs before the earlier-registered ProcKill() and removes the
                                459                 :                :      * entry before our PGPROC slot can be reused.
                                460                 :                :      */
                                461                 :            739 :     RegisterWaitLSNShmemExit();
                                462                 :                : 
  297                           463         [ +  + ]:            739 :     if (timeout > 0)
                                464                 :                :     {
                                465                 :            726 :         endtime = TimestampTzPlusMilliseconds(GetCurrentTimestamp(), timeout);
                                466                 :            726 :         wake_events |= WL_TIMEOUT;
                                467                 :                :     }
                                468                 :                : 
                                469                 :                :     /*
                                470                 :                :      * Add our process to the waiters heap.  It might happen that target LSN
                                471                 :                :      * gets reached before we do.  The check at the beginning of the loop
                                472                 :                :      * below prevents the race condition.
                                473                 :                :      */
                                474                 :            739 :     addLSNWaiter(targetLSN, lsnType);
                                475                 :                : 
                                476                 :                :     for (;;)
                                477                 :            104 :     {
                                478                 :                :         int         rc;
                                479                 :            843 :         long        delay_ms = -1;
                                480                 :                : 
                                481                 :                :         /* Get current LSN for the wait type */
  234                           482                 :            843 :         currentLSN = GetCurrentLSNForWaitType(lsnType);
                                483                 :                : 
                                484                 :                :         /* Check that recovery is still in-progress */
                                485   [ +  +  +  + ]:            843 :         if (WaitLSNTypeRequiresRecovery(lsnType) && !RecoveryInProgress())
                                486                 :                :         {
                                487                 :                :             /*
                                488                 :                :              * Recovery was ended, but check if target LSN was already
                                489                 :                :              * reached.
                                490                 :                :              */
  297                           491                 :              6 :             deleteLSNWaiter(lsnType);
                                492                 :                : 
                                493   [ +  +  +  + ]:              6 :             if (PromoteIsTriggered() && targetLSN <= currentLSN)
                                494                 :              1 :                 return WAIT_LSN_RESULT_SUCCESS;
                                495                 :              5 :             return WAIT_LSN_RESULT_NOT_IN_RECOVERY;
                                496                 :                :         }
                                497                 :                :         else
                                498                 :                :         {
                                499                 :                :             /* Check if the waited LSN has been reached */
                                500         [ +  + ]:            837 :             if (targetLSN <= currentLSN)
                                501                 :            719 :                 break;
                                502                 :                :         }
                                503                 :                : 
    9                           504         [ +  + ]:            118 :         CHECK_FOR_INTERRUPTS();
                                505                 :                : 
                                506                 :                :         /*
                                507                 :                :          * The target is not reached.  Normally we remain in the waiters heap
                                508                 :                :          * and can sleep again.  A wakeup can become stale, however, if the
                                509                 :                :          * position moves backwards after the waker removed us.  That happens
                                510                 :                :          * with the walreceiver-tracked positions: when streaming starts on a
                                511                 :                :          * new timeline, or after receiveStart was reset,
                                512                 :                :          * RequestXLogStreaming() re-seeds writtenUpto and flushedUpto with
                                513                 :                :          * the requested start position, which can be below what was published
                                514                 :                :          * before.  Re-register in that case and reread the position, since an
                                515                 :                :          * advance between the previous read and the re-add could not have
                                516                 :                :          * woken us.
                                517                 :                :          *
                                518                 :                :          * A wakeup that goes stale again sends us around the loop once more,
                                519                 :                :          * so interrupts are processed before we re-register: however often
                                520                 :                :          * that repeats, the wait stays cancellable.  Repeating requires a
                                521                 :                :          * fresh wakeup, hence the position reaching the target and falling
                                522                 :                :          * back below it, so it follows streaming restarts rather than burning
                                523                 :                :          * CPU.  The deadline is checked on every iteration that goes on to
                                524                 :                :          * sleep, which is the only place it matters.
                                525                 :                :          *
                                526                 :                :          * It is safe to read inHeap without the lock because only this
                                527                 :                :          * process sets it true.  If a waker clears it concurrently, it also
                                528                 :                :          * sets our latch, so we will recheck and re-register if necessary.
                                529                 :                :          */
                                530         [ +  + ]:            117 :         if (!procInfo->inHeap)
                                531                 :                :         {
                                532                 :              1 :             addLSNWaiter(targetLSN, lsnType);
                                533                 :              1 :             continue;
                                534                 :                :         }
                                535                 :                : 
  297                           536         [ +  + ]:            116 :         if (timeout > 0)
                                537                 :                :         {
                                538                 :             97 :             delay_ms = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), endtime);
                                539         [ +  + ]:             97 :             if (delay_ms <= 0)
                                540                 :             13 :                 break;
                                541                 :                :         }
                                542                 :                : 
                                543                 :            103 :         rc = WaitLatch(MyLatch, wake_events, delay_ms,
  234                           544                 :            103 :                        WaitLSNWaitEvents[lsnType]);
                                545                 :                : 
                                546                 :                :         /*
                                547                 :                :          * Emergency bailout if postmaster has died.  This is to avoid the
                                548                 :                :          * necessity for manual cleanup of all postmaster children.
                                549                 :                :          */
  297                           550         [ -  + ]:            103 :         if (rc & WL_POSTMASTER_DEATH)
  297 akorotkov@postgresql      551         [ #  # ]:UBC           0 :             ereport(FATAL,
                                552                 :                :                     errcode(ERRCODE_ADMIN_SHUTDOWN),
                                553                 :                :                     errmsg("terminating connection due to unexpected postmaster exit"),
                                554                 :                :                     errcontext("while waiting for LSN"));
                                555                 :                : 
  116 akorotkov@postgresql      556                 :CBC         103 :         ResetLatch(MyLatch);
                                557                 :                :     }
                                558                 :                : 
                                559                 :                :     /*
                                560                 :                :      * A progress waker, such as the startup process during WAL replay, may
                                561                 :                :      * already have removed this waiter through WaitLSNWakeup() before setting
                                562                 :                :      * its latch.  The inHeap flag makes this cleanup safe whether or not the
                                563                 :                :      * entry remains in the heap.
                                564                 :                :      */
  297                           565                 :            732 :     deleteLSNWaiter(lsnType);
                                566                 :                : 
                                567                 :                :     /*
                                568                 :                :      * If we didn't reach the target LSN, we must be exited by timeout.
                                569                 :                :      */
                                570         [ +  + ]:            732 :     if (targetLSN > currentLSN)
                                571                 :             13 :         return WAIT_LSN_RESULT_TIMEOUT;
                                572                 :                : 
                                573                 :            719 :     return WAIT_LSN_RESULT_SUCCESS;
                                574                 :                : }
        

Generated by: LCOV version 2.0-1