fix: implement exponential backoff strategy for concurrency exceptions in SaveChangesAsync

This commit is contained in:
2026-07-07 11:31:47 -04:00
parent 9bcb8501ab
commit 421a8cc092
@@ -352,18 +352,36 @@ public class JellyfinDbContext(DbContextOptions<JellyfinDbContext> options, ILog
} }
catch (DbUpdateConcurrencyException ex) catch (DbUpdateConcurrencyException ex)
{ {
// When marking items as played in quick succession, concurrency conflicts can occur. // Concurrency conflicts can occur when marking items as played in quick succession.
// To resolve this, we'll reload the conflicting entities from the database and retry. // To resolve this, we'll retry the operation with an exponential backoff strategy.
logger.LogWarning(ex, "Concurrency exception, retrying operation."); logger.LogWarning(ex, "Concurrency exception, retrying operation with exponential backoff.");
foreach (var entry in ex.Entries) var maxRetries = 3;
var delay = TimeSpan.FromMilliseconds(100);
for (var i = 0; i < maxRetries; i++)
{ {
// Reload the entity from the database to get the latest version try
await entry.ReloadAsync(cancellationToken).ConfigureAwait(false); {
foreach (var entry in ex.Entries)
{
// Reload the entity from the database to get the latest version
await entry.ReloadAsync(cancellationToken).ConfigureAwait(false);
}
// Retry saving changes after resolving conflicts
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken).ConfigureAwait(false);
}
catch (DbUpdateConcurrencyException retryEx) when (i < maxRetries - 1)
{
logger.LogWarning(retryEx, "Concurrency exception on retry {RetryCount}, delaying for {Delay}ms.", i + 1, delay.TotalMilliseconds);
await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
delay *= 2; // Exponential backoff
}
} }
// Retry saving changes after resolving conflicts // If all retries fail, rethrow the last exception
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken).ConfigureAwait(false); throw;
} }
catch (DbUpdateException ex) catch (DbUpdateException ex)
{ {