diff options
Diffstat (limited to 'src/backend/access/heap/heapam.c')
-rw-r--r-- | src/backend/access/heap/heapam.c | 91 |
1 files changed, 50 insertions, 41 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index af101689282..3acaf0266cb 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -5687,14 +5687,23 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask, Assert(TransactionIdIsValid(xid)); /* - * If the xid is older than the cutoff, it has to have aborted, - * otherwise the tuple would have gotten pruned away. + * The updating transaction cannot possibly be still running, but + * verify whether it has committed, and request to set the + * COMMITTED flag if so. (We normally don't see any tuples in + * this state, because they are removed by page pruning before we + * try to freeze the page; but this can happen if the updating + * transaction commits after the page is pruned but before + * HeapTupleSatisfiesVacuum). */ if (TransactionIdPrecedes(xid, cutoff_xid)) { - Assert(!TransactionIdDidCommit(xid)); - *flags |= FRM_INVALIDATE_XMAX; - xid = InvalidTransactionId; /* not strictly necessary */ + if (TransactionIdDidCommit(xid)) + *flags = FRM_MARK_COMMITTED | FRM_RETURN_IS_XID; + else + { + *flags |= FRM_INVALIDATE_XMAX; + xid = InvalidTransactionId; /* not strictly necessary */ + } } else { @@ -5765,18 +5774,17 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask, /* * It's an update; should we keep it? If the transaction is known - * aborted then it's okay to ignore it, otherwise not. However, - * if the Xid is older than the cutoff_xid, we must remove it. - * Note that such an old updater cannot possibly be committed, - * because HeapTupleSatisfiesVacuum would have returned - * HEAPTUPLE_DEAD and we would not be trying to freeze the tuple. - * - * Note the TransactionIdDidAbort() test is just an optimization - * and not strictly necessary for correctness. + * aborted or crashed then it's okay to ignore it, otherwise not. * * As with all tuple visibility routines, it's critical to test - * TransactionIdIsInProgress before the transam.c routines, + * TransactionIdIsInProgress before TransactionIdDidCommit, * because of race conditions explained in detail in tqual.c. + * + * We normally don't see committed updating transactions earlier + * than the cutoff xid, because they are removed by page pruning + * before we try to freeze the page; but it can happen if the + * updating transaction commits after the page is pruned but + * before HeapTupleSatisfiesVacuum. */ if (TransactionIdIsCurrentTransactionId(xid) || TransactionIdIsInProgress(xid)) @@ -5784,46 +5792,33 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask, Assert(!TransactionIdIsValid(update_xid)); update_xid = xid; } - else if (!TransactionIdDidAbort(xid)) + else if (TransactionIdDidCommit(xid)) { /* - * Test whether to tell caller to set HEAP_XMAX_COMMITTED - * while we have the Xid still in cache. Note this can only - * be done if the transaction is known not running. + * The transaction committed, so we can tell caller to set + * HEAP_XMAX_COMMITTED. (We can only do this because we know + * the transaction is not running.) */ - if (TransactionIdDidCommit(xid)) - update_committed = true; Assert(!TransactionIdIsValid(update_xid)); + update_committed = true; update_xid = xid; } /* + * Not in progress, not committed -- must be aborted or crashed; + * we can ignore it. + */ + + /* * If we determined that it's an Xid corresponding to an update * that must be retained, additionally add it to the list of - * members of the new Multis, in case we end up using that. (We + * members of the new Multi, in case we end up using that. (We * might still decide to use only an update Xid and not a multi, * but it's easier to maintain the list as we walk the old members * list.) - * - * It is possible to end up with a very old updater Xid that - * crashed and thus did not mark itself as aborted in pg_clog. - * That would manifest as a pre-cutoff Xid. Make sure to ignore - * it. */ if (TransactionIdIsValid(update_xid)) - { - if (!TransactionIdPrecedes(update_xid, cutoff_xid)) - { - newmembers[nnewmembers++] = members[i]; - } - else - { - /* cannot have committed: would be HEAPTUPLE_DEAD */ - Assert(!TransactionIdDidCommit(update_xid)); - update_xid = InvalidTransactionId; - update_committed = false; - } - } + newmembers[nnewmembers++] = members[i]; } else { @@ -5890,7 +5885,10 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask, * * It is assumed that the caller has checked the tuple with * HeapTupleSatisfiesVacuum() and determined that it is not HEAPTUPLE_DEAD - * (else we should be removing the tuple, not freezing it). + * (else we should be removing the tuple, not freezing it). However, note + * that we don't remove HOT tuples even if they are dead, and it'd be incorrect + * to freeze them (because that would make them visible), so we mark them as + * update-committed, and needing further freezing later on. * * NB: cutoff_xid *must* be <= the current global xmin, to ensure that any * XID older than it could neither be running nor seen as running by any @@ -5995,7 +5993,18 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid, else if (TransactionIdIsNormal(xid) && TransactionIdPrecedes(xid, cutoff_xid)) { - freeze_xmax = true; + /* + * Must freeze regular XIDs older than the cutoff. We must not freeze + * a HOT-updated tuple, though; doing so would bring it back to life. + */ + if (HeapTupleHeaderIsHotUpdated(tuple)) + { + frz->t_infomask |= HEAP_XMAX_COMMITTED; + changed = true; + /* must not freeze */ + } + else + freeze_xmax = true; } if (freeze_xmax) |