mirror of
https://github.com/postgres/postgres.git
synced 2026-06-09 00:32:10 -04:00
Fix race in ReplicationSlotRelease() for ephemeral slots
When releasing an ephemeral replication slot, ReplicationSlotRelease() drops the slot via ReplicationSlotDropAcquired(). However, after dropping the slot, ReplicationSlotRelease() continued to use its local "slot" pointer, which still referenced the dropped slot's former shared-memory entry. It could then update fields such as effective_xmin in that entry. Once an ephemeral slot has been dropped (via ReplicationSlotDropAcquired()), its slot array entry can be reused immediately by another backend creating a new slot. As a result, those updates could corrupt the state of an unrelated replication slot. Fix by skipping those shared-memory updates for phemeral slots and performing them only for non-ephemeral slots, whose shared-memory entries remain valid after release. Backpatch to all supported versions. Author: Zhijie Hou <houzj.fnst@fujitsu.com> Reviewed-by: Masao Fujii <masao.fujii@gmail.com> Reviewed-by: Srinath Reddy Sadipiralla <srinath2133@gmail.com> Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com> Discussion: https://postgr.es/m/TY4PR01MB177184FF9EE916F577E1F554194082@TY4PR01MB17718.jpnprd01.prod.outlook.com Backpatch-through: 14
This commit is contained in:
parent
eb8e76e130
commit
f2081a7800
1 changed files with 36 additions and 34 deletions
|
|
@ -788,44 +788,46 @@ ReplicationSlotRelease(void)
|
|||
*/
|
||||
ReplicationSlotDropAcquired(is_logical);
|
||||
}
|
||||
|
||||
/*
|
||||
* If slot needed to temporarily restrain both data and catalog xmin to
|
||||
* create the catalog snapshot, remove that temporary constraint.
|
||||
* Snapshots can only be exported while the initial snapshot is still
|
||||
* acquired.
|
||||
*/
|
||||
if (!TransactionIdIsValid(slot->data.xmin) &&
|
||||
TransactionIdIsValid(slot->effective_xmin))
|
||||
{
|
||||
SpinLockAcquire(&slot->mutex);
|
||||
slot->effective_xmin = InvalidTransactionId;
|
||||
SpinLockRelease(&slot->mutex);
|
||||
ReplicationSlotsComputeRequiredXmin(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the time since the slot has become inactive. We get the current
|
||||
* time beforehand to avoid system call while holding the spinlock.
|
||||
*/
|
||||
now = GetCurrentTimestamp();
|
||||
|
||||
if (slot->data.persistency == RS_PERSISTENT)
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Mark persistent slot inactive. We're not freeing it, just
|
||||
* disconnecting, but wake up others that may be waiting for it.
|
||||
* If slot needed to temporarily restrain both data and catalog xmin
|
||||
* to create the catalog snapshot, remove that temporary constraint.
|
||||
* Snapshots can only be exported while the initial snapshot is still
|
||||
* acquired.
|
||||
*/
|
||||
SpinLockAcquire(&slot->mutex);
|
||||
slot->active_proc = INVALID_PROC_NUMBER;
|
||||
ReplicationSlotSetInactiveSince(slot, now, false);
|
||||
SpinLockRelease(&slot->mutex);
|
||||
ConditionVariableBroadcast(&slot->active_cv);
|
||||
}
|
||||
else
|
||||
ReplicationSlotSetInactiveSince(slot, now, true);
|
||||
if (!TransactionIdIsValid(slot->data.xmin) &&
|
||||
TransactionIdIsValid(slot->effective_xmin))
|
||||
{
|
||||
SpinLockAcquire(&slot->mutex);
|
||||
slot->effective_xmin = InvalidTransactionId;
|
||||
SpinLockRelease(&slot->mutex);
|
||||
ReplicationSlotsComputeRequiredXmin(false);
|
||||
}
|
||||
|
||||
MyReplicationSlot = NULL;
|
||||
/*
|
||||
* Set the time since the slot has become inactive. We get the current
|
||||
* time beforehand to avoid system call while holding the spinlock.
|
||||
*/
|
||||
now = GetCurrentTimestamp();
|
||||
|
||||
if (slot->data.persistency == RS_PERSISTENT)
|
||||
{
|
||||
/*
|
||||
* Mark persistent slot inactive. We're not freeing it, just
|
||||
* disconnecting, but wake up others that may be waiting for it.
|
||||
*/
|
||||
SpinLockAcquire(&slot->mutex);
|
||||
slot->active_proc = INVALID_PROC_NUMBER;
|
||||
ReplicationSlotSetInactiveSince(slot, now, false);
|
||||
SpinLockRelease(&slot->mutex);
|
||||
ConditionVariableBroadcast(&slot->active_cv);
|
||||
}
|
||||
else
|
||||
ReplicationSlotSetInactiveSince(slot, now, true);
|
||||
|
||||
MyReplicationSlot = NULL;
|
||||
}
|
||||
|
||||
/* might not have been set when we've been a plain slot */
|
||||
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||
|
|
|
|||
Loading…
Reference in a new issue