Received: from malur.postgresql.org ([2a02:16a8:dc51::56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1fqHKc-0005D7-46 for pgsql-hackers@arkaria.postgresql.org; Thu, 16 Aug 2018 12:24:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1fqHKa-0002Qu-Iz for pgsql-hackers@arkaria.postgresql.org; Thu, 16 Aug 2018 12:24:48 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1fqHKa-0002Qn-4I for pgsql-hackers@lists.postgresql.org; Thu, 16 Aug 2018 12:24:48 +0000 Received: from mail-wr1-x442.google.com ([2a00:1450:4864:20::442]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1fqHKW-0003X4-4Q for pgsql-hackers@lists.postgresql.org; Thu, 16 Aug 2018 12:24:46 +0000 Received: by mail-wr1-x442.google.com with SMTP id c13-v6so3987538wrt.1 for ; Thu, 16 Aug 2018 05:24:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=subject:from:to:references:message-id:date:user-agent:mime-version :in-reply-to:content-language; bh=QnMWPAWxwe2qiyeoFs88pZL7wLe7s8oYw6OL1NPuwHY=; b=V7uWG+T0/eYG1LWDumolFi2/C2VfIHfJvFJH9ODKYVlveebGjNwnt/wFFbtDNuk/KQ GKlzoRZl2OmnhyOmtGQUcx9jQGI0f00Z5g9RcSh9i4lHg//IEQ/fg/KImyeYMqgqbxIP 96aL+0y5tZjImm7ZMKoEFq01UiaIty5avwKWl81YNDJ3nJtW0ZgZ/Aiym2CbnUkoaJO+ I7wqQyQAZWygzV1bDctscH40WKQPYak7uSLNlNoX4hgK/gAzRFysYcTjiisrr3mBcrnp u+uW1z0WosEZQ08pnaKwUsAqN0MHa3wI9+Tsuwj/wkDel15hUGWxtug81l9e11sma/hC bffg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:references:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=QnMWPAWxwe2qiyeoFs88pZL7wLe7s8oYw6OL1NPuwHY=; b=mnVVkfgiQNKFufzw26GuJpdlGr2S/OCTaoX/iJeYey5ICedim+3LnSaisW3/rkFb3P Ezmz4bCcRij3TCuzt5/ELxK9MFfGp561wl+qfs1AG1jH1jq0cXjHUl/qeviWUf6HXxcq RsuIrOBuCUwMv/elddz6yIQQr5xbnbxSBb7Z1JR0QcmKa5TFx6+t8t+jZpbvNk8Am/fO KyX/3Yfrw+ucrv2EBH6qGlf7n2lXLxiLueh6rodyXroRGXvH4KG5O3BK8InLo7PMWdO0 fpmS+bxaxRCrVWFkSc+pR+6oz0P3nYkoBd9XVWnya0vwqYPXp1ZqisfuzpbKU3lFEf40 oTyA== X-Gm-Message-State: AOUpUlHPoxyHakvwVbdUFH1G+encuF2tpxj/HH39avmDK1uymMf4MYZF ffURwanHUSEfHp5f5NQz1N+LrnzgZQlGrPFrwTgNshmNsX+KsPzRxXNsK6VxAykW1DnEWyIHXTY KfcmthbagDHwJKwRdFCu066FBAyadqQH17kw75gaGbPSA0BhdAdDPsUHjNBsUEE7gnfdjemUTcD fOK94Nkiic8UEKitmffqk= X-Google-Smtp-Source: AA+uWPzW9F3srX3F1eH4otL0P/Le6buge1V0yOEFXejPYO6AexOVbE8nYxHCnk+s1ySxjvYq7ZZRbg== X-Received: by 2002:adf:f9c9:: with SMTP id w9-v6mr19208514wrr.105.1534422281990; Thu, 16 Aug 2018 05:24:41 -0700 (PDT) Received: from [10.137.2.19] (cst-prg-30-77.cust.vodafone.cz. [46.135.30.77]) by smtp.gmail.com with ESMTPSA id t198-v6sm1763658wmd.16.2018.08.16.05.24.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 16 Aug 2018 05:24:40 -0700 (PDT) Subject: Re: memory leak when serializing TRUNCATE in reorderbuffer From: Tomas Vondra To: Peter Eisentraut , pgsql-hackers@lists.postgresql.org References: <66175a41-9342-2845-652f-1bd4c3ee50aa@2ndquadrant.com> <04cfcf19-1a43-5d85-918d-56a8620377c4@2ndquadrant.com> Message-ID: <9b17d019-7261-a5b0-1d65-caf30f69a891@2ndquadrant.com> Date: Thu, 16 Aug 2018 14:24:38 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.5.2 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------B42011C1817B226ACCA4B358" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------B42011C1817B226ACCA4B358 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit 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 --------------B42011C1817B226ACCA4B358 Content-Type: text/x-patch; name="truncate-decoding-v2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="truncate-decoding-v2.patch" diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c index 59c003de9c..6e46884194 100644 --- a/src/backend/replication/logical/decode.c +++ b/src/backend/replication/logical/decode.c @@ -859,7 +859,7 @@ DecodeTruncate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) if (xlrec->flags & XLH_TRUNCATE_RESTART_SEQS) change->data.truncate.restart_seqs = true; change->data.truncate.nrelids = xlrec->nrelids; - change->data.truncate.relids = palloc(xlrec->nrelids * sizeof(Oid)); + change->data.truncate.relids = ReorderBufferGetRelids(ctx->reorder, xlrec->nrelids); memcpy(change->data.truncate.relids, xlrec->relids, xlrec->nrelids * sizeof(Oid)); ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r), diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 1d43a165ad..0e41040ac2 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -409,10 +409,16 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change) } break; /* no data in addition to the struct itself */ + case REORDER_BUFFER_CHANGE_TRUNCATE: + if (change->data.truncate.relids != NULL) + { + ReorderBufferReturnRelids(rb, change->data.truncate.relids); + change->data.truncate.relids = NULL; + } + break; case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID: case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID: - case REORDER_BUFFER_CHANGE_TRUNCATE: break; } @@ -451,6 +457,35 @@ ReorderBufferReturnTupleBuf(ReorderBuffer *rb, ReorderBufferTupleBuf *tuple) } /* + * Get an array for relids of truncated relations. We use the global context + * for the whole reorder buffer - none of the existing ones seems like a good + * match (change/txn are SLAB, so we can't use them anyway, and tup_context + * is meant for tuple data, not for relids). We could add yet another context, + * but it seems like an overkill. So just use rb->context directly. + */ +Oid * +ReorderBufferGetRelids(ReorderBuffer *rb, int nrelids) +{ + Oid *relids; + Size alloc_len; + + alloc_len = sizeof(Oid) * nrelids; + + relids = (Oid *) MemoryContextAlloc(rb->context, alloc_len); + + return relids; +} + +/* + * Free an array of relids. + */ +void +ReorderBufferReturnRelids(ReorderBuffer *rb, Oid *relids) +{ + pfree(relids); +} + +/* * Return the ReorderBufferTXN from the given buffer, specified by Xid. * If create is true, and a transaction doesn't already exist, create it * (with the given LSN, and as top transaction if that's specified); @@ -2412,6 +2447,26 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn, break; } case REORDER_BUFFER_CHANGE_TRUNCATE: + { + Size size; + char *data; + + /* account for the OIDs of truncated relations */ + size = sizeof(Oid) * change->data.truncate.nrelids; + sz += size; + + /* make sure we have enough space */ + ReorderBufferSerializeReserve(rb, sz); + + data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange); + /* might have been reallocated above */ + ondisk = (ReorderBufferDiskChange *) rb->outbuf; + + memcpy(data, change->data.truncate.relids, size); + data += size; + + break; + } case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID: case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID: @@ -2695,6 +2750,16 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn, } /* the base struct contains all the data, easy peasy */ case REORDER_BUFFER_CHANGE_TRUNCATE: + { + Oid *relids; + + relids = ReorderBufferGetRelids(rb, + change->data.truncate.nrelids); + memcpy(relids, data, change->data.truncate.nrelids * sizeof(Oid)); + change->data.truncate.relids = relids; + + break; + } case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID: case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID: diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h index 1f52f6bde7..7787edf7b6 100644 --- a/src/include/replication/reorderbuffer.h +++ b/src/include/replication/reorderbuffer.h @@ -402,6 +402,9 @@ void ReorderBufferReturnTupleBuf(ReorderBuffer *, ReorderBufferTupleBuf *tuple) ReorderBufferChange *ReorderBufferGetChange(ReorderBuffer *); void ReorderBufferReturnChange(ReorderBuffer *, ReorderBufferChange *); +Oid * ReorderBufferGetRelids(ReorderBuffer *, int nrelids); +void ReorderBufferReturnRelids(ReorderBuffer *, Oid *relids); + void ReorderBufferQueueChange(ReorderBuffer *, TransactionId, XLogRecPtr lsn, ReorderBufferChange *); void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, Snapshot snapshot, XLogRecPtr lsn, bool transactional, const char *prefix, --------------B42011C1817B226ACCA4B358--