WAITFOR RECEIVE结果偏移/延迟1个结果

时间:2013-09-13 03:27:56

标签: c# sql sql-server ado.net service-broker

我正在使用SQL Service Broker。一个连接使用XML消息在队列上发送消息。我有一个WAITFOR (RECEIVE TOP (1) ...), TIMEOUT 5000的独立连接。这可以在单个Management Studio查询中进行WHILE (1=1)循环,也可以使用循环reader.NextResult()执行单个ADO.NET SQL命令。

所有这些似乎都被正确配置,因为我最终得到了我期望的每一个结果。

但问题是最近的结果总是被阻止!

假设它已经运行并且没有SEND s,所以它只是每5秒钟在控制台上打印“无”时间。

现在我从SQL Management Studio查询SENDSEND命令成功立即完成。在正常超时间隔之前,我立即在控制台中获得“无”。然后在 next 超时间隔,我的“更新”事件出现!

如果我在“无”运行后发出SEND两条消息,那么我将立即获得“无”,然后获得一个“更新”。同样,在 next 超时间隔,将出现第二个“更新”。

我可以SEND十条消息并立即获得“无”和九条“更新”。然后我等待五秒钟超时并获得最后的“更新”。

所有SEND都在同一个对话中,我永远不会结束对话。我不保留。

如果在RECEIVE循环运行时对队列执行脏读,则队列始终为空。如果我停止RECEIVE循环,它会填满。如果我从另一个Management Studio查询窗口接收,则另一条线索是当我停止查询时,始终显示最终输出。这两个事实让我觉得队列正在立即出现,但是有些东西在读者端被锁定(但是什么?)。

起初我认为这只是我之前看到的管理工作室的奇怪行为,PRINT(?)有时被推迟。但我不希望在ADO.NET中显示相同的行为

也许它与SSB无关,只是WAITFOR或只是多个ResultSet的流媒体。我会调查这些选项,但我希望有人可能会在此期间认识到这一点。

这些症状对您来说是否熟悉?

谢谢!

  • 是否有某种方法可以在下一次阻止操作之前将结果刷新到连接?结果/结果集交付是否延期?
  • 我没有在SEND之后立即看到sp_lock结果中的任何额外锁定(也没有)

对于@Rikalous,这是ADO.NET

using (var cmd = connection.CreateCommand())
{
    cmd.CommandTimeout = 0;
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.CommandText = "upWaitForReceive";

    using (var reader = cmd.ExecuteReader())
    {
        do
        {
            while (reader.Read()) // Individual messages received
            {
                string eventType = reader["EventType"] as string;

                // do something with the message
            }
        } while (reader.NextResult()); // next batch
    }
}

sql循环是:

SELECT 'Initialized' AS EventType

WHILE (1=1) BEGIN
    ;DECLARE 
        @ConversationHandle UNIQUEIDENTIFIER, 
        @MessageTypeName SYSNAME, 
        @MessageBody XML

    ;WAITFOR(
        RECEIVE TOP (1) @ConversationHandle = conversation_handle, @MessageTypeName = message_type_name, @MessageBody = message_body
        FROM [dbo].[{0}_EventsQueue]
    ), TIMEOUT 5000

    IF (@@ROWCOUNT = 0) BEGIN
        SELECT 'None' AS EventType; -- Allow the blocking call to spin anyway
        CONTINUE;
    END

    IF (@MessageTypeName = ...) BEGIN
        SELECT ...
    END ELSE IF (@MessageTypeName = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') BEGIN
        END CONVERSATION @ConversationHandle
        SELECT @Message = 'Conversation Terminated' -- Not Expected
        RAISERROR (@Message, 11, 1)
        RETURN
    END
END

;END CONVERSATION @ConversationHandle -- This will never be reached.  But if it were, this is what should be done

2 个答案:

答案 0 :(得分:1)

不会立即发送打印消息。我经常看到打印调试消息的任意高延迟。我认为SQL Server等待网络数据包已满或者其他类型的缓冲。

如果您只是运行一个无限循环,每秒打印一条小消息,您可能会在N秒后立即获得前N条消息。这可能只是TCP的情况。也许共享内存更快,我不知道。

但我从未见过要延迟的结果集。因此,请执行SELECT 'msg'尝试“打印”。

对客户端的延迟写入问题众所周知:

  1. http://connect.microsoft.com/SQLServer/feedback/details/124994/print-output-lags-in-stored-procedure-it-is-always-delay-during-execution-of-a-batch-or-a-stored-procedure
  2. https://stackoverflow.com/a/13047982/122718
  3. http://msdn.microsoft.com/en-us/library/ms178592.aspx
  4. 另一方面,代码中的所有副作用(写入表格或其他DML)都会立即发生。只有客户端应用会收到延迟的PRINT条消息。这种延迟根本不会影响您的生产应用,因为所有有用的副作用都会立即发生。

    简而言之,要么忽略问题,要么使用RAISERROR('Message', 0, 0) WITH NOWAIT。仅当严重性大于10时才会抛出异常。

答案 1 :(得分:1)

我相信RAISERROR(...)WITH NOWAIT应该刷新缓冲区,但我已经玩了很长时间了。