我们使用SQL Service Broker队列来通知我们的应用程序新记录,这些记录满足添加到另一个应用程序数据库中的表的某些条件。这是通过让after insert
触发器针对for xml
虚拟表运行inserted
查询并将任何结果插入特定服务代理队列来实现的。然后,我们有一个Notifier
对象从服务代理队列接收,并为收到的每条消息调用回调。我们从Service Broker队列接收的代码如下:
let receiveXmlMessage connection transaction (cancellation: CancellationToken) queueName messageTypeName =
task {
let commandTimeout = if cancellation.IsCancellationRequested then 1 else 0
let receiveQuery =
sprintf """WAITFOR
(
RECEIVE TOP(1)
@message = CONVERT(xml, message_body),
@messageType = message_type_name,
@dialogId = conversation_handle
FROM dbo.[%s]
), TIMEOUT 60000;""" (sanitize queueName)
use receiveCommand =
match transaction with
| Some tx -> new SqlCommand(receiveQuery, connection, tx, CommandTimeout = commandTimeout)
| None -> new SqlCommand(receiveQuery, connection, CommandTimeout = commandTimeout)
receiveCommand.Parameters.AddRange([| SqlParameter("@message", SqlDbType.Xml, Direction = ParameterDirection.Output);
SqlParameter("@messageType", SqlDbType.NVarChar, Direction = ParameterDirection.Output, Size = 256);
SqlParameter("@dialogId", SqlDbType.UniqueIdentifier, Direction = ParameterDirection.Output); |])
try
let! receiveResult = receiveCommand.ExecuteNonQueryAsync(if commandTimeout = 0 then cancellation else CancellationToken.None)
if receiveResult > 0
then let messageType = receiveCommand.Parameters.["@messageType"].Value |> unbox<string>
let dialogId = receiveCommand.Parameters.["@dialogId"].Value |> unbox<Guid>
if messageType = messageTypeName
then do! endConversation connection transaction dialogId
return receiveCommand.Parameters.["@message"].Value |> unbox<string> |> XDocument.Parse
else return XDocument()
else return XDocument()
with | ex ->
log.errorxf ex "Failed to receive message from Service Broker Queue %s" queueName
return! Task.FromException ex
}
这几个月工作正常,处理数百万条消息,直到几天前,当我们有另一个进程导致我们监视的数据库被广泛阻塞时,我们的DBA必须终止几个数据库会话以减轻争用。自从此事件发生以来,我们的应用程序在尝试从Service Broker队列接收时遇到以下错误:
2018-01-11 07:50:27.183-05:00 [31] ERROR - Failed to receive message from Service Broker Queue Notifier_Queue
System.Data.SqlClient.SqlException (0x80131904): A severe error occurred on the current command. The results, if any, should be discarded.
Operation cancelled by user.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader()
at System.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult)
at System.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Application.Common.Sql.ServiceBroker.receiveXmlMessage@257-3.Invoke(Unit unitVar0)
at Application.Common.TaskBuilder.tryWith[a](FSharpFunc`2 step, FSharpFunc`2 catch)
新消息已成功添加到队列中,我们可以使用SSMS从同一队列接收消息,甚至可以作为与应用程序相同的用户运行的F#交互式会话。它似乎只是我们的应用程序受到影响,但它确实会影响我们的应用程序的所有实例,在不同的服务器上,只要它们连接到此特定数据库。我们已经尝试重新启动应用程序和SQL Server,我们尝试运行ALTER DATABASE ... SET NEW_BROKER WITH ROLLBACK IMMEDIATE
。我们尝试过的任何内容都没有带来任何影响,我们最终仍然遇到相同的异常,并且我们仍有数十万个会话保持CONVERSING
状态,因为我们调用END CONVERSATION
的代码仅在调用之后调用成功接收消息。
我们的SQL Service Broker队列设置为模仿独白模式,如this blog post中所述。
我们如何诊断我们的应用程序从SQL Server返回的这种非特定异常的原因?当问题首次发生时,还有什么我们可以尝试诊断和/或纠正我们的应用程序和SQL Service Broker之间发生的任何变化吗?
答案 0 :(得分:0)
当我们尝试从Service Broker队列接收时,我们终于找到了导致此错误的原因。事实证明,传递给我们的CancellationToken
函数的receiveXmlMessage
被我们的应用程序中的其他逻辑取消,该逻辑监视conversing
次会话的次数并尝试重新创建我们的Notifier
conversing
1}}如果conversing
个会话的数量超过某个阈值且最近关闭的会话超过另一个阈值,则为对象。由于最近关闭的会话时代的逻辑错误,实际上只有Notifier
个会话的数量被用于重置conversing
,并且当上周发生数据库阻止时,累积了150,000次CancellationToken
个对话。这导致我们的应用程序在我们尝试从Service Broker接收消息时不断取消conversing
。一旦我们关闭了应用程序,清理了所有A severe error occurred on the current command. The results, if any, should be discarded.
个对话,并修复了上次关闭对话的日期数学中的错误,错误就停止了。
对于遇到该消息的任何人,可能需要注意:
CancellationToken
这可能是ExecuteNonQueryAsync
传递给ExecuteReaderAsync
上的ExecuteScalarAsync
/ ExecuteXmlReaderAsync
/ SqlCommand
/ Commit A -> Create file with code
Commit B -> Add file header
方法的结果在方法执行时被取消。