sqlx
sqlx copied to clipboard
Incapable of returning a `fetch` `Stream` from a trait method generated by `tonic-build`
Hi all, I ran into what seems to be an unsolvable issue combining sqlx
and tonic-build
generated trait methods which use the server-streaming feature of gRPC/proto3, with the following example code to illustrate what's going on:
#[tonic::async_trait]
impl FooServer for FooServerImpl {
// Associated type to refer to the `Stream` in `get_bars`
type GetBarsStream = BoxedStream<'???, Result<Bar, tonic::Status>>;
// ^ this lifetime can only be `'static`, there's no way to refer to `&self` at this point
// unless `tonic-build`/`prost-build` are modified to add a lifetime to every associated stream type
async fn get_bars(&self, request: tonic::Request<GetBarsRequest>) -> Result<tonic::Response<Self::GetBarsStream>, tonic::Status> {
Ok(tonic::Response::new(
sqlx::query_as::<_, Bar>("SELECT ... FROM ... WHERE ...")
.fetch(&self.db_pool).map_err(|_| ...)
// ^ since all executors are references, this causes a lifetime error since there's no way to make the stream `'static`
// which as far as I can tell, makes doing this literally impossible without writing some very awkward `unsafe`
// it's also not possible to `acquire` a connection inside of the method then use it in the
// stream since its then a dangling reference on return
))
}
}
Is there any possibility that this can be resolved by somehow creating some kind of owned Executor
? I tried, but obviously immediately ran into issues with it requiring longer lived lifetimes from consuming self
. I'd really like to not need to return everything all at once but it seems unavoidable while using sqlx
.
This is a big issue for us as well.
Can we get some visibility on this ticket?
For those that find this issue, I solved this problem by using:
let pool = Box::leak::<'static>(Box::new(self.pool.clone()));
The logic being:
a) It's safe to leak memory (according to rust).
b) The pool is alive as long as the application is alive.
c) We explicitly call pool.close()
when the application closes, so even if there's something keeping the app from closing, the pool itself will reject new requests for connections..