mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
Fix leaking of small spilled subtransactions during logical decoding.
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
This commit is contained in:
parent
b4166a8df9
commit
3bdea167eb
@ -893,7 +893,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn)
|
|||||||
{
|
{
|
||||||
ReorderBufferChange *cur_change;
|
ReorderBufferChange *cur_change;
|
||||||
|
|
||||||
if (txn->nentries != txn->nentries_mem)
|
if (txn->serialized)
|
||||||
{
|
{
|
||||||
/* serialize remaining changes */
|
/* serialize remaining changes */
|
||||||
ReorderBufferSerializeTXN(rb, txn);
|
ReorderBufferSerializeTXN(rb, txn);
|
||||||
@ -922,7 +922,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn)
|
|||||||
{
|
{
|
||||||
ReorderBufferChange *cur_change;
|
ReorderBufferChange *cur_change;
|
||||||
|
|
||||||
if (cur_txn->nentries != cur_txn->nentries_mem)
|
if (cur_txn->serialized)
|
||||||
{
|
{
|
||||||
/* serialize remaining changes */
|
/* serialize remaining changes */
|
||||||
ReorderBufferSerializeTXN(rb, cur_txn);
|
ReorderBufferSerializeTXN(rb, cur_txn);
|
||||||
@ -1142,7 +1142,7 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
|
|||||||
Assert(found);
|
Assert(found);
|
||||||
|
|
||||||
/* remove entries spilled to disk */
|
/* remove entries spilled to disk */
|
||||||
if (txn->nentries != txn->nentries_mem)
|
if (txn->serialized)
|
||||||
ReorderBufferRestoreCleanup(rb, txn);
|
ReorderBufferRestoreCleanup(rb, txn);
|
||||||
|
|
||||||
/* deallocate */
|
/* deallocate */
|
||||||
@ -2124,6 +2124,7 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
|
|||||||
Assert(spilled == txn->nentries_mem);
|
Assert(spilled == txn->nentries_mem);
|
||||||
Assert(dlist_is_empty(&txn->changes));
|
Assert(dlist_is_empty(&txn->changes));
|
||||||
txn->nentries_mem = 0;
|
txn->nentries_mem = 0;
|
||||||
|
txn->serialized = true;
|
||||||
|
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
CloseTransientFile(fd);
|
CloseTransientFile(fd);
|
||||||
|
@ -212,6 +212,15 @@ typedef struct ReorderBufferTXN
|
|||||||
*/
|
*/
|
||||||
uint64 nentries_mem;
|
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
|
* List of ReorderBufferChange structs, including new Snapshots and new
|
||||||
* CommandIds
|
* CommandIds
|
||||||
|
Loading…
x
Reference in New Issue
Block a user