如何用SqlRetryLogicBaseProvider处理Azure SQL伸缩时的连接重试
Azure SQL伸缩时重试逻辑不触发的问题解决
错误号0无法触发重试的原因
错误号0并非SQL Server返回的原生瞬态错误码,而是Microsoft.Data.SqlClient客户端在无法获取服务器明确错误响应时使用的占位符。这类错误通常属于非瞬态场景(比如连接彻底中断且无服务器反馈),即便手动添加到TransientErrors列表,重试逻辑也不会触发——客户端内部会先对错误的“可重试性”做基础判定,0不在默认的可重试错误分类中,手动添加无法覆盖这一判定。
Azure SQL伸缩会话Kill错误未触发重试的核心问题
你遇到的「Cannot continue the execution because the session is in the kill state」错误,对应的实际SQL错误码可能并非你预期的0,建议在catch块中打印SqlException.Number确认真实错误码:
catch (Exception ex) { if (ex is SqlException sqlEx) { Console.WriteLine($"ERROR CODE: {sqlEx.Number}, MESSAGE: {sqlEx.Message}"); } else { Console.WriteLine($"ERROR: {ex.Message}"); } }
此外,你的代码存在以下几个影响重试逻辑的问题:
- 重复设置重试提供器:同时给
SqlConnection和SqlCommand绑定了RetryLogicProvider,可能导致内部逻辑冲突,建议只给SqlCommand设置(或只给连接设置,二选一)。 - 静态连接的状态混乱:使用静态
SqlConnection,重试时手动关闭再打开连接,容易导致连接状态不一致,建议改用局部连接对象。 - 重试事件的强制转换风险:将
SqlRetryingEventArgs的s参数强制转为SqlCommand,若触发重试的是连接对象,会抛出空引用异常,直接终止重试流程。
修正后的代码示例
internal class Program { private const string CnnStringFormat = "Data Source = localhost; Initial Catalog = dbtest; User ID = sa; Password=aaaaaa;Encrypt=True;TrustServerCertificate=True"; static void Main(string[] args) { var options = new SqlRetryLogicOption() { NumberOfTries = 5, MaxTimeInterval = TimeSpan.FromSeconds(60), DeltaTime = TimeSpan.FromSeconds(15), // 保留经确认的瞬态错误码,移除无效的0 TransientErrors = new List<int> { -1, -2, 109, 233, 997, 1222, 10060, -2146232060, 596 } }; var provider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(options); provider.Retrying += (object sender, SqlRetryingEventArgs e) => { NotificaRetry(e); // 无需手动关闭/打开连接,重试逻辑会自动处理连接重建 }; SqlDataReader leitor = null; try { // 使用局部连接,避免静态对象的状态问题 using (var connection = new SqlConnection(CnnStringFormat)) { connection.Open(); using (var comando = new SqlCommand("spGetLoadTest", connection) { CommandType = System.Data.CommandType.StoredProcedure, CommandTimeout = 300, RetryLogicProvider = provider // 仅给命令设置重试提供器 }) { leitor = comando.ExecuteReader(); var count = leitor.Depth; var colunas = leitor.FieldCount; } } } catch (Exception ex) { if (ex is SqlException sqlEx) { Console.WriteLine($"ERROR CODE: {sqlEx.Number}, MESSAGE: {sqlEx.Message}"); } else { Console.WriteLine($"ERROR: {ex.Message}"); } } finally { leitor?.Close(); } } private static void NotificaRetry(SqlRetryingEventArgs e) { int attempts = e.RetryCount + 1; Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"attempt {attempts} - current delay time:{e.Delay}"); Console.WriteLine($"error: {e.Exceptions[e.RetryCount].Message}"); // 修正索引,避免第一次重试时索引越界 Console.ForegroundColor = ConsoleColor.DarkGray; } }
关键说明
- 移除错误号0:由于0是客户端占位符,无法触发重试,直接从
TransientErrors中移除。 - 局部连接+using语句:确保连接自动释放,避免状态混乱,重试时会自动重建连接。
- 仅给命令设置重试提供器:避免连接和命令的重试逻辑冲突,让命令层面的重试处理执行过程中的错误。
- 修正重试事件的异常索引:原代码中
e.Exceptions[e.RetryCount-1]会在第一次重试时出现索引越界,改为e.Exceptions[e.RetryCount]即可正确获取当前重试对应的异常。
内容的提问来源于stack exchange,提问作者duroforce
相关产品推荐
相关产品推荐

