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 1fVizx-0000lI-VS for pgsql-hackers@arkaria.postgresql.org; Wed, 20 Jun 2018 19:42:34 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1fVizv-0000EY-Eo for pgsql-hackers@arkaria.postgresql.org; Wed, 20 Jun 2018 19:42:31 +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 1fVizv-0000EO-0K for pgsql-hackers@lists.postgresql.org; Wed, 20 Jun 2018 19:42:31 +0000 Received: from mail-wr0-x242.google.com ([2a00:1450:400c:c0c::242]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1fVizn-00005w-3v for pgsql-hackers@lists.postgresql.org; Wed, 20 Jun 2018 19:42:29 +0000 Received: by mail-wr0-x242.google.com with SMTP id x4-v6so667043wro.11 for ; Wed, 20 Jun 2018 12:42:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=to:from:subject:message-id:date:user-agent:mime-version :content-language; bh=X/QiyPh70FyOCFtihV5UsBHwMwrcbnCn4UwUeCrd1QI=; b=z5KzPFEm5KjwiYH2eRQmt6SJ0wPJe1SlOb5/xl8/aX9en3/T/Mf2ReqK3FRU4UVOEt E0EqFBp9nTgkRsZNF9/Rsh9Wj++tBtJqeKkrI6O1thsr3c2abjKbgcL2G8b1C9dDT/j9 f6aelwUDYIq+liLsTFhHiJ2EU37QHMIyu/+Ws54NpIkrKTPZlcXIoZKtdA6HxX5Oe/MS kzejAQmJ3m+RPaOV7P9MG0iIwU45K224WpY7CjWYjiH/yscLuTv2t6AigLEBJ88nRnsD BS12gX/dyS+Huhti5k/ew7vSjFNf54kEvQhOt/mIjbULEQ+fgsfJ3qRzB5E5cQ8Mm8NJ eIVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version:content-language; bh=X/QiyPh70FyOCFtihV5UsBHwMwrcbnCn4UwUeCrd1QI=; b=Yd5gCS+6Mf5Jcvqo0sUdmMCuDYsBv8IemmXrnNHRyF+QQvef8CEkAcvakJhZVJH4ll ojNW8+Unw2v/1I/Qc9xF6co05pU5mxGaVeKL3/XS9F3cyHMqRX65sql1T+xMZN+hE9yr t0eMh7nGkNNfxKGX02T0Fe5XIJDiyiq/6mlS74ucYEvCXisTEuk1vO9DHTCCCwyYwNHn 9XfddG7ofJYS/LgaR9iDsjFpc6GcEq894OY3dNn8zxJjLnRQhW7el2K56WxyjUa/VBrR jX8MaeN+zc4yiXiHPvbdSSw6rWg5jFuiuOZeJQceYhuYzwKEaW3IkXAI6qLI4Oo6x1qT /jRQ== X-Gm-Message-State: APt69E09r/e8yC6xp99yyswBuVC+mPg5MTi8xVArafr66Lbp534zYA3g asb4QJmpiM2MHDXtLCk5W+w/m85uqdxiKmav3cplcZU1gc6mNgAUoVMlfDMy2iYMnA9a4sYszDZ hA1aBL6rsy3whJpL/LLaYt3xu6wU3etgWiV1eUo06yf7xr7mXOHjIVhohbuja9ZKsz3DDgLwM1+ UCfWlNTmi89zJvecn6 X-Google-Smtp-Source: ADUXVKKGMR9PdyuR5QSFV1IEOGhXx9rzmNux9c1i2pNXNGe0u+PzWR0wOl8xaSwMDGd4XpQhL5WZ8Q== X-Received: by 2002:adf:f045:: with SMTP id t5-v6mr17985863wro.260.1529523740870; Wed, 20 Jun 2018 12:42:20 -0700 (PDT) Received: from [10.137.2.21] (static-84-42-175-93.net.upcbroadband.cz. [84.42.175.93]) by smtp.gmail.com with ESMTPSA id y3-v6sm3269654wro.21.2018.06.20.12.42.19 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 20 Jun 2018 12:42:20 -0700 (PDT) To: pgsql-hackers@lists.postgresql.org From: Tomas Vondra Subject: memory leak when serializing TRUNCATE in reorderbuffer Message-ID: <66175a41-9342-2845-652f-1bd4c3ee50aa@2ndquadrant.com> Date: Wed, 20 Jun 2018 21:42:17 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.5.2 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------D1D14DDF9F38C54071BFB59F" 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. --------------D1D14DDF9F38C54071BFB59F Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit 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 --------------D1D14DDF9F38C54071BFB59F Content-Type: text/x-patch; name="truncate-spill-fix.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="truncate-spill-fix.patch" diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index e2f59bf580..582bedf1de 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -412,10 +412,14 @@ 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) + pfree(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; } @@ -2289,6 +2293,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: @@ -2569,6 +2593,16 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn, } /* the base struct contains all the data, easy peasy */ case REORDER_BUFFER_CHANGE_TRUNCATE: + { + Size size; + + size = change->data.truncate.nrelids * sizeof(Oid); + + change->data.truncate.relids = MemoryContextAllocZero(rb->context, size); + + memcpy(change->data.truncate.relids, data, size); + break; + } case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM: case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID: case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID: --------------D1D14DDF9F38C54071BFB59F--