为何C#中DoWithRetry的try/catch无法捕获Kusto限流异常?
问题描述
我尝试在C#中用一个可复用的try/catch重试逻辑执行Kusto查询,代码如下:
public static T DoWithRetry<T>(Func<T> func, TimeSpan sleepPeriod, int tryCount = 3) { if (tryCount <= 0) throw new ArgumentOutOfRangeException(nameof(tryCount)); while(true) { try { return func(); } catch (Exception ex) { if (--tryCount == 0) throw; Console.WriteLine($"Encountered exception {ex.Message}"); Console.WriteLine($"Retrying in {sleepPeriod}..."); Thread.Sleep(sleepPeriod); } } }
调用方式:
try { var res = DoWithRetry(() => { return kustoClient.ExecuteQuery($"KustoQuery"); }, TimeSpan.FromMinutes(3)); } catch (Exception ex) { Console.Error.WriteLine($"Exception Message: {ex.Message}"); return "failed, please check."; }
但遇到Kusto抛出限流异常时,该异常从未被DoWithRetry内部的try/catch捕获,反而被外层try/catch捕获。如果在lambda中手动抛出异常:
var res = DoWithRetry(() => { throw new KustoRequestThrottledException(); }, TimeSpan.FromMinutes(3));
异常却能被DoWithRetry内部的try/catch捕获。请问是什么原因导致Kusto的异常无法被内部捕获?
问题原因与解决方法
核心原因
Kusto客户端的ExecuteQuery方法返回的是延迟执行的结果集(比如IEnumerable或类似惰性枚举类型)。当你在lambda中return kustoClient.ExecuteQuery(...)时,查询并没有立即执行,只是返回了一个代表查询结果的待枚举对象。真正的查询执行发生在你外层代码使用这个结果(例如遍历、转换结果)的时候,此时已经脱离了DoWithRetry内部的try/catch块,所以异常会被外层的try/catch捕获。
而手动抛出异常是在lambda执行时立即触发的,因此会被DoWithRetry的内部try/catch直接捕获,进入重试逻辑。
解决方案
1. 强制立即执行查询(同步场景)
在lambda内部将延迟执行的结果转为立即执行的集合,比如用ToList()或ToArray(),确保查询在DoWithRetry的try/catch范围内执行:
var res = DoWithRetry(() => { // 用ToList()强制枚举结果,触发查询执行 return kustoClient.ExecuteQuery($"KustoQuery").ToList(); }, TimeSpan.FromMinutes(3));
2. 使用异步重试逻辑(推荐)
如果Kusto客户端提供了异步版本的ExecuteQueryAsync方法,建议使用异步重试逻辑,避免阻塞线程,同时确保异常在异步调用时被正确捕获:
// 异步重试方法 public static async Task<T> DoWithRetryAsync<T>(Func<Task<T>> func, TimeSpan sleepPeriod, int tryCount = 3) { if (tryCount <= 0) throw new ArgumentOutOfRangeException(nameof(tryCount)); while (true) { try { return await func(); } catch (Exception ex) { if (--tryCount == 0) throw; Console.WriteLine($"Encountered exception {ex.Message}"); Console.WriteLine($"Retrying in {sleepPeriod}..."); await Task.Delay(sleepPeriod); } } }
调用示例:
try { var res = await DoWithRetryAsync(async () => { // 异步执行查询并等待结果 return await kustoClient.ExecuteQueryAsync($"KustoQuery"); }, TimeSpan.FromMinutes(3)); } catch (Exception ex) { Console.Error.WriteLine($"Exception Message: {ex.Message}"); return "failed, please check."; }
内容的提问来源于stack exchange,提问作者Nick Wong
相关产品推荐
相关产品推荐

