@@ -925,6 +925,17 @@ for (let stmtIndex = 0; stmtIndex < statementCount; stmtIndex++) {
925925
926926### Control Evaluation of Tasks
927927
928+ DuckDB splits work into relatively short tasks. By controlling the
929+ evaluation of these tasks explicitly, better cooperative multithreading
930+ can be accomplished. This is especially important for maximum throughput
931+ in the Node environment, which has a small, fixed number of threads for
932+ running asynchronous work.
933+ (See https://docs.libuv.org/en/stable/threadpool.html )
934+
935+ Below is a low-level way of controlling the evaluation of processing.
936+ See the "startThenRead" and "startStreamThenRead" methods for helpers
937+ that do much of this for you.
938+
928939``` ts
929940import { DuckDBPendingResultState } from ' @duckdb/node-api' ;
930941
@@ -935,7 +946,7 @@ async function sleep(ms) {
935946}
936947
937948const prepared = await connection .prepare (' from range(10_000_000)' );
938- const pending = prepared .start ();
949+ const pending = prepared .startStream ();
939950while (pending .runTask () !== DuckDBPendingResultState .RESULT_READY ) {
940951 console .log (' not ready' );
941952 await sleep (1 );
@@ -945,6 +956,12 @@ const result = await pending.getResult();
945956// ...
946957```
947958
959+ Equivalently, using ` startStreamThenRead ` :
960+
961+ ``` ts
962+ const reader = await connection .startStreamThenRead (sql );
963+ ```
964+
948965### Ways to run SQL
949966
950967``` ts
@@ -1030,11 +1047,51 @@ const pending = await connection.start(sql);
10301047const pending = await connection .start (sql , values );
10311048const pending = await connection .start (sql , values , types );
10321049
1050+ // The methods beginning with "startThenRead" provide some, but not full,
1051+ // cooperative multithreading. They use pending results to split processing
1052+ // into short tasks, but they fully materialize the result, which can
1053+ // take some time (and memory). For full cooperative multithreading,
1054+ // see the "startStreamThenRead" methods below.
1055+ const reader = await connection .startThenRead (sql );
1056+ const reader = await connection .startThenRead (sql , values );
1057+ const reader = await connection .startThenRead (sql , values , types );
1058+
1059+ const reader = await connection .startThenReadAll (sql );
1060+ const reader = await connection .startThenReadAll (sql , values );
1061+ const reader = await connection .startThenReadAll (sql , values , types );
1062+
1063+ const reader = await connection .startThenReadUntil (sql , targetRowCount );
1064+ const reader =
1065+ await connection .startThenReadUntil (sql , targetRowCount , values );
1066+ const reader =
1067+ await connection .startThenReadUntil (sql , targetRowCount , values , types );
1068+
10331069// Create a pending, streaming result.
10341070const pending = await connection .startStream (sql );
10351071const pending = await connection .startStream (sql , values );
10361072const pending = await connection .startStream (sql , values , types );
10371073
1074+ // The methods beginning with "startStreamThenRead" are the best options
1075+ // for cooperative multithreading. By creating a streaming result, they
1076+ // prevent the result from being fully materialized. By using a pending
1077+ // result, they split processing into short tasks, preventing any single
1078+ // task from occupying a thread for too long.
1079+ const reader = await connection .startStreamThenRead (sql );
1080+ const reader = await connection .startStreamThenRead (sql , values );
1081+ const reader = await connection .startStreamThenRead (sql , values , types );
1082+
1083+ const reader = await connection .startStreamThenReadAll (sql );
1084+ const reader = await connection .startStreamThenReadAll (sql , values );
1085+ const reader =
1086+ await connection .startStreamThenReadAll (sql , values , types );
1087+
1088+ const reader =
1089+ await connection .startStreamThenReadUntil (sql , targetRowCount );
1090+ const reader =
1091+ await connection .startStreamThenReadUntil (sql , targetRowCount , values );
1092+ const reader = await connection .startStreamThenReadUntil (
1093+ sql , targetRowCount , values , types );
1094+
10381095// Create a pending result from a prepared statement.
10391096const pending = await prepared .start ();
10401097const pending = await prepared .startStream ();
@@ -1043,6 +1100,9 @@ while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) {
10431100 // optionally sleep or do other work between tasks
10441101}
10451102
1103+ // Or, run tasks (cooperatively) until the result is ready.
1104+ await pending .runAllTasks ();
1105+
10461106// Retrieve the result. If not yet READY, will run until it is.
10471107const result = await pending .getResult ();
10481108
@@ -1058,14 +1118,30 @@ const reader = await pending.readUntil(targetRowCount);
10581118
10591119// Asynchronously retrieve data for all rows:
10601120const columns = await result .getColumns ();
1121+ const columnsJS = await result .getColumnsJS ();
10611122const columnsJson = await result .getColumnsJson ();
10621123const columnsObject = await result .getColumnsObject ();
1124+ const columnsObjectJS = await result .getColumnsObjectJS ();
10631125const columnsObjectJson = await result .getColumnsObjectJson ();
10641126const rows = await result .getRows ();
1127+ const rowsJS = await result .getRowsJS ();
10651128const rowsJson = await result .getRowsJson ();
10661129const rowObjects = await result .getRowObjects ();
1130+ const rowObjectsJS = await result .getRowObjectsJS ();
10671131const rowObjectsJson = await result .getRowObjectsJson ();
10681132
1133+ // Asynchronous iterators can be used to retrieve partial data:
1134+ for await (const chunk of result ) {
1135+ // ...
1136+ }
1137+
1138+ // Each chunk can be converted to rows:
1139+ for await (const rows of result .yieldRows ()) {
1140+ // ...
1141+ }
1142+ // See also variations of "yieldRow" for returning row objects
1143+ // (instead of row arrays), and with JS, JSON, or custom conversion.
1144+
10691145// From a reader
10701146
10711147// First, (asynchronously) read some rows:
@@ -1075,12 +1151,16 @@ await reader.readUntil(targetRowCount);
10751151
10761152// Then, (synchronously) get result data for the rows read:
10771153const columns = reader .getColumns ();
1154+ const columnsJS = reader .getColumnsJS ();
10781155const columnsJson = reader .getColumnsJson ();
10791156const columnsObject = reader .getColumnsObject ();
1157+ const columnsObjectJS = reader .getColumnsObjectJS ();
10801158const columnsObjectJson = reader .getColumnsObjectJson ();
10811159const rows = reader .getRows ();
1160+ const rowsJS = reader .getRowsJS ();
10821161const rowsJson = reader .getRowsJson ();
10831162const rowObjects = reader .getRowObjects ();
1163+ const rowObjectsJS = reader .getRowObjectsJS ();
10841164const rowObjectsJson = reader .getRowObjectsJson ();
10851165
10861166// Individual values can also be read directly:
0 commit comments