Thread
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Integrate FullTransactionIds deeper into two-phase code
- 62a17a92833d 19 (unreleased) landed
-
Merge copies of converting an XID to a FullTransactionId.
- 1587f7b9fc1a 17.3 landed
- 81772a495ec9 18.0 landed
-
Revert recent changes related to handling of 2PC files at recovery
- 4d6d7bdc0642 13.19 landed
- 060f9f5ea537 14.16 landed
- 42c900d31e57 15.11 landed
- 4d72357c40ee 16.7 landed
- d1bf86a62211 17.3 landed
- a6c70f68cdeb 18.0 landed
-
Fix failures with incorrect epoch handling for 2PC files at recovery
- c3de0f9eed38 17.3 landed
- 7e125b20eed6 18.0 landed
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Vitaly Davydov <v.davydov@postgrespro.ru> — 2024-12-25T16:18:18Z
Hi Michael, Thank you for the explanation and the patch! I'm happy, that I seem to be on the right way. On Wednesday, December 25, 2024 08:04 MSK, Michael Paquier <michael@paquier.xyz> wrote: > Vitaly, have you seen that in the wild as an effect of future 2PC files? I haven't heard about this problem in production, but I've encountered it when I did some development in two-phase functionality. I fixed it by swapping the if blocks and it solved my problem. It is pretty easy to reproduce it on the master branch: 1. Start an instance with enabled prepared transactions 2. Create a simple prepared transaction 3. Do checkpoint to write the transaction two-phase state into a file in pg_twophase subdirectory 4. Copy the created file, change its name to reflect a future xid (in my case: cp 00000000000002E8 00000000000FF2E8) 5. Commit the prepared transaction 6. Stop the instance with -m immediate 7. Start the instance After starting, you can get an error like "could not start server". In the log file you can find a message like: LOG: database system was not properly shut down; automatic recovery in progress FATAL: could not access status of transaction 1045224 DETAIL: Could not read from file "pg_xact/0000" at offset 253952: read too few bytes. 2024-12-25 18:38:30.606 MSK [795557] LOG: startup process (PID 795560) exited with exit code 1 I tried your patch and it seems the server is started successfully. But I've found another problem in my synthetic test - it can not remove the file with the following message: LOG: database system was not properly shut down; automatic recovery in progress WARNING: removing future two-phase state file for transaction 1045224 WARNING: could not remove file "pg_twophase/FFFFFFFF000FF2E8": No such file or directory LOG: redo starts at 0/1762978 The fill will never be removed automatically. I guess, it is because we incorrectly calculate the two-phase file name using TwoPhaseFilePath in RemoveTwoPhaseFile in this scenario. It can be fixed if to pass file path directly from RecoverPreparedTransactions or StandbyRecoverPreparedTransaction into ProcessTwoPhaseBuffer -> RemoveTwoPhaseFile. I did it in the proposed patch https://www.postgresql.org/message-id/cedbe-65e0c000-1-6db17700%40133269862 (it is incomplete now). With best regards, Vitaly
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2024-12-25T22:42:34Z
On Wed, Dec 25, 2024 at 07:18:18PM +0300, Vitaly Davydov wrote: > I tried your patch and it seems the server is started > successfully. But I've found another problem in my synthetic test - > it can not remove the file with the following message: > > LOG: database system was not properly shut down; automatic recovery in progress > WARNING: removing future two-phase state file for transaction 1045224 > WARNING: could not remove file "pg_twophase/FFFFFFFF000FF2E8": No such file or directory > LOG: redo starts at 0/1762978 That works properly up to v16~. I have slept over this problem and I think that we'd better backpatch the fix for the first problem. Now, I also want to add a test to provide coverage. As you say, that's simple: - Create an empty file with a future XID name - Restart the server. - Scan the logs for the WARNING where the future file is removed. > The fill will never be removed automatically. > I guess, it is because we incorrectly calculate the two-phase file > name using TwoPhaseFilePath in RemoveTwoPhaseFile in this > scenario. It can be fixed if to pass file path directly from > RecoverPreparedTransactions or StandbyRecoverPreparedTransaction > into ProcessTwoPhaseBuffer -> RemoveTwoPhaseFile. Yeah, I've noticed that as well. We are dealing with a second bug that affects 17~ caused by the switch to FullTransactionIds for 2PC file names, which had been introduced in 5a1dfde8334b (pretty sure that it is the culprit, did not bisect). Attempting to remove a future file triggers an assertion failure: #3 0x00007fed046324f0 in __GI_abort () at ./stdlib/abort.c:79 #4 0x0000563e68a9bd07 in ExceptionalCondition (conditionName=0x563e68c66fc3 "epoch > 0", fileName=0x563e68c66b0b "twophase.c", lineNumber=953) at assert.c:66 #5 0x0000563e67683060 in AdjustToFullTransactionId (xid=4095) at twophase.c:953 #6 0x0000563e67683092 in TwoPhaseFilePath (path=0x7ffcb515b4a0 "h\307!\227>V", xid=4095) at twophase.c:963 #7 0x0000563e67686603 in RemoveTwoPhaseFile (xid=4095, giveWarning=true) at twophase.c:1728 #8 0x0000563e67688989 in ProcessTwoPhaseBuffer (xid=4095, prepare_start_lsn=0, fromdisk=true, setParent=false, setNextXid=false) at twophase.c:2219 #9 0x0000563e676872a6 in restoreTwoPhaseData () at twophase.c:1924 #10 0x0000563e676b5c8b in StartupXLOG () at xlog.c:5642 So this means that we are dealing with two different bugs, and that we need to fix the assertion failure of the second problem first down to 17, then fix the first problem on all stable branches with a test to cover both the first and second problems. > I did it in the proposed patch > https://www.postgresql.org/message-id/cedbe-65e0c000-1-6db17700%40133269862 > (it is incomplete now). I suspect that this can be still dangerous as-is while complicating the code with more possible paths for the removal of the 2PC files, because we may still build a file name from an XID, and that's what's causing this issue.. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2024-12-26T05:34:48Z
On Thu, Dec 26, 2024 at 07:42:34AM +0900, Michael Paquier wrote: > I suspect that this can be still dangerous as-is while complicating > the code with more possible paths for the removal of the 2PC files, > because we may still build a file name from an XID, and that's what's > causing this issue.. Here are two patches to address both issues: - 0001 for the epoch calculation, down to 17, which would take care of the underflow problem when having a 2PC file that has an XID in the future at epoch 0. It is true that it could be smarter when dealing with files from other epochs, and that this is a problem in v17~. I think that this should integrate better with FullTransactionIds moving forward rather than pass the file names. That would be something quite invasive, only for HEAD. At least that's my impression. - 0002, to take care of the future file issue, down to 13. This includes a TAP test to demonstrate the problem. The test needs a tweak for the 2PC file name in v17~. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Vitaly Davydov <v.davydov@postgrespro.ru> — 2024-12-26T15:11:25Z
Hi Michael, > Here are two patches to address both issues: Thank you for the preparing the patches and test simplification. My bad, I overcompilcated it. I concerned about twophase file name generation. The function TwoPhaseFilePath() is pretty straitforward and unambiguous in 16 and earlier versions. The logic of this function in 17+ seems to be more complex. I do not understand it clearly. But, I guess, it will work incorrectly after turning to a newer epoch, because the epoch is calculated from TransamVariables->nextXid, but not from problem xid. The same problem may happen if we are in epoch 1 or greater. It will produce a wrong file name, because the epoch will be obtained from the last xid, not from file name xid. In another words, the function AdjustToFullTransactionId assumes that if xid > TransamVariables->nextXid, then the xid from the previous epoch. I may be not the case in our scenario. > I suspect that this can be still dangerous as-is while complicating the code with more possible paths for the removal of the 2PC files Agree, but we may pass file name into TwoPhaseFilePath if any, instead of creation of two functions as in the patch. The cost - two new if conditions. Using file names is pretty safe. Once we read the file and extract xid from its name, just pass this file name to TwoPhaseFilePath(). If not, try to generate it. Anyway, I do not insist on it, just try to discuss. With best regards, Vitaly
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2024-12-27T07:37:02Z
On Thu, Dec 26, 2024 at 06:11:25PM +0300, Давыдов Виталий wrote: > I concerned about twophase file name generation. The function > TwoPhaseFilePath() is pretty straitforward and unambiguous in 16 and > earlier versions. The logic of this function in 17+ seems to be more > complex. I do not understand it clearly. But, I guess, it will work > incorrectly after turning to a newer epoch, because the epoch is > calculated from TransamVariables->nextXid, but not from problem > xid. The same problem may happen if we are in epoch 1 or greater. It > will produce a wrong file name, because the epoch will be obtained > from the last xid, not from file name xid. In another words, the > function AdjustToFullTransactionId assumes that if xid > > TransamVariables->nextXid, then the xid from the previous epoch. I > may be not the case in our scenario. Yeah. At this stage, we can pretty much say that the whole idea of relying AdjustToFullTransactionId() is broken, because we would build 2PC file names based on wrong assumptions, while orphaned files could be in the far past or far future depending on the epoch. TBH, we'll live better if we remove AdjustToFullTransactionId() and sprinkle a bit more the knowledge of FullTransactionIds to build correctly the 2PC file path in v17~. I've been playing with this code for a couple of hours and finished with the attached patch. I have wondered if ReadTwoPhaseFile() should gain the same knowledge as TwoPhaseFilePath(), but decided to limit the invasiness because we always call ReadTwoPhaseFile() once we don't have any orphaned files, for all the phases of recovery. So while this requires a bit more logic that depends on FullTransactionIdFromEpochAndXid() and ReadNextFullTransactionId() to build a FullTransactionId and get the current epoch, that's still acceptable as we only store an XID in the 2PC file. So please see the attached. You will note that RemoveTwoPhaseFile(), ProcessTwoPhaseBuffer() and TwoPhaseFilePath() now require a FullTransactionId, hence callers need to think about the epoch to use. That should limit future errors compared to passing the file name as optional argument. What do you think? -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Vitaly Davydov <v.davydov@postgrespro.ru> — 2024-12-27T15:16:24Z
On Friday, December 27, 2024 10:37 MSK, Michael Paquier <michael@paquier.xyz> wrote: > So please see the attached. You will note that RemoveTwoPhaseFile(), > ProcessTwoPhaseBuffer() and TwoPhaseFilePath() now require a > FullTransactionId, hence callers need to think about the epoch to use. > That should limit future errors compared to passing the file name as > optional argument. In general, I like your solution to use FullTransactionId. I haven't found any evident problems. I just would like to propose to create a couple of new functions like RemoveTwoPhaseFileInCurrentEpoch, TwoPhaseFilePathInCurrentEpoch which accept TransactionId instead FullTransactionId. It may make the code clearer and result into less boilerplate code. I tried to do some hand testing. It seems, the problem is gone with the patch. Thank you! As an idea, I would like to propose to store FullTransactionId in global transaction state instead of TransactionId. I'm not sure, it will consume significant additional memory, but it make the code more clear and probably result into less number of locks. With best regards, Vitaly
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2024-12-30T01:08:31Z
On Fri, Dec 27, 2024 at 06:16:24PM +0300, Vitaly Davydov wrote: > In general, I like your solution to use FullTransactionId. I haven't > found any evident problems. I just would like to propose to create a > couple of new functions like RemoveTwoPhaseFileInCurrentEpoch, > TwoPhaseFilePathInCurrentEpoch which accept TransactionId instead > FullTransactionId. It may make the code clearer and result into less > boilerplate code. I tried to do some hand testing. It seems, the > problem is gone with the patch. Thanks for the review. Agreed that it would be a good thing to limit the number of paths calling ReadNextFullTransactionId(), but I did not like much the suggestion TwoPhaseFilePathInCurrentEpoch(), feeling that it was more important to keep a single code path in charge of building the file names. Instead, I have gone with a new FullTransactionIdFromCurrentEpoch() that replaces AdjustToFullTransactionId(). It cleans up most of the calls to ReadNextFullTransactionId() compared to the previous patch. It is true that these couple with calls to ProcessTwoPhaseBuffer(), but the result felt OK this way in the scope of this fix. > As an idea, I would like to propose to store FullTransactionId in > global transaction state instead of TransactionId. I'm not sure, it > will consume significant additional memory, but it make the code > more clear and probably result into less number of locks. FWIW, I was wondering about doing the same thing. However, I have concluded that this some refactoring work out of the scope of fixing the primary issue we have here, as we are hit by the way the file names are built when we attempt to remove them. Note that the memory footprint of storing a FullTransactionId in twophase.c's GlobalTransactionData does not worry me much. It is more important to not increase the size of the two-phase state data, impacting files on disk and their WAL records. This size argument has been mentioned on the thread that has added epochs to the 2PC file names, as far as I recall. At the end, I have applied two patches, the first one down to 13 that took care of the "future" issue, with tests added in the v14~v16 range. v13 was lacking a perl routine, and it's not a big deal to not have coverage for this code path anyway. The second patch has been applied down to v17, to fix the epoch issue, with the more advanced tests. If you have a suggestion of patch to plug in a FullTransactionId into GlobalTransactionData rather than a TransactionId, feel free to propose something! Help is always welcome, and this would be HEAD-only work, making it less urgent to deal with. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Vitaly Davydov <v.davydov@postgrespro.ru> — 2025-01-09T08:21:38Z
On Monday, December 30, 2024 04:08 MSK, Michael Paquier <michael@paquier.xyz> wrote: > Instead, I have gone with a new > FullTransactionIdFromCurrentEpoch() that replaces > AdjustToFullTransactionId(). It cleans up most of the calls to > ReadNextFullTransactionId() compared to the previous patch. It is > true that these couple with calls to ProcessTwoPhaseBuffer(), but the > result felt OK this way in the scope of this fix. Could you please send the latest version of the patch, if any? I haven't found any new patches in the latest email. Happy New Year! With best regards, Vitaly
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-10T04:49:48Z
On Thu, Jan 09, 2025 at 11:21:38AM +0300, Давыдов Виталий wrote: > Could you please send the latest version of the patch, if any? I haven't found any new patches in the latest email. I've applied fixes for this stuff as of e3584258154f (down to 13) and 7e125b20eed6 (down to 17) with tests for all the supported branches, meaning that we should be done here. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Noah Misch <noah@leadboat.com> — 2025-01-16T01:00:51Z
On Mon, Dec 30, 2024 at 10:08:31AM +0900, Michael Paquier wrote: > On Fri, Dec 27, 2024 at 06:16:24PM +0300, Vitaly Davydov wrote: > > As an idea, I would like to propose to store FullTransactionId in > > global transaction state instead of TransactionId. I'm not sure, it > > will consume significant additional memory, but it make the code > > more clear and probably result into less number of locks. > > FWIW, I was wondering about doing the same thing. However, I have > concluded that this some refactoring work out of the scope of fixing > the primary issue we have here, as we are hit by the way the file > names are built when we attempt to remove them. > > Note that the memory footprint of storing a FullTransactionId in > twophase.c's GlobalTransactionData does not worry me much. It is more > important to not increase the size of the two-phase state data, > impacting files on disk and their WAL records. This size argument > has been mentioned on the thread that has added epochs to the 2PC file > names, as far as I recall. I agree with accepting +4 bytes in GlobalTransactionData. > At the end, I have applied two patches, the first one down to 13 that > took care of the "future" issue, with tests added in the v14~v16 > range. v13 was lacking a perl routine, and it's not a big deal to not > have coverage for this code path anyway. The second patch has been > applied down to v17, to fix the epoch issue, with the more advanced > tests. > > If you have a suggestion of patch to plug in a FullTransactionId into > GlobalTransactionData rather than a TransactionId, feel free to > propose something! Help is always welcome, and this would be > HEAD-only work, making it less urgent to deal with. I suspect we should do that and back-patch to v17 before the next releases, because commit 7e125b2 wrote this function: /* * Compute FullTransactionId for the given TransactionId, using the current * epoch. */ static inline FullTransactionId FullTransactionIdFromCurrentEpoch(TransactionId xid) { FullTransactionId fxid; FullTransactionId nextFullXid; uint32 epoch; nextFullXid = ReadNextFullTransactionId(); epoch = EpochFromFullTransactionId(nextFullXid); fxid = FullTransactionIdFromEpochAndXid(epoch, xid); return fxid; } I think "using the current epoch" is wrong for half of the nextFullXid values having epoch > 0. For example, nextFullId==2^32 is in epoch 1, but all the allowable XIDs are in epoch 0. (I mean "allowable" in the sense of AssertTransactionIdInAllowableRange().) From then until we assign another 2^31 XIDs, epochs 0 and 1 are both expected in XID values. After 2^31 XID assignments, every allowable XID be in epoch 1. Hence, twophase.c would need two-epoch logic like we have in widen_snapshot_xid() and XLogRecGetFullXid(). Is that right? (I wrote this in a hurry, so this email may have more than the standard level of errors.) Before commit 7e125b2, twophase also had that logic. I didn't work out the user-visible consequences of that logic's new absence here, but I bet on twophase recovery breakage. Similar problem here (up to two epochs are acceptable, not just one): + /* Discard files from past epochs */ + if (EpochFromFullTransactionId(fxid) < EpochFromFullTransactionId(nextXid)) I wrote the attached half-baked patch to fix those, but I tend to think it's better to use FullTransactionId as many places as possible in twophase.c. (We'd still need to convert XIDs that we read from xl_xact_prepare records, along the lines of XLogRecGetFullXid().) How do you see it? -
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-16T07:50:09Z
On Wed, Jan 15, 2025 at 05:00:51PM -0800, Noah Misch wrote: > I agree with accepting +4 bytes in GlobalTransactionData. Let's just bite the bullet and do that on HEAD and v17, then, integrating deeper FullTransactionIds into the internals of twophase.c. > I think "using the current epoch" is wrong for half of the nextFullXid values > having epoch > 0. For example, nextFullId==2^32 is in epoch 1, but all the > allowable XIDs are in epoch 0. (I mean "allowable" in the sense of > AssertTransactionIdInAllowableRange().) From then until we assign another > 2^31 XIDs, epochs 0 and 1 are both expected in XID values. After 2^31 XID > assignments, every allowable XID be in epoch 1. Hence, twophase.c would need > two-epoch logic like we have in widen_snapshot_xid() and XLogRecGetFullXid(). > Is that right? (I wrote this in a hurry, so this email may have more than the > standard level of errors.) Before commit 7e125b2, twophase also had that > logic. I didn't work out the user-visible consequences of that logic's new > absence here, but I bet on twophase recovery breakage. Similar problem here > (up to two epochs are acceptable, not just one): > > + /* Discard files from past epochs */ > + if (EpochFromFullTransactionId(fxid) < EpochFromFullTransactionId(nextXid)) Oops, you're right. Your suggestion to unify all that in a single routine is an excellent idea. Missed the bits in xid8funcs.c. > I wrote the attached half-baked patch to fix those, but I tend to think it's > better to use FullTransactionId as many places as possible in twophase.c. > (We'd still need to convert XIDs that we read from xl_xact_prepare records, > along the lines of XLogRecGetFullXid().) How do you see it? I'm all for integrating more FullTransactionIds now that these reflect in the file names, and do a deeper cut. As far as I understand, the most important point of the logic is to detect and discard the future files first in restoreTwoPhaseData() -> ProcessTwoPhaseBuffer() when scanning the contents of pg_twophase at the beginning of recovery. Once this filtering is done, it should be safe to use your FullTransactionIdFromAllowableAt() when doing the fxid <-> xid transitions between the records and the files on disk flushed by a restartpoint which store an XID, and the shmem state of GlobalTransactionData with a fxid. With the additions attached, FullTransactionIdFromAllowableAt() gets down from 8 to 6 calls in twophase.c. The change related to MarkAsPreparingGuts() seems optional, though. I am trying to figure out how to write a regression test to trigger this error, lacking a bit of time today. That's going to require more trickery with pg_resetwal to make that cheap, or something like that.. Attached are some suggestions, as of a 0002 that applies on top of your 0001. XLogRecGetFullXid() is used nowhere. This could be removed, perhaps, or not? -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Noah Misch <noah@leadboat.com> — 2025-01-16T20:52:54Z
On Thu, Jan 16, 2025 at 04:50:09PM +0900, Michael Paquier wrote: > On Wed, Jan 15, 2025 at 05:00:51PM -0800, Noah Misch wrote: > > I think "using the current epoch" is wrong for half of the nextFullXid values > > having epoch > 0. For example, nextFullId==2^32 is in epoch 1, but all the > > allowable XIDs are in epoch 0. (I mean "allowable" in the sense of > > AssertTransactionIdInAllowableRange().) From then until we assign another > > 2^31 XIDs, epochs 0 and 1 are both expected in XID values. After 2^31 XID > > assignments, every allowable XID be in epoch 1. Hence, twophase.c would need > > two-epoch logic like we have in widen_snapshot_xid() and XLogRecGetFullXid(). > > Is that right? (I wrote this in a hurry, so this email may have more than the > > standard level of errors.) Before commit 7e125b2, twophase also had that > > logic. I didn't work out the user-visible consequences of that logic's new > > absence here, but I bet on twophase recovery breakage. Similar problem here > > (up to two epochs are acceptable, not just one): > > > > + /* Discard files from past epochs */ > > + if (EpochFromFullTransactionId(fxid) < EpochFromFullTransactionId(nextXid)) > > Oops, you're right. Your suggestion to unify all that in a single > routine is an excellent idea. Missed the bits in xid8funcs.c. Added. > > I wrote the attached half-baked patch to fix those, but I tend to think it's > > better to use FullTransactionId as many places as possible in twophase.c. > > (We'd still need to convert XIDs that we read from xl_xact_prepare records, > > along the lines of XLogRecGetFullXid().) How do you see it? > > I'm all for integrating more FullTransactionIds now that these reflect > in the file names, and do a deeper cut. > > As far as I understand, the most important point of the logic is to > detect and discard the future files first in restoreTwoPhaseData() -> > ProcessTwoPhaseBuffer() when scanning the contents of pg_twophase at > the beginning of recovery. Once this filtering is done, it should be > safe to use your FullTransactionIdFromAllowableAt() when doing > the fxid <-> xid transitions between the records and the files on disk > flushed by a restartpoint which store an XID, and the shmem state of > GlobalTransactionData with a fxid. (I did not expect that a function called restoreTwoPhaseData() would run before a function called PrescanPreparedTransactions(), but so it is.) How is it that restoreTwoPhaseData() -> ProcessTwoPhaseBuffer() can safely call TransactionIdDidAbort() when we've not replayed WAL to make CLOG consistent? What can it assume about the value of TransamVariables->nextXid at that early time? > With the additions attached, FullTransactionIdFromAllowableAt() gets > down from 8 to 6 calls in twophase.c. The change related to > MarkAsPreparingGuts() seems optional, though. Thanks. It's probably not worth doing at that level of reduction. I tried spreading fxid further and got it down to three conversions, corresponding to redo of each of XLOG_XACT_PREPARE, XLOG_XACT_COMMIT_PREPARED, and XLOG_XACT_ABORT_PREPARED. I'm attaching the WIP of that. It's not as satisfying as I expected, so FullTransactionIdFromCurrentEpoch-v0.patch may yet be the better direction. The ProcessTwoPhaseBuffer() code to remove "past two-phase state" seems best-effort, independent of this change. Just because an XID is in a potentially-acceptable epoch doesn't mean TransactionIdDidCommit() will find clog for it. I've not studied whether that matters. Incidentally, this comment about when a function is called: * PrescanPreparedTransactions * * Scan the shared memory entries of TwoPhaseState and determine the range * of valid XIDs present. This is run during database startup, after we * have completed reading WAL. TransamVariables->nextXid has been set to * one more than the highest XID for which evidence exists in WAL. doesn't match the timing of the actual earliest call: /* REDO */ if (InRecovery) { ... if (ArchiveRecoveryRequested && EnableHotStandby) { ... if (wasShutdown) oldestActiveXID = PrescanPreparedTransactions(&xids, &nxids); else oldestActiveXID = checkPoint.oldestActiveXid; ... PerformWalRecovery(); performedWalRecovery = true; > I am trying to figure > out how to write a regression test to trigger this error, lacking a > bit of time today. That's going to require more trickery with > pg_resetwal to make that cheap, or something like that.. Attached are > some suggestions, as of a 0002 that applies on top of your 0001. Thanks. I'd value having your regression test, but manual-ish testing could suffice if it's too hard. > XLogRecGetFullXid() is used nowhere. This could be removed, perhaps, > or not? Maybe. Looks like it was born unused in 67b9b3c (2019-07), so removing may as well be a separate discussion. -
Re: An improvement of ProcessTwoPhaseBuffer logic
Noah Misch <noah@leadboat.com> — 2025-01-17T00:52:21Z
On Thu, Jan 16, 2025 at 12:52:54PM -0800, Noah Misch wrote: > On Thu, Jan 16, 2025 at 04:50:09PM +0900, Michael Paquier wrote: > > As far as I understand, the most important point of the logic is to > > detect and discard the future files first in restoreTwoPhaseData() -> > > ProcessTwoPhaseBuffer() when scanning the contents of pg_twophase at > > the beginning of recovery. Once this filtering is done, it should be > > safe to use your FullTransactionIdFromAllowableAt() when doing > > the fxid <-> xid transitions between the records and the files on disk > > flushed by a restartpoint which store an XID, and the shmem state of > > GlobalTransactionData with a fxid. > > (I did not expect that a function called restoreTwoPhaseData() would run before > a function called PrescanPreparedTransactions(), but so it is.) > > How is it that restoreTwoPhaseData() -> ProcessTwoPhaseBuffer() can safely > call TransactionIdDidAbort() when we've not replayed WAL to make CLOG > consistent? What can it assume about the value of TransamVariables->nextXid > at that early time? I think it can't use CLOG safely. I regret answering my own question now as opposed to refraining from asking it, but hopefully this will save you time. Since EndPrepare() flushes the XLOG_XACT_PREPARE record before anything writes to pg_twophase, presence of a pg_twophase file tells us its record once existed in the flushed WAL stream. Even before we've started the server on a base backup, each pg_twophase file pertains to an XLOG_XACT_PREPARE LSN no later than the end-of-backup checkpoint. If a later file exists, the backup copied a file after the end-of-backup checkpoint started, which is a violation of the base backup spec. Elsewhere in recovery, we have the principle that data directory content means little until we reach a consistent state by replaying the end-of-backup checkpoint or by crash recovery reaching the end of WAL. For twophase, that calls for ignoring pg_twophase content. If a restartpoint has a pg_twophase file to write, we should allow that to overwrite an old file iff we've not reached consistency. (We must not read the old pg_twophase file, which may contain an unfinished write.) By the time we reach consistency, every file in pg_twophase will be applicable (not committed or aborted). Each file either predates the start-of-backup checkpoint (or crash recovery equivalent), or recovery wrote that file. We need readdir(pg_twophase) only at end of recovery, when we need _twophase_recover callbacks to restore enough state to accept writes. (During hot standby, state from XLOG_RUNNING_XACTS suffices[1] for the actions possible in that mode.) In other words, the root problem that led to commits e358425 and 7e125b2 was recovery interpreting pg_twophase file content before reaching consistency. We can't make the ProcessTwoPhaseBuffer() checks safe before reaching consistency. Is that right? Once ProcessTwoPhaseBuffer() stops running before consistency, it won't strictly need the range checks. We may as well have something like those range checks as a best-effort way to detect base backup spec violations. Thanks, nm [1] Incidentally, the comment lock_twophase_standby_recover incorrectly claims it runs "when starting up into hot standby mode."
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-17T02:04:03Z
On Thu, Jan 16, 2025 at 04:52:21PM -0800, Noah Misch wrote: > In other words, the root problem that led to commits e358425 and 7e125b2 was > recovery interpreting pg_twophase file content before reaching consistency. > We can't make the ProcessTwoPhaseBuffer() checks safe before reaching > consistency. Is that right? Once ProcessTwoPhaseBuffer() stops running > before consistency, it won't strictly need the range checks. We may as well > have something like those range checks as a best-effort way to detect base > backup spec violations. Oh, yeah, it seems like you have a very good point here. ProcessTwoPhaseBuffer() is an effort to use a unique path for the handling of this 2PC data, and we cannot do that safely at this stage. The problem is restoreTwoPhaseData(), which is called very early in the recovery process, while all the other code paths calling ProcessTwoPhaseBuffer() are done when we're doing reading WAL after we've reached consistency as far as I can see. This is a wrong concept since 728bd991c3c4. 5a1dfde8334b has made that much harder to think about, while not integrating fully things. It also looks like we may be able to just get rid of the CLOG checks entirely if we overwrite existing files as long as we are not in a consistent state, as you say. Hmm. I agree that e358425 and 7e125b2 are weird attempts that just try to work around the real issue, and that they're making some cases wrong while on it with potential file removals. +typedef void (*TwoPhaseCallback) (FullTransactionId fxid, uint16 info, void *recdata, uint32 len); Based on your latest patch, I doubt that we'll be able to do any of that in the back-branches. That's much nicer in the long term to show what this code relies on. I've been thinking about the strategy to use here, and here are some rough steps: - Let's first revert e358425 and 7e125b2. This indeed needs to be reworked, and there is a release coming by. - Your refactoring around xid8funcs.c is a good idea on its own. This can be an independent patch. - Let's figure out how much surgery is required with a focus on HEAD for now (I'm feeling that more non-backpatchable pieces are going to be needed here), then extract the relevant pieces that could be backpatchable. Hard to say if this is going to be doable at this stage, or even worth doing, but it will be possible to dig into the details once we're happy with the state of HEAD. My first intuition is that the risk of touching the back-branches may not be worth the potential reward based on the lack of field complaints (not seen customer cases, tbh): high risk, low reward. Another point to consider is if we'd better switch 2PC files to store a fxid rather than a xid.. Perhaps that's not necessary, but if we're using FullTransactionIds across the full stack of twophase.c that could make the whole better with even less xid <-> fxid translations in all these paths. There is always the counter-argument of the extra 4 bytes in the 2PC files and the records, though. -- Michael -
Re: An improvement of ProcessTwoPhaseBuffer logic
Noah Misch <noah@leadboat.com> — 2025-01-17T02:44:16Z
On Fri, Jan 17, 2025 at 11:04:03AM +0900, Michael Paquier wrote: > On Thu, Jan 16, 2025 at 04:52:21PM -0800, Noah Misch wrote: > > In other words, the root problem that led to commits e358425 and 7e125b2 was > > recovery interpreting pg_twophase file content before reaching consistency. > > We can't make the ProcessTwoPhaseBuffer() checks safe before reaching > > consistency. Is that right? Once ProcessTwoPhaseBuffer() stops running > > before consistency, it won't strictly need the range checks. We may as well > > have something like those range checks as a best-effort way to detect base > > backup spec violations. > > Oh, yeah, it seems like you have a very good point here. > ProcessTwoPhaseBuffer() is an effort to use a unique path for the > handling of this 2PC data, and we cannot do that safely at this stage. > The problem is restoreTwoPhaseData(), which is called very early in Agreed. > the recovery process, while all the other code paths calling > ProcessTwoPhaseBuffer() are done when we're doing reading WAL after > we've reached consistency as far as I can see. This is a wrong > concept since 728bd991c3c4. 5a1dfde8334b has made that much harder to > think about, while not integrating fully things. It also looks like > we may be able to just get rid of the CLOG checks entirely if we > overwrite existing files as long as we are not in a consistent state, > as you say. Hmm. > > I agree that e358425 and 7e125b2 are weird attempts that just try to > work around the real issue, and that they're making some cases wrong > while on it with potential file removals. > > +typedef void (*TwoPhaseCallback) (FullTransactionId fxid, uint16 info, > void *recdata, uint32 len); > > Based on your latest patch, I doubt that we'll be able to do any of > that in the back-branches. That's much nicer in the long term to show > what this code relies on. The twophase.c bits of that patch turned out to be orthogonal to the key issue, so that's fine. I suspect the signature changes would be okay to back-patch to v17, subject to PGXN confirmation of no extension using those APIs. (The TwoPhaseCallback list is hard-coded; extensions can't add callbacks.) That said, the rationale for back-patching fxid stuff is gone. > I've been thinking about the strategy to use here, and here are some > rough steps: > - Let's first revert e358425 and 7e125b2. This indeed needs to be > reworked, and there is a release coming by. +1. e358425 is borderline, but it's hard to rule out ways of it making recovery unlink pg_twophase files improperly. Released code may also be doing that, but that code's seven years of history suggests it does so infrequently if at all. We don't have as much evidence about what the frequency would be under e358425. > - Your refactoring around xid8funcs.c is a good idea on its own. This > can be an independent patch. I got here on a yak shave for postgr.es/m/20250111214454.9a.nmisch@google.com. I expect that project will still want FullTransactionIdFromAllowableAt(). If so, I'll include it in that thread's patch series. > - Let's figure out how much surgery is required with a focus on HEAD > for now (I'm feeling that more non-backpatchable pieces are going to > be needed here), then extract the relevant pieces that could be > backpatchable. Hard to say if this is going to be doable at this > stage, or even worth doing, but it will be possible to dig into the > details once we're happy with the state of HEAD. My first intuition > is that the risk of touching the back-branches may not be worth the > potential reward based on the lack of field complaints (not seen > customer cases, tbh): high risk, low reward. Let's see how it turns out. My first intuition would be to target a back-patch, because a corner-case PITR failure is the kind of thing that can go unreported and yet be hard to forgive. I'm not confident I could fix both that other thread's data loss bug and $SUBJECT in time for the 2024-02 releases. (By $SUBJECT, I mean the seven-year-old bug of interpreting pg_twophase before recovery consistency, which has a higher chance of causing spurious recovery failure starting in v17.) Would your or someone else like to fix $SUBJECT, before or after 2024-02 releases? I'd make time to review a patch. > Another point to consider is if we'd better switch 2PC files to store > a fxid rather than a xid.. Perhaps that's not necessary, but if we're > using FullTransactionIds across the full stack of twophase.c that > could make the whole better with even less xid <-> fxid translations > in all these paths. There is always the counter-argument of the extra > 4 bytes in the 2PC files and the records, though. Yes, perhaps so. It could also be an option to store it only in the pg_twophase file, not in the WAL record. Files are a lot rarer. Similarly, I wondered if pg_twophase files should contain the LSN of the XLOG_XACT_PREPARE record, which would make the file's oldness unambiguous in a way fxid doesn't achieve. I didn't come up with a problem whose solution requires it, though. Thanks, nm
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-17T03:16:15Z
On Thu, Jan 16, 2025 at 06:44:16PM -0800, Noah Misch wrote: > On Fri, Jan 17, 2025 at 11:04:03AM +0900, Michael Paquier wrote: >> +typedef void (*TwoPhaseCallback) (FullTransactionId fxid, uint16 info, >> void *recdata, uint32 len); >> >> Based on your latest patch, I doubt that we'll be able to do any of >> that in the back-branches. That's much nicer in the long term to show >> what this code relies on. > > The twophase.c bits of that patch turned out to be orthogonal to the key > issue, so that's fine. I suspect the signature changes would be okay to > back-patch to v17, subject to PGXN confirmation of no extension using those > APIs. (The TwoPhaseCallback list is hard-coded; extensions can't add > callbacks.) That said, the rationale for back-patching fxid stuff is gone. I would not bet on this part. >> - Let's first revert e358425 and 7e125b2. This indeed needs to be >> reworked, and there is a release coming by. > > +1. e358425 is borderline, but it's hard to rule out ways of it making > recovery unlink pg_twophase files improperly. Released code may also be doing > that, but that code's seven years of history suggests it does so infrequently > if at all. We don't have as much evidence about what the frequency would be > under e358425. Thanks. I'll got do that now as I can look at the buildfarm today, then look at the rest. >> - Your refactoring around xid8funcs.c is a good idea on its own. This >> can be an independent patch. > > I got here on a yak shave for postgr.es/m/20250111214454.9a.nmisch@google.com. > I expect that project will still want FullTransactionIdFromAllowableAt(). If > so, I'll include it in that thread's patch series. Okay. > I'm not confident I could fix both that other thread's data loss bug and > $SUBJECT in time for the 2024-02 releases. (By $SUBJECT, I mean the > seven-year-old bug of interpreting pg_twophase before recovery consistency, > which has a higher chance of causing spurious recovery failure starting in > v17.) Would your or someone else like to fix $SUBJECT, before or after > 2024-02 releases? I'd make time to review a patch. Yes, I can work on these two and dig into the pieces, once the state of the branch a bit cleaner. I'm not sure that a fully-ready patch will be able to emerge within two weeks as this may need some interations, but let's see. Backpatching something to remove the clog lookups does not seem that complicated, actually, now that I'm looking at it. Simpler in ~16 of course, but it does not seem that bad for 17~ as well if taken the simplest way. >> Another point to consider is if we'd better switch 2PC files to store >> a fxid rather than a xid.. Perhaps that's not necessary, but if we're >> using FullTransactionIds across the full stack of twophase.c that >> could make the whole better with even less xid <-> fxid translations >> in all these paths. There is always the counter-argument of the extra >> 4 bytes in the 2PC files and the records, though. > > Yes, perhaps so. It could also be an option to store it only in the > pg_twophase file, not in the WAL record. Files are a lot rarer. It would be slightly simpler to do both, I guess. Less translations between fxids and xids involved this way. > Similarly, I wondered if pg_twophase files should contain the LSN of the > XLOG_XACT_PREPARE record, which would make the file's oldness unambiguous in a > way fxid doesn't achieve. I didn't come up with a problem whose solution > requires it, though. Not sure about this one. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Vitaly Davydov <v.davydov@postgrespro.ru> — 2025-01-22T12:45:30Z
Dear Hackers, I hope you may find this patch worth to consider. You may consider this patch as one more step to a complete migration to FullTransactionId in twophase.c for 17+ versions. The patch fixes TwoPhaseFilePath function. The current version gets TransactionId as an argument and makes a path to the file with twophase transaction state. The problem - this logic is ambiguous, because xid doesn't contain the epoch. By replacing the argument with FullTransactionId, the logic of this function becomes straightforward. All the responsibility to properly form a full xid were delegated to the caller functions. The change in TwoPhaseFilePath implies to migrate to FullTransactionId in some other functions. There is a function AdjustToFullTransactionId (in the current master) which converts xid to full xid. The problem with this function is that it works properly only with those xids that are inside the current xid range which is defined by the xid horizon (wraparound), because it applies some assumptions concerning epoch definition (two epoches may be in action). I assume that if a xid is coming from in-memory states, it has to be in the current xid range. Based on this assumption, I would conclude that if the xid is coming via the interface (external) functions which are defined in twophase.h, AdjustToFullTransactionId can be applied to such xid. There is another story when we define xid from two phase file names, when reading pg_twophase directory. In 17+ twophase file names was changed to contain tx epoch as well. Once we work with twophase files, we have to use full xids. Function AdjustToFullTransactionId is not applicable in this case, because pg_twophase directory may contain any garbage files with future or past full xids which are outside of the current range. Based on these assumptions (AdjustToFullTransactionId or use full xids) some other functions were modified as shown in the patch. With best regards, Vitaly
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-27T05:59:51Z
On Thu, Jan 16, 2025 at 06:44:16PM -0800, Noah Misch wrote: > I got here on a yak shave for postgr.es/m/20250111214454.9a.nmisch@google.com. > I expect that project will still want FullTransactionIdFromAllowableAt(). If > so, I'll include it in that thread's patch series. Note that this one has been committed as 81772a495ec9 by Noah. I am going to spend some time looking at the rest. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-28T05:02:43Z
On Wed, Jan 22, 2025 at 03:45:30PM +0300, Vitaly Davydov wrote: > I hope you may find this patch worth to consider. You may consider > this patch as one more step to a complete migration to > FullTransactionId in twophase.c for 17+ versions. Heavy refactorings are only going to be something for 18~, as far as my analysis goes. > I assume that if a xid is coming from in-memory states, it has to be > in the current xid range. Based on this assumption, I would conclude > that if the xid is coming via the interface (external) functions > which are defined in twophase.h, AdjustToFullTransactionId can be > applied to such xid. > > There is another story when we define xid from two phase file names, > when reading pg_twophase directory. In 17+ twophase file names was > changed to contain tx epoch as well. Once we work with twophase > files, we have to use full xids. Function AdjustToFullTransactionId > is not applicable in this case, because pg_twophase directory may > contain any garbage files with future or past full xids which are > outside of the current range. > > Based on these assumptions (AdjustToFullTransactionId or use full > xids) some other functions were modified as shown in the patch. Yeah, we need to do something here. Anyway, I don't think what you are posting here is ambitious enough for two reasons: - We should try to limit the number of times where we do maths between TransactionId and FullTransactionId. It's going to be cleaner if we make the 2PC interface require FullTransactionIds all the time (note that the point of storing a fxid or an xid is different). Noah's proposal at [1] is much closer to the long-term picture that would look adapted. - The CLOG lookups that can happen in ProcessTwoPhaseBuffer() during recovery while a consistent state is not reached are still possible (planning to start a different thread about this specific issue). [1]: https://www.postgresql.org/message-id/20250116205254.65.nmisch@google.com -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Vitaly Davydov <v.davydov@postgrespro.ru> — 2025-01-29T12:00:54Z
It seems, there are much deeper problems with twophase transactions as I thought. I'm interested in fixing twophase transactions, because I support a solution which actively uses twophase transactions. I'm interested to get more deeply into the twophase functionality. Below, I just want to clarify some ideas behind twophase transactions. I appreciate, if you comment my point of view, or just ignore this email if you find it too naive and boring. Two phase files are created after checkpoint to keep twophase states on disk after WAL truncation. For transactions, that are inside the checkpoint horizon, we do not create two phase files because the states are currently stored in the WAL. Based on the thesis above, I guess, we have to read only those twophase files which are related to the transactions before the latest checkpoint. Its full xid should be lesser than TransamVariables->nextXid (which is the same as ControlFile->checkPointCopy.nextXid at the moment of StartupXLOG -> restoreTwoPhaseData call). The files with greater (or equal) full xids, should be ignored and removed. That's all what we need in restoreTwoPhaseData, I believe. In the current implementation, such check is applied in restoreTwoPhaseData -> ProcessTwoPhaseBuffer but after checking for xid in CLOG. I'm not sure, why we check CLOG here. Once we truncate the WAL on checkpoint and save twophase states into pg_twophase, these files must store states of real transactions from past. I mean, if someone creates a stub file with full xid < TransamVariables->nextXid, we have no means (except CLOG ?) to check that this file belongs to a real transaction from past. CLOG check seems to be a weak attempt to deal with it. At this point, I'm not sure that CLOG may contain states for all full xids of existing twophase files. I guess, we should call restoreTwoPhaseData at start of recovery but we shouldn't check CLOG at this stage. May be it is reasonable to check some not so old xids which are greater than the current CLOG horizon, but I'm not sure how CLOG segments are managed and how the horizon is moving. There is another question about the loading order of twophase files but I think it doesn't matter in which order we load these files. But I would prefer to load it in full xid ascending order. On Tuesday, January 28, 2025 08:02 MSK, Michael Paquier <michael@paquier.xyz> wrote: > Noah's > proposal at [1] is much closer to the long-term picture that would > look adapted. > - The CLOG lookups that can happen in ProcessTwoPhaseBuffer() during > recovery while a consistent state is not reached are still possible > (planning to start a different thread about this specific issue). > > [1]: https://www.postgresql.org/message-id/20250116205254.65.nmisch@google.com Agree, thank you, but my simple patch with some adjustments and swapping of checks in ProcessTwoPhaseBuffer may be back-ported. It doesn't fix all the problems but may help to fix the problem with twophase files related to broken latest WAL segments. With best regards, Vitaly
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-29T23:57:23Z
On Wed, Jan 29, 2025 at 03:00:54PM +0300, Vitaly Davydov wrote: > It seems, there are much deeper problems with twophase transactions > as I thought. I'm interested in fixing twophase transactions, > because I support a solution which actively uses twophase > transactions. I'm interested to get more deeply into the twophase > functionality. Below, I just want to clarify some ideas behind > twophase transactions. I appreciate, if you comment my point of > view, or just ignore this email if you find it too naive and boring. (Please be careful about your email indentation. These are hard to read.) > Two phase files are created after checkpoint to keep twophase states > on disk after WAL truncation. For transactions, that are inside the > checkpoint horizon, we do not create two phase files because the > states are currently stored in the WAL. Yeah, the oldest XID horizon stored in the checkpoint records would be stuck if you have a long-running 2PC transaction. It's actually something I have been brewing a bit for the last few days: we should discard early at recovery files that are older than the horizon in the checkpoint record we've retrieved at the beginning of recovery. So the early check can be tighter with past files. > Based on the thesis above, I guess, we have to read only those > twophase files which are related to the transactions before the > latest checkpoint. Its full xid should be lesser than > TransamVariables->nextXid (which is the same as > ControlFile->checkPointCopy.nextXid at the moment of StartupXLOG -> > restoreTwoPhaseData call). The files with greater (or equal) full > xids, should be ignored and removed. That's all what we need in > restoreTwoPhaseData, I believe. Yeah. It is important to do that only in restoreTwoPhaseData(). The check about already-aborted and already-committed 2PC files is something we have to keep as well. We should only do it at the end of recovery. > In the current implementation, such check is applied in > restoreTwoPhaseData -> ProcessTwoPhaseBuffer but after checking for > xid in CLOG. I'm not sure, why we check CLOG here. Once we truncate > the WAL on checkpoint and save twophase states into pg_twophase, > these files must store states of real transactions from past. Based on my analysis, the CLOG check ought to happen only in RecoverPreparedTransactions(), which is the last step taken at recovery. The future and past checks only need to happen in restoreTwoPhaseData(). I'm been bumping my head on my desk on this area for a few days now, but I think that the solution is simple: these checks should be moved *outside* ProcessTwoPhaseBuffer() and applied one layer higher in the two places I've mentioning above, where they matter. > There is another question about the loading order of twophase files > but I think it doesn't matter in which order we load these > files. But I would prefer to load it in full xid ascending order. I don't see a need in doing that in the short-term, but perhaps you have a point with the ordering of the entries in the TwoPhaseState shmem data if there are many entries to handle. Another idea would be to switch to a hash table. Note that I've actually found what looks like a potential data corruption bug in the logic while doing this investigation and adding TAP tests to automate all that: if we detect a 2PC file as already aborted or committed in ProcessTwoPhaseBuffer() while scanning TwoPhaseState->numPrepXacts, we could finish by calling PrepareRedoRemove(), which itself *manipulates* TwoPhaseState, decrementing numPrepXacts. This can leave dangling entries in TwoPhaseState if you have multiple TwoPhaseState entries (my implemented TAP tests have been doing exactly that). This is relevant at the end of recovery where CLOG lookups are OK to do. So the situation is a bit of a mess in all the branches, a bit worse in 17~ due to the fact that epochs are poorly integrated, but I'm getting there with a set of patches mostly ready to be sent, and I think that this would be OK for a backpatch. My plan is to start a new thread to deal with the matter, because that's a bit larger than the topic you have raised on this thread. I'll add you and Noah in CC for awareness. -- Michael
-
Re: An improvement of ProcessTwoPhaseBuffer logic
Michael Paquier <michael@paquier.xyz> — 2025-01-30T07:05:24Z
On Thu, Jan 30, 2025 at 08:57:23AM +0900, Michael Paquier wrote: > So the situation is a bit of a mess in all the branches, a bit worse > in 17~ due to the fact that epochs are poorly integrated, but I'm > getting there with a set of patches mostly ready to be sent, and I > think that this would be OK for a backpatch. My plan is to start a > new thread to deal with the matter, because that's a bit larger than > the topic you have raised on this thread. I'll add you and Noah in CC > for awareness. And done that here, with a closer lookup at everything I've bumped into: https://www.postgresql.org/message-id/Z5sd5O9JO7NYNK-C%40paquier.xyz -- Michael