Skip to content

feat(rust/core): add async traits for Driver, Database, Connection, Statement - #3714

Open
if0ne wants to merge 14 commits into
apache:mainfrom
if0ne:rust/async-trait
Open

feat(rust/core): add async traits for Driver, Database, Connection, Statement#3714
if0ne wants to merge 14 commits into
apache:mainfrom
if0ne:rust/async-trait

Conversation

@if0ne

Copy link
Copy Markdown
Contributor

Hi, I’m back again. This is a continuation of this PR: #3712

I’ve returned with a draft of the asynchronous traits. They fully mirror the synchronous API (with a couple of differences in input arguments), adding Future + Send as the return type.

As further development, I can propose:

  • Abstract away from async executors and introduce an AsyncExecutor trait with a single block_on method. In adbc_core, add helper structures like SyncDriverWrapper<E: AsyncExecutor, D: AsyncDriver> and others, and implement the corresponding synchronous traits for them.

  • Add some procedural macro magic to automatically implement the synchronous traits by generating the code described above.

@if0ne
if0ne marked this pull request as draft November 14, 2025 08:07
@github-actionsgithub-actionsBot added this to the ADBC Libraries 22 milestone Nov 14, 2025
@if0ne

Copy link
Copy Markdown
ContributorAuthor

What do you think? @lidavidm and @eitsupi

@lidavidm

Copy link
Copy Markdown
Member

CC @felipecrv@mbrobbel too

@if0neif0ne changed the title feat(rust/adbc_core): add async traits for Driver, Database, Connection, Statementfeat(rust/core): add async traits for Driver, Database, Connection, StatementNov 14, 2025

@mbrobbelmbrobbel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Send bound stuff I would suggest using https://docs.rs/trait-variant/0.1.2/trait_variant/ (as suggested in https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits/#async-fn-in-public-traits).

For async vs sync I would suggest using the async trait as the default and provided sync helper traits and structs. But the truth is; there is no elegant solution available today for this.

@if0ne

Copy link
Copy Markdown
ContributorAuthor

trait_variant will help solve the Send bound problem, but it introduces another problem: the macro does not add additional Send bound to function input arguments if they are of an opaque type. Should we use explicit types instead?

@if0ne
if0neforce-pushed the rust/async-trait branch 2 times, most recently from 2888512 to 61963f8CompareNovember 14, 2025 14:09

@felipecrvfelipecrv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the code in blocking is moved back to the current location I can give another review.

@lidavidm do we really need everything to be a Future, even get/set options?

Comment threadrust/core/src/non_blocking.rs Outdated
Comment threadrust/core/src/non_blocking.rs Outdated
Comment threadrust/core/src/non_blocking.rs Outdated
Comment threadrust/core/src/sync.rs
@if0ne

Copy link
Copy Markdown
ContributorAuthor

Once the code in blocking is moved back to the current location I can give another review.

@lidavidm do we really need everything to be a Future, even get/set options?

I made the AsyncOptionable because in the DataFusion implementation, setting options involved asynchronous operations. It might be possible to make get_option_* sync, but I would keep set_option async.

https://github.com/if0ne/arrow-adbc/blob/61963f8e450926da1657f31112db9498f789b380/rust/driver/datafusion/src/lib.rs#L247

@lidavidm

Copy link
Copy Markdown
Member

Even get_option may involve I/O in some cases (e.g. if the driver has to run a query to fetch some information), unfortunately

@if0ne
if0ne marked this pull request as ready for review November 26, 2025 10:25

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does arrow-rs not already have a trait for this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that arrow-rs does not implement ArrowAsyncDeviceStreamHandler etc yet. I think we need to figure this out in arrow-rs

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related issue apache/arrow-rs#7228

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the merge, I’ll get back to this PR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged (at least the trait). We can bump to the next version when available (possibly via feature? Or maybe splitting async traits into a new crate?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will split the current PR into three pull requests:

  1. Moving the existing structures into a separate module
  2. Adding sendable async traits as an async feature
  3. Adding bridge structures, which will probably also be under the async feature

Comment threadrust/core/src/options.rs
Comment threadrust/core/src/schemas.rs
Comment threadrust/core/src/executor.rs Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there needs to be explanation somewhere of what "local" means (traits that are not Send?)

Additionally, does it make sense to have non-Send variants? It's not clear to me that that's useful (when would a connection be limited to a single thread?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about flexibility. I think that, in practical terms, most drivers will implement the async trait with a Send bound. But someone might want to implement an async Rust driver that uses an FFI library to connect to a specialized database.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it actually worth optimizing for that flexibility, though? (Versus, say, expecting that such a very specialized database would instead manage this internally?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because as it stands, this means duplicating every method/trait

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it’s not worth supporting the non-Send variant (maybe someday in the future, when the crate stabilizes and reaches versions > 1.0.0).

Personally, I was planning to use adbc for the wasm target, and as far as I understand, all the necessary structures are Send.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: I found another driver (which seems to be fairly recent): https://github.com/marconae/exarrow-rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sedona-db implementation is unused to my knowledge so don't worry about any compatibility issues there!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marconae sorry for the random ping, but would you like to weigh in on async ADBC interfaces? 🙂

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about flexibility.

We might end up offering too much flexibility in a standard that is supposed to make database access more uniform. If a database doesn't allow its handles to move between threads, it's the responsibility of the ADBC driver wrapping these database APIs to fix for that rather than bubbling up these restrictions to Rust users of ADBC via the type system which basically means a rewrite of the entire application depending on the ADBC driver that it uses.

@marconaemarconaeJan 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lidavidm thx for including me. I had to go async, because I am fetching data from an async web-socket protocol. Maybe the my pattern is a bit unusual. For me it was fine to work against a synchronous library and wrap it.

if0ne added 13 commits December 5, 2025 13:09
…or DataFusion driver
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
…d sync wrappers
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
lidavidm pushed a commit that referenced this pull request Feb 9, 2026
A small step toward implementing #3714
Signed-off-by: if0ne <pavel.agafonov.al@gmail.com>
@lidavidmlidavidm removed this from the ADBC Libraries 23 milestone Apr 7, 2026
msrathore-db added a commit to adbc-drivers/databricks that referenced this pull request Apr 23, 2026
Adds a short §20 to the design covering the convergence plan with
apache/arrow-adbc#3714 (active upstream PR that adds async traits to
adbc_core). Three items:
1. Adopt RPITIT style (`fn foo() -> impl Future<Output=T> + Send`)
instead of `#[async_trait]` from day one. Supersedes prior
references to async-trait in §5.4/§5.5. Zero-cost convergence
when #3714 lands.
2. Convergence is a mechanical ~200 LOC refactor when upstream
merges — no behavioral change, napi-rs binding unaffected. Our
AsyncResultReader becomes Pin<Box<dyn RecordBatchStream + Send>>;
our inherent methods become trait impls; our Databricks-specific
metadata method variants go away since upstream `get_objects`
covers them. Our AsyncAuthProvider stays — ADBC has no auth trait.
3. Deep-dive analysis of the upstream PR and the Apache Node driver
manager (@apache-arrow/adbc-driver-manager) lives in a companion
reference doc (adbc-upstream-references.md) to keep the design
focused on architecture rather than investigation notes.
Why this was not folded into §19 (Apache Node binding validation):
different subject matter (runtime trait shape vs binding-layer
patterns), different convergence paths (we already implement
adbc_core sync traits; we'll add async ones; we don't adopt the
Node package).
Co-authored-by: Isaac
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@if0ne@lidavidm@felipecrv@marconae@mbrobbel@paleolimbot@eitsupi