Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possibility to build sqlx without runtime #1627

Closed
heroin-moose opened this issue Jan 13, 2022 · 5 comments
Closed

Possibility to build sqlx without runtime #1627

heroin-moose opened this issue Jan 13, 2022 · 5 comments

Comments

@heroin-moose
Copy link

Currently sqlx requires one of the runtime-* features enabled. However, when using sqlx in a crate that only defines types and derives FromRow it may be a little problematic, because crate users can have different runtimes.

@05storm26
Copy link
Contributor

05storm26 commented Jan 13, 2022

Create 3 corresponding features like: sqlx-runtime-actix, sqlx-runtime-tokio, sqlx-runtime-async-std which each enable the proper sqlx runtime feature like this: "sqlx/runtime-actix", "sqlx/runtime-tokio", "sqlx/runtime-async-std". Users of your library will then be able to select whichever feature they would like to use in sqlx through those options.

You can see how it's done in sea-orm: https://github.com/SeaQL/sea-orm/blob/master/Cargo.toml

@heroin-moose
Copy link
Author

Yeah, but do crate that only needs derives really need to advertise flags to match the chosen runtime?

@zdenek-crha
Copy link

TLDR

Larger projects may need to split db-related stuff into multiple crates and ability to compile without runtime would simplify that use case.

See use cases and longer summary below:

Crate with shared data types

I have a crate that defines shared data types for multi-crate project. Different downstream crates will use the data types in different ways - some with db-related stuff, some without (yes, there is serde feature too). The only code in the crate related to sqlx is something like this:

pub enum State {
    Pass  = 1,
    Fail  = 2,
    Skip  = 3,
    ....
}

#[cfg(feature="postgres")] 
mod postgres {
     impl Encode<'_, Postgres> for super::State {
       ...
    }

    impl Decode<'_, Postgres> for super::State {
       ...
    }

    impl Type<Postgres> for super::State {
       ...
    }
}

On this level, the crate does not need to select runtime. It does not even know which runtime will be used by downstream crate.

The easiest solution is to 'just select runtime', but wrong selection can result in two or more crates selecting different runtime and conflict on compilation. (or just compile bunch of dead code, I did not experiment to much with actual setup).

Crate with db access layer

Similar case as above. I have a crate that implements a report on database. It will consist of input/output data types, some database queries. But again, it will not run those queries itself. The sole use is to be included in downstream crate that will start runtime and use shared access layer definition to run them.

If I try to do something like that, I will again end with same issue as in the first case.

Solution from sea-orm

I have checked the sea-orm referred in previous comment. The solution would probably work, but I will force me to have runtime selection features duplicated in every crate that needs stuff from sqlx. Whenever it uses runtime or not.

That seems like a lot of duplicate code in (possibly many) crates. And it is not easy solution to think about and implement correctly, especially for people who are just starting to use feature flags in their crates (like me :-))

My use case is three tier architecture with crates providing shared data, crates building queries using that data and finally service that runs it all together. Think dependency tree like this:

- foobar-service (connects to db and needs runtime)
   - db-report-foo (just nice API over SQL queries, no need for runtime)
     - db-data-foo (just reusable data types, no need for runtime)
   - db-report-bar 
     - db-data-bar
   - db-report-foobar 
     - db-data-foo 
     - db-data-bar

That's 6 crates that need to 'tunnel' runtime selection features instead of just setting it in the top level crate that actually needs it. Not even talking about need to compile runtime in 5 crates that will never need the code, wasting resources.

Attempt to solve the issue

I have checked the sql-core crate, thinking that it will define just the shared data types, traits without forcing runtime selection. Unfortunately, the runtime must be selected even on this level.

There does not seem to be any lower-level sqlx crate that would provide me with just traits/data types witch which I can develop my db access layer without tying it to one runtime.

Note 1: Yes, I could probably put it all into single crate and spare myself of some features-related difficulties. For smaller project, sure and that is how I actually started. But with growing number of reports to implement, splitting them into crates become necessity.

Note 2: I can't see spending the effort of duplicating feature flag selection code on many levels. It grates on me, but I will hard-code runtime selection on all levels (:disappointed:)

Summary

From my point of view, forcing runtime selection in every crate that uses sqlx will get cumbersome when project gets larger or has need for organizing code in chunks due to readability/maintainability reasons. Any of following solutions would work:

  • allow sqlx dependency without selecting runtime
  • allow sqlx-core dependency without selecting runtime for crates that do not need it
  • have different sqlx-? crates that expose parts of the functionality needed for writing reusable, db-related code (ie de/encoding, queries, ....)

The sea-orm solution is workable, but pain to manage with multiple dependency levels and many crates.

Is there a technical reason why runtime must be selected? Or is it just so people do not forget to select runtime and then wonder why their code does not work?

@emschwartz
Copy link

emschwartz commented Dec 2, 2022

I have the exact use case @heroin-moose and @zdenek-crha describe where I have a bunch of types in one crate and want to derive FromRow on them without pulling the runtime in as a dependency of that crate.

@abonander
Copy link
Collaborator

This is fixed in the 0.7.0 release which is currently in an alpha cycle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants