Skip to content

Fix local plan cache growth for prepared statements - #8824

Open
Hamid Akhtar (hqakhtar) wants to merge 2 commits into
mainfrom
hamid/local_plan_cache_oom_fix
Open

Fix local plan cache growth for prepared statements#8824
Hamid Akhtar (hqakhtar) wants to merge 2 commits into
mainfrom
hamid/local_plan_cache_oom_fix

Conversation

@hqakhtar

Copy link
Copy Markdown

PR #8371 made cache lookup reject plans whose task list did not contain exactly one task. Deferred-pruning plans keep the persistent plan's task list empty, so every execution missed the existing local plan and added another one. This caused unbounded memory growth.

Separate cache eligibility from lookup. Use the current job for eligibility and the persistent distributed plan for cache storage. Keep the multi-shard reuse guard and allocate cache entries in the persistent plan's memory context.

Add a regression helper that reads the generic plan's cache size. Verify that repeated single-shard executions retain one entry and a multi-shard execution does not add another.

PR #8371 made cache lookup reject plans whose task list did not contain
exactly one task. Deferred-pruning plans keep the persistent plan's task
list empty, so every execution missed the existing local plan and added
another one. This caused unbounded memory growth.

Separate cache eligibility from lookup. Use the current job for eligibility
and the persistent distributed plan for cache storage. Keep the multi-shard
reuse guard and allocate cache entries in the persistent plan's memory
context.

Add a regression helper that reads the generic plan's cache size. Verify
that repeated single-shard executions retain one entry and a multi-shard
execution does not add another.
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.73%. Comparing base (92d0b92) to head (7b10b7a).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8824      +/-   ##
==========================================
- Coverage   88.73%   88.73%   -0.01%     
==========================================
  Files         289      290       +1     
  Lines       65013    65072      +59     
  Branches     8203     8212       +9     
==========================================
+ Hits        57691    57740      +49     
- Misses       4954     4958       +4     
- Partials     2368     2374       +6     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hqakhtar
Hamid Akhtar (hqakhtar) force-pushed the hamid/local_plan_cache_oom_fix branch from 078d7dc to 0a10bdd Compare September 3, 2026 13:08
{
PlannedStmt *localPlan = GetCachedLocalPlan(task, originalDistributedPlan);
if (localPlan != NULL)
Assert(planAddedToCache);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we ever consume planAddedToCache, or should this actually be?:

Suggested change
Assert(planAddedToCache);
Assert(*planAddedToCache);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calling functions neither consume planAddedtoCache nor the return value of CacheLocalPlanForShardQuery function, however, from an API interface perspective, this seems to be a fair design where the relevant plan is return with an indicator if it was returned from cache or newly created.

/*
* CacheLocalPlanForShardQuery replaces the relation OIDs in the job query
* with shard relation OIDs and then plans the query and caches the result
* in the originalDistributedPlan (which may be preserved across executions).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider updating the comment for this function - given that it now does eligibility checking, and returns either NULL when caching is not supported, or a plan when it is, and also returns whether or not a plan was added to the cache.

DistributedPlan *
originalDistributedPlan,
ParamListInfo paramListInfo,
bool *planAddedToCached);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: planAddedToCached -> planAddedToCache

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Fixed.

Comment thread src/backend/distributed/executor/citus_custom_scan.c

@colm-mchugh Colm (colm-mchugh) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm, few non-blocking comments. Please add a DESCRIPTION line before merge.

Copy the updated local plan cache to the per-execution plan after adding a
new entry, avoiding redundant shard-query planning. Also clarify the cache
function documentation and fix the planAddedToCache parameter name.
@hqakhtar
Hamid Akhtar (hqakhtar) force-pushed the hamid/local_plan_cache_oom_fix branch from da87bec to 7b10b7a Compare September 9, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants