On Wed, 11 Dec 2024 at 11:39, Zhijie Hou (Fujitsu)
<houzj.f...@fujitsu.com> wrote:
>
> On Wednesday, December 11, 2024 12:28 PM vignesh C <vignes...@gmail.com> 
> wrote:
> > Hi,
> >
> > I'm starting a new thread for one of the issues reported by Sawada-san at 
> > [1].
> >
> > This is a memory leak on CacheMemoryContext when using pgoutput via SQL
> > APIs:
> > /* Map must live as long as the session does. */
> > oldctx = MemoryContextSwitchTo(CacheMemoryContext);
> >
> > entry->attrmap = build_attrmap_by_name_if_req(indesc, outdesc, false);
> >
> > MemoryContextSwitchTo(oldctx);
> > RelationClose(ancestor);
> >
> > entry->attrmap is pfree'd only when validating the RelationSyncEntry
> > so remains even after logical decoding API calls.
> >
> > This issue can be reproduced with the following test:
> > -- Create table
> > create table t1( c1 int, c2 int, c3 int) PARTITION BY LIST (c1);
> > create table t1_1( c2 int, c1 int, c3 int);
> > ALTER TABLE t1 ATTACH PARTITION t1_1 FOR VALUES IN (0, 1, 2, 3);
> >
> > -- Create publication
> > create publication pub1 FOR TABLE t1, t1_1 WITH
> > (publish_via_partition_root = true);
> >
> > -- Create replication slot
> > SELECT 'init' FROM pg_create_logical_replication_slot('test', 'pgoutput');
> >
> > -- Run the below steps between Test start and Test end many times to
> > see the memory usage getting increased
> > -- Test start
> > insert into t1 values( 1);
> > SELECT count(*) FROM pg_logical_slot_get_binary_changes('test', NULL,
> > NULL, 'proto_version', '4', 'publication_names', 'pub1');
> >
> > -- Memory increases after each invalidation and insert
> > SELECT * FROM pg_backend_memory_contexts where name =
> > 'CacheMemoryContext';
> > -- Test end
> >
> > The attached patch resolves a memory leak by ensuring that the
> > attribute map is properly freed during plugin shutdown. This process
> > is triggered by the SQL API when the decoding context is being
> > released. Additionally, I am conducting a review to identify and
> > address any similar memory leaks that may exist elsewhere in the code.
>
> Thanks for reporting the issue and share the fix.
>
> I am not sure if freeing them in shutdown callback is safe, because shutdown
> callback Is not invoked in case of ERRORs. I think we'd better allocate them
> under cachectx in the beginning to avoid freeing them explicitly.
>
> The attachment is a POC that I internally experimented before. It replaces not
> only the attrmap's context, but also others which were allocated under
> CacheMemoryContext, to be consistent. Note that I have not tested it deeply.
> Feel free to use if it works for you.
>
> But the patch can only be used since PG15 where cachectx is introduced. In
> older braches, we may need to think some other ways.

Since we cannot add a new member to PGOutputData, how about creating a
new global static memory context specifically for storing the relation
entry attribute map? This memory context could be allocated and reset
during pgoutput_startup. This way, if a SQL call is made after an
error, the context will be reset, preventing memory bloat as addressed
in the attached patch. Thoughts?

Regards,
Vignesh
From d9c7159903bc9266a0846269c971631f36bb8748 Mon Sep 17 00:00:00 2001
From: Vignesh <vignes...@gmail.com>
Date: Thu, 12 Dec 2024 19:01:32 +0530
Subject: [PATCH vPG14_] Fix memory leak in pgoutput relation attribute map

The pgoutput module caches relation attribute map and frees it upon
invalidation.  However, this was not getting freed incase of SQL functions like
pg_logical_slot_{get,peek}_changes() which would bloat its cache memory usage.
Every pg_logical_slot_{get,peek}_changes() call for changes on partition table
creates more bloat. To address this issue, a new global static memory context
has been introduced, which is reset during pgoutput_startup to avoid
bloat after each pg_logical_slot_{get,peek}_changes() call.
---
 src/backend/replication/pgoutput/pgoutput.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 78c81888c4..ef2e6ab723 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -64,6 +64,7 @@ static void pgoutput_stream_commit(struct LogicalDecodingContext *ctx,
 
 static bool publications_valid;
 static bool in_streaming;
+static MemoryContext relentrycachectx = NULL;
 
 static List *LoadPublications(List *pubnames);
 static void publication_invalidation_cb(Datum arg, int cacheid,
@@ -345,6 +346,13 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
 		/* Disable the streaming during the slot initialization mode. */
 		ctx->streaming = false;
 	}
+
+	if (relentrycachectx == NULL)
+		relentrycachectx = AllocSetContextCreate(CacheMemoryContext,
+												 "logical replication relation entry cache context",
+												 ALLOCSET_SMALL_SIZES);
+	else
+		MemoryContextReset(relentrycachectx);
 }
 
 /*
@@ -464,7 +472,7 @@ maybe_send_schema(LogicalDecodingContext *ctx,
 		MemoryContext oldctx;
 
 		/* Map must live as long as the session does. */
-		oldctx = MemoryContextSwitchTo(CacheMemoryContext);
+		oldctx = MemoryContextSwitchTo(relentrycachectx);
 
 		/*
 		 * Make copies of the TupleDescs that will live as long as the map
-- 
2.43.0

Reply via email to