mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 11:29:25 +00:00
Retry transactions if there's a serialization failure during commit
This commit is contained in:
parent
0ed731780a
commit
d97a8364ad
1 changed files with 87 additions and 76 deletions
|
@ -2131,47 +2131,30 @@ impl Database {
|
||||||
F: Send + Fn(TransactionHandle) -> Fut,
|
F: Send + Fn(TransactionHandle) -> Fut,
|
||||||
Fut: Send + Future<Output = Result<T>>,
|
Fut: Send + Future<Output = Result<T>>,
|
||||||
{
|
{
|
||||||
let body = async {
|
loop {
|
||||||
loop {
|
let (tx, result) = self.run(self.with_transaction(&f)).await?;
|
||||||
let (tx, result) = self.with_transaction(&f).await?;
|
match result {
|
||||||
match result {
|
Ok(result) => {
|
||||||
Ok(result) => {
|
match self.run(async move { Ok(tx.commit().await?) }).await {
|
||||||
tx.commit().await?;
|
Ok(()) => return Ok(result),
|
||||||
return Ok(result);
|
Err(error) => {
|
||||||
}
|
if is_serialization_error(&error) {
|
||||||
Err(error) => {
|
|
||||||
tx.rollback().await?;
|
|
||||||
match error {
|
|
||||||
Error::Database(
|
|
||||||
DbErr::Exec(sea_orm::RuntimeErr::SqlxError(error))
|
|
||||||
| DbErr::Query(sea_orm::RuntimeErr::SqlxError(error)),
|
|
||||||
) if error
|
|
||||||
.as_database_error()
|
|
||||||
.and_then(|error| error.code())
|
|
||||||
.as_deref()
|
|
||||||
== Some("40001") =>
|
|
||||||
{
|
|
||||||
// Retry (don't break the loop)
|
// Retry (don't break the loop)
|
||||||
|
} else {
|
||||||
|
return Err(error);
|
||||||
}
|
}
|
||||||
error @ _ => return Err(error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(error) => {
|
||||||
|
self.run(tx.rollback()).await?;
|
||||||
|
if is_serialization_error(&error) {
|
||||||
|
// Retry (don't break the loop)
|
||||||
|
} else {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
{
|
|
||||||
if let Some(background) = self.background.as_ref() {
|
|
||||||
background.simulate_random_delay().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.runtime.as_ref().unwrap().block_on(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
{
|
|
||||||
body.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2180,53 +2163,38 @@ impl Database {
|
||||||
F: Send + Fn(TransactionHandle) -> Fut,
|
F: Send + Fn(TransactionHandle) -> Fut,
|
||||||
Fut: Send + Future<Output = Result<(RoomId, T)>>,
|
Fut: Send + Future<Output = Result<(RoomId, T)>>,
|
||||||
{
|
{
|
||||||
let body = async {
|
loop {
|
||||||
loop {
|
let (tx, result) = self.run(self.with_transaction(&f)).await?;
|
||||||
let (tx, result) = self.with_transaction(&f).await?;
|
match result {
|
||||||
match result {
|
Ok((room_id, data)) => {
|
||||||
Ok((room_id, data)) => {
|
let lock = self.rooms.entry(room_id).or_default().clone();
|
||||||
let lock = self.rooms.entry(room_id).or_default().clone();
|
let _guard = lock.lock_owned().await;
|
||||||
let _guard = lock.lock_owned().await;
|
match self.run(async move { Ok(tx.commit().await?) }).await {
|
||||||
tx.commit().await?;
|
Ok(()) => {
|
||||||
return Ok(RoomGuard {
|
return Ok(RoomGuard {
|
||||||
data,
|
data,
|
||||||
_guard,
|
_guard,
|
||||||
_not_send: PhantomData,
|
_not_send: PhantomData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tx.rollback().await?;
|
if is_serialization_error(&error) {
|
||||||
match error {
|
|
||||||
Error::Database(
|
|
||||||
DbErr::Exec(sea_orm::RuntimeErr::SqlxError(error))
|
|
||||||
| DbErr::Query(sea_orm::RuntimeErr::SqlxError(error)),
|
|
||||||
) if error
|
|
||||||
.as_database_error()
|
|
||||||
.and_then(|error| error.code())
|
|
||||||
.as_deref()
|
|
||||||
== Some("40001") =>
|
|
||||||
{
|
|
||||||
// Retry (don't break the loop)
|
// Retry (don't break the loop)
|
||||||
|
} else {
|
||||||
|
return Err(error);
|
||||||
}
|
}
|
||||||
error @ _ => return Err(error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(error) => {
|
||||||
|
self.run(tx.rollback()).await?;
|
||||||
|
if is_serialization_error(&error) {
|
||||||
|
// Retry (don't break the loop)
|
||||||
|
} else {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
{
|
|
||||||
if let Some(background) = self.background.as_ref() {
|
|
||||||
background.simulate_random_delay().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.runtime.as_ref().unwrap().block_on(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
{
|
|
||||||
body.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2254,6 +2222,49 @@ impl Database {
|
||||||
|
|
||||||
Ok((tx, result))
|
Ok((tx, result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn run<F, T>(&self, future: F) -> T
|
||||||
|
where
|
||||||
|
F: Future<Output = T>,
|
||||||
|
{
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
if let Some(background) = self.background.as_ref() {
|
||||||
|
background.simulate_random_delay().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = self.runtime.as_ref().unwrap().block_on(future);
|
||||||
|
|
||||||
|
if let Some(background) = self.background.as_ref() {
|
||||||
|
background.simulate_random_delay().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
{
|
||||||
|
future.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_serialization_error(error: &Error) -> bool {
|
||||||
|
const SERIALIZATION_FAILURE_CODE: &'static str = "40001";
|
||||||
|
match error {
|
||||||
|
Error::Database(
|
||||||
|
DbErr::Exec(sea_orm::RuntimeErr::SqlxError(error))
|
||||||
|
| DbErr::Query(sea_orm::RuntimeErr::SqlxError(error)),
|
||||||
|
) if error
|
||||||
|
.as_database_error()
|
||||||
|
.and_then(|error| error.code())
|
||||||
|
.as_deref()
|
||||||
|
== Some(SERIALIZATION_FAILURE_CODE) =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TransactionHandle(Arc<Option<DatabaseTransaction>>);
|
struct TransactionHandle(Arc<Option<DatabaseTransaction>>);
|
||||||
|
|
Loading…
Reference in a new issue