我有一个方法调用SQLServer函数对表执行自由文本搜索.该函数有时会在第一次调用时导致SQLException:"全文查询字符串的单词分解超时".所以我通常想重试该请求,因为它会在后续请求中成功.构造重试逻辑的好方法是什么.目前我有以下内容:
var retryCount = 0;
var results = new List<UserSummaryDto>();
using (var ctx = new UsersDataContext(ConfigurationManager.ConnectionStrings[CONNECTION_STRING_KEY].ConnectionString))
{
for (; ; )
{
try
{
results = ctx.SearchPhoneList(value, maxRows)
.Select(user => user.ToDto())
.ToList();
break;
}
catch (SqlException)
{
retryCount++;
if (retryCount > MAX_RETRY) throw;
}
}
}
return results;
Run Code Online (Sandbox Code Playgroud)
gbn*_*gbn 17
我将异常处理更改为仅重试某些错误:
这些是基本的"可重试"错误
catch (SqlException ex)
{
if !(ex.Number == 1205 || ex.Number == 1204 || ... )
{
throw
}
retryCount++;
if (retryCount > MAX_RETRY) throw;
}
Run Code Online (Sandbox Code Playgroud)
编辑,我清理忘了等待所以你不要锤击SQL框:
编辑2:
我是开发人员DBA,不做太多C#.我的答案是纠正调用的异常处理...
Dav*_*rke 12
感谢所有的反馈.我自己回答这个问题,所以我可以从给出的答案中加入元素.如果我错过了什么,请告诉我.我的方法变成:
var results = new List<UserSummaryDto>();
Retry<UsersDataContext>(ctx => results = ctx.SearchPhoneList(value, maxRows)
.Select(user => user.ToDto())
.ToList());
return results;
Run Code Online (Sandbox Code Playgroud)
我重构了原始的重用方法.还有很多层次的筑巢.它还依赖于数据上下文的默认构造函数,这可能限制性太强.@Martin,我考虑过包括你的PreserveStackTrace方法,但在这种情况下我认为它确实没有增加足够的价值 - 很高兴知道以备参考感谢:
private const int MAX_RETRY = 2;
private const double LONG_WAIT_SECONDS = 5;
private const double SHORT_WAIT_SECONDS = 0.5;
private static readonly TimeSpan longWait = TimeSpan.FromSeconds(LONG_WAIT_SECONDS);
private static readonly TimeSpan shortWait = TimeSpan.FromSeconds(SHORT_WAIT_SECONDS);
private enum RetryableSqlErrors
{
Timeout = -2,
NoLock = 1204,
Deadlock = 1205,
WordbreakerTimeout = 30053,
}
private void Retry<T>(Action<T> retryAction) where T : DataContext, new()
{
var retryCount = 0;
using (var ctx = new T())
{
for (;;)
{
try
{
retryAction(ctx);
break;
}
catch (SqlException ex)
when (ex.Number == (int) RetryableSqlErrors.Timeout &&
retryCount < MAX_RETRY)
{
Thread.Sleep(longWait);
}
catch (SqlException ex)
when (Enum.IsDefined(typeof(RetryableSqlErrors), ex.Number) &&
retryCount < MAX_RETRY)
{
Thread.Sleep(shortWait);
}
retryCount++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我对sql的retryables的枚举看起来像这样:
SqlConnectionBroken = -1,
SqlTimeout = -2,
SqlOutOfMemory = 701,
SqlOutOfLocks = 1204,
SqlDeadlockVictim = 1205,
SqlLockRequestTimeout = 1222,
SqlTimeoutWaitingForMemoryResource = 8645,
SqlLowMemoryCondition = 8651,
SqlWordbreakerTimeout = 30053
Run Code Online (Sandbox Code Playgroud)
这不是好的风格,但有时你必须这样做,因为你根本无法改变现有的代码并且必须处理它.
我在这种情况下使用以下通用方法.请注意PreserveStackTrace()方法,它有时在重新抛出场景中非常有用.
public static void RetryBeforeThrow<T>(Action action, int retries, int timeout) where T : Exception
{
if (action == null)
throw new ArgumentNullException("action", string.Format("Argument '{0}' cannot be null.", "action"));
int tries = 1;
do
{
try
{
action();
return;
}
catch (T ex)
{
if (retries <= 0)
{
PreserveStackTrace(ex);
throw;
}
Thread.Sleep(timeout);
}
}
while (tries++ < retries);
}
/// <summary>
/// Sets a flag on an <see cref="T:System.Exception"/> so that all the stack trace information is preserved
/// when the exception is re-thrown.
/// </summary>
/// <remarks>This is useful because "throw" removes information, such as the original stack frame.</remarks>
/// <see href="http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx"/>
public static void PreserveStackTrace(Exception ex)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(ex, null);
}
Run Code Online (Sandbox Code Playgroud)
你会这样称呼它:
RetryBeforeThrow<SqlException>(() => MethodWhichFails(), 3, 100);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
16424 次 |
最近记录: |