Asynchronous execution support for Custom Scan

Started by Kazutaka Onishiover 3 years ago15 messages
#1Kazutaka Onishi
onishi@heterodb.com
2 attachment(s)

Hello,

I suggest supporting asynchronous execution for Custom Scan.
Since v14, PostgreSQL supports asynchronous execution for Foreign Scan.
This patch enables asynchronous execution by applying the process for
Foreign Scan to Custom Scan .

The patch is divided into 2 parts, source and documents(sgml).

Regards,

Attachments:

v1-0002-async-custom-scan-document.patchapplication/octet-stream; name=v1-0002-async-custom-scan-document.patchDownload
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 93d96f2f56..2ad5c22efa 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -76,9 +76,10 @@ typedef struct CustomPath
     capabilities.  <structfield>flags</structfield> should include
     <literal>CUSTOMPATH_SUPPORT_BACKWARD_SCAN</literal> if the custom path can support
     a backward scan, <literal>CUSTOMPATH_SUPPORT_MARK_RESTORE</literal> if it
-    can support mark and restore,
-    and <literal>CUSTOMPATH_SUPPORT_PROJECTION</literal> if it can perform
-    projections.  (If <literal>CUSTOMPATH_SUPPORT_PROJECTION</literal> is not
+    can support mark and restore, <literal>CUSTOMPATH_SUPPORT_PROJECTION</literal> if it 
+    can perform projections and <literal>CUSTOMPATH_SUPPORT_ASYNC_EXECUTION</literal> 
+    if it can support asynchronous execution.
+      (If <literal>CUSTOMPATH_SUPPORT_PROJECTION</literal> is not
     set, the scan node will only be asked to produce Vars of the scanned
     relation; while if that flag is set, the scan node must be able to
     evaluate scalar expressions over these Vars.)
@@ -385,6 +386,61 @@ void (*ExplainCustomScan) (CustomScanState *node,
     be shown even without this callback, but the callback allows the display
     of additional, private state.
    </para>
+
+   <para>
+<programlisting>
+void (*AsyncCustomScanRequest) (AsyncRequest *areq);
+</programlisting>
+    Produce one tuple asynchronously from the custom-scan plan node. 
+    <literal>areq</literal> is
+     the <structname>AsyncRequest</structname> struct describing the
+     custom-scan node and the parent
+     <structname>Append</structname> node that requested the tuple from it.
+     This function should store the tuple into the slot specified by
+     <literal>areq-&gt;result</literal>, and set
+     <literal>areq-&gt;request_complete</literal> to <literal>true</literal>;
+     or if it needs to wait on an event external to the core server such as
+     network I/O, and cannot produce any tuple immediately, set the flag to
+     <literal>false</literal>, and set
+     <literal>areq-&gt;callback_pending</literal> to <literal>true</literal>
+     for the custom-scan node to get a callback from
+     the callback functions described below.  If no more tuples are available,
+     set the slot to NULL or an empty slot, and the
+     <literal>areq-&gt;request_complete</literal> flag to
+     <literal>true</literal>.  It's recommended to use
+     <function>ExecAsyncRequestDone</function> or
+     <function>ExecAsyncRequestPending</function> to set the output parameters
+     in the <literal>areq</literal>.
+   </para>
+
+   <para>
+<programlisting>
+void (*AsyncCustomScanConfigureWait) (AsyncRequest *areq);
+</programlisting>
+     Configure a file descriptor event for which the
+     custom-scan node wishes to wait.
+     This function will only be called when the
+     custom-scan node has the
+     <literal>areq-&gt;callback_pending</literal> flag set, and should add
+     the event to the <structfield>as_eventset</structfield> of the parent
+     <structname>Append</structname> node described by the
+     <literal>areq</literal>.  See the comments for
+     <function>ExecAsyncConfigureWait</function> in
+     <filename>src/backend/executor/execAsync.c</filename> for additional
+     information.  When the file descriptor event occurs,
+     <function>AsyncCustomScanNotify</function> will be called.
+   </para>
+
+   <para>
+<programlisting>
+void (*AsyncCustomScanNotify) (AsyncRequest *areq);
+</programlisting>
+     Process a relevant event that has occurred, then produce one tuple
+     asynchronously from the custom-scan node.
+     This function should set the output parameters in the
+     <literal>areq</literal> in the same way as
+     <function>AsyncCustomScanRequest</function>.
+   </para>
   </sect2>
  </sect1>
 </chapter>
v1-0001-allow-custon-scan-asynchronous-execution.patchapplication/octet-stream; name=v1-0001-allow-custon-scan-asynchronous-execution.patchDownload
diff --git a/src/backend/executor/execAsync.c b/src/backend/executor/execAsync.c
index d8d79e972e..fc2b2742fe 100644
--- a/src/backend/executor/execAsync.c
+++ b/src/backend/executor/execAsync.c
@@ -18,6 +18,7 @@
 #include "executor/executor.h"
 #include "executor/nodeAppend.h"
 #include "executor/nodeForeignscan.h"
+#include "executor/nodeCustom.h"
 
 /*
  * Asynchronously request a tuple from a designed async-capable node.
@@ -37,6 +38,9 @@ ExecAsyncRequest(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanRequest(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanRequest(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
@@ -70,6 +74,9 @@ ExecAsyncConfigureWait(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanConfigureWait(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanConfigureWait(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
@@ -96,6 +103,9 @@ ExecAsyncNotify(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanNotify(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanNotify(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 8f56bd8a23..5c52caea95 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -226,3 +226,36 @@ ExecShutdownCustomScan(CustomScanState *node)
 	if (methods->ShutdownCustomScan)
 		methods->ShutdownCustomScan(node);
 }
+
+/*
+ * Asynchronous execution support
+ */
+void
+ExecAsyncCustomScanRequest(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncRequest != NULL);
+	methods->CustomScanAsyncRequest(areq);
+}
+
+void
+ExecAsyncCustomScanConfigureWait(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncConfigureWait != NULL);
+	methods->CustomScanAsyncConfigureWait(areq);
+}
+
+void
+ExecAsyncCustomScanNotify(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncNotify != NULL);
+	methods->CustomScanAsyncNotify(areq);
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e37f2933eb..8dbffc1323 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1175,6 +1175,20 @@ mark_async_capable_plan(Plan *plan, Path *path)
 										((ProjectionPath *) path)->subpath))
 				return true;
 			return false;
+		case T_CustomPath:
+			{
+				/*
+				* If the generated plan node includes a Result node for the
+				* projection, we can't execute it asynchronously.
+				*/
+				if (IsA(plan, Result))
+					return false;
+				
+				CustomPath *customPath = castNode(CustomPath, path);
+				if (customPath->flags & CUSTOMPATH_SUPPORT_ASYNC_EXECUTION)
+					break;
+				return false;
+			}
 		default:
 			return false;
 	}
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
index 5ef890144f..c2efdd20e6 100644
--- a/src/include/executor/nodeCustom.h
+++ b/src/include/executor/nodeCustom.h
@@ -39,4 +39,11 @@ extern void ExecCustomScanInitializeWorker(CustomScanState *node,
 										   ParallelWorkerContext *pwcxt);
 extern void ExecShutdownCustomScan(CustomScanState *node);
 
+/*
+ * Asynchronous execution support
+ */
+extern void ExecAsyncCustomScanRequest(AsyncRequest *areq);
+extern void ExecAsyncCustomScanConfigureWait(AsyncRequest *areq);
+extern void ExecAsyncCustomScanNotify(AsyncRequest *areq);
+
 #endif							/* NODECUSTOM_H */
diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h
index 34936db894..59f29ed54c 100644
--- a/src/include/nodes/extensible.h
+++ b/src/include/nodes/extensible.h
@@ -84,6 +84,7 @@ extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
 #define CUSTOMPATH_SUPPORT_BACKWARD_SCAN	0x0001
 #define CUSTOMPATH_SUPPORT_MARK_RESTORE		0x0002
 #define CUSTOMPATH_SUPPORT_PROJECTION		0x0004
+#define CUSTOMPATH_SUPPORT_ASYNC_EXECUTION	0x0008
 
 /*
  * Custom path methods.  Mostly, we just need to know how to convert a
@@ -155,6 +156,11 @@ typedef struct CustomExecMethods
 	void		(*ExplainCustomScan) (CustomScanState *node,
 									  List *ancestors,
 									  ExplainState *es);
+
+	/* Support functions for asynchronous execution */
+	void 		(*CustomScanAsyncRequest) (AsyncRequest *areq);
+	void 		(*CustomScanAsyncConfigureWait) (AsyncRequest *areq);
+	void 		(*CustomScanAsyncNotify) (AsyncRequest *areq);
 } CustomExecMethods;
 
 extern void RegisterCustomScanMethods(const CustomScanMethods *methods);
#2Kazutaka Onishi
onishi@heterodb.com
In reply to: Kazutaka Onishi (#1)
1 attachment(s)
Re: Asynchronous execution support for Custom Scan

v1 patch occurs gcc warnings, I fixed it.

2022年8月13日(土) 22:42 Kazutaka Onishi <onishi@heterodb.com>:

Show quoted text

Hello,

I suggest supporting asynchronous execution for Custom Scan.
Since v14, PostgreSQL supports asynchronous execution for Foreign Scan.
This patch enables asynchronous execution by applying the process for
Foreign Scan to Custom Scan .

The patch is divided into 2 parts, source and documents(sgml).

Regards,

Attachments:

v2-0001-allow-custon-scan-asynchronous-execution.patchapplication/octet-stream; name=v2-0001-allow-custon-scan-asynchronous-execution.patchDownload
diff --git a/src/backend/executor/execAsync.c b/src/backend/executor/execAsync.c
index d8d79e972e..fc2b2742fe 100644
--- a/src/backend/executor/execAsync.c
+++ b/src/backend/executor/execAsync.c
@@ -18,6 +18,7 @@
 #include "executor/executor.h"
 #include "executor/nodeAppend.h"
 #include "executor/nodeForeignscan.h"
+#include "executor/nodeCustom.h"
 
 /*
  * Asynchronously request a tuple from a designed async-capable node.
@@ -37,6 +38,9 @@ ExecAsyncRequest(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanRequest(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanRequest(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
@@ -70,6 +74,9 @@ ExecAsyncConfigureWait(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanConfigureWait(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanConfigureWait(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
@@ -96,6 +103,9 @@ ExecAsyncNotify(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanNotify(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanNotify(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 8f56bd8a23..5c52caea95 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -226,3 +226,36 @@ ExecShutdownCustomScan(CustomScanState *node)
 	if (methods->ShutdownCustomScan)
 		methods->ShutdownCustomScan(node);
 }
+
+/*
+ * Asynchronous execution support
+ */
+void
+ExecAsyncCustomScanRequest(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncRequest != NULL);
+	methods->CustomScanAsyncRequest(areq);
+}
+
+void
+ExecAsyncCustomScanConfigureWait(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncConfigureWait != NULL);
+	methods->CustomScanAsyncConfigureWait(areq);
+}
+
+void
+ExecAsyncCustomScanNotify(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncNotify != NULL);
+	methods->CustomScanAsyncNotify(areq);
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e37f2933eb..ebd4fbb5b0 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1175,6 +1175,20 @@ mark_async_capable_plan(Plan *plan, Path *path)
 										((ProjectionPath *) path)->subpath))
 				return true;
 			return false;
+		case T_CustomPath:
+			{
+				CustomPath *customPath = castNode(CustomPath, path);
+				/*
+				* If the generated plan node includes a Result node for the
+				* projection, we can't execute it asynchronously.
+				*/
+				if (IsA(plan, Result))
+					return false;
+				
+				if (customPath->flags & CUSTOMPATH_SUPPORT_ASYNC_EXECUTION)
+					break;
+				return false;
+			}
 		default:
 			return false;
 	}
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
index 5ef890144f..c2efdd20e6 100644
--- a/src/include/executor/nodeCustom.h
+++ b/src/include/executor/nodeCustom.h
@@ -39,4 +39,11 @@ extern void ExecCustomScanInitializeWorker(CustomScanState *node,
 										   ParallelWorkerContext *pwcxt);
 extern void ExecShutdownCustomScan(CustomScanState *node);
 
+/*
+ * Asynchronous execution support
+ */
+extern void ExecAsyncCustomScanRequest(AsyncRequest *areq);
+extern void ExecAsyncCustomScanConfigureWait(AsyncRequest *areq);
+extern void ExecAsyncCustomScanNotify(AsyncRequest *areq);
+
 #endif							/* NODECUSTOM_H */
diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h
index 34936db894..59f29ed54c 100644
--- a/src/include/nodes/extensible.h
+++ b/src/include/nodes/extensible.h
@@ -84,6 +84,7 @@ extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
 #define CUSTOMPATH_SUPPORT_BACKWARD_SCAN	0x0001
 #define CUSTOMPATH_SUPPORT_MARK_RESTORE		0x0002
 #define CUSTOMPATH_SUPPORT_PROJECTION		0x0004
+#define CUSTOMPATH_SUPPORT_ASYNC_EXECUTION	0x0008
 
 /*
  * Custom path methods.  Mostly, we just need to know how to convert a
@@ -155,6 +156,11 @@ typedef struct CustomExecMethods
 	void		(*ExplainCustomScan) (CustomScanState *node,
 									  List *ancestors,
 									  ExplainState *es);
+
+	/* Support functions for asynchronous execution */
+	void 		(*CustomScanAsyncRequest) (AsyncRequest *areq);
+	void 		(*CustomScanAsyncConfigureWait) (AsyncRequest *areq);
+	void 		(*CustomScanAsyncNotify) (AsyncRequest *areq);
 } CustomExecMethods;
 
 extern void RegisterCustomScanMethods(const CustomScanMethods *methods);
#3Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Kazutaka Onishi (#2)
Re: Asynchronous execution support for Custom Scan

Hi Onishi-san,

On Sun, Aug 14, 2022 at 12:59 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

v1 patch occurs gcc warnings, I fixed it.

Thanks for working on this!

I'd like to review this (though, I'm not sure I can have time for it
in the next commitfet), but I don't think we can review this without
any example. Could you provide it? I think a simple example is
better for ease of review.

Best regards,
Etsuro Fujita

#4Kohei KaiGai
kaigai@heterodb.com
In reply to: Etsuro Fujita (#3)
Re: Asynchronous execution support for Custom Scan

I internally suggested him to expand the ctidscan module for the PoC purpose.
https://github.com/kaigai/ctidscan

Even though it does not have asynchronous capability actually, but
suitable to ensure
API works and small enough for reviewing.

Best regards,

2022年8月22日(月) 17:55 Etsuro Fujita <etsuro.fujita@gmail.com>:

Hi Onishi-san,

On Sun, Aug 14, 2022 at 12:59 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

v1 patch occurs gcc warnings, I fixed it.

Thanks for working on this!

I'd like to review this (though, I'm not sure I can have time for it
in the next commitfet), but I don't think we can review this without
any example. Could you provide it? I think a simple example is
better for ease of review.

Best regards,
Etsuro Fujita

--
HeteroDB, Inc / The PG-Strom Project
KaiGai Kohei <kaigai@heterodb.com>

#5Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Kohei KaiGai (#4)
Re: Asynchronous execution support for Custom Scan

Hi KaiGai-san,

On Tue, Aug 23, 2022 at 6:26 PM Kohei KaiGai <kaigai@heterodb.com> wrote:

I internally suggested him to expand the ctidscan module for the PoC purpose.
https://github.com/kaigai/ctidscan

Even though it does not have asynchronous capability actually, but
suitable to ensure
API works and small enough for reviewing.

Seems like a good idea.

Thanks!

Best regards,
Etsuro Fujita

#6Kazutaka Onishi
onishi@heterodb.com
In reply to: Etsuro Fujita (#5)
Re: Asynchronous execution support for Custom Scan

Hi, Fujii-san,

The asynchronous version "ctidscan" plugin is ready.
Please check this.
https://github.com/0-kaz/ctidscan/tree/async_sample

I've confirmed this works correctly by running SQL shown below.
The query plan shows 2 custom scan works asynchronously.

postgres=# LOAD 'ctidscan';
LOAD
postgres=# EXPLAIN ANALYZE SELECT * FROM t1 WHERE ctid BETWEEN
'(2,1)'::tid AND '(3,10)'::tid
UNION
SELECT * FROM (SELECT * FROM t1 WHERE ctid BETWEEN '(2,115)'::tid AND
'(3,10)'::tid);
QUERY
PLAN
------------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=3.55..5.10 rows=155 width=36) (actual
time=0.633..0.646 rows=130 loops=1)
Group Key: t1.a, t1.b
Batches: 1 Memory Usage: 48kB
-> Append (cost=0.01..2.77 rows=155 width=36) (actual
time=0.035..0.590 rows=146 loops=1)
-> Async Custom Scan (ctidscan) on t1 (cost=0.01..1.00
rows=134 width=37) (actual time=0.009..0.129 rows=130 loops=1)
Filter: ((ctid >= '(2,1)'::tid) AND (ctid <= '(3,10)'::tid))
Rows Removed by Filter: 30
ctid quals: ((ctid >= '(2,1)'::tid) AND (ctid <= '(3,10)'::tid))
-> Async Custom Scan (ctidscan) on t1 t1_1 (cost=0.01..1.00
rows=21 width=37) (actual time=0.003..0.025 rows=16 loops=1)
Filter: ((ctid >= '(2,115)'::tid) AND (ctid <= '(3,10)'::tid))
Rows Removed by Filter: 144
ctid quals: ((ctid >= '(2,115)'::tid) AND (ctid <=
'(3,10)'::tid))
Planning Time: 0.314 ms
Execution Time: 0.762 ms
(14 rows)

Regards,

2022年8月26日(金) 17:18 Etsuro Fujita <etsuro.fujita@gmail.com>:

Show quoted text

Hi KaiGai-san,

On Tue, Aug 23, 2022 at 6:26 PM Kohei KaiGai <kaigai@heterodb.com> wrote:

I internally suggested him to expand the ctidscan module for the PoC purpose.
https://github.com/kaigai/ctidscan

Even though it does not have asynchronous capability actually, but
suitable to ensure
API works and small enough for reviewing.

Seems like a good idea.

Thanks!

Best regards,
Etsuro Fujita

#7Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Kazutaka Onishi (#6)
Re: Asynchronous execution support for Custom Scan

On Fri, Sep 2, 2022 at 10:43 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

The asynchronous version "ctidscan" plugin is ready.

Thanks for that!

I looked at the extended version quickly. IIUC, it uses the proposed
APIs, but actually executes ctidscans *synchronously*, so it does not
improve performance. Right?

Anyway, that version seems to be useful for testing that the proposed
APIs works well. So I'll review the proposed patches with it. I'm
not Fujii-san, though. :-)

Best regards,
Etsuro Fujita

#8Kazutaka Onishi
onishi@heterodb.com
In reply to: Etsuro Fujita (#7)
Re: Asynchronous execution support for Custom Scan

Fujita-san,

I'm sorry for my error on your name...

IIUC, it uses the proposed
APIs, but actually executes ctidscans *synchronously*, so it does not
improve performance. Right?

Exactly.
The actual CustomScan that supports asynchronous execution will
start processing in CustomScanAsyncRequest,
configure to detect completion via file descriptor in
CustomScanAsyncConfigureWait,
and receive the result in CustomScanAsyncNotify.

So I'll review the proposed patches with it.

Thank you!

2022年9月5日(月) 15:27 Etsuro Fujita <etsuro.fujita@gmail.com>:

Show quoted text

On Fri, Sep 2, 2022 at 10:43 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

The asynchronous version "ctidscan" plugin is ready.

Thanks for that!

I looked at the extended version quickly. IIUC, it uses the proposed
APIs, but actually executes ctidscans *synchronously*, so it does not
improve performance. Right?

Anyway, that version seems to be useful for testing that the proposed
APIs works well. So I'll review the proposed patches with it. I'm
not Fujii-san, though. :-)

Best regards,
Etsuro Fujita

#9Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Kazutaka Onishi (#8)
Re: Asynchronous execution support for Custom Scan

On Mon, Sep 5, 2022 at 10:32 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

I'm sorry for my error on your name...

No problem.

IIUC, it uses the proposed
APIs, but actually executes ctidscans *synchronously*, so it does not
improve performance. Right?

Exactly.
The actual CustomScan that supports asynchronous execution will
start processing in CustomScanAsyncRequest,
configure to detect completion via file descriptor in
CustomScanAsyncConfigureWait,
and receive the result in CustomScanAsyncNotify.

Ok, thanks!

Best regards,
Etsuro Fujita

#10Ronan Dunklau
ronan.dunklau@aiven.io
In reply to: Etsuro Fujita (#9)
Re: Asynchronous execution support for Custom Scan

Le mardi 6 septembre 2022, 11:29:55 CET Etsuro Fujita a écrit :

On Mon, Sep 5, 2022 at 10:32 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

I'm sorry for my error on your name...

No problem.

IIUC, it uses the proposed

APIs, but actually executes ctidscans *synchronously*, so it does not
improve performance. Right?

Exactly.
The actual CustomScan that supports asynchronous execution will
start processing in CustomScanAsyncRequest,
configure to detect completion via file descriptor in
CustomScanAsyncConfigureWait,
and receive the result in CustomScanAsyncNotify.

Ok, thanks!

Thanks for this patch, seems like a useful addition to the CustomScan API.
Just to nitpick: there are extraneous tabs in createplan.c on a blank line.

Sorry for the digression, but I know your ctidscan module had been proposed
for inclusion in contrib a long time ago, and I wonder if the rationale for
not including it could have changed. We still don't have tests which cover
CustomScan, and I can think of at least a few use cases where this customscan
is helpful and not merely testing code.

One of those use case is when performing migrations on a table, and one wants
to update the whole table by filling a new column with a computed value. You
obviously don't want to do it in a single transaction, so you end up batching
updates using an index looking for null values. If you want to do this, it's
much faster to update rows in a range of block, performing a first series of
batch updating all such block ranges, and then finally update the ones we
missed transactionally (inserted in a block we already processed while in the
middle of the batch, or in new blocks resulting from a relation extension).

Best regards,

--
Ronan Dunklau

#11Kazutaka Onishi
onishi@heterodb.com
In reply to: Ronan Dunklau (#10)
1 attachment(s)
Re: Asynchronous execution support for Custom Scan

Thank you for your comment.
I've removed the tabs.

I can think of at least a few use cases where this customscan is helpful and not merely testing code.

IIUC, we already can use ctid in the where clause on the latest
PostgreSQL, can't we?

2022年11月22日(火) 18:07 Ronan Dunklau <ronan.dunklau@aiven.io>:

Show quoted text

Le mardi 6 septembre 2022, 11:29:55 CET Etsuro Fujita a écrit :

On Mon, Sep 5, 2022 at 10:32 PM Kazutaka Onishi <onishi@heterodb.com> wrote:

I'm sorry for my error on your name...

No problem.

IIUC, it uses the proposed

APIs, but actually executes ctidscans *synchronously*, so it does not
improve performance. Right?

Exactly.
The actual CustomScan that supports asynchronous execution will
start processing in CustomScanAsyncRequest,
configure to detect completion via file descriptor in
CustomScanAsyncConfigureWait,
and receive the result in CustomScanAsyncNotify.

Ok, thanks!

Thanks for this patch, seems like a useful addition to the CustomScan API.
Just to nitpick: there are extraneous tabs in createplan.c on a blank line.

Sorry for the digression, but I know your ctidscan module had been proposed
for inclusion in contrib a long time ago, and I wonder if the rationale for
not including it could have changed. We still don't have tests which cover
CustomScan, and I can think of at least a few use cases where this customscan
is helpful and not merely testing code.

One of those use case is when performing migrations on a table, and one wants
to update the whole table by filling a new column with a computed value. You
obviously don't want to do it in a single transaction, so you end up batching
updates using an index looking for null values. If you want to do this, it's
much faster to update rows in a range of block, performing a first series of
batch updating all such block ranges, and then finally update the ones we
missed transactionally (inserted in a block we already processed while in the
middle of the batch, or in new blocks resulting from a relation extension).

Best regards,

--
Ronan Dunklau

Attachments:

v3-0001-allow-custon-scan-asynchronous-execution.patchapplication/octet-stream; name=v3-0001-allow-custon-scan-asynchronous-execution.patchDownload
diff --git a/src/backend/executor/execAsync.c b/src/backend/executor/execAsync.c
index d8d79e972e..fc2b2742fe 100644
--- a/src/backend/executor/execAsync.c
+++ b/src/backend/executor/execAsync.c
@@ -18,6 +18,7 @@
 #include "executor/executor.h"
 #include "executor/nodeAppend.h"
 #include "executor/nodeForeignscan.h"
+#include "executor/nodeCustom.h"
 
 /*
  * Asynchronously request a tuple from a designed async-capable node.
@@ -37,6 +38,9 @@ ExecAsyncRequest(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanRequest(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanRequest(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
@@ -70,6 +74,9 @@ ExecAsyncConfigureWait(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanConfigureWait(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanConfigureWait(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
@@ -96,6 +103,9 @@ ExecAsyncNotify(AsyncRequest *areq)
 		case T_ForeignScanState:
 			ExecAsyncForeignScanNotify(areq);
 			break;
+		case T_CustomScanState:
+			ExecAsyncCustomScanNotify(areq);
+			break;
 		default:
 			/* If the node doesn't support async, caller messed up. */
 			elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index a76ec43b9f..efa4316ff1 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -235,3 +235,36 @@ ExecShutdownCustomScan(CustomScanState *node)
 	if (methods->ShutdownCustomScan)
 		methods->ShutdownCustomScan(node);
 }
+
+/*
+ * Asynchronous execution support
+ */
+void
+ExecAsyncCustomScanRequest(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncRequest != NULL);
+	methods->CustomScanAsyncRequest(areq);
+}
+
+void
+ExecAsyncCustomScanConfigureWait(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncConfigureWait != NULL);
+	methods->CustomScanAsyncConfigureWait(areq);
+}
+
+void
+ExecAsyncCustomScanNotify(AsyncRequest *areq)
+{
+	CustomScanState *node = (CustomScanState *) areq->requestee;
+	const CustomExecMethods *methods = node->methods;
+
+	Assert(methods->CustomScanAsyncNotify != NULL);
+	methods->CustomScanAsyncNotify(areq);
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 66139928e8..30330b8ee3 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1175,6 +1175,20 @@ mark_async_capable_plan(Plan *plan, Path *path)
 										((ProjectionPath *) path)->subpath))
 				return true;
 			return false;
+		case T_CustomPath:
+			{
+				CustomPath *customPath = castNode(CustomPath, path);
+				/*
+				* If the generated plan node includes a Result node for the
+				* projection, we can't execute it asynchronously.
+				*/
+				if (IsA(plan, Result))
+					return false;
+
+				if (customPath->flags & CUSTOMPATH_SUPPORT_ASYNC_EXECUTION)
+					break;
+				return false;
+			}
 		default:
 			return false;
 	}
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
index 5ef890144f..c2efdd20e6 100644
--- a/src/include/executor/nodeCustom.h
+++ b/src/include/executor/nodeCustom.h
@@ -39,4 +39,11 @@ extern void ExecCustomScanInitializeWorker(CustomScanState *node,
 										   ParallelWorkerContext *pwcxt);
 extern void ExecShutdownCustomScan(CustomScanState *node);
 
+/*
+ * Asynchronous execution support
+ */
+extern void ExecAsyncCustomScanRequest(AsyncRequest *areq);
+extern void ExecAsyncCustomScanConfigureWait(AsyncRequest *areq);
+extern void ExecAsyncCustomScanNotify(AsyncRequest *areq);
+
 #endif							/* NODECUSTOM_H */
diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h
index 820b904841..cfa0c8a6ef 100644
--- a/src/include/nodes/extensible.h
+++ b/src/include/nodes/extensible.h
@@ -84,6 +84,7 @@ extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *extnode
 #define CUSTOMPATH_SUPPORT_BACKWARD_SCAN	0x0001
 #define CUSTOMPATH_SUPPORT_MARK_RESTORE		0x0002
 #define CUSTOMPATH_SUPPORT_PROJECTION		0x0004
+#define CUSTOMPATH_SUPPORT_ASYNC_EXECUTION	0x0008
 
 /*
  * Custom path methods.  Mostly, we just need to know how to convert a
@@ -155,6 +156,11 @@ typedef struct CustomExecMethods
 	void		(*ExplainCustomScan) (CustomScanState *node,
 									  List *ancestors,
 									  ExplainState *es);
+
+	/* Support functions for asynchronous execution */
+	void 		(*CustomScanAsyncRequest) (AsyncRequest *areq);
+	void 		(*CustomScanAsyncConfigureWait) (AsyncRequest *areq);
+	void 		(*CustomScanAsyncNotify) (AsyncRequest *areq);
 } CustomExecMethods;
 
 extern void RegisterCustomScanMethods(const CustomScanMethods *methods);
#12Ronan Dunklau
ronan.dunklau@aiven.io
In reply to: Kazutaka Onishi (#11)
Re: Asynchronous execution support for Custom Scan

IIUC, we already can use ctid in the where clause on the latest
PostgreSQL, can't we?

Oh, sorry, I missed the TidRangeScan. My apologies for the noise.

Best regards,

--
Ronan Dunklau

#13Kohei KaiGai
kaigai@heterodb.com
In reply to: Ronan Dunklau (#12)
Re: Asynchronous execution support for Custom Scan

IIUC, we already can use ctid in the where clause on the latest
PostgreSQL, can't we?

Oh, sorry, I missed the TidRangeScan. My apologies for the noise.

I made the ctidscan extension when we developed CustomScan API
towards v9.5 or v9.6, IIRC. It would make sense just an example of
CustomScan API (e.g, PG-Strom code is too large and complicated
to learn about this API), however, makes no sense from the standpoint
of the functionality.

Best regards,

2022年12月1日(木) 22:27 Ronan Dunklau <ronan.dunklau@aiven.io>:

IIUC, we already can use ctid in the where clause on the latest
PostgreSQL, can't we?

Oh, sorry, I missed the TidRangeScan. My apologies for the noise.

Best regards,

--
Ronan Dunklau

--
HeteroDB, Inc / The PG-Strom Project
KaiGai Kohei <kaigai@heterodb.com>

#14vignesh C
vignesh21@gmail.com
In reply to: Kohei KaiGai (#13)
Re: Asynchronous execution support for Custom Scan

On Fri, 2 Dec 2022 at 05:05, Kohei KaiGai <kaigai@heterodb.com> wrote:

IIUC, we already can use ctid in the where clause on the latest
PostgreSQL, can't we?

Oh, sorry, I missed the TidRangeScan. My apologies for the noise.

I made the ctidscan extension when we developed CustomScan API
towards v9.5 or v9.6, IIRC. It would make sense just an example of
CustomScan API (e.g, PG-Strom code is too large and complicated
to learn about this API), however, makes no sense from the standpoint
of the functionality.

This patch has been moving from one CF to another CF without any
discussion happening. It has been more than one year since any
activity in this thread. I don't see there is much interest in this
patch. I prefer to return this patch in this CF unless someone is
interested in the patch and takes it forward.

Regards,
Vignesh

#15vignesh C
vignesh21@gmail.com
In reply to: vignesh C (#14)
Re: Asynchronous execution support for Custom Scan

On Sun, 14 Jan 2024 at 11:21, vignesh C <vignesh21@gmail.com> wrote:

On Fri, 2 Dec 2022 at 05:05, Kohei KaiGai <kaigai@heterodb.com> wrote:

IIUC, we already can use ctid in the where clause on the latest
PostgreSQL, can't we?

Oh, sorry, I missed the TidRangeScan. My apologies for the noise.

I made the ctidscan extension when we developed CustomScan API
towards v9.5 or v9.6, IIRC. It would make sense just an example of
CustomScan API (e.g, PG-Strom code is too large and complicated
to learn about this API), however, makes no sense from the standpoint
of the functionality.

This patch has been moving from one CF to another CF without any
discussion happening. It has been more than one year since any
activity in this thread. I don't see there is much interest in this
patch. I prefer to return this patch in this CF unless someone is
interested in the patch and takes it forward.

Since the author or no one else showed interest in taking it forward
and the patch had no activity for more than 1 year, I have changed the
status to RWF. Feel free to add a new CF entry when someone is
planning to work on this.

Regards,
Vignesh