From: Tomas Vondra Date: Fri, 2 May 2025 21:04:39 +0000 (+0200) Subject: Fix memory leak in _gin_parallel_merge X-Git-Tag: REL_18_BETA1~23 X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=1681a70df3d68b6f9dc82645f97f8d4668edc42f;p=postgresql.git Fix memory leak in _gin_parallel_merge To insert the merged GIN entries in _gin_parallel_merge, the leader calls ginEntryInsert(). This may allocate memory, e.g. for a new leaf tuple. This was allocated in the PortalContext, and kept until the end of the index build. For most GIN indexes the amount of leaked memory is negligible, but for custom opclasses with large keys it may cause OOMs. Fixed by calling ginEntryInsert() in a temporary memory context, reset after each insert. Other ginEntryInsert() callers do this too, except that the context is reset after batches of inserts. More frequent resets don't seem to hurt performance, it may even help it a bit. Report and fix by Vinod Sridharan. Author: Vinod Sridharan Reviewed-by: Tomas Vondra Discussion: https://postgr.es/m/CAFMdLD4p0VBd8JG=Nbi=BKv6rzFAiGJ_sXSFrw-2tNmNZFO5Kg@mail.gmail.com --- diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c index a7b7b5996e3..a65acd89104 100644 --- a/src/backend/access/gin/gininsert.c +++ b/src/backend/access/gin/gininsert.c @@ -1669,6 +1669,8 @@ _gin_parallel_merge(GinBuildState *state) */ while ((tup = tuplesort_getgintuple(state->bs_sortstate, &tuplen, true)) != NULL) { + MemoryContext oldCtx; + CHECK_FOR_INTERRUPTS(); /* @@ -1685,10 +1687,15 @@ _gin_parallel_merge(GinBuildState *state) */ AssertCheckItemPointers(buffer); + oldCtx = MemoryContextSwitchTo(state->tmpCtx); + ginEntryInsert(&state->ginstate, buffer->attnum, buffer->key, buffer->category, buffer->items, buffer->nitems, &state->buildStats); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->tmpCtx); + /* discard the existing data */ GinBufferReset(buffer); } @@ -1711,10 +1718,15 @@ _gin_parallel_merge(GinBuildState *state) */ AssertCheckItemPointers(buffer); + oldCtx = MemoryContextSwitchTo(state->tmpCtx); + ginEntryInsert(&state->ginstate, buffer->attnum, buffer->key, buffer->category, buffer->items, buffer->nfrozen, &state->buildStats); + MemoryContextSwitchTo(oldCtx); + MemoryContextReset(state->tmpCtx); + /* truncate the data we've just discarded */ GinBufferTrim(buffer); }