I am trying to write a toy web server that gets user info from an OAuth provider and displays their name on the index page.
I have a tower::Service
impl that takes in a request; an auth response from oauth provider, and does a few things with a database backend via Diesel.
Service Impl
pub(crate) struct AppOAuthEntry {
// Fairly nested 'inner'
inner: GetAuth<...<...<...<...<...<...<...<GetSession>>>>>>>>,
}
impl Service<AuthResponse> for AppOAuthEntry {
type Response = UserSession;
...
...
Each is a service with a corresponding tower::Layer
pub(crate) struct GetAuthLayer {
auth_exists: AuthExists<StoreError>,
auth_create: AuthCreate<StoreError>,
}
Backend Database Services
Each of those fields are BoxCloneService
and look like this
type AuthExists<E> = BoxCloneService<Request, Response, E>
BoxCloneService
because
-
Layer requires the services to be
Clone
to createService
instanceimpl<S> Layer<S> for GetAuthLayer { type Service = GetAuth<S>; fn layer(&self, inner: S) -> Self::Service { GetAuth { inner, auth_exists: self.auth_exists.clone(), auth_create: self.auth_create.clone(), } } }
-
So I could swap backend if needed, as long as that backend implements required
Service
trait.Here's an example of one such backend
pub(crate) struct Postgres { pool: Pool<ConnectionManager<PgConnection>>, }
where
Service
impl is basically thisfn call(&mut self, Create(req): Create<Request>) -> Self::Future { ... ... let connection = self.get(); ... ... async move { let resp = connection?.transaction(|conn| { let record = diesel::insert_into(auth::table) .values(req) .get_result(conn)?; Ok(record) })?; Ok(resp) } .boxed() }
Axum Server
Finally, this is how I want to use it with Axum
let app = Router::new()
.route("/", get(route::index))
.nest(
"/login",
Router::new()
.route(
"/google",
get(route::login::google).with_state(...),
)
...
...
...
)
.with_state(AppOAuthEntry::new(...));
^^^
^^^
Errors
It was here that I found out none of my Service
impls are Sync
! And BoxCloneService
is specifically !Sync
help: the trait `Sync` is not implemented for
`(dyn tower::util::boxed_clone::CloneService...
This refers to those struct fields that are BoxCloneService
like AuthCreate
.
Which leads to this error
35 | pub(crate) struct AppOAuthEntry {
| ^^^^^^^^^^^^^
note: required by a bound in `Router::<S, B>::new`
|
105 | S: Clone + Send + Sync + 'static,
| ^^^^ required by this bound in
`Router::<S, B>::new`
Question(s)
So my question is, what should I do to fix this?
I haven't tried it yet, but perhaps wrapping those store services in Rc<RefCell<BoxCloneService<...>>>
could work? Seems the easiest solution but I am not sure if it'd be safe.
Maybe using Mutex
? Although I don't think it's worth it because none of those services are modifying internal state (they are just wrappers around db transaction
).
BoxService does implement Sync
but not Clone
so maybe using Arc
like Arc::clone(...)
could work, although I won't be able to borrow the service mutably which is required by call
method.