On 7/5/21 21:05, Etsuro Fujita wrote:
I think it would be better to start a new thread for this, and add the
patch to the next CF so that it doesn’t get lost.

Current implementation of async append choose asynchronous subplans at the phase of an append plan creation. This is safe approach, but we loose some optimizations, such of flattening trivial subqueries and can't execute some simple queries asynchronously. For example:

EXPLAIN (ANALYZE, TIMING OFF, SUMMARY OFF, COSTS OFF)
(SELECT * FROM f1 WHERE a < 10) UNION ALL
(SELECT * FROM f2 WHERE a < 10);

But, as I could understand, we can choose these subplans later, at the init append phase when all optimizations already passed.
In attachment - implementation of the proposed approach.

Initial script for the example see in the parent thread [1].


[1] https://www.postgresql.org/message-id/a38bb206-8340-9528-5ef6-37de2d5cb1a3%40postgrespro.ru


--
regards,
Andrey Lepikhov
Postgres Professional
From 395b1d62389cf40520a4afd87c11301aa2b17df2 Mon Sep 17 00:00:00 2001
From: "Andrey V. Lepikhov" <a.lepik...@postgrespro.ru>
Date: Tue, 11 May 2021 08:43:03 +0500
Subject: [PATCH] Defer selection of asynchronous subplans to the executor
 initial phase.

---
 contrib/postgres_fdw/postgres_fdw.c     | 10 +++++++++-
 src/backend/executor/execAmi.c          |  7 +++----
 src/backend/executor/nodeAppend.c       | 19 +++++++++++++++++++
 src/backend/nodes/copyfuncs.c           |  1 -
 src/backend/nodes/outfuncs.c            |  1 -
 src/backend/nodes/readfuncs.c           |  1 -
 src/backend/optimizer/plan/createplan.c | 17 +----------------
 src/include/nodes/plannodes.h           |  1 -
 src/include/optimizer/planmain.h        |  1 +
 9 files changed, 33 insertions(+), 25 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c 
b/contrib/postgres_fdw/postgres_fdw.c
index 4ff58d9c27..3e151a6790 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1245,6 +1245,7 @@ postgresGetForeignPlan(PlannerInfo *root,
        bool            has_final_sort = false;
        bool            has_limit = false;
        ListCell   *lc;
+       ForeignScan *fsplan;
 
        /*
         * Get FDW private data created by postgresGetForeignUpperPaths(), if 
any.
@@ -1429,7 +1430,7 @@ postgresGetForeignPlan(PlannerInfo *root,
         * field of the finished plan node; we can't keep them in private state
         * because then they wouldn't be subject to later planner processing.
         */
-       return make_foreignscan(tlist,
+       fsplan = make_foreignscan(tlist,
                                                        local_exprs,
                                                        scan_relid,
                                                        params_list,
@@ -1437,6 +1438,13 @@ postgresGetForeignPlan(PlannerInfo *root,
                                                        fdw_scan_tlist,
                                                        fdw_recheck_quals,
                                                        outer_plan);
+
+       /* If appropriate, consider participation in async operations */
+       fsplan->scan.plan.async_capable = (enable_async_append &&
+                                                                          
best_path->path.pathkeys == NIL &&
+                                                                          
!fsplan->scan.plan.parallel_safe &&
+                                                                          
is_async_capable_path((Path *)best_path));
+       return fsplan;
 }
 
 /*
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index b3726a54f3..4e70f4eb54 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -524,6 +524,9 @@ ExecSupportsBackwardScan(Plan *node)
        if (node->parallel_aware)
                return false;
 
+       if (node->async_capable)
+               return false;
+
        switch (nodeTag(node))
        {
                case T_Result:
@@ -536,10 +539,6 @@ ExecSupportsBackwardScan(Plan *node)
                        {
                                ListCell   *l;
 
-                               /* With async, tuples may be interleaved, so 
can't back up. */
-                               if (((Append *) node)->nasyncplans > 0)
-                                       return false;
-
                                foreach(l, ((Append *) node)->appendplans)
                                {
                                        if (!ExecSupportsBackwardScan((Plan *) 
lfirst(l)))
diff --git a/src/backend/executor/nodeAppend.c 
b/src/backend/executor/nodeAppend.c
index 3c1f12adaf..363cf9f4a5 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -117,6 +117,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
        int                     firstvalid;
        int                     i,
                                j;
+       ListCell   *l;
+       bool            consider_async = false;
 
        /* check for unsupported flags */
        Assert(!(eflags & EXEC_FLAG_MARK));
@@ -197,6 +199,23 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
        appendplanstates = (PlanState **) palloc(nplans *
                                                                                
         sizeof(PlanState *));
 
+       /* If appropriate, consider async append */
+       consider_async = (list_length(node->appendplans) > 1);
+
+       if (!consider_async)
+       {
+               foreach(l, node->appendplans)
+               {
+                       Plan *subplan = (Plan *) lfirst(l);
+
+                       /* Check to see if subplan can be executed 
asynchronously */
+                       if (subplan->async_capable)
+                       {
+                               subplan->async_capable = false;
+                       }
+               }
+       }
+
        /*
         * call ExecInitNode on each of the valid plans to be executed and save
         * the results into the appendplanstates array.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 90770a89b0..a44185d7fc 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -243,7 +243,6 @@ _copyAppend(const Append *from)
         */
        COPY_BITMAPSET_FIELD(apprelids);
        COPY_NODE_FIELD(appendplans);
-       COPY_SCALAR_FIELD(nasyncplans);
        COPY_SCALAR_FIELD(first_partial_plan);
        COPY_NODE_FIELD(part_prune_info);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8da8b14f0e..cd5dbce76a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -433,7 +433,6 @@ _outAppend(StringInfo str, const Append *node)
 
        WRITE_BITMAPSET_FIELD(apprelids);
        WRITE_NODE_FIELD(appendplans);
-       WRITE_INT_FIELD(nasyncplans);
        WRITE_INT_FIELD(first_partial_plan);
        WRITE_NODE_FIELD(part_prune_info);
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3772ea07df..3f5879951b 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1717,7 +1717,6 @@ _readAppend(void)
 
        READ_BITMAPSET_FIELD(apprelids);
        READ_NODE_FIELD(appendplans);
-       READ_INT_FIELD(nasyncplans);
        READ_INT_FIELD(first_partial_plan);
        READ_NODE_FIELD(part_prune_info);
 
diff --git a/src/backend/optimizer/plan/createplan.c 
b/src/backend/optimizer/plan/createplan.c
index 7003238d76..b1f2a493f0 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -82,7 +82,6 @@ static List *get_gating_quals(PlannerInfo *root, List *quals);
 static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
                                                                List 
*gating_quals);
 static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
-static bool is_async_capable_path(Path *path);
 static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path,
                                                                int flags);
 static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath 
*best_path,
@@ -1097,7 +1096,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
  * is_async_capable_path
  *             Check whether a given Path node is async-capable.
  */
-static bool
+bool
 is_async_capable_path(Path *path)
 {
        switch (nodeTag(path))
@@ -1135,7 +1134,6 @@ create_append_plan(PlannerInfo *root, AppendPath 
*best_path, int flags)
        List       *pathkeys = best_path->path.pathkeys;
        List       *subplans = NIL;
        ListCell   *subpaths;
-       int                     nasyncplans = 0;
        RelOptInfo *rel = best_path->path.parent;
        PartitionPruneInfo *partpruneinfo = NULL;
        int                     nodenumsortkeys = 0;
@@ -1143,7 +1141,6 @@ create_append_plan(PlannerInfo *root, AppendPath 
*best_path, int flags)
        Oid                *nodeSortOperators = NULL;
        Oid                *nodeCollations = NULL;
        bool       *nodeNullsFirst = NULL;
-       bool            consider_async = false;
 
        /*
         * The subpaths list could be empty, if every child was proven empty by
@@ -1207,11 +1204,6 @@ create_append_plan(PlannerInfo *root, AppendPath 
*best_path, int flags)
                tlist_was_changed = (orig_tlist_length != 
list_length(plan->plan.targetlist));
        }
 
-       /* If appropriate, consider async append */
-       consider_async = (enable_async_append && pathkeys == NIL &&
-                                         !best_path->path.parallel_safe &&
-                                         list_length(best_path->subpaths) > 1);
-
        /* Build the plan for each child */
        foreach(subpaths, best_path->subpaths)
        {
@@ -1280,12 +1272,6 @@ create_append_plan(PlannerInfo *root, AppendPath 
*best_path, int flags)
 
                subplans = lappend(subplans, subplan);
 
-               /* Check to see if subplan can be executed asynchronously */
-               if (consider_async && is_async_capable_path(subpath))
-               {
-                       subplan->async_capable = true;
-                       ++nasyncplans;
-               }
        }
 
        /*
@@ -1318,7 +1304,6 @@ create_append_plan(PlannerInfo *root, AppendPath 
*best_path, int flags)
        }
 
        plan->appendplans = subplans;
-       plan->nasyncplans = nasyncplans;
        plan->first_partial_plan = best_path->first_partial_path;
        plan->part_prune_info = partpruneinfo;
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 841401be20..00f4f5f8ed 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -251,7 +251,6 @@ typedef struct Append
        Plan            plan;
        Bitmapset  *apprelids;          /* RTIs of appendrel(s) formed by this 
node */
        List       *appendplans;
-       int                     nasyncplans;    /* # of asynchronous plans */
 
        /*
         * All 'appendplans' preceding this index are non-partial plans. All
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index bf1adfc52a..8a96a19e5f 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -115,5 +115,6 @@ extern Plan *set_plan_references(PlannerInfo *root, Plan 
*plan);
 extern void record_plan_function_dependency(PlannerInfo *root, Oid funcid);
 extern void record_plan_type_dependency(PlannerInfo *root, Oid typid);
 extern bool extract_query_dependencies_walker(Node *node, PlannerInfo *root);
+extern bool is_async_capable_path(Path *path);
 
 #endif                                                 /* PLANMAIN_H */
-- 
2.31.1

Reply via email to