Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1ikTIf-0004I0-JL for pgsql-hackers@arkaria.postgresql.org; Thu, 26 Dec 2019 13:35:37 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1ikTIe-0004JU-0v for pgsql-hackers@arkaria.postgresql.org; Thu, 26 Dec 2019 13:35:36 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1ikTId-0004JN-FJ for pgsql-hackers@lists.postgresql.org; Thu, 26 Dec 2019 13:35:35 +0000 Received: from cyclops.postgrespro.ru ([93.174.131.138] helo=mail.postgrespro.ru) by makus.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1ikTIa-0008RT-OL for pgsql-hackers@postgresql.org; Thu, 26 Dec 2019 13:35:34 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.postgrespro.ru (Postfix) with ESMTP id ABD5A21C48C3; Thu, 26 Dec 2019 16:35:31 +0300 (MSK) X-Virus-Scanned: Debian amavisd-new at postgrespro.ru X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=x tagged_above=-99 required=4 WHITELISTED tests=[] autolearn=unavailable Received: from [192.168.27.223] (gw.postgrespro.ru [93.174.131.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) by mail.postgrespro.ru (Postfix) with ESMTPSA id 439EA21C3EA3; Thu, 26 Dec 2019 16:35:31 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mail; t=1577367331; bh=g7nBNj1XqkQKX3X709RkfbCOShEsQhzyUwCASOCG+a8=; h=Subject:To:Cc:References:From:Date:In-Reply-To; b=c6n5af9oO92GA/CUzYmxrrVReaiAxX5KbsWGMyGvBmk/6yH1rf0n7G3mMxq7zl3Jl iLD5bZZcuRf/R2eg6AqmsR1qDZBx+m89K4jUAb/5J2QFD9LPIUe4kBUmw14DZYlkUH XgPstyygJvYZ8wUDktpXR+09AHc0rnfPo8Lyt5Mw= Subject: Re: Physical replication slot advance is not persistent To: Kyotaro Horiguchi Cc: pgsql-hackers@postgresql.org, michael@paquier.xyz, simon@2ndquadrant.com References: <20191225.130337.442597881772691026.horikyota.ntt@gmail.com> <24b9a157-d95e-96c2-2fde-9cf73f05008c@postgrespro.ru> <20191226.173349.2159296357637287391.horikyota.ntt@gmail.com> From: Alexey Kondratov Message-ID: <682b1960-cb89-8738-dbdf-8033662fb80f@postgrespro.ru> Date: Thu, 26 Dec 2019 16:35:31 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.2.2 MIME-Version: 1.0 In-Reply-To: <20191226.173349.2159296357637287391.horikyota.ntt@gmail.com> Content-Type: multipart/mixed; boundary="------------B598E29D2E1288944F99A73C" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------B598E29D2E1288944F99A73C Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 8bit On 26.12.2019 11:33, Kyotaro Horiguchi wrote: > At Wed, 25 Dec 2019 20:28:04 +0300, Alexey Kondratov wrote in >>> Yep, it helps with physical replication slot persistence after >>> advance, but the whole validation (moveto <= endlsn) does not make >>> sense for me. The value of moveto should be >= than minlsn == >>> confirmed_flush / restart_lsn, while endlsn == retlsn is also always >>> initialized with confirmed_flush / restart_lsn. Thus, your condition >>> seems to be true in any case, even if it was no-op one, which we were >>> intended to catch. > ... >> If I get it correctly, then we already keep previous slot position in >> the minlsn, so we just have to compare endlsn with minlsn and treat >> endlsn <= minlsn as a no-op without slot state flushing. > I think you're right about the condition. (endlsn cannot be less than > minlsn, though) But I came to think that we shouldn't use locations in > that decision. > >> Attached is a patch that does this, so it fixes the bug without >> affecting any user-facing behavior. Detailed comment section and DEBUG >> output are also added. What do you think now? >> >> I have also forgotten to mention that all versions down to 11.0 should >> be affected with this bug. > pg_replication_slot_advance is the only caller of > pg_logical/physical_replication_slot_advacne so there's no apparent > determinant on who-does-what about dirtying and other housekeeping > calculation like *ComputeRequired*() functions, but the current shape > seems a kind of inconsistent between logical and physical. > > I think pg_logaical/physical_replication_slot_advance should dirty the > slot if they actually changed anything. And > pg_replication_slot_advance should do the housekeeping if the slots > are dirtied. (Otherwise both the caller function should dirty the > slot in lieu of the two.) > > The attached does that. Both approaches looks fine for me: my last patch with as minimal intervention as possible and yours refactoring. I think that it is a right direction to let everyone who modifies slot->data also mark slot as dirty. I found some comment section in your code as rather misleading: +        /* +         * We don't need to dirty the slot only for the above change, but dirty +         * this slot for the same reason with +         * pg_logical_replication_slot_advance. +         */ We just modified MyReplicationSlot->data, which is "On-Disk data of a replication slot, preserved across restarts.", so it definitely should be marked as dirty, not because pg_logical_replication_slot_advance does the same. Also I think that using this transient variable in ReplicationSlotIsDirty is not necessary. MyReplicationSlot is already a pointer to the slot in shared memory. +    ReplicationSlot *slot = MyReplicationSlot; + +    Assert(MyReplicationSlot != NULL); + +    SpinLockAcquire(&slot->mutex); Otherwise it looks fine for me, so attached is the same diff, but with these proposed corrections. Another concern is that ReplicationSlotIsDirty is added with the only one user. It also cannot be used by SaveSlotToPath due to the simultaneous usage of both flags dirty and just_dirtied there. In that way, I hope that we should call ReplicationSlotSave unconditionally in the pg_replication_slot_advance, so slot will be saved or not automatically based on the slot->dirty flag. In the same time, ReplicationSlotsComputeRequiredXmin and ReplicationSlotsComputeRequiredLSN should be called by anyone, who modifies xmin and LSN fields in the slot. Otherwise, currently we are getting some leaky abstractions. Regards -- Alexey Kondratov Postgres Professional https://www.postgrespro.com Russian Postgres Company --------------B598E29D2E1288944F99A73C Content-Type: text/x-patch; charset=UTF-8; name="0001-Make-sure-to-save-updated-physical-slot-v2.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-Make-sure-to-save-updated-physical-slot-v2.diff" diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 21ae8531b3..edf661521a 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -672,6 +672,23 @@ ReplicationSlotMarkDirty(void) SpinLockRelease(&slot->mutex); } +/* + * Verify whether currently acquired slot is dirty. + */ +bool +ReplicationSlotIsDirty(void) +{ + bool dirty; + + Assert(MyReplicationSlot != NULL); + + SpinLockAcquire(&MyReplicationSlot->mutex); + dirty = MyReplicationSlot->dirty; + SpinLockRelease(&MyReplicationSlot->mutex); + + return dirty; +} + /* * Convert a slot that's marked as RS_EPHEMERAL to a RS_PERSISTENT slot, * guaranteeing it will be there after an eventual crash. diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index 6683fc3f9b..d7a16a9071 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -370,6 +370,12 @@ pg_physical_replication_slot_advance(XLogRecPtr moveto) MyReplicationSlot->data.restart_lsn = moveto; SpinLockRelease(&MyReplicationSlot->mutex); retlsn = moveto; + + /* + * Dirty the slot as we updated data that is meant to be + * persistent on disk. + */ + ReplicationSlotMarkDirty(); } return retlsn; @@ -574,9 +580,8 @@ pg_replication_slot_advance(PG_FUNCTION_ARGS) nulls[0] = false; /* Update the on disk state when lsn was updated. */ - if (XLogRecPtrIsInvalid(endlsn)) + if (ReplicationSlotIsDirty()) { - ReplicationSlotMarkDirty(); ReplicationSlotsComputeRequiredXmin(false); ReplicationSlotsComputeRequiredLSN(); ReplicationSlotSave(); diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h index 3a5763fb07..f76b84571f 100644 --- a/src/include/replication/slot.h +++ b/src/include/replication/slot.h @@ -189,6 +189,7 @@ extern void ReplicationSlotRelease(void); extern void ReplicationSlotCleanup(void); extern void ReplicationSlotSave(void); extern void ReplicationSlotMarkDirty(void); +extern bool ReplicationSlotIsDirty(void); /* misc stuff */ extern bool ReplicationSlotValidateName(const char *name, int elevel); --------------B598E29D2E1288944F99A73C--