我有一个后台任务,每200毫秒轮询一次SQL Server数据库。
代码如下:
listener = await Task.Factory.StartNew(async () =>
{
try
{
while (true)
{
topToken.ThrowIfCancellationRequested();
try
{
using (var dbConnection = new SqlConnection(ConnectionString))
using (var command = new SqlCommand("marc.GetEvents", dbConnection))
{
await command.Connection.OpenAsync().ConfigureAwait(false);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@fromId", lastEventId);
using (var reader = await command.ExecuteReaderAsync(topToken).ConfigureAwait(false))
{
int received = lastEventId;
while (await reader.ReadAsync(topToken).ConfigureAwait(false))
{
/// do stuff...
}
lastEventId = received;
}
}
await Task.Delay(PollIntervalMilliseconds, topToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
if (ex is SqlException && topToken.IsCancellationRequested)
{
throw new OperationCanceledException("Operation cancelled by user", ex);
}
logger.Warn(ex, $"Exception on polling Codeks db. Waiting {delayOnSqlError}ms..."); // this is hit
_OnReaderEvent.OnError(ex);
await Task.Delay(delayOnSqlError, topToken).ConfigureAwait(false); // probably not executed
}
}
}
catch (OperationCanceledException)
{
logger.Info("Listening task ended. Service is stopping?");
}
catch (Exception ex)
{
logger.Error(ex, "General exception"); // falling here
}
}, TaskCreationOptions.LongRunning).ConfigureAwait(false);
今天,我收到有关此任务过早结束这一事实的报告。根据日志,第一个catch
集被命中,并报告SQL异常:
2018-08-01 17:42:08.6348 |警告|轮询Codeks数据库时发生异常。等待5000毫秒... System.Data.SqlClient.SqlException(0x80131904):事务(进程ID 53)在锁定时死锁|与另一个进程通信缓冲资源,并已被选为死锁受害者。重新运行事务。
但没有延迟,它是从循环中立即掉到外部catch
上的,这完全相同。
2018-08-01 17:42:08.6488 |错误| Jantar.CodeksConnector |常规异常System.Data.SqlClient.SqlException(0x80131904):事务(进程ID 53)在锁定时死锁|与另一个进程通信缓冲资源,并已被选为死锁受害者。重新运行事务。 在System.Data.SqlClient.SqlConnection.OnError(SqlException异常,布尔值breakConnection,操作
1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction) 在System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj,布尔调用方HasConnectionLock,布尔asyncClose) 在System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior,SqlCommand cmdHandler,SqlDataReader dataStream,BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject stateObj,Boolean和dataReady) 在System.Data.SqlClient.SqlDataReader.TryHasMoreRows(Boolean&moreRows) 在System.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout,Boolean&更多) 在System.Data.SqlClient.SqlDataReader处。<> c__DisplayClass189_0.b__0(任务t) 在System.Data.SqlClient.SqlDataReader.InvokeRetryable [T](Func {2 moreFunc, TaskCompletionSource
1来源,IDisposable objectToDispose)处 ---从之前引发异常的位置开始的堆栈结束跟踪--- 在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(任务任务) 在Jantar.CodeksConnector。d.MoveNext() ---从之前引发异常的位置开始的堆栈结束跟踪--- 在System.Reactive.PlatformServices.ExceptionServicesImpl.Rethrow(Exception异常) 在System.Reactive.Stubs。<> c。<。cctor> b__2_1(异常异常) 在System.Reactive.AnonymousSafeObserver 1.OnError(Exception error) at System.Reactive.Linq.ObservableImpl.SelectMany
2. .OnError(异常错误)处 在System.Reactive.Linq.ObservableImpl.Where1._.OnError(Exception error) at System.Reactive.Linq.ObservableImpl.AsObservable
1。 .OnError(异常错误) 在System.Reactive.Observer1.OnError(Exception error) at System.Reactive.Subjects.Subject
1.OnError(异常错误)处 在Jantar.CodeksConnector中。d.MoveNext()
我没主意了...
[更新:08.03]
@sellotape为我指明了正确的方向。根据更新后的第二个日志条目,stacktrace清楚地表明Subject<T>.onError(ex)
抛出了异常(我将其删除是因为它是一个错误)。这是一个双重错误,因为没有错误订阅者。我不知道在这种情况下,只有在有任何订户的情况下,该异常才会被重新引发,而在没有订户的情况下,该异常将被吞下。
答案 0 :(得分:0)
虽然这不是您问题的直接答案,但是您已将其标记为“ System.Reactive”,因此我想我可能会(大致)向您展示代码的Rx解决方案。请记住,我无法准确地提供/// do stuff...
的代码,所以我做了。
这里是Rx:
IObservable<string> query =
from t in Observable.Interval(TimeSpan.FromMilliseconds(PollIntervalMilliseconds))
from x in Observable.Using(
() => new SqlConnection(ConnectionString),
dbConnection =>
Observable.Using(
() =>
{
var c = new SqlCommand("marc.GetEvents", dbConnection);
c.CommandType = CommandType.StoredProcedure;
c.Parameters.AddWithValue("@fromId", lastEventId);
return c;
},
command =>
from o in Observable.FromAsync(() => command.Connection.OpenAsync())
from reader in Observable.FromAsync(() => command.ExecuteReaderAsync(topToken))
let received = lastEventId
from r in Observable.FromAsync(() => reader.ReadAsync(topToken))
select reader.GetFieldValue<string>(0)))
select x;