Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1whWEO-000H69-2n for pgsql-hackers@arkaria.postgresql.org; Wed, 08 Jul 2026 17:38:45 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1whWEN-008Wq9-0k for pgsql-hackers@arkaria.postgresql.org; Wed, 08 Jul 2026 17:38:43 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1whWEM-008Wq1-2a for pgsql-hackers@lists.postgresql.org; Wed, 08 Jul 2026 17:38:43 +0000 Received: from mail-qv1-xf31.google.com ([2607:f8b0:4864:20::f31]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1whWEL-00000000BqD-1qpj for pgsql-hackers@lists.postgresql.org; Wed, 08 Jul 2026 17:38:42 +0000 Received: by mail-qv1-xf31.google.com with SMTP id 6a1803df08f44-8efcef23d21so8104406d6.2 for ; Wed, 08 Jul 2026 10:38:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783532321; cv=none; d=google.com; s=arc-20260327; b=pWTGZ9j2Vp3g4yrOkUXiY8+g8yqKShcdSN3bwouTur2yYBeMTFBGmiSC2RQd/wNQIT Pp6pA64BDmk9uXkTSvbLD2V8h8dDi2oHXNG81iH8JvNwAcATMCUYVqTd4NJqAje1sE9D gCRUerXVBQSSp50PuQF8iVmPvfUM+JM/6eQxmId0VQLp/rVf9zQAB9LAV8sIlu9BJb6o HdmiiIqVOgOgACIaUgwrp5QtrgMIrmV2nIvlCZGwy1C8EhPv3K142ZDCObnIzrgp1WaU 5wcoR7S7xWyJOaiP+SjqUmZ0TAaaW0lBooIQDJ9Ub6pgnl8dGyhX4cNOp/320E/otRa3 g+ZA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=0t2NKkC4pNtSnsHMWAqDZXUJuJRc0hhOBtkoIBdqVjA=; fh=9Kj0Q9U8lzpl28/V8bqiv+f3dml4OnkEGJdKhpvsvF4=; b=B1rUhPlXcBayxD2A5o4OMeYohrwNstcZ55XeKnlCwuKXd4ewoVZIPp/rLAz3Il0AaJ Dn2XbTiN+VE+TfFcXph3XfhfQMVLrhHxeygI4tyHfilPS5ft9QcdG5CTwIC/3OEU+nIf 5P6PK0+KpElj4bBvap+fmt7zQS9iKPDdFKzKpImYMeqNPYf9wPDaFXic2JmX1/U0DbcX 1q8t2EK8x55Ke4l/mq6dWaaqzcOlpy/OFwFdcToJYeZFYoCNoeShc/nrwicH6RsUGYCU +xl600ofoAb1hNoqcsLP5BIVHsPU+hv/Z3Q1VLUierLAFZokeVhcn0e7FpUYkAGEm0u6 LaHw==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783532321; x=1784137121; darn=lists.postgresql.org; h=content-transfer-encoding:content-type:cc:to:subject:message-id :date:from:in-reply-to:references:mime-version:from:to:cc:subject :date:message-id:reply-to:content-type; bh=0t2NKkC4pNtSnsHMWAqDZXUJuJRc0hhOBtkoIBdqVjA=; b=mAHiI6ACJAYZ2d/TgxT7jRO/EJhvSIs8yGy/1fgHYxEWkDasqbPxis7mUm3MGECm3l oZKfGhRWw0GD+pVylkn3nlgIWD+TVrBCMQ5ANo/agAjZyMnH0QIsDSFNaTdG2jb9ZWio XtcRWn1WpuFIVDoAQiXHB6nAgqMaH/Enw7eCM024DRkyqfxycQkNLTm+gOlLQG3do2// 7vDnyzmFGX8T5NRny440E56TZP4rktuUeftX2in7i0ZOd1rWpezynFsb7GZl91UHXL/w U1IaMITn3q6MHYQQa99lVDOsPgmJqDWCA/KuZR8rUvdAktp8ihxaNZMGkjHhxzC7tI5y 8XkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783532321; x=1784137121; h=content-transfer-encoding:content-type:cc:to:subject:message-id :date:from:in-reply-to:references:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=0t2NKkC4pNtSnsHMWAqDZXUJuJRc0hhOBtkoIBdqVjA=; b=RgJuJHnp8A+7gWdGzPk4HtWqIvlbrmvdZ/JGJk1ozP1cnLC1ZFSDxvOr3BuD7gISre oAuWaVanRXlLpTQ35p7deTLzBAWkKZzSx1rxuo6Nl3OEzjVGZv+zC+YbzMnZpkCobIct ky7L6ifnVBtM8PPtZwgeK3UXqak2oiEnQ7bJfC6w2yz9bWeT1Cw4q5cL1CzZSitGETk1 kAwjd9nyBEAzB69fgwKyNgVjt8OFACf1MADne4X7B27xqa3cxo2FGx/VxquTmZi9qZ+y pb8RoNL4RN7xMukAXureznenvHkHbPAWfydd+pPGXLtbpUYpKcJZ6NRVy3bvQ7y+ESgf iREg== X-Forwarded-Encrypted: i=1; AHgh+Rpcr06lvGoj7MBhsfjDybPsR6BuAKZvCCWCTZbU/3IbYbmJuac7xs8mKAxx78AdTeEvqW1AG51NcvQISudd@lists.postgresql.org X-Gm-Message-State: AOJu0YxRgFNJ7OMhMkyha2PjjEE9BZWC+L/YzHgNiayewySalwbhkFLw AYJuIX3Qpj5Ae7IXMsGlkNB4YHkRflDC37ydt0mDKc6PLNg764ZOJaQ7ia6EJI3D8IrFfzQcs2n /iv/QWodYIc6/CuoMPPveZwkZOi7Nuaw= X-Gm-Gg: AfdE7cmalIFwSJPqf7nc6JwXLM1AJ4URpfH6vPPqqwk/QkVuCcVgGXScI4CDV9TVg5t GKjoHrH6LaJuloh01P+XQQ9FQJpL4SCqP9HPs3h52NPsVQMeBmDt1wlU+fj1sqb5+Otk5aBlphw zSR2h+FQUtbPCrvRQL7qND/dnHf9QrRlPPCDcIx0OpteRpqtztmsv4+sL6vA6B2w08IgJfc/Blw B3xfxjWPO7W6ppHGfdnfoM5D/N5k5PCnVoEml2wW5NTURMSOAzFIMwg1k9n+JBExOQ+F2aK2b/x dicmd4n8RT8rZU3+twjDZqgOatxUtDTLqqYrhDa07Zya7Eiv3jBL2zZ/X+XS5BeHQrKz8g== X-Received: by 2002:ad4:5ae1:0:b0:8ef:4ed2:316 with SMTP id 6a1803df08f44-8fec05dd331mr39521936d6.1.1783532320731; Wed, 08 Jul 2026 10:38:40 -0700 (PDT) MIME-Version: 1.0 References: <1959506.1775527693@sss.pgh.pa.us> In-Reply-To: From: Alexander Korotkov Date: Wed, 8 Jul 2026 20:38:28 +0300 X-Gm-Features: AVVi8CdSoHfbnY0OIxQ9tHXtseSrgl-60lZHVt5_jgZ8dV6zoKojX7uFLLG-GgE Message-ID: Subject: Re: Implement waiting for wal lsn replay: reloaded To: Xuneng Zhou Cc: Heikki Linnakangas , Andres Freund , Tom Lane , Peter Eisentraut , Thomas Munro , =?UTF-8?Q?=C3=81lvaro_Herrera?= , Chao Li , pgsql-hackers , Michael Paquier , jian he , Tomas Vondra , Yura Sokolov Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Wed, Jul 8, 2026 at 3:08=E2=80=AFPM Xuneng Zhou w= rote: > On Mon, Jul 6, 2026 at 10:17=E2=80=AFPM Xuneng Zhou wrote: > > > > On Mon, Jul 6, 2026 at 9:49=E2=80=AFPM Xuneng Zhou wrote: > > > > > > Hi Heikki, > > > > > > Thanks for looking into this! > > > > > > On Mon, Jul 6, 2026 at 7:04=E2=80=AFPM Heikki Linnakangas wrote: > > > /* > > > > > * Apply the record > > > > > */ > > > > > ApplyWalRecord(xlogreader, record, &replayT= LI); > > > > > > > > > > /* > > > > > * Wake up processes waiting for standby re= play, write, or flush > > > > > * LSN to reach current replay position. R= eplay implies that the > > > > > * WAL was already written and flushed to d= isk, so write and flush > > > > > * waiters can be woken at the replay posit= ion too. > > > > > */ > > > > > WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, > > > > > XLogRecoveryCtl->= lastReplayedEndRecPtr); > > > > > WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, > > > > > XLogRecoveryCtl->= lastReplayedEndRecPtr); > > > > > WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, > > > > > XLogRecoveryCtl->= lastReplayedEndRecPtr); > > > > > > > > That's not wrong, but I've got some comments: > > > > > > > > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lo= ck or > > > > atomics. That's ok, no other process modifies lastReplayedEndRecPtr= , but > > > > it feels a little dirty. > > > > > > > > 2. We're now doing three extra function calls on every WAL record. = This > > > > is a very hot path, and most of the time, we'll just take the fast = path > > > > in WaitLSNWakeup to return without doing anything. Andres and other= s > > > > assumed up-thread that it's negligible (we used to have pre-checks = here > > > > in the caller), but I wonder if you did any performance testing? > > > > > > Agreed, this is a hot path. The performance impact of these extra > > > calls doing real work hasn't been measured yet. I'll do some testing. > > > > > > > 3. There are other "wakeup" calls inside ApplyWalRecord(), to wake = up > > > > walsenders and walreceivers. They could perhaps use the same wait-l= sn > > > > machinery now, but that's v20 material. However, I think these > > > > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(),= so > > > > that we'd have all the wakeup actions in one place. > > > > > > + 1. This makes the code safer and more readable. > > > > > > > 4. Once you move those calls inside ApplyWalRecord(), like this: > > > > > > > > > @@ -1979,20 +1979,30 @@ ApplyWalRecord(XLogReaderState *xlogreade= r, XLogRecord *record, TimeLineID *repl > > > > > /* > > > > > * Update lastReplayedEndRecPtr after this record has bee= n successfully > > > > > * replayed. > > > > > */ > > > > > SpinLockAcquire(&XLogRecoveryCtl->info_lck); > > > > > XLogRecoveryCtl->lastReplayedReadRecPtr =3D xlogreader->R= eadRecPtr; > > > > > XLogRecoveryCtl->lastReplayedEndRecPtr =3D xlogreader->En= dRecPtr; > > > > > XLogRecoveryCtl->lastReplayedTLI =3D *replayTLI; > > > > > SpinLockRelease(&XLogRecoveryCtl->info_lck); > > > > > > > > > > + /* > > > > > + * Wake up processes waiting for standby replay, write, o= r flush LSN to > > > > > + * reach current replay position. Replay implies that th= e WAL was already > > > > > + * written and flushed to disk, so write and flush waiter= s can be woken at > > > > > + * the replay position too. > > > > > + */ > > > > > + WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->E= ndRecPtr); > > > > > + WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->En= dRecPtr); > > > > > + WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->En= dRecPtr); > > > > > + > > > > > /* ------ > > > > > * Wakeup walsenders: > > > > > * > > > > > * On the standby, the WAL is flushed first (which will o= nly wake up > > > > > * physical walsenders) and then applied, which will only= wake up logical > > > > > * walsenders. > > > > > > > > It becomes clear that you don't actually need the memory barrier in= side > > > > WaitLSNWakeup(). Not sure if they're needed for other callers, but = here > > > > we have just released a spinlock, which acts as a memory barrier. I= t > > > > might not be worth relaxing, but it does seem a little silly. > > > > > > If we made the move here, I think the memory barrier could be relaxed > > > since other callers are guarded by either the spinlock or full-barrie= r > > > atomic write already. We might also want to make the contract of > > > > OK, the 'if' here is redundant... > > After revisiting the memory barrier in WaitLSNWakeup and why it is > introduced there in a80a593ab63 rather than recalling it from memory, > I think relaxing it here could be unsafe. > > In WaitLSNWakeup(), use pg_atomic_read_membarrier_u64() in the > fast-path check so the waker's preceding position store is globally > visible before minWaitedLSN is read. > > Without the barrier in WaitLSNWakeup(), this interleaving is possible: > > Initial: > minWaitedLSN =3D PG_UINT64_MAX > replayLSN =3D 90 > > Waiter: > stores minWaitedLSN =3D 100 > reads replayLSN before the waker publishes the new replay position > sees replayLSN =3D 90 > decides it should sleep > > Waker: > publishes replayLSN =3D 100 > reads old minWaitedLSN =3D PG_UINT64_MAX > skips the wakeup > > Then the waiter goes to sleep even though replay has reached its > target LSN. To avoid this, we still need to make sure that the > publication of replayLSN precedes the read of minWaitedLSN, so that > the waker cannot decide "nobody is waiting" before its own progress is > still not visible to the waiter. Yes, I also think the memory barrier for waker between publishing replayLSN and reading minWaitedLSN is required. However, sequence of three WaitLSNWakeup() calls makes 3 memory barriers while only one is required. We could introduce a hierarchy for WAIT_LSN_TYPE_STANDBY_*: WAIT_LSN_TYPE_STANDBY_FLUSH implies WAIT_LSN_TYPE_STANDBY_WRITE, WAIT_LSN_TYPE_STANDBY_REPLAY implies WAIT_LSN_TYPE_STANDBY_WRITE and WAIT_LSN_TYPE_STANDBY_FLUSH. Then ApplyWalRecord() can call WaitLSNWakeup() only once and make only 1 memory barrier. ------ Regards, Alexander Korotkov Supabase