mirror of
https://github.com/Radarr/Radarr
synced 2025-12-06 08:28:50 +01:00
New: Retry SQLite writes for database is locked errors
(cherry picked from commit 2e1289b9248a70ce50bde52a66d3a589f3dcb8f5)
This commit is contained in:
parent
e63691935d
commit
cf465899b4
1 changed files with 29 additions and 3 deletions
|
|
@ -1,13 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Data.SQLite;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Instrumentation;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using Polly;
|
||||||
|
using Polly.Retry;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
|
|
@ -40,12 +45,31 @@ public interface IBasicRepository<TModel>
|
||||||
public class BasicRepository<TModel> : IBasicRepository<TModel>
|
public class BasicRepository<TModel> : IBasicRepository<TModel>
|
||||||
where TModel : ModelBase, new()
|
where TModel : ModelBase, new()
|
||||||
{
|
{
|
||||||
|
private static readonly ILogger Logger = NzbDroneLogger.GetLogger(typeof(BasicRepository<TModel>));
|
||||||
|
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly PropertyInfo _keyProperty;
|
private readonly PropertyInfo _keyProperty;
|
||||||
private readonly List<PropertyInfo> _properties;
|
private readonly List<PropertyInfo> _properties;
|
||||||
private readonly string _updateSql;
|
private readonly string _updateSql;
|
||||||
private readonly string _insertSql;
|
private readonly string _insertSql;
|
||||||
|
|
||||||
|
private static ResiliencePipeline RetryStrategy => new ResiliencePipelineBuilder()
|
||||||
|
.AddRetry(new RetryStrategyOptions
|
||||||
|
{
|
||||||
|
ShouldHandle = new PredicateBuilder().Handle<SQLiteException>(ex => ex.ResultCode == SQLiteErrorCode.Busy),
|
||||||
|
Delay = TimeSpan.FromMilliseconds(100),
|
||||||
|
MaxRetryAttempts = 3,
|
||||||
|
BackoffType = DelayBackoffType.Exponential,
|
||||||
|
UseJitter = true,
|
||||||
|
OnRetry = args =>
|
||||||
|
{
|
||||||
|
Logger.Warn(args.Outcome.Exception, "Failed writing to database. Retry #{0}", args.AttemptNumber);
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
protected readonly IDatabase _database;
|
protected readonly IDatabase _database;
|
||||||
protected readonly string _table;
|
protected readonly string _table;
|
||||||
|
|
||||||
|
|
@ -186,7 +210,9 @@ private string GetInsertSql()
|
||||||
private TModel Insert(IDbConnection connection, IDbTransaction transaction, TModel model)
|
private TModel Insert(IDbConnection connection, IDbTransaction transaction, TModel model)
|
||||||
{
|
{
|
||||||
SqlBuilderExtensions.LogQuery(_insertSql, model);
|
SqlBuilderExtensions.LogQuery(_insertSql, model);
|
||||||
var multi = connection.QueryMultiple(_insertSql, model, transaction);
|
|
||||||
|
var multi = RetryStrategy.Execute(static (state, _) => state.connection.QueryMultiple(state._insertSql, state.model, state.transaction), (connection, _insertSql, model, transaction));
|
||||||
|
|
||||||
var multiRead = multi.Read();
|
var multiRead = multi.Read();
|
||||||
var id = (int)(multiRead.First().id ?? multiRead.First().Id);
|
var id = (int)(multiRead.First().id ?? multiRead.First().Id);
|
||||||
_keyProperty.SetValue(model, id);
|
_keyProperty.SetValue(model, id);
|
||||||
|
|
@ -381,7 +407,7 @@ private void UpdateFields(IDbConnection connection, IDbTransaction transaction,
|
||||||
|
|
||||||
SqlBuilderExtensions.LogQuery(sql, model);
|
SqlBuilderExtensions.LogQuery(sql, model);
|
||||||
|
|
||||||
connection.Execute(sql, model, transaction: transaction);
|
RetryStrategy.Execute(static (state, _) => state.connection.Execute(state.sql, state.model, transaction: state.transaction), (connection, sql, model, transaction));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateFields(IDbConnection connection, IDbTransaction transaction, IList<TModel> models, List<PropertyInfo> propertiesToUpdate)
|
private void UpdateFields(IDbConnection connection, IDbTransaction transaction, IList<TModel> models, List<PropertyInfo> propertiesToUpdate)
|
||||||
|
|
@ -393,7 +419,7 @@ private void UpdateFields(IDbConnection connection, IDbTransaction transaction,
|
||||||
SqlBuilderExtensions.LogQuery(sql, model);
|
SqlBuilderExtensions.LogQuery(sql, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.Execute(sql, models, transaction: transaction);
|
RetryStrategy.Execute(static (state, _) => state.connection.Execute(state.sql, state.models, transaction: state.transaction), (connection, sql, models, transaction));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual SqlBuilder PagedBuilder() => Builder();
|
protected virtual SqlBuilder PagedBuilder() => Builder();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue