aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2016-02-07 11:39:22 -0500
committerRobert Haas <rhaas@postgresql.org>2016-02-07 11:41:33 -0500
commit7c944bd903392829608a9fba5b0e68c4fe89abf8 (patch)
tree258a592bf8ddb522d3522415ad76b0dfa0f8d250 /src
parenta1c1af2a1f6099c039f145c1edb52257f315be51 (diff)
downloadpostgresql-7c944bd903392829608a9fba5b0e68c4fe89abf8.tar.gz
postgresql-7c944bd903392829608a9fba5b0e68c4fe89abf8.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/transam/parallel.c4
-rw-r--r--src/backend/commands/explain.c14
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/nodes/readfuncs.c1
-rw-r--r--src/backend/optimizer/plan/createplan.c5
-rw-r--r--src/backend/optimizer/plan/planner.c73
-rw-r--r--src/backend/utils/misc/guc.c24
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample1
-rw-r--r--src/include/nodes/plannodes.h1
-rw-r--r--src/include/nodes/relation.h3
-rw-r--r--src/include/optimizer/planmain.h9
12 files changed, 118 insertions, 20 deletions
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index bf2e691f577..4f91cd0265d 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -22,6 +22,7 @@
#include "libpq/pqformat.h"
#include "libpq/pqmq.h"
#include "miscadmin.h"
+#include "optimizer/planmain.h"
#include "storage/ipc.h"
#include "storage/sinval.h"
#include "storage/spin.h"
@@ -1079,7 +1080,8 @@ ParallelExtensionTrampoline(dsm_segment *seg, shm_toc *toc)
static void
ParallelErrorContext(void *arg)
{
- errcontext("parallel worker, PID %d", *(int32 *) arg);
+ if (force_parallel_mode != FORCE_PARALLEL_REGRESS)
+ errcontext("parallel worker, PID %d", *(int32 *) arg);
}
/*
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 25d8ca075d4..ee13136b7fd 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -23,6 +23,7 @@
#include "foreign/fdwapi.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/tcopprot.h"
@@ -572,6 +573,7 @@ void
ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
{
Bitmapset *rels_used = NULL;
+ PlanState *ps;
Assert(queryDesc->plannedstmt != NULL);
es->pstmt = queryDesc->plannedstmt;
@@ -580,7 +582,17 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
es->deparse_cxt = deparse_context_for_plan_rtable(es->rtable,
es->rtable_names);
- ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
+
+ /*
+ * Sometimes we mark a Gather node as "invisible", which means that it's
+ * not displayed in EXPLAIN output. The purpose of this is to allow
+ * running regression tests with force_parallel_mode=regress to get the
+ * same results as running the same tests with force_parallel_mode=off.
+ */
+ ps = queryDesc->planstate;
+ if (IsA(ps, GatherState) &&((Gather *) ps->plan)->invisible)
+ ps = outerPlanState(ps);
+ ExplainNode(ps, NIL, NULL, NULL, es);
}
/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a8b79fa8c31..e54d1744b0a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -334,6 +334,7 @@ _copyGather(const Gather *from)
*/
COPY_SCALAR_FIELD(num_workers);
COPY_SCALAR_FIELD(single_copy);
+ COPY_SCALAR_FIELD(invisible);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d59b9546543..3e1c3e6be57 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -443,6 +443,7 @@ _outGather(StringInfo str, const Gather *node)
WRITE_INT_FIELD(num_workers);
WRITE_BOOL_FIELD(single_copy);
+ WRITE_BOOL_FIELD(invisible);
}
static void
@@ -1824,6 +1825,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
WRITE_BOOL_FIELD(hasRowSecurity);
WRITE_BOOL_FIELD(parallelModeOK);
WRITE_BOOL_FIELD(parallelModeNeeded);
+ WRITE_BOOL_FIELD(wholePlanParallelSafe);
WRITE_BOOL_FIELD(hasForeignJoin);
}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 6c461513d64..e4d41ee95b2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2053,6 +2053,7 @@ _readGather(void)
READ_INT_FIELD(num_workers);
READ_BOOL_FIELD(single_copy);
+ READ_BOOL_FIELD(invisible);
READ_DONE();
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 54ff7f623d4..6e0db080383 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a09b4b5b479..a3cc27464c1 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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)
@@ -293,6 +301,35 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
}
/*
+ * 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
* set_plan_references' tree traversal, but for now it has to be separate
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 66c479141f3..31a69cac723 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -379,6 +379,19 @@ static const struct config_enum_entry huge_pages_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry force_parallel_mode_options[] = {
+ {"off", FORCE_PARALLEL_OFF, false},
+ {"on", FORCE_PARALLEL_ON, false},
+ {"regress", FORCE_PARALLEL_REGRESS, false},
+ {"true", FORCE_PARALLEL_ON, true},
+ {"false", FORCE_PARALLEL_OFF, true},
+ {"yes", FORCE_PARALLEL_ON, true},
+ {"no", FORCE_PARALLEL_OFF, true},
+ {"1", FORCE_PARALLEL_ON, true},
+ {"0", FORCE_PARALLEL_OFF, true},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -863,6 +876,7 @@ static struct config_bool ConfigureNamesBool[] =
true,
NULL, NULL, NULL
},
+
{
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
@@ -3672,6 +3686,16 @@ static struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"force_parallel_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ gettext_noop("Forces use of parallel query facilities."),
+ gettext_noop("If possible, run query using a parallel worker and with parallel restrictions.")
+ },
+ &force_parallel_mode,
+ FORCE_PARALLEL_OFF, force_parallel_mode_options,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 029114fc22d..09b2003dbe2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -313,6 +313,7 @@
#from_collapse_limit = 8
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
+#force_parallel_mode = off
#------------------------------------------------------------------------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 55d6bbe8f0e..ae224cfa314 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -775,6 +775,7 @@ typedef struct Gather
Plan plan;
int num_workers;
bool single_copy;
+ bool invisible; /* suppress EXPLAIN display (for testing)? */
} Gather;
/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 595438cb24d..96198aeec18 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -108,6 +108,9 @@ typedef struct PlannerGlobal
bool parallelModeOK; /* parallel mode potentially OK? */
bool parallelModeNeeded; /* parallel mode actually required? */
+
+ bool wholePlanParallelSafe; /* is the entire plan parallel safe? */
+
bool hasForeignJoin; /* does have a pushed down foreign join */
} PlannerGlobal;
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 7ae73676e8e..eaa642bc57e 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -17,9 +17,18 @@
#include "nodes/plannodes.h"
#include "nodes/relation.h"
+/* possible values for force_parallel_mode */
+typedef enum
+{
+ FORCE_PARALLEL_OFF,
+ FORCE_PARALLEL_ON,
+ FORCE_PARALLEL_REGRESS
+} ForceParallelMode;
+
/* GUC parameters */
#define DEFAULT_CURSOR_TUPLE_FRACTION 0.1
extern double cursor_tuple_fraction;
+extern int force_parallel_mode;
/* query_planner callback to compute query_pathkeys */
typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);