【问题标题】:SQL Service Broker Severe Error Receiving Messages after DB BlockingSQL Service Broker 在 DB 阻塞后接收消息时出现严重错误
【发布时间】:2018-01-16 19:46:01
【问题描述】:

我们使用 SQL Service Broker 队列来通知我们的应用程序符合某些条件的新记录被添加到另一个应用程序的数据库中的表中。这是通过让after insert 触发器使用for xmlinserted 虚拟表运行查询并将任何结果插入特定的服务代理队列来实现的。然后我们有一个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 之间的任何变化?

【问题讨论】:

    标签: sql .net sql-server f# service-broker


    【解决方案1】:

    当我们尝试从 Service Broker 队列接收数据时,我们终于找出了导致此错误的原因。事实证明,传递给我们的receiveXmlMessage 函数的CancellationToken 被我们应用程序中的其他逻辑取消,该逻辑监控conversing 对话的数量并尝试重新创建我们的Notifier 对象,如果数量conversing 对话超过某个阈值,并且最近关闭的对话早于另一个阈值。由于最近关闭对话的年龄逻辑中的错误,实际上只有conversing 对话的数量被用于重置Notifier,当上周发生数据库阻塞时,超过 150,000 个conversing对话积累。这导致我们的应用程序在我们尝试从 Service Broker 接收消息时不断取消CancellationToken。一旦我们关闭了我们的应用程序,清理了所有conversing 对话,并修复了上次关闭对话的日期数学中的错误,错误就停止了发生。

    对于遇到该消息的任何人来说,注意这一点可能很有用:

    A severe error occurred on the current command.  The results, if any, should be discarded.
    

    这可能是传递给 SqlCommand 上的 ExecuteNonQueryAsync/ExecuteReaderAsync/ExecuteScalarAsync/ExecuteXmlReaderAsync 方法的 CancellationToken 在方法执行时被取消的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-22
      • 2013-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多