agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH 7/7] Make Append node async-aware. 57+ messages / 2 participants [nested] [flat]
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 2.9.2 ----Next_Part(Mon_Aug_29_17_08_36_2016_213)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Mon_Aug_29_17_08_36_2016_213)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 94 ++++++++++++++++++++++++++++++++------- src/include/nodes/execnodes.h | 2 + 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..1c0d26e 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,49 +199,104 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { - for (;;) + int n_notready = 0; + PlanState *subnode; + int i, n; + + n = node->as_whichplan; + + for (i = 0 ; i < node->as_nplans ; i++) { - PlanState *subnode; TupleTableSlot *result; + if (node->as_async) + { + if (n >= node->as_nplans) + n = 0; + + if (node->as_finished[n]) + { + n++; + continue; + } + } + /* * figure out which subplan we are currently processing */ - subnode = node->appendplans[node->as_whichplan]; + subnode = node->appendplans[n]; /* - * get a tuple from the subplan + * execute the subplan to get a result if it is not ready yet */ - result = ExecProcNode(subnode); + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + /* + * This subnode is not ready yet when asynchrony is not allowed, + * immediately wait for this subnode. + */ + if (!subnode->result_ready) + { + if (node->as_async) + { + n_notready++; + n++; + continue; + } + ExecAsyncWaitForNode(subnode); + } + + Assert(subnode->result_ready); + + result = ExecConsumeResult((PlanState *)subnode); if (!TupIsNull(result)) { /* - * If the subplan gave us something then return it as-is. We do - * NOT make use of the result slot that was set up in - * ExecInitAppend; there's no need for it. + * If the subplan gave us something then return it + * as-is. We do NOT make use of the result slot that was + * set up in ExecInitAppend; there's no need for it. */ + node->as_whichplan = n; ExecReturnTuple(&node->ps, result); return; } + /* Tuple has been exhausted on this subnode */ + if (node->as_async) + { + node->as_finished[n++] = true; + continue; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by * ExecInitAppend. */ if (ScanDirectionIsForward(node->ps.state->es_direction)) - node->as_whichplan++; + { + if (n >= node->as_nplans - 1) + break; + n++; + } else - node->as_whichplan--; - if (!exec_append_initialize_next(node)) { - ExecReturnTuple(&node->ps, - ExecClearTuple(node->ps.ps_ResultTupleSlot)); - return; + if (n == 0) + break; + n--; } + } - /* Else loop back and try to get a tuple from the new subplan */ + /* + * We are finished if reached here and no subnodes are not-ready + */ + if (n_notready == 0) + { + node->as_whichplan = n; + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); } } @@ -277,6 +337,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 9121537..e4f2bb6 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1170,6 +1170,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gentbl.sql" CREATE EXTENSION postgres_fdw; CREATE TABLE pl (a int, b int); CREATE TABLE cl1 (LIKE pl) INHERITS (pl); CREATE TABLE cl2 (LIKE pl) INHERITS (pl); CREATE TABLE cl3 (LIKE pl) INHERITS (pl); CREATE TABLE cl4 (LIKE pl) INHERITS (pl); INSERT INTO cl1 (SELECT a, a FROM generate_series(0000000, 0999999) a); INSERT INTO cl2 (SELECT a, a FROM generate_series(1000000, 1999999) a); INSERT INTO cl3 (SELECT a, a FROM generate_series(2000000, 2999999) a); INSERT INTO cl4 (SELECT a, a FROM generate_series(3000000, 3999999) a); CREATE TABLE t0 (LIKE pl); INSERT INTO t0 (SELECT a, a FROM generate_series(0000000, 9999999) a); CREATE SERVER sv0 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE SERVER sv4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '/tmp', dbname 'postgres'); CREATE USER MAPPING FOR public SERVER sv0; CREATE USER MAPPING FOR public SERVER sv1; CREATE USER MAPPING FOR public SERVER sv2; CREATE USER MAPPING FOR public SERVER sv3; CREATE USER MAPPING FOR public SERVER sv4; CREATE FOREIGN TABLE ft10 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft20 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft30 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft40 (a int, b int) SERVER sv0 OPTIONS (table_name 'cl4'); CREATE FOREIGN TABLE ft11 (a int, b int) SERVER sv1 OPTIONS (table_name 'cl1'); CREATE FOREIGN TABLE ft22 (a int, b int) SERVER sv2 OPTIONS (table_name 'cl2'); CREATE FOREIGN TABLE ft33 (a int, b int) SERVER sv3 OPTIONS (table_name 'cl3'); CREATE FOREIGN TABLE ft44 (a int, b int) SERVER sv4 OPTIONS (table_name 'cl4'); CREATE TABLE pf0 (LIKE pl); ALTER FOREIGN TABLE ft10 INHERIT pf0; ALTER FOREIGN TABLE ft20 INHERIT pf0; ALTER FOREIGN TABLE ft30 INHERIT pf0; ALTER FOREIGN TABLE ft40 INHERIT pf0; CREATE table pf1 (LIKE pl); ALTER FOREIGN TABLE ft11 INHERIT pf1; ALTER FOREIGN TABLE ft22 INHERIT pf1; ALTER FOREIGN TABLE ft33 INHERIT pf1; ALTER FOREIGN TABLE ft44 INHERIT pf1; ANALYZE; ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="testrun.sh" #! /bin/bash function do_test() { echo $1 for i in $(seq 1 10); do psql postgres -c "set log_min_duration_statement = 0; set client_min_messages=log; select sum(a) from $1"; done | grep LOG } for i in "t0" "pl" "pf0" "pf1"; do do_test $i done ----Next_Part(Wed_Jul_06_16_29_14_2016_599)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Wed_Jul_06_16_29_14_2016_599)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH 7/7] Make Append node async-aware. @ 2016-06-28 09:52 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Kyotaro Horiguchi @ 2016-06-28 09:52 UTC (permalink / raw) Change append node to be capable to handle asynchronous children properly. As soon as it receives !async_ready from a child, it moves to the next child and if no child is ready, it sleeps until at least one of them become ready. --- src/backend/executor/nodeAppend.c | 67 +++++++++++++++++++++++++++++++++++++-- src/include/nodes/execnodes.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index e0ce8c6..9a4063a 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -58,6 +58,7 @@ #include "postgres.h" #include "executor/execdebug.h" +#include "executor/execAsync.h" #include "executor/nodeAppend.h" static bool exec_append_initialize_next(AppendState *appendstate); @@ -121,6 +122,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags) { AppendState *appendstate = makeNode(AppendState); PlanState **appendplanstates; + bool *finished; int nplans; int i; ListCell *lc; @@ -134,14 +136,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags) nplans = list_length(node->appendplans); appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); - + finished = (bool *) palloc0(nplans * sizeof(bool)); + /* * create new AppendState for our append node */ appendstate->ps.plan = (Plan *) node; appendstate->ps.state = estate; appendstate->appendplans = appendplanstates; + appendstate->as_finished = finished; appendstate->as_nplans = nplans; + appendstate->as_async = ((eflags & EXEC_FLAG_BACKWARD) == 0); /* * Miscellaneous initialization @@ -194,6 +199,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) void ExecAppend(AppendState *node) { + int stopplan = node->as_whichplan; + for (;;) { PlanState *subnode; @@ -207,7 +214,36 @@ ExecAppend(AppendState *node) /* * get a tuple from the subplan */ - result = ExecProcNode(subnode); + Assert(!node->as_finished[node->as_whichplan]); + + if (!subnode->result_ready) + ExecExecuteNode(subnode); + + if (!subnode->result_ready) + { + if (node->as_async) + { + /* Move to the next living node */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + /* No node is ready yet, return as not-ready */ + if (node->as_whichplan == stopplan) + return; + + /* Try the next node */ + continue; + } + + /* If not async, immediately wait for this subnode */ + ExecAsyncWaitForNode(subnode); + } + + result = ExecConsumeResult((PlanState *) subnode); if (!TupIsNull(result)) { @@ -220,6 +256,31 @@ ExecAppend(AppendState *node) return; } + if (node->as_async) + { + node->as_finished[node->as_whichplan] = true; + stopplan = node->as_whichplan; + + /* Find the next living subnode */ + do + { + node->as_whichplan = + (node->as_whichplan + 1) % node->as_nplans; + } while (node->as_whichplan != stopplan && + node->as_finished[node->as_whichplan]); + + if (node->as_whichplan != stopplan) + { + stopplan = node->as_whichplan; + continue; + } + + /* All subnodes are exhausted. Finish this node. */ + ExecReturnTuple(&node->ps, + ExecClearTuple(node->ps.ps_ResultTupleSlot)); + return; + } + /* * Go on to the "next" subplan in the appropriate direction. If no * more subplans, return the empty slot set up for us by @@ -277,6 +338,8 @@ ExecReScanAppend(AppendState *node) { PlanState *subnode = node->appendplans[i]; + node->as_finished[i] = false; + /* * ExecReScan doesn't know about my subplans, so I have to do * changed-parameter signaling myself. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b72decc..b0a86c5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1177,6 +1177,8 @@ typedef struct AppendState { PlanState ps; /* its first field is NodeTag */ PlanState **appendplans; /* array of PlanStates for my inputs */ + bool as_async; /* true to allow async execution */ + bool *as_finished; /* array of the running state of subplans */ int as_nplans; int as_whichplan; } AppendState; -- 1.8.3.1 ----Next_Part(Thu_Jul_21_18_50_07_2016_597)-- Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers ----Next_Part(Thu_Jul_21_18_50_07_2016_597)---- ^ permalink raw reply [nested|flat] 57+ messages in thread
* [PATCH v3] Avoid orphaned objects dependencies @ 2024-03-29 15:43 Bertrand Drouvot <[email protected]> 0 siblings, 0 replies; 57+ messages in thread From: Bertrand Drouvot @ 2024-03-29 15:43 UTC (permalink / raw) It's currently possible to create orphaned objects dependencies, for example: Scenario 1: session 1: begin; drop schema schem; session 2: create a function in the schema schem session 1: commit; With the above, the function created in session 2 would be linked to a non existing schema. Scenario 2: session 1: begin; create a function in the schema schem session 2: drop schema schem; session 1: commit; With the above, the function created in session 1 would be linked to a non existing schema. To avoid those scenarios, a new lock (that conflicts with a lock taken by DROP) has been put in place when the dependencies are being recorded. With this in place, the drop schema in scenario 2 would be locked. Also, after locking the object, the patch checks that the object still exists: with this in place session 2 in scenario 1 would be locked and would report an error once session 1 committs (that would not be the case should session 1 abort the transaction). The patch adds a few tests for some dependency cases (that would currently produce orphaned objects): - schema and function (as the above scenarios) - function and type - table and type --- src/backend/catalog/dependency.c | 54 ++++++++++++++ src/backend/catalog/objectaddress.c | 70 +++++++++++++++++++ src/backend/catalog/pg_depend.c | 6 ++ src/include/catalog/dependency.h | 1 + src/include/catalog/objectaddress.h | 1 + src/test/modules/Makefile | 1 + src/test/modules/meson.build | 1 + .../test_dependencies_locks/.gitignore | 3 + .../modules/test_dependencies_locks/Makefile | 14 ++++ .../expected/test_dependencies_locks.out | 49 +++++++++++++ .../test_dependencies_locks/meson.build | 12 ++++ .../specs/test_dependencies_locks.spec | 39 +++++++++++ 12 files changed, 251 insertions(+) 40.5% src/backend/catalog/ 29.6% src/test/modules/test_dependencies_locks/expected/ 18.7% src/test/modules/test_dependencies_locks/specs/ 8.3% src/test/modules/test_dependencies_locks/ diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index d4b5b2ade1..a49357bbe2 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1517,6 +1517,60 @@ AcquireDeletionLock(const ObjectAddress *object, int flags) } } +/* + * depLockAndCheckObject + * + * Lock the object that we are about to record a dependency on. + * After it's locked, verify that it hasn't been dropped while we + * weren't looking. If the object has been dropped, this function + * does not return! + */ +void +depLockAndCheckObject(const ObjectAddress *object) +{ + char *object_description; + + /* + * Those don't rely on LockDatabaseObject() when being dropped (see + * AcquireDeletionLock()). Also it looks like they can not produce + * orphaned dependent objects when being dropped. + */ + if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) + return; + + object_description = getObjectDescription(object, true); + + /* assume we should lock the whole object not a sub-object */ + LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock); + + /* check if object still exists */ + if (!ObjectByIdExist(object, false)) + { + /* + * It might be possible that we are creating it (for example creating + * a composite type while creating a relation), so bypass the syscache + * lookup and use a dirty snaphot instead to cover this scenario. + */ + if (!ObjectByIdExist(object, true)) + { + /* + * If the object has been dropped before we get a chance to get + * its description, then emit a generic error message. That looks + * like a good compromise over extra complexity. + */ + if (object_description) + ereport(ERROR, errmsg("%s does not exist", object_description)); + else + ereport(ERROR, errmsg("a dependent object does not exist")); + } + } + + if (object_description) + pfree(object_description); + + return; +} + /* * ReleaseDeletionLock - release an object deletion lock * diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 7b536ac6fd..c2b873dd81 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -2590,6 +2590,76 @@ get_object_namespace(const ObjectAddress *address) return oid; } +/* + * ObjectByIdExist + * + * Return whether the given object exists. + * + * Works for most catalogs, if no special processing is needed. + */ +bool +ObjectByIdExist(const ObjectAddress *address, bool use_dirty_snapshot) +{ + HeapTuple tuple; + int cache = -1; + const ObjectPropertyType *property; + + if (!use_dirty_snapshot) + { + property = get_object_property_data(address->classId); + + cache = property->oid_catcache_id; + } + + if (cache >= 0) + { + /* Fetch tuple from syscache. */ + tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId)); + + if (!HeapTupleIsValid(tuple)) + { + return false; + } + + ReleaseSysCache(tuple); + + return true; + } + else + { + Relation rel; + ScanKeyData skey[1]; + SysScanDesc scan; + SnapshotData DirtySnapshot; + Snapshot snapshot; + + if (use_dirty_snapshot) + { + InitDirtySnapshot(DirtySnapshot); + snapshot = &DirtySnapshot; + } + else + snapshot = NULL; + + rel = table_open(address->classId, AccessShareLock); + + ScanKeyInit(&skey[0], + get_object_attnum_oid(address->classId), + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(address->objectId)); + + scan = systable_beginscan(rel, get_object_oid_index(address->classId), true, + snapshot, 1, skey); + + /* we expect exactly one match */ + tuple = systable_getnext(scan); + systable_endscan(scan); + table_close(rel, AccessShareLock); + + return (HeapTupleIsValid(tuple)); + } +} + /* * Return ObjectType for the given object type as given by * getObjectTypeDescription; if no valid ObjectType code exists, but it's a diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index f85a898de8..6bb218f5dd 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -106,6 +106,12 @@ recordMultipleDependencies(const ObjectAddress *depender, if (isObjectPinned(referenced)) continue; + /* + * Acquire a lock and check object still exists while recording the + * dependency. XXX - Should we do so only for DEPENDENCY_NORMAL? + */ + depLockAndCheckObject(referenced); + if (slot_init_count < max_slots) { slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc), diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index ec654010d4..8915548711 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -94,6 +94,7 @@ typedef struct ObjectAddresses ObjectAddresses; /* in dependency.c */ extern void AcquireDeletionLock(const ObjectAddress *object, int flags); +extern void depLockAndCheckObject(const ObjectAddress *object); extern void ReleaseDeletionLock(const ObjectAddress *object); diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index 3a70d80e32..04891abcc1 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -53,6 +53,7 @@ extern void check_object_ownership(Oid roleid, Node *object, Relation relation); extern Oid get_object_namespace(const ObjectAddress *address); +extern bool ObjectByIdExist(const ObjectAddress *address, bool use_dirty_snapshot); extern bool is_objectclass_supported(Oid class_id); extern const char *get_object_class_descr(Oid class_id); diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 256799f520..75f357100f 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -17,6 +17,7 @@ SUBDIRS = \ test_copy_callbacks \ test_custom_rmgrs \ test_ddl_deparse \ + test_dependencies_locks \ test_dsa \ test_dsm_registry \ test_extensions \ diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build index d8fe059d23..60305dcccd 100644 --- a/src/test/modules/meson.build +++ b/src/test/modules/meson.build @@ -16,6 +16,7 @@ subdir('test_bloomfilter') subdir('test_copy_callbacks') subdir('test_custom_rmgrs') subdir('test_ddl_deparse') +subdir('test_dependencies_locks') subdir('test_dsa') subdir('test_dsm_registry') subdir('test_extensions') diff --git a/src/test/modules/test_dependencies_locks/.gitignore b/src/test/modules/test_dependencies_locks/.gitignore new file mode 100644 index 0000000000..bf000faac4 --- /dev/null +++ b/src/test/modules/test_dependencies_locks/.gitignore @@ -0,0 +1,3 @@ +# Generated subdirectories +/log/ +/output_iso diff --git a/src/test/modules/test_dependencies_locks/Makefile b/src/test/modules/test_dependencies_locks/Makefile new file mode 100644 index 0000000000..7491048380 --- /dev/null +++ b/src/test/modules/test_dependencies_locks/Makefile @@ -0,0 +1,14 @@ +# src/test/modules/test_dependencies_locks/Makefile + +ISOLATION = test_dependencies_locks + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_dependencies_locks +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_dependencies_locks/expected/test_dependencies_locks.out b/src/test/modules/test_dependencies_locks/expected/test_dependencies_locks.out new file mode 100644 index 0000000000..d0980f77d5 --- /dev/null +++ b/src/test/modules/test_dependencies_locks/expected/test_dependencies_locks.out @@ -0,0 +1,49 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_begin s1_create_function_in_schema s2_drop_schema s1_commit +step s1_begin: BEGIN; +step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; +step s2_drop_schema: DROP SCHEMA testschema; <waiting ...> +step s1_commit: COMMIT; +step s2_drop_schema: <... completed> +ERROR: cannot drop schema testschema because other objects depend on it + +starting permutation: s2_begin s2_drop_schema s1_create_function_in_schema s2_commit +step s2_begin: BEGIN; +step s2_drop_schema: DROP SCHEMA testschema; +step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...> +step s2_commit: COMMIT; +step s1_create_function_in_schema: <... completed> +ERROR: schema testschema does not exist + +starting permutation: s1_begin s1_create_function_with_type s2_drop_foo_type s1_commit +step s1_begin: BEGIN; +step s1_create_function_with_type: CREATE FUNCTION footype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; +step s2_drop_foo_type: DROP TYPE public.foo; <waiting ...> +step s1_commit: COMMIT; +step s2_drop_foo_type: <... completed> +ERROR: cannot drop type foo because other objects depend on it + +starting permutation: s2_begin s2_drop_foo_type s1_create_function_with_type s2_commit +step s2_begin: BEGIN; +step s2_drop_foo_type: DROP TYPE public.foo; +step s1_create_function_with_type: CREATE FUNCTION footype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...> +step s2_commit: COMMIT; +step s1_create_function_with_type: <... completed> +ERROR: type foo does not exist + +starting permutation: s1_begin s1_create_table_with_type s2_drop_footab_type s1_commit +step s1_begin: BEGIN; +step s1_create_table_with_type: CREATE TABLE tabtype(a footab); +step s2_drop_footab_type: DROP TYPE public.footab; <waiting ...> +step s1_commit: COMMIT; +step s2_drop_footab_type: <... completed> +ERROR: cannot drop type footab because other objects depend on it + +starting permutation: s2_begin s2_drop_footab_type s1_create_table_with_type s2_commit +step s2_begin: BEGIN; +step s2_drop_footab_type: DROP TYPE public.footab; +step s1_create_table_with_type: CREATE TABLE tabtype(a footab); <waiting ...> +step s2_commit: COMMIT; +step s1_create_table_with_type: <... completed> +ERROR: type footab does not exist diff --git a/src/test/modules/test_dependencies_locks/meson.build b/src/test/modules/test_dependencies_locks/meson.build new file mode 100644 index 0000000000..92a978ab93 --- /dev/null +++ b/src/test/modules/test_dependencies_locks/meson.build @@ -0,0 +1,12 @@ +# Copyright (c) 2024, PostgreSQL Global Development Group + +tests += { + 'name': 'test_dependencies_locks', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'isolation': { + 'specs': [ + 'test_dependencies_locks', + ], + }, +} diff --git a/src/test/modules/test_dependencies_locks/specs/test_dependencies_locks.spec b/src/test/modules/test_dependencies_locks/specs/test_dependencies_locks.spec new file mode 100644 index 0000000000..fd15bd2a78 --- /dev/null +++ b/src/test/modules/test_dependencies_locks/specs/test_dependencies_locks.spec @@ -0,0 +1,39 @@ +setup +{ + CREATE SCHEMA testschema; + CREATE TYPE public.foo as enum ('one', 'two'); + CREATE TYPE public.footab as enum ('three', 'four'); +} + +teardown +{ + DROP FUNCTION IF EXISTS testschema.foo(); + DROP FUNCTION IF EXISTS footype(num foo); + DROP TABLE IF EXISTS tabtype; + DROP SCHEMA IF EXISTS testschema; + DROP TYPE IF EXISTS public.foo; + DROP TYPE IF EXISTS public.footab; +} + +session "s1" + +step "s1_begin" { BEGIN; } +step "s1_create_function_in_schema" { CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; } +step "s1_create_function_with_type" { CREATE FUNCTION footype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; } +step "s1_create_table_with_type" { CREATE TABLE tabtype(a footab); } +step "s1_commit" { COMMIT; } + +session "s2" + +step "s2_begin" { BEGIN; } +step "s2_drop_schema" { DROP SCHEMA testschema; } +step "s2_drop_foo_type" { DROP TYPE public.foo; } +step "s2_drop_footab_type" { DROP TYPE public.footab; } +step "s2_commit" { COMMIT; } + +permutation "s1_begin" "s1_create_function_in_schema" "s2_drop_schema" "s1_commit" +permutation "s2_begin" "s2_drop_schema" "s1_create_function_in_schema" "s2_commit" +permutation "s1_begin" "s1_create_function_with_type" "s2_drop_foo_type" "s1_commit" +permutation "s2_begin" "s2_drop_foo_type" "s1_create_function_with_type" "s2_commit" +permutation "s1_begin" "s1_create_table_with_type" "s2_drop_footab_type" "s1_commit" +permutation "s2_begin" "s2_drop_footab_type" "s1_create_table_with_type" "s2_commit" -- 2.34.1 --+Mfbf1cGg0Kwp7Pc-- ^ permalink raw reply [nested|flat] 57+ messages in thread
end of thread, other threads:[~2024-03-29 15:43 UTC | newest] Thread overview: 57+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2016-06-28 09:52 [PATCH 7/7] Make Append node async-aware. Kyotaro Horiguchi <[email protected]> 2024-03-29 15:43 [PATCH v3] Avoid orphaned objects dependencies Bertrand Drouvot <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox