fix: retry transient postgres polling saves
This commit is contained in:
@@ -743,15 +743,40 @@ public sealed class LiveRoomPollingBackgroundService : BackgroundService, ILiveR
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
return;
|
||||
}
|
||||
catch (DbUpdateException ex) when (attempt < 5 && IsTransientDatabaseException(ex))
|
||||
catch (Exception ex) when (attempt < 5 && IsTransientDatabaseException(ex))
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(300 * Math.Pow(2, attempt - 1)), cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTransientDatabaseException(DbUpdateException exception) =>
|
||||
exception.InnerException is NpgsqlException npgsqlException && npgsqlException.IsTransient;
|
||||
private static bool IsTransientDatabaseException(Exception exception)
|
||||
{
|
||||
if (exception is DbUpdateException dbUpdateException)
|
||||
{
|
||||
return dbUpdateException.InnerException is not null &&
|
||||
IsTransientDatabaseException(dbUpdateException.InnerException);
|
||||
}
|
||||
|
||||
if (exception is NpgsqlException npgsqlException && npgsqlException.IsTransient)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (exception is TimeoutException or IOException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (exception is InvalidOperationException invalidOperationException &&
|
||||
invalidOperationException.Message.Contains("transient failure", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return exception.InnerException is not null &&
|
||||
IsTransientDatabaseException(exception.InnerException);
|
||||
}
|
||||
|
||||
private static string? Truncate(string? value, int maxLength)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ using LiveRecorder.Infrastructure.Services;
|
||||
using LiveRecorder.WebApi.Middleware;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Npgsql;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var resetRecordingData = args.Contains("--reset-recording-data", StringComparer.OrdinalIgnoreCase);
|
||||
@@ -114,9 +115,32 @@ builder.Services.AddHttpClient("bilibili", client =>
|
||||
var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection")
|
||||
?? throw new InvalidOperationException("ConnectionStrings:DefaultConnection is required.");
|
||||
|
||||
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(defaultConnection);
|
||||
if (connectionStringBuilder.Timeout <= 0)
|
||||
{
|
||||
connectionStringBuilder.Timeout = 15;
|
||||
}
|
||||
|
||||
if (connectionStringBuilder.CommandTimeout <= 0)
|
||||
{
|
||||
connectionStringBuilder.CommandTimeout = 60;
|
||||
}
|
||||
|
||||
if (connectionStringBuilder.KeepAlive <= 0)
|
||||
{
|
||||
connectionStringBuilder.KeepAlive = 30;
|
||||
}
|
||||
|
||||
builder.Services.AddDbContext<LiveRecorderDbContext>(options =>
|
||||
options.UseNpgsql(defaultConnection, npgsql =>
|
||||
npgsql.MigrationsAssembly(typeof(LiveRecorderDbContext).Assembly.FullName)));
|
||||
options.UseNpgsql(connectionStringBuilder.ConnectionString, npgsql =>
|
||||
{
|
||||
npgsql.MigrationsAssembly(typeof(LiveRecorderDbContext).Assembly.FullName);
|
||||
npgsql.CommandTimeout(connectionStringBuilder.CommandTimeout);
|
||||
npgsql.EnableRetryOnFailure(
|
||||
maxRetryCount: 5,
|
||||
maxRetryDelay: TimeSpan.FromSeconds(10),
|
||||
errorCodesToAdd: null);
|
||||
}));
|
||||
|
||||
builder.Services.AddScoped<IUnitOfWork>(provider => provider.GetRequiredService<LiveRecorderDbContext>());
|
||||
builder.Services.AddScoped<IAppSettingRepository, AppSettingRepository>();
|
||||
|
||||
Reference in New Issue
Block a user