postgres.git / summary / log / commit / refs

commit    22665f2106248793cfd8dd0a49fe3c5f7a4bfdfe
Author:   Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date:     Mon Oct 21 06:49:21 2024 +0000
First released in: 18.0, 12.21, 13.17, 14.14, 15.9, 16.5, 17.1

    Fix race condition in committing a serializable transaction
    
    The finished transaction list can contain XIDs that are older than the
    serializable global xmin. It's a short-lived state;
    ClearOldPredicateLocks() removes any such transactions from the list,
    and it's called whenever the global xmin advances. But if another
    backend calls SummarizeOldestCommittedSxact() in that window, it will
    call SerialAdd() on an XID that's older than the global xmin, or if
    there are no more transactions running, when global xmin is
    invalid. That trips the assertion in SerialAdd().
    
    Fixes bug #18658 reported by Andrew Bille. Thanks to Alexander Lakhin
    for analysis. Backpatch to all versions.
    
    Discussion: https://www.postgresql.org/message-id/18658-7dab125ec688c70b%40postgresql.org


src/backend/storage/lmgr/predicate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index 1af41213b41..44c6a136d42 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -854,12 +854,17 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo) LWLockAcquire(SerialSLRULock, LW_EXCLUSIVE); /* - * If no serializable transactions are active, there shouldn't be anything - * to push out to the SLRU. Hitting this assert would mean there's - * something wrong with the earlier cleanup logic. + * If 'xid' is older than the global xmin (== tailXid), there's no need to + * store it, after all. This can happen if the oldest transaction holding + * back the global xmin just finished, making 'xid' uninteresting, but + * ClearOldPredicateLocks() has not yet run. */ tailXid = serialControl->tailXid; - Assert(TransactionIdIsValid(tailXid)); + if (!TransactionIdIsValid(tailXid) || TransactionIdPrecedes(xid, tailXid)) + { + LWLockRelease(SerialSLRULock); + return; + } /* * If the SLRU is currently unused, zero out the whole active region from [parent: 1257fea164ee]