-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
begin
is not cancel-safe
#2054
Comments
In general, cancellation safety has been a thorn in our side and it's been very difficult to cover it from all possible angles. However, in this case I'm rather surprised as I would expect there to only be two outcomes to a
If Flume is allowing for a third possibility where the message is sent but the method suspends and gives an opportunity for the call to be cancelled, I think that's a case of broken expectations on Flume's part. |
Except I completely forgot about the second |
But yeah, it's highly likely that |
Same here :)
That seems to be the case: zesterer/flume#104 (comment)
So you don't think it's worth trying to fix it? I think there are ways to make it work, even for other drivers than sqlite. |
I realized that there is a similar problem with cancellation of |
The proposed concurrency safe API could be cancellation safe. The whole transaction pipeline is executed by "commit finalizer" method that could create RAII guard that includes rollback logic. In Postgres case when transaction pipeline could squash into single implicit transaction it is executed in a single batch and there is no need to cancel it - it's either applied / rolled backed by server on query error or rolled backed by server after TCP stack closes servers half of connection (when cancellation happens concurrently with client half TCP connection failure and implicit pipeline is not sent fully). |
Should this issue be reopened since for other databases, |
just get rid of that |
an example for // could be begin, commit or rollback
fn begin(conn: &mut DuckDBConnection) -> BoxFuture<'_, Result<(), sqlx_core::Error>> {
let (sender, receiver) = oneshot::channel::<Result<(), DuckDBError>>();
let send_result = conn.sender.send(
ConnectionMessage::Begin(sender)).map_err(|_| sqlx_core::Error::WorkerCrashed);
match send_result {
Err(_) => {
Box::pin(async { Err(sqlx_core::Error::WorkerCrashed) })
}
Ok(_) => {
Box::pin(receiver.map(|result| convert_received_error(result)))
}
}
} |
Bug Description
At least in sqlite, when the future returned from
Connection::begin
is cancelled, theBEGIN
statement might still get executed but theTransaction
struct is not created and so there is no way to commit / rollback it.Minimal Reproduction
Notes
I'm not sure whether sqlx even aims to be cancel-safe but I think it should be. Cancel-unsafety can be a source of bugs that are very hard to track down. As a minimum, it should be clearly documented that a function is not cancel safe.
Also, it's possible this issue exists for other db backends as well but I haven't tested it (I'm only using sqlite).
Idea for a fix
Modify
Worker::begin
somehow like this:Begin
commandRollback
on dropawait
the response from the commandHowever, this assumes that
flume::Sender::send_async
is cancel-safe, that is, it's guaranteed that if it's cancelled, the message is not sent. The flume docs currently don't talk about this. I started a discussion about it here zesterer/flume#104.Info
default-features = false, features = ["runtime-tokio-rustls", "sqlite"]
rustc --version
:rustc 1.61.0 (fe5b13d68 2022-05-18)
The text was updated successfully, but these errors were encountered: