1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-24 10:47:04 +03:00

Delay build of Memoize hash table until executor run

Previously this hash table was built during executor startup.  This
could cause long delays in EXPLAIN (without ANALYZE) when the planner
opts to use a large Memoize hash table.

No backpatch for now due to lack of complaints.

Author: David Rowley
Discussion: https://postgr.es/m/CAApHDvoJktJ5XL=Kjh2a2TFr64R-7eQZV-+jcJrUwoES2GLiWg@mail.gmail.com
This commit is contained in:
David Rowley 2024-01-30 12:37:03 +13:00
parent c85977d8fe
commit 57f59396bb

View File

@ -278,11 +278,14 @@ MemoizeHash_equal(struct memoize_hash *tb, const MemoizeKey *key1,
} }
/* /*
* Initialize the hash table to empty. * Initialize the hash table to empty. The MemoizeState's hashtable field
* must point to NULL.
*/ */
static void static void
build_hash_table(MemoizeState *mstate, uint32 size) build_hash_table(MemoizeState *mstate, uint32 size)
{ {
Assert(mstate->hashtable == NULL);
/* Make a guess at a good size when we're not given a valid size. */ /* Make a guess at a good size when we're not given a valid size. */
if (size == 0) if (size == 0)
size = 1024; size = 1024;
@ -400,8 +403,10 @@ remove_cache_entry(MemoizeState *mstate, MemoizeEntry *entry)
static void static void
cache_purge_all(MemoizeState *mstate) cache_purge_all(MemoizeState *mstate)
{ {
uint64 evictions = mstate->hashtable->members; uint64 evictions = 0;
PlanState *pstate = (PlanState *) mstate;
if (mstate->hashtable != NULL)
evictions = mstate->hashtable->members;
/* /*
* Likely the most efficient way to remove all items is to just reset the * Likely the most efficient way to remove all items is to just reset the
@ -410,8 +415,8 @@ cache_purge_all(MemoizeState *mstate)
*/ */
MemoryContextReset(mstate->tableContext); MemoryContextReset(mstate->tableContext);
/* Make the hash table the same size as the original size */ /* NULLify so we recreate the table on the next call */
build_hash_table(mstate, ((Memoize *) pstate->plan)->est_entries); mstate->hashtable = NULL;
/* reset the LRU list */ /* reset the LRU list */
dlist_init(&mstate->lru_list); dlist_init(&mstate->lru_list);
@ -707,6 +712,10 @@ ExecMemoize(PlanState *pstate)
Assert(node->entry == NULL); Assert(node->entry == NULL);
/* first call? we'll need a hash table. */
if (unlikely(node->hashtable == NULL))
build_hash_table(node, ((Memoize *) pstate->plan)->est_entries);
/* /*
* We're only ever in this state for the first call of the * We're only ever in this state for the first call of the
* scan. Here we have a look to see if we've already seen the * scan. Here we have a look to see if we've already seen the
@ -1051,8 +1060,11 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
/* Zero the statistics counters */ /* Zero the statistics counters */
memset(&mstate->stats, 0, sizeof(MemoizeInstrumentation)); memset(&mstate->stats, 0, sizeof(MemoizeInstrumentation));
/* Allocate and set up the actual cache */ /*
build_hash_table(mstate, node->est_entries); * Because it may require a large allocation, we delay building of the
* hash table until executor run.
*/
mstate->hashtable = NULL;
return mstate; return mstate;
} }
@ -1062,6 +1074,7 @@ ExecEndMemoize(MemoizeState *node)
{ {
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
/* Validate the memory accounting code is correct in assert builds. */ /* Validate the memory accounting code is correct in assert builds. */
if (node->hashtable != NULL)
{ {
int count; int count;
uint64 mem = 0; uint64 mem = 0;