Trust is earned, not given

A different perspective

2024-11-02 · Projects

DotNetCode, part 2: ResilientSqlAccess — retry with backoff for SQL that blips

Databases fail transiently all the time: a dropped connection, a deadlock, Azure SQL throttling your load, a failover. Most of those succeed on the second try a moment later. ResilientSqlAccess (part of DotNetCode) is a small SQL Server / Azure SQL data-access library with that retry built in, via the Polly resilience library.

The problem it removes

Without a policy, every call site grows the same bandage:

for (int attempt = 0; attempt < 3; attempt++) {
    try { return DoQuery(); }
    catch (SqlException ex) when (IsTransient(ex)) { Thread.Sleep(500 * attempt); }
}

That is wrong in five subtle ways per copy (which errors are transient? how long to sleep? jitter? what about CancellationToken? output parameters?). The library centralises the answers: SqlRetryOptions holds the count, the delay, the backoff shape — constant, linear or exponential — and which SQL error numbers count as retryable.

Graceful results instead of exceptions

Every call returns a SqlResult: the value, output parameters, the return value, and on failure a Succeeded = false plus ErrorMessage — no thrown exception for ordinary failures. Callers check a flag instead of wrapping every call in try/catch, which keeps the happy path readable. A Retrying event fires with a SqlRetryEventArgs so your logging can watch retries happen.

Project layout — small on purpose

ResilientSqlClient.cs (entry point), SqlResult.cs, SqlRetryOptions.cs, SqlRetryBackoffType.cs, SqlRetryEventArgs.cs, an Internal/ folder, multi-targeted for net48;net6.0;net8.0;net10.0 (see part 1 for why), with runnable console samples for both worlds. Dependencies: Microsoft.Data.SqlClient, Polly, logging abstractions — all of which support every target, which is what makes the single-codebase approach possible.

Repository: github.com/bobhuang1/DotNetCode/tree/master/ResilientSqlAccess