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 1whR4k-000Dm1-39 for pgsql-hackers@arkaria.postgresql.org; Wed, 08 Jul 2026 12:08:27 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1whR4i-007AdQ-0i for pgsql-hackers@arkaria.postgresql.org; Wed, 08 Jul 2026 12:08:24 +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 1whR4h-007Ad5-2M for pgsql-hackers@lists.postgresql.org; Wed, 08 Jul 2026 12:08:24 +0000 Received: from mail-ej1-x630.google.com ([2a00:1450:4864:20::630]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1whR4g-000000009K0-0AZk for pgsql-hackers@lists.postgresql.org; Wed, 08 Jul 2026 12:08:23 +0000 Received: by mail-ej1-x630.google.com with SMTP id a640c23a62f3a-c125bce2294so64196666b.3 for ; Wed, 08 Jul 2026 05:08:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783512496; cv=none; d=google.com; s=arc-20260327; b=Za+dc6smOwYgGAtTIuFmbbhUe/V6hHce2KkjVBtqH51qTUiYCvyRaG5pJPPM0pfy5q kqiEY0D159W0sew7oX9grZTQ6WNJT7MoexCgcrmcEI6HL+Yf0n0mMyIGaK87w1nUAsSv WHHS074TjhyAtIE3DewCs3U7uxCsvX1jo2RlUTp/zL0VoAuAdCe+8jUcUiRWwJemnxfc 1a53+oO5hHEL6h48YnwbTJI99UwN1aSZbKhMZAJ4uljEKQLJvQcUUunUYOUfizUt0WBl o4uR8apsuI1IHZYrM8b6F/jh3Pu8e+cYryVlHy3Z8O2+j4G88Fa/iDJ11e6t0ncw8NRi YgEA== 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=zUJ5i4zvRTtnV6QS/K2myRrTH9xCusfoUwZOMT26Qpg=; fh=FtcAXh8ccF2nF89D3HUXFxvbib0KIZ/3E5xSHeUsMCY=; b=MERrwAq6jeirSyitb02Z80E+mcTu2ZAnawB6EPzwqsdvPGxi5/GQupfYneJvfGN/l6 EUgKiI1EdhAacyqKHcZrKcoAtvpnfesWrejKQNh4iYqXs2Mo0vp6B+Ofb6QgHqwCTN1B nbCth56vYpXI7OeYJEsEUqAfuW6QXngbzr9G8xYk7idU+I2ohKhA6INMzEfDQ6iLCUSA ROJuKVvQ2qt/5n1XBYheHb6xd6mxfQBaaJ7ILKJ/IyQAIJRE65192+kzs2KR3LUrVgys 6DyDgLELkxiEVNbja6NwWz0FW4tU75IH0ReKYovyadPu/KTTwJ9jG7jty9J2KQAjjIa7 YTRg==; 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=1783512496; x=1784117296; 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=zUJ5i4zvRTtnV6QS/K2myRrTH9xCusfoUwZOMT26Qpg=; b=NUsQciyLOI/CH2M+9O/mZlcrTuWxY1NrKd2H/BKmIHGnHzZFwOygRgOoI+4rciJXl3 z8vacsA5CAOm5ikCWJVvcbA/OHg1F5HFcO3661tJA//RMYXmviExnoh1KEB1XmVpF1y+ byRqWsxAP0Vr2mSLy68+odfcfXKNP2EYpSRO2IVsfsqDCk0zby3mpomLr56YwVWWOqNe c3L6qHuAFKIlbIsvjNeaEI73J53fPepL0MI5K5/dMyUsqAnnU4YSPn+OXFfLqo76B/Pr 1vMx27OprzLoItAHXtcR77wm0tIXYSYrB9cEN8hpFE9/VYlgZ0fEZwOpoaxrNvqzEHk9 QssA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783512496; x=1784117296; 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=zUJ5i4zvRTtnV6QS/K2myRrTH9xCusfoUwZOMT26Qpg=; b=i6+OXV44rp7hskucgLU+AB+lXxoEHlSdYLJMtPlH393gX9rNMajt3z1YwulNkxrNad 2V2rfobHq3WQwlb0oQ/4FNcnbw5CDgxzWjy8SUiKp3uf6coZ286Bl2NO13EAv2WP15zX 2Lann/4bshofxWU0bKTk+Sn300zFBXGyCV3fItse6z9NN9dPEp+IiDvXU4Ww8jJrOXN4 lH5hTF3dQDPP+jrYUlOEi/fUwXKi78WYO0AgBdI2Zgpewuo1Qn0rGdzbYTvi3zaRUl+B vF2Kvsvdb3LsdKIWPbQ9qBJ2BcCgs8ZyR5yrhzxMIG8rLmniTOnZyecnHI3/FF5JhCJ6 uvKg== X-Forwarded-Encrypted: i=1; AHgh+RrdT466rvlALpVWunDZFN/+VC6Y4k9zFQOfvI5YoD4A4TU4nY2r68/yee+P44wRaZC1b94IBItHHJvcZp+f@lists.postgresql.org X-Gm-Message-State: AOJu0YwQ8aGblB+6K4hnwwC5MmosR9sl3xzd/jUxaYx3yzS1inJQ1CYn fNtCpR1ct+j9mCRAzpMV1m08A/qIRVE/8yvIDTj1X/BhydpprUBqIeS/iPyiC4zZKbLP8jsSaK6 SEjxsKr3CpUv0ZH7wbTrrP1VnKQzSb78= X-Gm-Gg: AfdE7cnis+CmHmJ3VLzX8CMEWMU3eCs240sXajF1GCKDCjY1ahejY6/irTz3efXUZP3 LbIj2vlAEiYOueOXzAArAYrffZPXCuHOi+AcUUhXDF2v/m3hlU4RgWQR/V8kcvS3xHU7/p8wJx/ 8ncvgEtHsvDPYK930ryCRKi7f2qzWZIB0jcKnaCwLIlGm2mryMhLNdryO3ASvMnqrTBRduBGw63 3aZ2USvQHV2YqbvTKG2SxmU474xID5WAeq0PnSoNoA9gxIhnffgIJKKvkuHYJfyxLV+Ps/yzjR0 XqdpyHhU+Fqb7WSnDi8xqF86+nwxvLiqtLTaCL+5AeF6DADZSE3Q0FMGhhPDLp9w/R0D5Qt43R2 3IY9uxaqWmh9ZNl7K/hS7glipcaw= X-Received: by 2002:a17:907:8d8d:b0:bee:4562:3308 with SMTP id a640c23a62f3a-c15ce015d04mr111481066b.26.1783512495278; Wed, 08 Jul 2026 05:08:15 -0700 (PDT) MIME-Version: 1.0 References: <1959506.1775527693@sss.pgh.pa.us> In-Reply-To: From: Xuneng Zhou Date: Wed, 8 Jul 2026 20:08:01 +0800 X-Gm-Features: AVVi8Cf-peE--zpvpMQVkTUZWFiQrboVIc9RqEXcCpboYd3QiTNPmEn-pblQLrU Message-ID: Subject: Re: Implement waiting for wal lsn replay: reloaded To: Heikki Linnakangas Cc: Alexander Korotkov , 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 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, &replayTLI= ); > > > > > > > > /* > > > > * Wake up processes waiting for standby repl= ay, write, or flush > > > > * LSN to reach current replay position. Rep= lay implies that the > > > > * WAL was already written and flushed to dis= k, so write and flush > > > > * waiters can be woken at the replay positio= n too. > > > > */ > > > > WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, > > > > XLogRecoveryCtl->la= stReplayedEndRecPtr); > > > > WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, > > > > XLogRecoveryCtl->la= stReplayedEndRecPtr); > > > > WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, > > > > XLogRecoveryCtl->la= stReplayedEndRecPtr); > > > > > > That's not wrong, but I've got some comments: > > > > > > 1. It's reading XLogRecoveryCtl->lastReplayedEndRecPtr without a lock= 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. Th= is > > > is a very hot path, and most of the time, we'll just take the fast pa= th > > > in WaitLSNWakeup to return without doing anything. Andres and others > > > assumed up-thread that it's negligible (we used to have pre-checks he= re > > > 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-lsn > > > machinery now, but that's v20 material. However, I think these > > > WaitLSNWakeup() calls should also be moved inside ApplyWalRecord(), s= o > > > 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 *xlogreader,= XLogRecord *record, TimeLineID *repl > > > > /* > > > > * Update lastReplayedEndRecPtr after this record has been = successfully > > > > * replayed. > > > > */ > > > > SpinLockAcquire(&XLogRecoveryCtl->info_lck); > > > > XLogRecoveryCtl->lastReplayedReadRecPtr =3D xlogreader->Rea= dRecPtr; > > > > XLogRecoveryCtl->lastReplayedEndRecPtr =3D xlogreader->EndR= ecPtr; > > > > XLogRecoveryCtl->lastReplayedTLI =3D *replayTLI; > > > > SpinLockRelease(&XLogRecoveryCtl->info_lck); > > > > > > > > + /* > > > > + * Wake up processes waiting for standby replay, write, or = flush LSN to > > > > + * reach current replay position. Replay implies that the = WAL was already > > > > + * written and flushed to disk, so write and flush waiters = can be woken at > > > > + * the replay position too. > > > > + */ > > > > + WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_REPLAY, xlogreader->End= RecPtr); > > > > + WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, xlogreader->EndR= ecPtr); > > > > + WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, xlogreader->EndR= ecPtr); > > > > + > > > > /* ------ > > > > * Wakeup walsenders: > > > > * > > > > * On the standby, the WAL is flushed first (which will onl= y wake up > > > > * physical walsenders) and then applied, which will only w= ake up logical > > > > * walsenders. > > > > > > It becomes clear that you don't actually need the memory barrier insi= de > > > WaitLSNWakeup(). Not sure if they're needed for other callers, but he= re > > > we have just released a spinlock, which acts as a memory barrier. It > > > 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-barrier > > 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. -- Regards, Xuneng Zhou HighGo Software Co., Ltd.