1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Introduce a new GUC force_parallel_mode for testing purposes.

When force_parallel_mode = true, we enable the parallel mode restrictions
for all queries for which this is believed to be safe.  For the subset of
those queries believed to be safe to run entirely within a worker, we spin
up a worker and run the query there instead of running it in the
original process.  When force_parallel_mode = regress, make additional
changes to allow the regression tests to run cleanly even though parallel
workers have been injected under the hood.

Taken together, this facilitates both better user testing and better
regression testing of the parallelism code.

Robert Haas, with help from Amit Kapila and Rushabh Lathia.
This commit is contained in:
Robert Haas
2016-02-07 11:39:22 -05:00
parent a1c1af2a1f
commit 7c944bd903
13 changed files with 163 additions and 20 deletions

View File

@ -212,6 +212,10 @@ create_plan(PlannerInfo *root, Path *best_path)
/* Recursively process the path tree */
plan = create_plan_recurse(root, best_path);
/* Update parallel safety information if needed. */
if (!best_path->parallel_safe)
root->glob->wholePlanParallelSafe = false;
/* Check we successfully assigned all NestLoopParams to plan nodes */
if (root->curOuterParams != NIL)
elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
@ -4829,6 +4833,7 @@ make_gather(List *qptlist,
plan->righttree = NULL;
node->num_workers = nworkers;
node->single_copy = single_copy;
node->invisible = false;
return node;
}

View File

@ -48,10 +48,12 @@
#include "storage/dsm_impl.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
/* GUC parameter */
/* GUC parameters */
double cursor_tuple_fraction = DEFAULT_CURSOR_TUPLE_FRACTION;
int force_parallel_mode = FORCE_PARALLEL_OFF;
/* Hook for plugins to get control in planner() */
planner_hook_type planner_hook = NULL;
@ -230,25 +232,31 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
!has_parallel_hazard((Node *) parse, true);
/*
* glob->parallelModeOK should tell us whether it's necessary to impose
* the parallel mode restrictions, but we don't actually want to impose
* them unless we choose a parallel plan, so that people who mislabel
* their functions but don't use parallelism anyway aren't harmed.
* However, it's useful for testing purposes to be able to force the
* restrictions to be imposed whenever a parallel plan is actually chosen
* or not.
* glob->parallelModeNeeded should tell us whether it's necessary to
* impose the parallel mode restrictions, but we don't actually want to
* impose them unless we choose a parallel plan, so that people who
* mislabel their functions but don't use parallelism anyway aren't
* harmed. But when force_parallel_mode is set, we enable the restrictions
* whenever possible for testing purposes.
*
* (It's been suggested that we should always impose these restrictions
* whenever glob->parallelModeOK is true, so that it's easier to notice
* incorrectly-labeled functions sooner. That might be the right thing to
* do, but for now I've taken this approach. We could also control this
* with a GUC.)
* glob->wholePlanParallelSafe should tell us whether it's OK to stick a
* Gather node on top of the entire plan. However, it only needs to be
* accurate when force_parallel_mode is 'on' or 'regress', so we don't
* bother doing the work otherwise. The value we set here is just a
* preliminary guess; it may get changed from true to false later, but
* not visca versa.
*/
#ifdef FORCE_PARALLEL_MODE
glob->parallelModeNeeded = glob->parallelModeOK;
#else
glob->parallelModeNeeded = false;
#endif
if (force_parallel_mode == FORCE_PARALLEL_OFF || !glob->parallelModeOK)
{
glob->parallelModeNeeded = false;
glob->wholePlanParallelSafe = false; /* either false or don't care */
}
else
{
glob->parallelModeNeeded = true;
glob->wholePlanParallelSafe =
!has_parallel_hazard((Node *) parse, false);
}
/* Determine what fraction of the plan is likely to be scanned */
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
@ -292,6 +300,35 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
top_plan = materialize_finished_plan(top_plan);
}
/*
* At present, we don't copy subplans to workers. The presence of a
* subplan in one part of the plan doesn't preclude the use of parallelism
* in some other part of the plan, but it does preclude the possibility of
* regarding the entire plan parallel-safe.
*/
if (glob->subplans != NULL)
glob->wholePlanParallelSafe = false;
/*
* Optionally add a Gather node for testing purposes, provided this is
* actually a safe thing to do.
*/
if (glob->wholePlanParallelSafe &&
force_parallel_mode != FORCE_PARALLEL_OFF)
{
Gather *gather = makeNode(Gather);
gather->plan.targetlist = top_plan->targetlist;
gather->plan.qual = NIL;
gather->plan.lefttree = top_plan;
gather->plan.righttree = NULL;
gather->num_workers = 1;
gather->single_copy = true;
gather->invisible = (force_parallel_mode == FORCE_PARALLEL_REGRESS);
root->glob->parallelModeNeeded = true;
top_plan = &gather->plan;
}
/*
* If any Params were generated, run through the plan tree and compute
* each plan node's extParam/allParam sets. Ideally we'd merge this into