Skip to content

Commit d411cab

Browse files
authored
fix(rust/driver/datafusion): using datafusion driver in async runtime (#3712)
Closes #3711 This is not an ideal solution, but we need to think about sync/async ergonomics in the future. At the moment, there is a problem: the inability to use this approach in single-thread runtime. --------- Signed-off-by: if0ne <pavel.agafonov.al@gmail.com> Signed-off-by: Pavel Agafonov <pavel.agafonov.al@gmail.com>
1 parent 7271f3c commit d411cab

2 files changed

Lines changed: 65 additions & 20 deletions

File tree

rust/driver/datafusion/src/lib.rs

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ use datafusion_substrait::logical_plan::consumer::from_substrait_plan;
2525
use datafusion_substrait::substrait::proto::Plan;
2626
use prost::Message;
2727
use std::fmt::Debug;
28+
use std::future::Future;
2829
use std::sync::Arc;
2930
use std::vec::IntoIter;
30-
use tokio::runtime::Runtime;
3131

3232
use arrow_array::builder::{
3333
BooleanBuilder, Int32Builder, Int64Builder, ListBuilder, MapBuilder, MapFieldNames,
@@ -48,6 +48,31 @@ use adbc_core::{
4848
schemas, Connection, Database, Driver, Optionable, Statement,
4949
};
5050

51+
pub enum Runtime {
52+
Handle(tokio::runtime::Handle),
53+
Tokio(tokio::runtime::Runtime),
54+
}
55+
56+
impl Runtime {
57+
pub fn new(handle: Option<tokio::runtime::Handle>) -> std::io::Result<Self> {
58+
if let Some(handle) = handle {
59+
Ok(Self::Handle(handle))
60+
} else {
61+
let runtime = tokio::runtime::Builder::new_multi_thread()
62+
.enable_all()
63+
.build()?;
64+
Ok(Self::Tokio(runtime))
65+
}
66+
}
67+
68+
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
69+
match self {
70+
Runtime::Handle(handle) => tokio::task::block_in_place(|| handle.block_on(future)),
71+
Runtime::Tokio(runtime) => runtime.block_on(future),
72+
}
73+
}
74+
}
75+
5176
#[derive(Debug)]
5277
pub struct SingleBatchReader {
5378
batch: Option<RecordBatch>,
@@ -109,13 +134,23 @@ impl RecordBatchReader for DataFusionReader {
109134
}
110135

111136
#[derive(Default)]
112-
pub struct DataFusionDriver {}
137+
pub struct DataFusionDriver {
138+
handle: Option<tokio::runtime::Handle>,
139+
}
140+
141+
impl DataFusionDriver {
142+
pub fn new(handle: Option<tokio::runtime::Handle>) -> Self {
143+
Self { handle }
144+
}
145+
}
113146

114147
impl Driver for DataFusionDriver {
115148
type DatabaseType = DataFusionDatabase;
116149

117150
fn new_database(&mut self) -> Result<Self::DatabaseType> {
118-
Ok(Self::DatabaseType {})
151+
Ok(Self::DatabaseType {
152+
handle: self.handle.clone(),
153+
})
119154
}
120155

121156
fn new_database_with_opts(
@@ -127,15 +162,19 @@ impl Driver for DataFusionDriver {
127162
),
128163
>,
129164
) -> adbc_core::error::Result<Self::DatabaseType> {
130-
let mut database = Self::DatabaseType {};
165+
let mut database = Self::DatabaseType {
166+
handle: self.handle.clone(),
167+
};
131168
for (key, value) in opts {
132169
database.set_option(key, value)?;
133170
}
134171
Ok(database)
135172
}
136173
}
137174

138-
pub struct DataFusionDatabase {}
175+
pub struct DataFusionDatabase {
176+
handle: Option<tokio::runtime::Handle>,
177+
}
139178

140179
impl Optionable for DataFusionDatabase {
141180
type Option = OptionDatabase;
@@ -186,10 +225,7 @@ impl Database for DataFusionDatabase {
186225
fn new_connection(&self) -> Result<Self::ConnectionType> {
187226
let ctx = SessionContext::new();
188227

189-
let runtime = tokio::runtime::Builder::new_multi_thread()
190-
.enable_all()
191-
.build()
192-
.unwrap();
228+
let runtime = Runtime::new(self.handle.clone()).unwrap();
193229

194230
Ok(DataFusionConnection {
195231
runtime: Arc::new(runtime),
@@ -208,10 +244,7 @@ impl Database for DataFusionDatabase {
208244
) -> adbc_core::error::Result<Self::ConnectionType> {
209245
let ctx = SessionContext::new();
210246

211-
let runtime = tokio::runtime::Builder::new_multi_thread()
212-
.enable_all()
213-
.build()
214-
.unwrap();
247+
let runtime = Runtime::new(self.handle.clone()).unwrap();
215248

216249
let mut connection = DataFusionConnection {
217250
runtime: Arc::new(runtime),

rust/driver/datafusion/tests/test_datafusion.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use datafusion_substrait::logical_plan::producer::to_substrait_plan;
2626
use datafusion_substrait::substrait::proto::Plan;
2727
use prost::Message;
2828

29-
fn get_connection() -> DataFusionConnection {
30-
let mut driver = DataFusionDriver::default();
29+
fn get_connection(handle: Option<tokio::runtime::Handle>) -> DataFusionConnection {
30+
let mut driver = DataFusionDriver::new(handle);
3131
let database = driver.new_database().unwrap();
3232
database.new_connection().unwrap()
3333
}
@@ -80,7 +80,7 @@ fn execute_substrait(connection: &mut DataFusionConnection, plan: Plan) -> Recor
8080

8181
#[test]
8282
fn test_connection_options() {
83-
let mut connection = get_connection();
83+
let mut connection = get_connection(None);
8484

8585
let current_catalog = connection
8686
.get_option_string(OptionConnection::CurrentCatalog)
@@ -119,7 +119,7 @@ fn test_connection_options() {
119119

120120
#[test]
121121
fn test_get_objects_database() {
122-
let mut connection = get_connection();
122+
let mut connection = get_connection(None);
123123

124124
let objects = get_objects(&connection);
125125

@@ -134,7 +134,7 @@ fn test_get_objects_database() {
134134

135135
#[test]
136136
fn test_execute_sql() {
137-
let mut connection = get_connection();
137+
let mut connection = get_connection(None);
138138

139139
execute_update(&mut connection, "CREATE TABLE IF NOT EXISTS datafusion.public.example (c1 INT, c2 VARCHAR) AS VALUES(1,'HELLO'),(2,'DATAFUSION'),(3,'!')");
140140

@@ -146,7 +146,7 @@ fn test_execute_sql() {
146146

147147
#[test]
148148
fn test_ingest() {
149-
let mut connection = get_connection();
149+
let mut connection = get_connection(None);
150150

151151
execute_update(&mut connection, "CREATE TABLE IF NOT EXISTS datafusion.public.example (c1 INT, c2 VARCHAR) AS VALUES(1,'HELLO'),(2,'DATAFUSION'),(3,'!')");
152152

@@ -172,7 +172,7 @@ fn test_ingest() {
172172

173173
#[test]
174174
fn test_execute_substrait() {
175-
let mut connection = get_connection();
175+
let mut connection = get_connection(None);
176176

177177
execute_update(&mut connection, "CREATE TABLE IF NOT EXISTS datafusion.public.example (c1 INT, c2 VARCHAR) AS VALUES(1,'HELLO'),(2,'DATAFUSION'),(3,'!')");
178178

@@ -198,3 +198,15 @@ fn test_execute_substrait() {
198198
assert_eq!(batch.num_rows(), 3);
199199
assert_eq!(batch.num_columns(), 2);
200200
}
201+
202+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
203+
async fn test_running_in_async() {
204+
let mut connection = get_connection(Some(tokio::runtime::Handle::current()));
205+
206+
execute_update(&mut connection, "CREATE TABLE IF NOT EXISTS datafusion.public.example (c1 INT, c2 VARCHAR) AS VALUES(1,'HELLO'),(2,'DATAFUSION'),(3,'!')");
207+
208+
let batch = execute_sql_query(&mut connection, "SELECT * FROM datafusion.public.example");
209+
210+
assert_eq!(batch.num_rows(), 3);
211+
assert_eq!(batch.num_columns(), 2);
212+
}

0 commit comments

Comments
 (0)