I want to ensure that when I do a context.SaveChanges()
, this is retried because the database might be temporarily down.
So far all I've found involves writing a lot of code that I'd then need to maintain, so is there something ready, an out-of-the-box tool, that I can use for resiliency?
CodePudding user response:
I've created a small library called ResilientSaveChanges.EFCore
that allows resilient context.SaveChanges
/ SaveChangesAsync
in Entity Framework Core, logging of long-running transactions and limiting of concurrent SaveChanges. It's straight to the point.
Available on GitHub and NuGet. Tried and tested in production on multiple private projects.
CodePudding user response:
Yes, connection resiliency is available in EF Core. For MySQL, it's available through the Pomelo driver's EnabelRetryOnFailure()
option. The Github Blame shows this was added 5 years ago which is a bit surprising. An overload added 3 years ago allows specifying extra errors to retry.
This code taken from one of the integration tests shows how it's used:
services.AddDbContextPool<AppDb>(
options => options.UseMySql(
GetConnectionString(),
AppConfig.ServerVersion,
mysqlOptions =>
{
mysqlOptions.MaxBatchSize(AppConfig.EfBatchSize);
mysqlOptions.UseNewtonsoftJson();
if (AppConfig.EfRetryOnFailure > 0)
{
mysqlOptions.EnableRetryOnFailure(AppConfig.EfRetryOnFailure, TimeSpan.FromSeconds(5), null);
}
}
));
Without parameters EnableRetryOnFailure()
uses the default retry count and maximum delay which are 6 and 30 seconds.
The third parameter is an ICollection<int>
of additional errors to retry.
By default, the MySqlTransientExceptionDetector class specifies that only transient exceptions are retried, ie those that have the IsTransient
property set, or timeout exceptions.
public static bool ShouldRetryOn([NotNull] Exception ex)
=> ex is MySqlException mySqlException
? mySqlException.IsTransient
: ex is TimeoutException;