Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jPhUC-0000uZ-Oc for pgsql-hackers@arkaria.postgresql.org; Sat, 18 Apr 2020 07:01:57 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1jPhU9-0008F6-Qt for pgsql-hackers@arkaria.postgresql.org; Sat, 18 Apr 2020 07:01:53 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jPhU9-0008Ew-AX for pgsql-hackers@lists.postgresql.org; Sat, 18 Apr 2020 07:01:53 +0000 Received: from mail-pg1-x542.google.com ([2607:f8b0:4864:20::542]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1jPhU5-0001xu-4s for pgsql-hackers@postgresql.org; Sat, 18 Apr 2020 07:01:52 +0000 Received: by mail-pg1-x542.google.com with SMTP id g6so2245501pgs.9 for ; Sat, 18 Apr 2020 00:01:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=lR2ZHT65od6eUYr0ULpKKI4BJgyt/5xw1kxTLiYeOG0=; b=OcVX3FbgE7l5vWlunQ6FE/OBT00cY0a6tQ8JrnOFujrG5jJOkyD82sl/MOiu9IU065 2K+60shbFChKGR/QPEB8fBgegWGmbQ16Hgr6H1D+qouije7Ey1lqVNK7ekkklJft8X5r uN+2e6a9x6J4XGBj84ESZtsw0PpyvyoVkKXTo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=lR2ZHT65od6eUYr0ULpKKI4BJgyt/5xw1kxTLiYeOG0=; b=tEo2GxNvM+6oMExPMAwr9L2N0XaeNc5wexm3j7zscR08S5v9QR6un9yWyVBt3krmlH 028nWpSqQ1k1tAJVzvwmJ3wc+uK78kJNUSnce6z8iV0TLT4zv5MT8mn8zuxUOeisnxya o7kkENrG8bEqqgxY/Ih/S5uA4ilMdXbaJIsZKm3Cod7kpOh/SSl1+gaqzbGIdYt7ylVC 72Jtk2ZW2X1wmkRzXwmaSrBqc4yvCOlCCUofXXlyHexhYJheIOuCpfkMOI9AociTqiaO lsRPxQIEpX2sUdPSH/MdwHgfiGhdMIS3F9wHvwUlZCdreaZiai92cAV4MaW0UYFcdezB CXfQ== X-Gm-Message-State: AGi0Pub8Nk/DBx1uCEPqBSZJSQhB8XUQie7ApLlw4Kgzv2kwQnGhXkwM DpKju8kNX4b5keRZyqjaG9b3EQ== X-Google-Smtp-Source: APiQypIfnIzFHW1iYtK6TQfrubFHImMbeSCIn0+c1yzMquO7V5MBfD+AJocVs2i4u6v5sHS42G0hXg== X-Received: by 2002:a63:fd03:: with SMTP id d3mr6573192pgh.6.1587193306651; Sat, 18 Apr 2020 00:01:46 -0700 (PDT) Received: from rfd.leadboat.com (108-233-125-46.lightspeed.sntcca.sbcglobal.net. [108.233.125.46]) by smtp.gmail.com with ESMTPSA id w126sm11684923pfb.117.2020.04.18.00.01.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 18 Apr 2020 00:01:45 -0700 (PDT) Date: Sat, 18 Apr 2020 00:01:42 -0700 From: Noah Misch To: Fujii Masao , Kyotaro Horiguchi Cc: Tom Lane , pgsql-hackers@postgresql.org Subject: Re: 001_rep_changes.pl stalls Message-ID: <20200418070142.GA1075445@rfd.leadboat.com> References: <20200406063649.GA3738151@rfd.leadboat.com> <20200414013849.GA886648@rfd.leadboat.com> <8807.1586828716@sss.pgh.pa.us> <20200417054146.GA1061007@rfd.leadboat.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="sm4nu43k4a2Rpi4c" Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.24 (2015-08-30) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --sm4nu43k4a2Rpi4c Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Apr 17, 2020 at 05:06:29PM +0900, Kyotaro Horiguchi wrote: > At Fri, 17 Apr 2020 17:00:15 +0900 (JST), Kyotaro Horiguchi wrote in > > By the way, if latch is consumed in WalSndLoop, succeeding call to > > WalSndWaitForWal cannot be woke-up by the latch-set. Doesn't that > > cause missing wakeups? (in other words, overlooking of wakeup latch). > > - Since the only source other than timeout of walsender wakeup is latch, > - we should avoid wasteful consuming of latch. (It is the same issue > - with [1]). > > + Since walsender is wokeup by LSN advancement via latch, we should > + avoid wasteful consuming of latch. (It is the same issue with [1]). > > > > If wakeup signal is not remembered on walsender (like > > InterruptPending), WalSndPhysical cannot enter a sleep with > > confidence. No; per latch.h, "What must be avoided is placing any checks for asynchronous events after WaitLatch and before ResetLatch, as that creates a race condition." In other words, the thing to avoid is calling ResetLatch() without next examining all pending work that a latch would signal. Each walsender.c WaitLatch call does follow the rules. On Sat, Apr 18, 2020 at 12:29:58AM +0900, Fujii Masao wrote: > On 2020/04/17 14:41, Noah Misch wrote: > >1. Make XLogSendPhysical() more like XLogSendLogical(), calling > > WalSndWaitForWal() when no WAL is available. A quick version of this > > passes tests, but I'll need to audit WalSndWaitForWal() for things that are > > wrong for physical replication. > > (1) makes even physical replication walsender sleep in two places and > which seems to make the code for physical replication complicated > more than necessary. I'd like to avoid (1) if possible. Good point. > >2. Make XLogSendLogical() more like XLogSendPhysical(), returning when > > insufficient WAL is available. This complicates the xlogreader.h API to > > pass back "wait for this XLogRecPtr", and we'd then persist enough state to > > resume decoding. This doesn't have any advantages to make up for those. > > > >3. Don't avoid waiting in WalSndLoop(); instead, fix the stall by copying the > > WalSndKeepalive() call from WalSndWaitForWal() to WalSndLoop(). This risks > > further drift between the two wait sites; on the other hand, one could > > refactor later to help avoid that. > > Since the additional call of WalSndKeepalive() is necessary only for > logical replication, it should be copied to, e.g., XLogSendLogical(), > instead of WalSndLoop()? For example, when XLogSendLogical() sets > WalSndCaughtUp to true, it should call WalSndKeepalive()? We'd send a keepalive even when pq_flush_if_writable() can't empty the output buffer. That could be acceptable, but it's not ideal. > The root problem seems that when WAL record that's no-opes for > logical rep is processed, keep alive message has not sent immediately, > in spite of that we want pg_stat_replication to be updated promptly. The degree of promptness should be predictable, at least. If we removed the WalSndKeepalive() from WalSndWaitForWal(), pg_stat_replication updates would not be prompt, but they would be predictable. I do, however, think prompt updates are worthwhile. > (3) seems to try to address this problem straightly and looks better to me. > > >4. Keep the WalSndLoop() wait, but condition it on !logical. This is the > > minimal fix, but it crudely punches through the abstraction between > > WalSndLoop() and its WalSndSendDataCallback. > > (4) also looks good because it's simple, if we can redesign those > functions in good shape. Let's do that. I'm attaching the replacement implementation and the revert of v1. --sm4nu43k4a2Rpi4c Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="WalSndLoop-stall-v1revert.patch" Author: Noah Misch Commit: Noah Misch Revert "When WalSndCaughtUp, sleep only in WalSndWaitForWal()." This reverts commit 421685812290406daea58b78dfab0346eb683bbb. It caused idle physical walsenders to busy-wait, as reported by Fujii Masao. Discussion: https://postgr.es/m/20200417054146.GA1061007@rfd.leadboat.com diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index fc475d1..122d884 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1428,10 +1428,8 @@ WalSndWaitForWal(XLogRecPtr loc) /* * We only send regular messages to the client for full decoded * transactions, but a synchronous replication and walsender shutdown - * possibly are waiting for a later location. So, before sleeping, we - * send a ping containing the flush location. If the receiver is - * otherwise idle, this keepalive will trigger a reply. Processing the - * reply will update these MyWalSnd locations. + * possibly are waiting for a later location. So we send pings + * containing the flush location every now and then. */ if (MyWalSnd->flush < sentPtr && MyWalSnd->write < sentPtr && @@ -2316,16 +2314,20 @@ WalSndLoop(WalSndSendDataCallback send_data) WalSndKeepaliveIfNecessary(); /* - * Block if we have unsent data. Let WalSndWaitForWal() handle any - * other blocking; idle receivers need its additional actions. + * We don't block if not caught up, unless there is unsent data + * pending in which case we'd better block until the socket is + * write-ready. This test is only needed for the case where the + * send_data callback handled a subset of the available data but then + * pq_flush_if_writable flushed it all --- we should immediately try + * to send more. */ - if (pq_is_send_pending()) + if ((WalSndCaughtUp && !streamingDoneSending) || pq_is_send_pending()) { long sleeptime; int wakeEvents; wakeEvents = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT | - WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE; + WL_SOCKET_READABLE; /* * Use fresh timestamp, not last_processing, to reduce the chance @@ -2333,6 +2335,9 @@ WalSndLoop(WalSndSendDataCallback send_data) */ sleeptime = WalSndComputeSleeptime(GetCurrentTimestamp()); + if (pq_is_send_pending()) + wakeEvents |= WL_SOCKET_WRITEABLE; + /* Sleep until something happens or we time out */ (void) WaitLatchOrSocket(MyLatch, wakeEvents, MyProcPort->sock, sleeptime, --sm4nu43k4a2Rpi4c Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="WalSndLoop-stall-v2.patch" Author: Noah Misch Commit: Noah Misch In caught-up logical walsender, sleep only in WalSndWaitForWal(). Before sleeping, WalSndWaitForWal() sends a keepalive if MyWalSnd->write < sentPtr. When the latest physical LSN yields no logical replication messages (a common case), that keepalive elicits a reply. Processing the reply updates pg_stat_replication.replay_lsn. WalSndLoop() lacks that; when WalSndLoop() slept, replay_lsn advancement could stall until wal_receiver_status_interval elapsed. This sometimes stalled src/test/subscription/t/001_rep_changes.pl for up to 10s. Reviewed by FIXME. Discussion: https://postgr.es/m/FIXME diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 9e56115..a880195 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1428,8 +1428,10 @@ WalSndWaitForWal(XLogRecPtr loc) /* * We only send regular messages to the client for full decoded * transactions, but a synchronous replication and walsender shutdown - * possibly are waiting for a later location. So we send pings - * containing the flush location every now and then. + * possibly are waiting for a later location. So, before sleeping, we + * send a ping containing the flush location. If the receiver is + * otherwise idle, this keepalive will trigger a reply. Processing the + * reply will update these MyWalSnd locations. */ if (MyWalSnd->flush < sentPtr && MyWalSnd->write < sentPtr && @@ -2314,14 +2316,14 @@ WalSndLoop(WalSndSendDataCallback send_data) WalSndKeepaliveIfNecessary(); /* - * We don't block if not caught up, unless there is unsent data - * pending in which case we'd better block until the socket is - * write-ready. This test is only needed for the case where the - * send_data callback handled a subset of the available data but then - * pq_flush_if_writable flushed it all --- we should immediately try - * to send more. + * Block if we have unsent data. XXX For logical replication, let + * WalSndWaitForWal(), handle any other blocking; idle receivers need + * its additional actions. For physical replication, also block if + * caught up; its send_data does not block. */ - if ((WalSndCaughtUp && !streamingDoneSending) || pq_is_send_pending()) + if ((WalSndCaughtUp && send_data != XLogSendLogical && + !streamingDoneSending) || + pq_is_send_pending()) { long sleeptime; int wakeEvents; --sm4nu43k4a2Rpi4c--