Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 406d618

Browse files
committed
SSI has a race condition, where the order of commit sequence numbers of
transactions might not match the order the work done in those transactions become visible to others. The logic in SSI, however, assumed that it does. Fix that by having two sequence numbers for each serializable transaction, one taken before a transaction becomes visible to others, and one after it. This is easier than trying to make the the transition totally atomic, which would require holding ProcArrayLock and SerializableXactHashLock at the same time. By using prepareSeqNo instead of commitSeqNo in a few places where commit sequence numbers are compared, we can make those comparisons err on the safe side when we don't know for sure which committed first. Per analysis by Kevin Grittner and Dan Ports, but this approach to fix it is different from the original patch.
1 parent d7fb493 commit 406d618

File tree

2 files changed

+30
-20
lines changed

2 files changed

+30
-20
lines changed

src/backend/storage/lmgr/predicate.c

+11-18
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ InitPredicateLocks(void)
11901190
}
11911191
PredXact->OldCommittedSxact = CreatePredXact();
11921192
SetInvalidVirtualTransactionId(PredXact->OldCommittedSxact->vxid);
1193+
PredXact->OldCommittedSxact->prepareSeqNo = 0;
11931194
PredXact->OldCommittedSxact->commitSeqNo = 0;
11941195
PredXact->OldCommittedSxact->SeqNo.lastCommitBeforeSnapshot = 0;
11951196
SHMQueueInit(&PredXact->OldCommittedSxact->outConflicts);
@@ -1650,6 +1651,7 @@ RegisterSerializableTransactionInt(Snapshot snapshot)
16501651
/* Initialize the structure. */
16511652
sxact->vxid = vxid;
16521653
sxact->SeqNo.lastCommitBeforeSnapshot = PredXact->LastSxactCommitSeqNo;
1654+
sxact->prepareSeqNo = InvalidSerCommitSeqNo;
16531655
sxact->commitSeqNo = InvalidSerCommitSeqNo;
16541656
SHMQueueInit(&(sxact->outConflicts));
16551657
SHMQueueInit(&(sxact->inConflicts));
@@ -3267,8 +3269,8 @@ ReleasePredicateLocks(bool isCommit)
32673269
&& SxactIsCommitted(conflict->sxactIn))
32683270
{
32693271
if ((MySerializableXact->flags & SXACT_FLAG_CONFLICT_OUT) == 0
3270-
|| conflict->sxactIn->commitSeqNo < MySerializableXact->SeqNo.earliestOutConflictCommit)
3271-
MySerializableXact->SeqNo.earliestOutConflictCommit = conflict->sxactIn->commitSeqNo;
3272+
|| conflict->sxactIn->prepareSeqNo < MySerializableXact->SeqNo.earliestOutConflictCommit)
3273+
MySerializableXact->SeqNo.earliestOutConflictCommit = conflict->sxactIn->prepareSeqNo;
32723274
MySerializableXact->flags |= SXACT_FLAG_CONFLICT_OUT;
32733275
}
32743276

@@ -4407,18 +4409,13 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
44074409
{
44084410
SERIALIZABLEXACT *t2 = conflict->sxactIn;
44094411

4410-
/*
4411-
* Note that if T2 is merely prepared but not yet committed, we
4412-
* rely on t->commitSeqNo being InvalidSerCommitSeqNo, which is
4413-
* larger than any valid commit sequence number.
4414-
*/
44154412
if (SxactIsPrepared(t2)
44164413
&& (!SxactIsCommitted(reader)
4417-
|| t2->commitSeqNo <= reader->commitSeqNo)
4414+
|| t2->prepareSeqNo <= reader->commitSeqNo)
44184415
&& (!SxactIsCommitted(writer)
4419-
|| t2->commitSeqNo <= writer->commitSeqNo)
4416+
|| t2->prepareSeqNo <= writer->commitSeqNo)
44204417
&& (!SxactIsReadOnly(reader)
4421-
|| t2->commitSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot))
4418+
|| t2->prepareSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot))
44224419
{
44234420
failure = true;
44244421
break;
@@ -4459,17 +4456,11 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
44594456
{
44604457
SERIALIZABLEXACT *t0 = conflict->sxactOut;
44614458

4462-
/*
4463-
* Note that if the writer is merely prepared but not yet
4464-
* committed, we rely on writer->commitSeqNo being
4465-
* InvalidSerCommitSeqNo, which is larger than any valid commit
4466-
* sequence number.
4467-
*/
44684459
if (!SxactIsDoomed(t0)
44694460
&& (!SxactIsCommitted(t0)
4470-
|| t0->commitSeqNo >= writer->commitSeqNo)
4461+
|| t0->commitSeqNo >= writer->prepareSeqNo)
44714462
&& (!SxactIsReadOnly(t0)
4472-
|| t0->SeqNo.lastCommitBeforeSnapshot >= writer->commitSeqNo))
4463+
|| t0->SeqNo.lastCommitBeforeSnapshot >= writer->prepareSeqNo))
44734464
{
44744465
failure = true;
44754466
break;
@@ -4608,6 +4599,7 @@ PreCommit_CheckForSerializationFailure(void)
46084599
offsetof(RWConflictData, inLink));
46094600
}
46104601

4602+
MySerializableXact->prepareSeqNo = ++(PredXact->LastSxactCommitSeqNo);
46114603
MySerializableXact->flags |= SXACT_FLAG_PREPARED;
46124604

46134605
LWLockRelease(SerializableXactHashLock);
@@ -4782,6 +4774,7 @@ predicatelock_twophase_recover(TransactionId xid, uint16 info,
47824774
sxact->pid = 0;
47834775

47844776
/* a prepared xact hasn't committed yet */
4777+
sxact->prepareSeqNo = RecoverySerCommitSeqNo;
47854778
sxact->commitSeqNo = InvalidSerCommitSeqNo;
47864779
sxact->finishedBefore = InvalidTransactionId;
47874780

src/include/storage/predicate_internals.h

+19-2
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,26 @@ typedef struct SERIALIZABLEXACT
5757
{
5858
VirtualTransactionId vxid; /* The executing process always has one of
5959
* these. */
60+
61+
/*
62+
* We use two numbers to track the order that transactions commit. Before
63+
* commit, a transaction is marked as prepared, and prepareSeqNo is set.
64+
* Shortly after commit, it's marked as committed, and commitSeqNo is set.
65+
* This doesn't give a strict commit order, but these two values together
66+
* are good enough for us, as we can always err on the safe side and
67+
* assume that there's a conflict, if we can't be sure of the exact
68+
* ordering of two commits.
69+
*
70+
* Note that a transaction is marked as prepared for a short period during
71+
* commit processing, even if two-phase commit is not used. But with
72+
* two-phase commit, a transaction can stay in prepared state for some
73+
* time.
74+
*/
75+
SerCommitSeqNo prepareSeqNo;
6076
SerCommitSeqNo commitSeqNo;
61-
union /* these values are not both interesting at
62-
* the same time */
77+
78+
/* these values are not both interesting at the same time */
79+
union
6380
{
6481
SerCommitSeqNo earliestOutConflictCommit; /* when committed with
6582
* conflict out */

0 commit comments

Comments
 (0)