Re: [BUGS] Bug in Physical Replication Slots (at least 9.5)?
Andres Freund <andres@anarazel.de>
From: Andres Freund <andres@anarazel.de>
To: Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>
Cc: michael.paquier@gmail.com, nag1010@gmail.com, jdnelson@dyn.com, pgsql-hackers@postgresql.org
Date: 2017-09-04T07:04:34Z
Lists: pgsql-bugs, pgsql-hackers
Hi,
On 2017-09-04 15:51:51 +0900, Kyotaro HORIGUCHI wrote:
> SpinLockAcquire(&walsnd->mutex);
> + oldFlushPtr = walsnd->flush;
> walsnd->write = writePtr;
> walsnd->flush = flushPtr;
> walsnd->apply = applyPtr;
> @@ -1821,7 +1836,93 @@ ProcessStandbyReplyMessage(void)
> if (SlotIsLogical(MyReplicationSlot))
> LogicalConfirmReceivedLocation(flushPtr);
> else
> - PhysicalConfirmReceivedLocation(flushPtr);
> + {
> + /*
> + * Recovery on standby requires that a continuation record is
> + * available from single WAL source. For the reason, physical
> + * replication slot should stay in the first segment of the
> + * multiple segments that a continued record is spanning
> + * over. Since we look pages and don't look into individual record
> + * here, restartLSN may stay a bit too behind but it doesn't
> + * matter.
> + *
> + * Since the objective is avoiding to remove required segments,
> + * checking at the beginning of every segment is enough. But once
> + * restartLSN goes behind, check every page for quick restoration.
> + *
> + * restartLSN has a valid value only when it is behind flushPtr.
> + */
> + bool check_contrecords = false;
> +
> + if (restartLSN != InvalidXLogRecPtr)
> + {
> + /*
> + * We're retarding restartLSN, check continuation records
> + * at every page boundary for quick restoration.
> + */
> + if (restartLSN / XLOG_BLCKSZ != flushPtr / XLOG_BLCKSZ)
> + check_contrecords = true;
> + }
> + else if (oldFlushPtr != InvalidXLogRecPtr)
> + {
> + /*
> + * We're not retarding restartLSN , check continuation records
> + * only at segment boundaries. No need to check if
> + */
> + if (oldFlushPtr / XLOG_SEG_SIZE != flushPtr / XLOG_SEG_SIZE)
> + check_contrecords = true;
> + }
> +
> + if (check_contrecords)
> + {
> + XLogRecPtr rp;
> +
> + if (restartLSN == InvalidXLogRecPtr)
> + restartLSN = oldFlushPtr;
> +
> + rp = restartLSN - (restartLSN % XLOG_BLCKSZ);
> +
> + /*
> + * We may have let the record at flushPtr be sent, so it's
> + * worth looking
> + */
> + while (rp <= flushPtr)
> + {
> + XLogPageHeaderData header;
> +
> + /*
> + * If the page header is not available for now, don't move
> + * restartLSN forward. We can read it by the next chance.
> + */
> + if(sentPtr - rp >= sizeof(XLogPageHeaderData))
> + {
> + bool found;
> + /*
> + * Fetch the page header of the next page. Move
> + * restartLSN forward only if it is not a continuation
> + * page.
> + */
> + found = XLogRead((char *)&header, rp,
> + sizeof(XLogPageHeaderData), true);
> + if (found &&
> + (header.xlp_info & XLP_FIRST_IS_CONTRECORD) == 0)
> + restartLSN = rp;
> + }
> + rp += XLOG_BLCKSZ;
> + }
> +
> + /*
> + * If restartLSN is on the same page with flushPtr, it means
> + * that we are catching up.
> + */
> + if (restartLSN / XLOG_BLCKSZ == flushPtr / XLOG_BLCKSZ)
> + restartLSN = InvalidXLogRecPtr;
> + }
> +
> + /* restartLSN == InvalidXLogRecPtr means catching up */
> + PhysicalConfirmReceivedLocation(restartLSN != InvalidXLogRecPtr ?
> + restartLSN : flushPtr);
> + }
I've not read through the thread, but this seems like the wrong approach
to me. The receiving side should use a correct value, instead of putting
this complexity on the sender's side.
Don't we also use the value on the receiving side to delete old segments
and such?
- Andres
Commits
-
Fix scenario where streaming standby gets stuck at a continuation record.
- c06380e97692 9.4.18 landed
- 4ea8f7d4553e 9.5.13 landed
- 7b7521d65772 9.6.9 landed
- ca572db22f62 10.4 landed
- 066871980183 11.0 landed