Thread: memory leak when serializing TRUNCATE in reorderbuffer
Hi, While rebasing the logical replication patches on top of PG11, I've noticed that ReorderBufferSerializeChange claims this: case REORDER_BUFFER_CHANGE_TRUNCATE: ... /* ReorderBufferChange contains everything important */ That is not quite correct, though - the OIDs of truncated relations is stored in a separately palloc-ed array. So we only serialize the pointer to that array (which is part of ReorderBufferChange) and then read it back when restoring the change from disk. Now, this can't cause crashes, because the 'relids' array won't go away (more on this later), and so the pointer remains valid. But it's a memory leak - a quite small and not very common one, because people don't do TRUNCATE very often, particularly not with many tables. So I think we should fix and serialize/restore the OID array, just like we do for tuples, snapshots etc. See the attached fix. Another thing we should probably reconsider is where the relids is allocated - the pointer remains valid because we happen to allocate it in TopMemoryContext. It's not that bad because we don't free the other reorderbuffer contexts until the walsender exits anyway, but still. So I propose to allocate it in rb->context just like the other bits of data (snapshots, ...). Replacing the palloc() in DecodeTruncate() with something like: MemoryContextAlloc(ctx->reorder->context, xlrec->nrelids * sizeof(Oid)); should do the trick. The other places in decode.c don't allocate memory directly but call ReorderBufferGetTupleBuf() instead - perhaps we should introduce a similar wrapper here too. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachment
On 20/06/2018 21:42, Tomas Vondra wrote: > So I think we should fix and serialize/restore the OID array, just like > we do for tuples, snapshots etc. See the attached fix. Yes please. > Another thing we should probably reconsider is where the relids is > allocated - the pointer remains valid because we happen to allocate it > in TopMemoryContext. It's not that bad because we don't free the other > reorderbuffer contexts until the walsender exits anyway, but still. > > So I propose to allocate it in rb->context just like the other bits of > data (snapshots, ...). Replacing the palloc() in DecodeTruncate() with > something like: > > MemoryContextAlloc(ctx->reorder->context, > xlrec->nrelids * sizeof(Oid)); > > should do the trick. It's not clear from the code comments which context would be the appropriate one. More standard coding style would be to set the current memory context somewhere, but I suppose the reorderbuffer.c code isn't written that way. -- Peter Eisentraut http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 08/08/2018 09:19 PM, Peter Eisentraut wrote: > On 20/06/2018 21:42, Tomas Vondra wrote: >> So I think we should fix and serialize/restore the OID array, just like >> we do for tuples, snapshots etc. See the attached fix. > > Yes please. > OK, will do. >> Another thing we should probably reconsider is where the relids is >> allocated - the pointer remains valid because we happen to allocate it >> in TopMemoryContext. It's not that bad because we don't free the other >> reorderbuffer contexts until the walsender exits anyway, but still. >> >> So I propose to allocate it in rb->context just like the other bits of >> data (snapshots, ...). Replacing the palloc() in DecodeTruncate() with >> something like: >> >> MemoryContextAlloc(ctx->reorder->context, >> xlrec->nrelids * sizeof(Oid)); >> >> should do the trick. > > It's not clear from the code comments which context would be the > appropriate one. > > More standard coding style would be to set the current memory context > somewhere, but I suppose the reorderbuffer.c code isn't written that way. > IMHO the cleanest way is to add a method like ReorderBufferGetChange, which does the allocation internally. That way the memory context choice is up to reorderbuffer, not decode.c. That's at least consistent with what the rest of decode.c does. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 08/08/2018 09:47 PM, Tomas Vondra wrote: > > IMHO the cleanest way is to add a method like > ReorderBufferGetChange, which does the allocation internally. That > way the memory context choice is up to reorderbuffer, not decode.c. > That's at least consistent with what the rest of decode.c does. > OK, attached is a patch doing it along these lines. It introduces ReorderBufferGetRelids and ReorderBufferReturnRelids, which make the decision which context to use (so it's contained in reorderbuffer.c). Currently the main reorderbuffer context is used - we can't use the SLAB contexts, and the tup_context is meant for something else. I've considered adding yet another special-purpose context into reorderbuffer, but it seems like an overkill considering that TRUNCATE usually is not particularly common operation. So using the main context seems OK. I plan to commit this over the next couple of days, and backpatch it to pg11 where TRUNCATE decoding was introduced. It's clearly a memory leak, and there is no behavior change. Two more things I noticed while working on this: 1) We're using very different data types for nrelids on various places in the decoding. xl_heap_truncate uses uint32, logicalrep_write_truncate uses int and ReorderBufferChange uses Size. That seems somewhat strange, although it's unlikely to overflow (Who would truncate that many rels at the same time?). 2) The tup_context is allocated like this: buffer->tup_context = GenerationContextCreate(new_ctx, "Tuples", SLAB_LARGE_BLOCK_SIZE); Using SLAB_ constant for GenerationContextCreate is not a bug, but it's a bit confusing. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachment
I've pushed this, with some minor tweak, and backpatched to 11 (which is where TRUNCATE decoding was introduced). regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services