Skip to content

Commit 50a38f6

Browse files
committed
Create memory context for HashAgg with a reasonable maxBlockSize.
If the memory context's maxBlockSize is too big, a single block allocation can suddenly exceed work_mem. For Hash Aggregation, this can mean spilling to disk too early or reporting a confusing memory usage number for EXPLAN ANALYZE. Introduce CreateWorkExprContext(), which is like CreateExprContext(), except that it creates the AllocSet with a maxBlockSize that is reasonable in proportion to work_mem. Right now, CreateWorkExprContext() is only used by Hash Aggregation, but it may be generally useful in the future. Discussion: https://postgr.es/m/412a3fbf306f84d8d78c4009e11791867e62b87c.camel@j-davis.com
1 parent f0705bb commit 50a38f6

File tree

3 files changed

+58
-19
lines changed

3 files changed

+58
-19
lines changed

src/backend/executor/execUtils.c

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "executor/executor.h"
5454
#include "jit/jit.h"
5555
#include "mb/pg_wchar.h"
56+
#include "miscadmin.h"
5657
#include "nodes/nodeFuncs.h"
5758
#include "parser/parsetree.h"
5859
#include "partitioning/partdesc.h"
@@ -227,21 +228,13 @@ FreeExecutorState(EState *estate)
227228
MemoryContextDelete(estate->es_query_cxt);
228229
}
229230

230-
/* ----------------
231-
* CreateExprContext
232-
*
233-
* Create a context for expression evaluation within an EState.
234-
*
235-
* An executor run may require multiple ExprContexts (we usually make one
236-
* for each Plan node, and a separate one for per-output-tuple processing
237-
* such as constraint checking). Each ExprContext has its own "per-tuple"
238-
* memory context.
239-
*
240-
* Note we make no assumption about the caller's memory context.
241-
* ----------------
231+
/*
232+
* Internal implementation for CreateExprContext() and CreateWorkExprContext()
233+
* that allows control over the AllocSet parameters.
242234
*/
243-
ExprContext *
244-
CreateExprContext(EState *estate)
235+
static ExprContext *
236+
CreateExprContextInternal(EState *estate, Size minContextSize,
237+
Size initBlockSize, Size maxBlockSize)
245238
{
246239
ExprContext *econtext;
247240
MemoryContext oldcontext;
@@ -264,7 +257,9 @@ CreateExprContext(EState *estate)
264257
econtext->ecxt_per_tuple_memory =
265258
AllocSetContextCreate(estate->es_query_cxt,
266259
"ExprContext",
267-
ALLOCSET_DEFAULT_SIZES);
260+
minContextSize,
261+
initBlockSize,
262+
maxBlockSize);
268263

269264
econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
270265
econtext->ecxt_param_list_info = estate->es_param_list_info;
@@ -294,6 +289,52 @@ CreateExprContext(EState *estate)
294289
return econtext;
295290
}
296291

292+
/* ----------------
293+
* CreateExprContext
294+
*
295+
* Create a context for expression evaluation within an EState.
296+
*
297+
* An executor run may require multiple ExprContexts (we usually make one
298+
* for each Plan node, and a separate one for per-output-tuple processing
299+
* such as constraint checking). Each ExprContext has its own "per-tuple"
300+
* memory context.
301+
*
302+
* Note we make no assumption about the caller's memory context.
303+
* ----------------
304+
*/
305+
ExprContext *
306+
CreateExprContext(EState *estate)
307+
{
308+
return CreateExprContextInternal(estate, ALLOCSET_DEFAULT_SIZES);
309+
}
310+
311+
312+
/* ----------------
313+
* CreateWorkExprContext
314+
*
315+
* Like CreateExprContext, but specifies the AllocSet sizes to be reasonable
316+
* in proportion to work_mem. If the maximum block allocation size is too
317+
* large, it's easy to skip right past work_mem with a single allocation.
318+
* ----------------
319+
*/
320+
ExprContext *
321+
CreateWorkExprContext(EState *estate)
322+
{
323+
Size minContextSize = ALLOCSET_DEFAULT_MINSIZE;
324+
Size initBlockSize = ALLOCSET_DEFAULT_INITSIZE;
325+
Size maxBlockSize = ALLOCSET_DEFAULT_MAXSIZE;
326+
327+
/* choose the maxBlockSize to be no larger than 1/16 of work_mem */
328+
while (16 * maxBlockSize > work_mem * 1024L)
329+
maxBlockSize >>= 1;
330+
331+
if (maxBlockSize < ALLOCSET_DEFAULT_INITSIZE)
332+
maxBlockSize = ALLOCSET_DEFAULT_INITSIZE;
333+
334+
return CreateExprContextInternal(estate, minContextSize,
335+
initBlockSize, maxBlockSize);
336+
}
337+
297338
/* ----------------
298339
* CreateStandaloneExprContext
299340
*

src/backend/executor/nodeAgg.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3277,10 +3277,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
32773277
}
32783278

32793279
if (use_hashing)
3280-
{
3281-
ExecAssignExprContext(estate, &aggstate->ss.ps);
3282-
aggstate->hashcontext = aggstate->ss.ps.ps_ExprContext;
3283-
}
3280+
aggstate->hashcontext = CreateWorkExprContext(estate);
32843281

32853282
ExecAssignExprContext(estate, &aggstate->ss.ps);
32863283

src/include/executor/executor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ extern void end_tup_output(TupOutputState *tstate);
493493
extern EState *CreateExecutorState(void);
494494
extern void FreeExecutorState(EState *estate);
495495
extern ExprContext *CreateExprContext(EState *estate);
496+
extern ExprContext *CreateWorkExprContext(EState *estate);
496497
extern ExprContext *CreateStandaloneExprContext(void);
497498
extern void FreeExprContext(ExprContext *econtext, bool isCommit);
498499
extern void ReScanExprContext(ExprContext *econtext);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy