I have a database connection using rocket-db-pools, using sqlx (pg), but I noticed that it doesn't implement a transaction feature. I tried to init a transaction using a guard:
#[derive(Database)]
#[database("db_name")]
pub struct Main(sqlx::PgPool);
pub struct PgTransaction{
pub conn: sqlx::Transaction<'static, sqlx::Postgres>
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for PgTransaction {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, ()> {
let db = request.guard::<&Main>().await;
match db {
rocket::outcome::Outcome::Success(main) => {
match main.begin().await {
Ok(main) => {
Outcome::Success(
PgTransaction{
conn: main
}
)
}
Err(_) => Outcome::Error((Status::InternalServerError, ()))
}
},
_ => Outcome::Error((Status::InternalServerError, ()))
}
}
}
It does indeed create a transaction, since every change don't go to the database until I commit it, but the is a problem: since PgTransaction is a guard, I don't see how to get the same connection on a fairing. When using request.guard::<&PgTransaction>(), it just requests a new guard, so even if I run commit on the result of this call, nothing will change:
#[rocket::async_trait]
impl Fairing for Transaction {
fn info(&self) -> Info {
Info {
name: "Transaction manager",
kind: Kind::Response
}
}
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
match req.guard::<PgTransaction>().await {
Outcome::Success(pgt) => {
match res.status().class() {
StatusClass::Success | StatusClass::Redirection => {
println!("COMMIT");
pgt.conn.commit().await;
}
_ => {
println!("ROLLBACK");
pgt.conn.rollback().await;
}
};
},
_ => ()
};
}
}
I can commit every time on every route I use it, but in every other api framework I can just use a middleware to guarantee that every transaction is commited/rollbacked depending on the route response. So how can I do it in Rocket.rs?
I also tried using request.local_cache(Ok(main)) on the guard, and request.local_cache(Err(())) to get the connection back, but .execute on sqlx don't accept a reference to a PgPool, PgConnection or any other execution, it needs ownership