Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix failure to delete spill files of aborted transactions
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 5 Jan 2018 15:17:10 +0000 (12:17 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 5 Jan 2018 15:17:10 +0000 (12:17 -0300)
Logical decoding's reorderbuffer.c may spill transaction files to disk
when transactions are large.  These are supposed to be removed when they
become "too old" by xid; but file removal requires the boundary LSNs of
the transaction to be known.  The final_lsn is only set when we see the
commit or abort record for the transaction, but nothing sets the value
for transactions that crash, so the removal code misbehaves -- in
assertion-enabled builds, it crashes by a failed assertion.

To fix, modify the final_lsn of transactions that don't have a value
set, to the LSN of the very latest change in the transaction.  This
causes the spilled files to be removed appropriately.

Author: Atsushi Torikoshi
Reviewed-by: Kyotaro HORIGUCHI, Craig Ringer, Masahiko Sawada
Discussion: https://postgr.es/m/54e4e488-186b-a056-6628-50628e4e4ebc@lab.ntt.co.jp

src/backend/replication/logical/reorderbuffer.c
src/include/replication/reorderbuffer.h

index 742965d1d41d976780a721c96a8522a6c4e7b5c1..f2ebc5692ea2c9d8a73bcddc45b6a81f7685affc 100644 (file)
@@ -1617,8 +1617,8 @@ ReorderBufferAbortOld(ReorderBuffer *rb, TransactionId oldestRunningXid)
     * Iterate through all (potential) toplevel TXNs and abort all that are
     * older than what possibly can be running. Once we've found the first
     * that is alive we stop, there might be some that acquired an xid earlier
-    * but started writing later, but it's unlikely and they will cleaned up
-    * in a later call to ReorderBufferAbortOld().
+    * but started writing later, but it's unlikely and they will be cleaned
+    * up in a later call to this function.
     */
    dlist_foreach_modify(it, &rb->toplevel_by_lsn)
    {
@@ -1628,6 +1628,21 @@ ReorderBufferAbortOld(ReorderBuffer *rb, TransactionId oldestRunningXid)
 
        if (TransactionIdPrecedes(txn->xid, oldestRunningXid))
        {
+           /*
+            * We set final_lsn on a transaction when we decode its commit or
+            * abort record, but we never see those records for crashed
+            * transactions.  To ensure cleanup of these transactions, set
+            * final_lsn to that of their last change; this causes
+            * ReorderBufferRestoreCleanup to do the right thing.
+            */
+           if (txn->serialized && txn->final_lsn == 0)
+           {
+               ReorderBufferChange *last =
+                   dlist_tail_element(ReorderBufferChange, node, &txn->changes);
+
+               txn->final_lsn = last->lsn;
+           }
+
            elog(DEBUG2, "aborting old transaction %u", txn->xid);
 
            /* remove potential on-disk data, and deallocate this tx */
index 2ce2c2189f67c7bcea802976495292d32fae7253..17fe811d6e2f622fd79ad37171edda35075349af 100644 (file)
@@ -153,6 +153,8 @@ typedef struct ReorderBufferTXN
     * * plain abort record
     * * prepared transaction abort
     * * error during decoding
+    * * for a crashed transaction, the LSN of the last change, regardless of
+    *   what it was.
     * ----
     */
    XLogRecPtr  final_lsn;