mirror of
https://github.com/postgres/postgres.git
synced 2025-07-21 16:02:15 +03:00
Fix bogus "out of memory" reports in tuplestore.c.
The tuplesort/tuplestore memory management logic assumed that the chunk allocation overhead for its memtuples array could not increase when increasing the array size. This is and always was true for tuplesort, but we (I, I think) blindly copied that logic into tuplestore.c without noticing that the assumption failed to hold for the much smaller array elements used by tuplestore. Given rather small work_mem, this could result in an improper complaint about "unexpected out-of-memory situation", as reported by Brent DeSpain in bug #13530. The easiest way to fix this is just to increase tuplestore's initial array size so that the assumption holds. Rather than relying on magic constants, though, let's export a #define from aset.c that represents the safe allocation threshold, and make tuplestore's calculation depend on that. Do the same in tuplesort.c to keep the logic looking parallel, even though tuplesort.c isn't actually at risk at present. This will keep us from breaking it if we ever muck with the allocation parameters in aset.c. Back-patch to all supported versions. The error message doesn't occur pre-9.3, not so much because the problem can't happen as because the pre-9.3 tuplestore code neglected to check for it. (The chance of trouble is a great deal larger as of 9.3, though, due to changes in the array-size-increasing strategy.) However, allowing LACKMEM() to become true unexpectedly could still result in less-than-desirable behavior, so let's patch it all the way back.
This commit is contained in:
@ -89,7 +89,8 @@
|
|||||||
*
|
*
|
||||||
* With the current parameters, request sizes up to 8K are treated as chunks,
|
* With the current parameters, request sizes up to 8K are treated as chunks,
|
||||||
* larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS
|
* larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS
|
||||||
* to adjust the boundary point.
|
* to adjust the boundary point; and adjust ALLOCSET_SEPARATE_THRESHOLD in
|
||||||
|
* memutils.h to agree.
|
||||||
*--------------------
|
*--------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -385,7 +386,11 @@ AllocSetContextCreate(MemoryContext parent,
|
|||||||
* allocChunkLimit a power of two, because the requested and
|
* allocChunkLimit a power of two, because the requested and
|
||||||
* actually-allocated sizes of any chunk must be on the same side of the
|
* actually-allocated sizes of any chunk must be on the same side of the
|
||||||
* limit, else we get confused about whether the chunk is "big".
|
* limit, else we get confused about whether the chunk is "big".
|
||||||
|
*
|
||||||
|
* Also, allocChunkLimit must not exceed ALLOCSET_SEPARATE_THRESHOLD.
|
||||||
*/
|
*/
|
||||||
|
Assert(ALLOC_CHUNK_LIMIT == ALLOCSET_SEPARATE_THRESHOLD);
|
||||||
|
|
||||||
context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
|
context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
|
||||||
while (context->allocChunkLimit >
|
while (context->allocChunkLimit >
|
||||||
(Size) (maxBlockSize - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ))
|
(Size) (maxBlockSize - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ))
|
||||||
|
@ -531,7 +531,14 @@ tuplesort_begin_common(int workMem, bool randomAccess)
|
|||||||
state->tapeset = NULL;
|
state->tapeset = NULL;
|
||||||
|
|
||||||
state->memtupcount = 0;
|
state->memtupcount = 0;
|
||||||
state->memtupsize = 1024; /* initial guess */
|
|
||||||
|
/*
|
||||||
|
* Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD;
|
||||||
|
* see comments in grow_memtuples().
|
||||||
|
*/
|
||||||
|
state->memtupsize = Max(1024,
|
||||||
|
ALLOCSET_SEPARATE_THRESHOLD / sizeof(SortTuple) + 1);
|
||||||
|
|
||||||
state->memtuples = (SortTuple *) palloc(state->memtupsize * sizeof(SortTuple));
|
state->memtuples = (SortTuple *) palloc(state->memtupsize * sizeof(SortTuple));
|
||||||
|
|
||||||
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
||||||
@ -897,7 +904,7 @@ grow_memtuples(Tuplesortstate *state)
|
|||||||
state->memtupsize * sizeof(SortTuple));
|
state->memtupsize * sizeof(SortTuple));
|
||||||
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
||||||
if (LACKMEM(state))
|
if (LACKMEM(state))
|
||||||
elog(ERROR, "unexpected out-of-memory situation during sort");
|
elog(ERROR, "unexpected out-of-memory situation in tuplesort");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,14 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes)
|
|||||||
|
|
||||||
state->memtupdeleted = 0;
|
state->memtupdeleted = 0;
|
||||||
state->memtupcount = 0;
|
state->memtupcount = 0;
|
||||||
state->memtupsize = 1024; /* initial guess */
|
|
||||||
|
/*
|
||||||
|
* Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD;
|
||||||
|
* see comments in grow_memtuples().
|
||||||
|
*/
|
||||||
|
state->memtupsize = Max(16384 / sizeof(void *),
|
||||||
|
ALLOCSET_SEPARATE_THRESHOLD / sizeof(void *) + 1);
|
||||||
|
|
||||||
state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *));
|
state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *));
|
||||||
|
|
||||||
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
||||||
@ -644,6 +651,8 @@ tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
|
|||||||
repalloc(state->memtuples,
|
repalloc(state->memtuples,
|
||||||
state->memtupsize * sizeof(void *));
|
state->memtupsize * sizeof(void *));
|
||||||
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
USEMEM(state, GetMemoryChunkSpace(state->memtuples));
|
||||||
|
if (LACKMEM(state))
|
||||||
|
elog(ERROR, "unexpected out-of-memory situation in tuplestore");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,4 +136,12 @@ extern MemoryContext AllocSetContextCreate(MemoryContext parent,
|
|||||||
#define ALLOCSET_SMALL_INITSIZE (1 * 1024)
|
#define ALLOCSET_SMALL_INITSIZE (1 * 1024)
|
||||||
#define ALLOCSET_SMALL_MAXSIZE (8 * 1024)
|
#define ALLOCSET_SMALL_MAXSIZE (8 * 1024)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Threshold above which a request in an AllocSet context is certain to be
|
||||||
|
* allocated separately (and thereby have constant allocation overhead).
|
||||||
|
* Few callers should be interested in this, but tuplesort/tuplestore need
|
||||||
|
* to know it.
|
||||||
|
*/
|
||||||
|
#define ALLOCSET_SEPARATE_THRESHOLD 8192
|
||||||
|
|
||||||
#endif /* MEMUTILS_H */
|
#endif /* MEMUTILS_H */
|
||||||
|
Reference in New Issue
Block a user