Fix leaking of small spilled subtransactions during logical decoding.
authorAndres Freund
Mon, 19 Jun 2017 01:48:22 +0000 (18:48 -0700)
committerAndres Freund
Mon, 19 Jun 2017 02:12:56 +0000 (19:12 -0700)
When, during logical decoding, a transaction gets too big, it's
contents get spilled to disk. Not just the top-transaction gets
spilled, but *also* all of its subtransactions, even if they're not
that large themselves.  Unfortunately we didn't clean up
such small spilled subtransactions from disk.

Fix that, by keeping better track of whether a transaction has been
spilled to disk.

Author: Andres Freund
Reported-By: Dmitriy Sarafannikov, Fabrízio de Royes Mello
Discussion:
    https://postgr.es/m/1457621358.355011041@f382.i.mail.ru
    https://postgr.es/m/CAFcNs+qNMhNYii4nxpO6gqsndiyxNDYV0S=JNq0v_sEE+9PHXg@mail.gmail.com
Backpatch: 9.4-, where logical decoding was introduced

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

index 524946a2a273ba91d102f28fc1a1469e0babfd56..8f4f7f842bc2cf57a42afa70c90ea9702d93f62b 100644 (file)
@@ -893,7 +893,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn)
    {
        ReorderBufferChange *cur_change;
 
-       if (txn->nentries != txn->nentries_mem)
+       if (txn->serialized)
        {
            /* serialize remaining changes */
            ReorderBufferSerializeTXN(rb, txn);
@@ -922,7 +922,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn)
        {
            ReorderBufferChange *cur_change;
 
-           if (cur_txn->nentries != cur_txn->nentries_mem)
+           if (cur_txn->serialized)
            {
                /* serialize remaining changes */
                ReorderBufferSerializeTXN(rb, cur_txn);
@@ -1142,7 +1142,7 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
    Assert(found);
 
    /* remove entries spilled to disk */
-   if (txn->nentries != txn->nentries_mem)
+   if (txn->serialized)
        ReorderBufferRestoreCleanup(rb, txn);
 
    /* deallocate */
@@ -2124,6 +2124,7 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
    Assert(spilled == txn->nentries_mem);
    Assert(dlist_is_empty(&txn->changes));
    txn->nentries_mem = 0;
+   txn->serialized = true;
 
    if (fd != -1)
        CloseTransientFile(fd);
index 17e47b385b7b83ae30c089ed8b8e78f5f5c0d05e..8d8d418e8e397e6ba15ea4e4942e74792a0a9904 100644 (file)
@@ -212,6 +212,15 @@ typedef struct ReorderBufferTXN
     */
    uint64      nentries_mem;
 
+   /*
+    * Has this transaction been spilled to disk?  It's not always possible to
+    * deduce that fact by comparing nentries with nentries_mem, because
+    * e.g. subtransactions of a large transaction might get serialized
+    * together with the parent - if they're restored to memory they'd have
+    * nentries_mem == nentries.
+    */
+   bool        serialized;
+
    /*
     * List of ReorderBufferChange structs, including new Snapshots and new
     * CommandIds