Hi Peter,

Thanks for the pointers.  I spent some time looking at both threads.

The standby subxact-overflow export issue seems separate from this patch: it is
about preserving the contents of a recovery snapshot during export/import.

Scott's recovery-conflict issue is much closer.  My v2 makes
ProcArrayInstallImportedXmin() take ProcArrayLock exclusively, which prevents a
snapshot import from installing xmin concurrently with a procarray scan such as
GetConflictingVirtualXIDs().  However, I don't think that alone fixes Scott's
case.

Consider this sequence:

1. Standby recovery is replaying a cleanup record with snapshot conflict
   horizon H.

2. Backend A has xmin <= H, so GetConflictingVirtualXIDs() returns A's VXID.

3. GetConflictingVirtualXIDs() releases ProcArrayLock.  The recovery conflict
   wait list is now fixed to A's VXID.

4. Before A actually ends, backend B imports A's exported snapshot.

5. With v2, B's import takes ProcArrayLock exclusively, but the procarray scan
   in step 2 has already finished.  So B can still install the same old xmin.

6. Recovery waits for, or cancels, only A's VXID.

7. Once A is gone, recovery can replay the cleanup record even though B is still
   running with the imported snapshot.

One possible way to close that remaining hole, other than rescanning, would be
to mark source PGPROCs that have been selected as snapshot-conflict waiters by
recovery, and make ProcArrayInstallImportedXmin() reject importing from such a
source.

The intended sequence would be:

1. Standby recovery calls GetConflictingVirtualXIDs(H).

2. While holding ProcArrayLock, GetConflictingVirtualXIDs() finds backend A with
   xmin <= H.

3. Before releasing ProcArrayLock, recovery sets a flag in A's PGPROC saying
   that A's current VXID has been selected as a snapshot-conflict waiter.

4. Later, backend B tries to import A's exported snapshot.

5. ProcArrayInstallImportedXmin() takes ProcArrayLock exclusively and finds A as
   the source VXID.

6. Because A's PGPROC says that its current VXID is already a recovery
   snapshot-conflict waiter, B's import is rejected instead of installing A's
   old xmin.

7. A can no longer create new conflicting importers after recovery has selected
   it for conflict resolution.  Once recovery has waited for or cancelled A, the
   original wait list is complete.

This relies on the exclusive ProcArrayLock in ProcArrayInstallImportedXmin():
while recovery is scanning the procarray and setting the flag, a concurrent
import cannot slip through before the flag becomes visible.

I have not included that in this patch because it seems to belong to Scott's
recovery-conflict thread, and it changes the scope of the fix.  But I can help
explore that direction there if people think it is preferable to rescanning.

Regards,
Chee


Reply via email to