• Alvaro Herrera's avatar
    Fix freezing of a dead HOT-updated tuple · 20b65522
    Alvaro Herrera authored
    Vacuum calls page-level HOT prune to remove dead HOT tuples before doing
    liveness checks (HeapTupleSatisfiesVacuum) on the remaining tuples.  But
    concurrent transaction commit/abort may turn DEAD some of the HOT tuples
    that survived the prune, before HeapTupleSatisfiesVacuum tests them.
    This happens to activate the code that decides to freeze the tuple ...
    which resuscitates it, duplicating data.
    
    (This is especially bad if there's any unique constraints, because those
    are now internally violated due to the duplicate entries, though you
    won't know until you try to REINDEX or dump/restore the table.)
    
    One possible fix would be to simply skip doing anything to the tuple,
    and hope that the next HOT prune would remove it.  But there is a
    problem: if the tuple is older than freeze horizon, this would leave an
    unfrozen XID behind, and if no HOT prune happens to clean it up before
    the containing pg_clog segment is truncated away, it'd later cause an
    error when the XID is looked up.
    
    Fix the problem by having the tuple freezing routines cope with the
    situation: don't freeze the tuple (and keep it dead).  In the cases that
    the XID is older than the freeze age, set the HEAP_XMAX_COMMITTED flag
    so that there is no need to look up the XID in pg_clog later on.
    
    An isolation test is included, authored by Michael Paquier, loosely
    based on Daniel Wood's original reproducer.  It only tests one
    particular scenario, though, not all the possible ways for this problem
    to surface; it be good to have a more reliable way to test this more
    fully, but it'd require more work.
    In message https://postgr.es/m/20170911140103.5akxptyrwgpc25bw@alvherre.pgsql
    I outlined another test case (more closely matching Dan Wood's) that
    exposed a few more ways for the problem to occur.
    
    Backpatch all the way back to 9.3, where this problem was introduced by
    multixact juggling.  In branches 9.3 and 9.4, this includes a backpatch
    of commit e5ff9fefcd50 (of 9.5 era), since the original is not
    correctable without matching the coding pattern in 9.5 up.
    
    Reported-by: Daniel Wood
    Diagnosed-by: Daniel Wood
    Reviewed-by: Yi Wen Wong, Michaël Paquier
    Discussion: https://postgr.es/m/E5711E62-8FDF-4DCA-A888-C200BF6B5742@amazon.com
    20b65522
isolation_schedule 1.48 KB