Skip to content

Commit a786cf0

Browse files
committed
Fix memory leak in pgoutput with relation attribute map
pgoutput caches the attribute map of a relation, that is free()'d only when validating a RelationSyncEntry. However, this code path is not taken when calling any of the SQL functions able to do some logical decoding, like pg_logical_slot_{get,peek}_changes(), leaking some memory into CacheMemoryContext on repeated calls. This is a follow-up of c9b3d49, this time for v13 and v14. The relation attribute map is stored in a dedicated memory context, tracked with a static variable whose state is reset with a MemoryContext reset callback attached to PGOutputData->context. This implementation is similar to the approach taken by cfd6cbc. Reported-by: Masahiko Sawada Author: Vignesh C Reviewed-by: Hou Zhijie Discussion: https://postgr.es/m/CAD21AoDkAhQVSukOfH3_reuF-j4EU0-HxMqU3dU+bSTxsqT14Q@mail.gmail.com Discussion: https://postgr.es/m/CALDaNm1hewNAsZ_e6FF52a=9drmkRJxtEPrzCB6-9mkJyeBBqA@mail.gmail.com Backpatch-through: 13
1 parent a1d17a8 commit a786cf0

File tree

1 file changed

+20
-10
lines changed

1 file changed

+20
-10
lines changed

src/backend/replication/pgoutput/pgoutput.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ static bool pgoutput_origin_filter(LogicalDecodingContext *ctx,
5050
static bool publications_valid;
5151

5252
/*
53-
* Private memory context for publication data, created in
54-
* PGOutputData->context when starting pgoutput, and set to NULL when its
55-
* parent context is reset via a dedicated MemoryContextCallback.
53+
* Private memory contexts for publication data and relation attribute
54+
* map, created in PGOutputData->context when starting pgoutput, and set
55+
* to NULL when its parent context is reset via a dedicated
56+
* MemoryContextCallback.
5657
*/
5758
static MemoryContext pubctx = NULL;
59+
static MemoryContext cachectx = NULL;
5860

5961
static List *LoadPublications(List *pubnames);
6062
static void publication_invalidation_cb(Datum arg, int cacheid,
@@ -182,12 +184,14 @@ parse_output_parameters(List *options, uint32 *protocol_version,
182184
}
183185

184186
/*
185-
* Callback of PGOutputData->context in charge of cleaning pubctx.
187+
* Callback of PGOutputData->context in charge of cleaning pubctx and
188+
* cachectx.
186189
*/
187190
static void
188-
pgoutput_pubctx_reset_callback(void *arg)
191+
pgoutput_ctx_reset_callback(void *arg)
189192
{
190193
pubctx = NULL;
194+
cachectx = NULL;
191195
}
192196

193197
/*
@@ -211,8 +215,13 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
211215
"logical replication publication list context",
212216
ALLOCSET_SMALL_SIZES);
213217

218+
Assert(cachectx == NULL);
219+
cachectx = AllocSetContextCreate(ctx->context,
220+
"logical replication cache context",
221+
ALLOCSET_SMALL_SIZES);
222+
214223
mcallback = palloc0(sizeof(MemoryContextCallback));
215-
mcallback->func = pgoutput_pubctx_reset_callback;
224+
mcallback->func = pgoutput_ctx_reset_callback;
216225
MemoryContextRegisterResetCallback(ctx->context, mcallback);
217226

218227
ctx->output_plugin_private = data;
@@ -347,8 +356,8 @@ maybe_send_schema(LogicalDecodingContext *ctx,
347356
TupleDesc outdesc = RelationGetDescr(ancestor);
348357
MemoryContext oldctx;
349358

350-
/* Map must live as long as the session does. */
351-
oldctx = MemoryContextSwitchTo(CacheMemoryContext);
359+
/* Map must live as long as the logical decoding context. */
360+
oldctx = MemoryContextSwitchTo(cachectx);
352361

353362
/*
354363
* Make copies of the TupleDescs that will live as long as the map
@@ -613,8 +622,8 @@ pgoutput_origin_filter(LogicalDecodingContext *ctx,
613622
/*
614623
* Shutdown the output plugin.
615624
*
616-
* Note, we don't need to clean the data->context and pubctx as they are
617-
* child contexts of the ctx->context so they will be cleaned up by logical
625+
* Note, we don't need to clean the data->context, pubctx and cachectx as they
626+
* are child contexts of the ctx->context so they will be cleaned up by logical
618627
* decoding machinery.
619628
*/
620629
static void
@@ -628,6 +637,7 @@ pgoutput_shutdown(LogicalDecodingContext *ctx)
628637

629638
/* Better safe than sorry */
630639
pubctx = NULL;
640+
cachectx = NULL;
631641
}
632642

633643
/*

0 commit comments

Comments
 (0)