1

我正在实施许多在两个不同实例上工作的 SSB。它们是基于异步触发器的数据推送模式。

我的SQL 信息如下所示: Microsoft SQL Server Management Studio 10.50.2500.0 Microsoft Analysis Services Client Tools 10.50.2500.0 Microsoft Data Access Components (MDAC) 6.1.7601.17514 Microsoft MSXML 3.0 4.0 5.0 6.0 Microsoft Internet Explorer 9.0.8112.16421 Microsoft .NET Framework 2.0.50727.5448 操作系统 6.1.7601

我的场景主要如下图

  1. 多条记录作为Bulk in table 或One Record插入

  2. 该数据被发送到另一个数据库。

  3. 激活过程在 BEGIN TRAN 和 END TRAN 之间开始。

  4. 它验证此消息。

  5. 如果验证不成功,则应从队列中删除此消息,并将ACk发送回,因为该消息使用不同的 SSB 对象无效。

  6. 否则,将发送ACK消息,因为消息已成功读取

  7. 然后,激活过程调用另一个过程处理消息体。

  8. 这个 USP_Process_Records 也在BEGIN TRAN 和 END TRAN 之间

  9. 由于很多原因,根据我的某些业务需求,此过程可能会失败。

  10. 要么是 Pro SQL Server 2008 Service Broker。

  11. 所以在激活过程中,它要么进入 USP_Process_Records 的失败状态,要么进入 BEGIN CATCH 部分并回滚事务并发送失败 ACK

  12. 最后发现前面的read success ack根本没有发送,第二个发送正常。

    • 所以我对 Broker Service 中的事务管理感到非常困惑。

    • 我应该对每个单独的任务使用 BEGIN TRAN 并将其从接收和处理 UPS 中删除吗?

    • 我是否也应该在 USP_Process_Records 中使用 TRY、CATCH 并将错误返回给 USP_Receive_Records?

    • 我是否应该修改我的 TRY、CATCH 块 ar Receive 以避免此问题

最后,我希望即使之后出现问题也能发送所有确认,并希望完全避免 Poison 消息和回滚。提前致谢。

-顺便说一句,我使用 rusanu 博客进行代理服务错误处理阅读 Pro SQL Server 2008 Service Broker 事务管理部分。

查找以下 USP 示例。

--USP_Receive_Records

BEGIN TRY
BEGIN TRAN

        WHILE 1=1
        BEGIN
            SELECT @ReplyMessage = NULL, @TargetDlgHandle = NULL

            WAITFOR (RECEIVE TOP(1)
            @TargetDlgHandle=Conversation_Handle
            ,@ReplyMessage = CAST(message_body AS XML)
            ,@ReplyMessageName = Message_Type_Name
            FROM Q_Service_Receive), TIMEOUT 1000

            IF @TargetDlgHandle IS NULL 
            BREAK

            --Check if the message has the same message type expected
            IF @ReplyMessageName=N'Service_Msg'  
            BEGIN
                        --Send Batch Read Success ACK
                        --Send ACK Here
                        EXEC [dbo].[USP_ACKMsg_Send] @ACKMsg, @Service_Msg;
                        --Handle ACK Send failed!

                        -- Execute the USP_Service_Msg_Process for the batch rows
                        EXECUTE USP_Service_Msg_Process @ReplyMessageName, @RC OUTPUT;

                        --Case Processing Succeeded             
                        IF @RC=0
                        BEGIN
                            --Send Batch Read Success ACK
                        END

                        --SEND ACK Processing failed with Return Code to define cause of the error
                        ELSE
                        BEGIN
                            --Send Batch Processing Failed ACK
                        END

            END 
            END CONVERSATION @TargetDlgHandle;
        END
    COMMIT TRAN;
    END TRY

    BEGIN CATCH
    if (XACT_STATE()) = -1
        BEGIN
              rollback transaction;
        END;
    if (XACT_STATE()) = 1
        BEGIN
    DECLARE @error int, @message nvarchar(4000), @handle uniqueidentifier;
    SELECT @error = ERROR_NUMBER(), @message = ERROR_MESSAGE();
    END conversation @handle with error = @error description = @message;
    COMMIT;
        END
    END CATCH
END

--USP_Process_Records
BEGIN TRAN 
        While(@nCount <= @nodesCount)
        BEGIN

        IF(@S_HIS_Status = '02')
            BEGIN
                -- check N_Paid_Trans_ID is not nuul or zero or empty
                IF( @N_GET_ID IS NULL OR @N_GET_ID = 0 OR @N_GET_ID = '')
                    BEGIN
                        SET @RC = 8
                        RETURN;
                    END

                EXECUTE USP_Handle_Delivered_Service @N_GET_ID, @RC OUTPUT
                SELECT @myERROR = @@ERROR--, @myRowCount = @@ROWCOUNT
                IF @myERROR <> 0 OR @RC <> 0
                BEGIN
                    ROLLBACK TRAN
                END
            END

            --A lot of similar cases
END TRAN
4

1 回答 1

3

您将 BEGIN TRY/BEGIN CATCH 块与旧式 @@ERROR 检查混合在一起。它使错误处理事务处理几乎无法管理。考虑这段代码:

SELECT @myERROR = @@ERROR--, @myRowCount = @@ROWCOUNT
IF @myERROR <> 0 OR @RC <> 0
BEGIN
   ROLLBACK TRAN
END

你能按照这里涉及的控制流和事务流吗?代码在从 TRY/CATCH 块调用的上下文中执行,因此不应出现 @@ERROR 情况,并且控制流应跳转到 CATCH 块。但是等一下,如果在没有 TRY/CATCH 块的情况下从不同的上下文调用该过程怎么办?然后可以采用@@ERROR 情况,但这意味着控制流继续!即使设置了 TRY/CATCH 竞赛如果@RC 为非零,事务将回滚但控制流继续到现在将在每个语句独立事务的上下文中执行的下一个语句,因为整个包含事务已经回滚!换句话说,在这种情况下,您可以向您收到的消息发送响应 Ack(您只是将其回滚!)。难怪您会看到行为似乎不稳定的情况。

我建议您只坚持一种错误处理方式(唯一合理的方式是 BEGIN TRY/BEGIN CATCH 块)。不要在应用程序逻辑错误的情况下故意回滚,而是RAISERROR在必要时使用并依赖 CATCH 块进行回滚。还要按照异常处理和嵌套事务中显示的模板来设置您的过程的样式。此模板允许在发生错误时逐个消息决定回滚到事务中的安全点(即提交您的 RECEIVE 批消息,即使某些消息在处理过程中发生错误)。

于 2012-04-09T17:53:04.250 回答