]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix leaking of small spilled subtransactions during logical decoding.
authorAndres Freund <andres@anarazel.de>
Mon, 19 Jun 2017 01:48:22 +0000 (18:48 -0700)
committerAndres Freund <andres@anarazel.de>
Mon, 19 Jun 2017 02:13:50 +0000 (19:13 -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 a924772964dca24d87ead7ff39ffdbe7b9633642..ccbbcddc0b185dfcdce330c29772376ddb75e3d7 100644 (file)
@@ -873,7 +873,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn)
        {
                ReorderBufferChange *cur_change;
 
-               if (txn->nentries != txn->nentries_mem)
+               if (txn->serialized)
                {
                        /* serialize remaining changes */
                        ReorderBufferSerializeTXN(rb, txn);
@@ -902,7 +902,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);
@@ -1124,7 +1124,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 */
@@ -2086,6 +2086,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 fe35edf311255c3924080de1f043d4556400e924..b7c86614472ffbdf358addf0dc1b75c6ee9447ee 100644 (file)
@@ -206,6 +206,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