[COMMITTERS] pgsql: Fix freezing of a dead HOT-updated tuple - Mailing list pgsql-committers

From Alvaro Herrera
Subject [COMMITTERS] pgsql: Fix freezing of a dead HOT-updated tuple
Date
Msg-id E1dxa6T-00005t-5G@gemulon.postgresql.org
Whole thread Raw
List pgsql-committers
Fix freezing of a dead HOT-updated tuple

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

Branch
------
REL9_3_STABLE

Details
-------
https://git.postgresql.org/pg/commitdiff/d149aa762c05ba904e77f8cf27da7ad821f5ecd0

Modified Files
--------------
src/backend/access/heap/heapam.c                |  91 +++++++++++----------
src/backend/commands/vacuumlazy.c               |  16 ++--
src/test/isolation/expected/freeze-the-dead.out | 101 ++++++++++++++++++++++++
src/test/isolation/isolation_schedule           |   1 +
src/test/isolation/specs/freeze-the-dead.spec   |  27 +++++++
5 files changed, 187 insertions(+), 49 deletions(-)


--
Sent via pgsql-committers mailing list (pgsql-committers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-committers

pgsql-committers by date:

Previous
From: Peter Eisentraut
Date:
Subject: [COMMITTERS] pgsql: Run only top-level recursive lcov
Next
From: Peter Geoghegan
Date:
Subject: Re: [COMMITTERS] pgsql: Fix freezing of a dead HOT-updated tuple