using Dapper; using System.Data; namespace FabApprovalWorkerService.Services; public interface IDalService { Task> QueryAsync(string sql); Task ExecuteAsync(string sql); } public class DalService : IDalService { private static readonly int RETRIES = 3; private static readonly int BACKOFF_SECONDS_INTERVAL = 30; private readonly IDbConnectionService _connectionPoolService; private readonly ILogger _logger; public DalService(IDbConnectionService connectionPoolService, ILogger logger) { _connectionPoolService = connectionPoolService ?? throw new ArgumentNullException("IConnectionPoolService not injected"); _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); } public async Task> QueryAsync(string sql) { if (sql is null) throw new ArgumentNullException("sql cannot be null"); int remainingRetries = RETRIES; bool queryWasSuccessful = false; Exception? exception = null; IEnumerable result = new List(); while (!queryWasSuccessful && remainingRetries > 0) { int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL; Task.Delay(backoffSeconds * 1000).Wait(); try { _logger.LogInformation($"Attempting to perform query with {sql}. Remaining retries: {remainingRetries}"); using (IDbConnection conn = _connectionPoolService.GetConnection()) { result = await conn.QueryAsync(sql); } queryWasSuccessful = true; } catch (Exception ex) { _logger.LogError($"An exception occurred while attempting to perform a query. Exception: {ex.Message}"); exception = ex; } } if (!queryWasSuccessful && exception is not null) { throw exception; } return result; } public async Task ExecuteAsync(string sql) { if (sql is null) throw new ArgumentNullException("sql cannot be null"); int remainingRetries = RETRIES; bool queryWasSuccessful = false; Exception? exception = null; int rowsAffected = 0; while (!queryWasSuccessful && remainingRetries > 0) { int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL; Task.Delay(backoffSeconds * 1000).Wait(); try { _logger.LogInformation($"Attempting to execute {sql}. Remaining retries: {remainingRetries}"); using (IDbConnection conn = _connectionPoolService.GetConnection()) { rowsAffected = await conn.ExecuteAsync(sql); } queryWasSuccessful = true; } catch (Exception ex) { _logger.LogError($"An exception occurred while attempting to execute a query. Exception: {ex.Message}"); exception = ex; } } if (!queryWasSuccessful && exception is not null) { throw exception; } return rowsAffected; } }