启用 DTC 的 SQL Server 2017 AG 上的 tSQLt

Posted

技术标签:

【中文标题】启用 DTC 的 SQL Server 2017 AG 上的 tSQLt【英文标题】:tSQLt on SQL Server 2017 AG with DTC enabled 【发布时间】:2018-11-19 15:21:48 【问题描述】:

当我在 SQL Server 2017 上运行 tSQLt 测试时收到以下错误,该测试设置在 Always On 可用性组中,该可用性组在 AG 组上配置了 DTC_SUPPORT = PER_DB。如果我关闭 DTC 支持,它工作正常。我们的环境需要 DTC。有没有办法在 tSQLt 中解决这个问题,或者是否有不同的方法来配置 SQL,以便 DTC 和 tSQLt 都可以工作?

这是错误:

Test Procedure: [xxx].[zzfnCipValidateCustomerAddress].[test '12345' Zip Codes] on DbServer
[zzfnCipValidateCustomerAddress].[test '12345' Zip Codes] failed: (Error) Cannot promote the transaction to a distributed transaction because there is an active save point in this transaction.[16,1]zzfnCipValidateCustomerAddress.test '12345' Zip Codes,36 (There was also a ROLLBACK ERROR --> The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.tSQLt.Private_RunTest,160)

这是失败的测试之一:

CREATE PROCEDURE [zzfnCipValidateCustomerAddress].[test '12345' Zip Codes]
AS
BEGIN
--Assemble
/*
Test to determine if function is an Inline Table Valued function or not
since this function is being rewritten to be an ITVF function and this test should
pass both versions
*/
DECLARE @IsITVF BIT;

SELECT @IsITVF = IIF(fc.FunctionCount > 0, 1, 0)
FROM
(
    SELECT COUNT(*) AS FunctionCount
    FROM sys.sql_modules AS sm
        JOIN sys.objects AS o
            ON sm.object_id = o.object_id
    WHERE sm.object_id = OBJECT_ID('dbo.fnCipValidateCustomerAddress')
          AND o.type = 'IF' --'IF' = Inline Valued Table Function
) AS fc;

SELECT @IsITVF;


--DROP TABLE IF EXISTS zzfnCipValidateCustomerAddress.TestData;

CREATE TABLE zzfnCipValidateCustomerAddress.TestData
(
    AddressLine1 VARCHAR(100),
    AddressLine2 VARCHAR(100),
    City VARCHAR(50),
    StateAbbr VARCHAR(3),
    ZipCode VARCHAR(9)
);

INSERT INTO zzfnCipValidateCustomerAddress.TestData
(
    AddressLine1,
    AddressLine2,
    City,
    StateAbbr,
    ZipCode
)
VALUES
('Test', NULL, 'Test', 'AZ', '12345');

CREATE TABLE zzfnCipValidateCustomerAddress.Expected
(
    AddressLine1 VARCHAR(100),
    AddressLine2 VARCHAR(100),
    City VARCHAR(50),
    StateAbbr VARCHAR(3),
    ZipCode VARCHAR(9),
    CipExceptionReasonId INT
);

INSERT INTO zzfnCipValidateCustomerAddress.Expected
(
    AddressLine1,
    AddressLine2,
    City,
    StateAbbr,
    ZipCode,
    CipExceptionReasonId
)
VALUES
('Test', NULL, 'Test', 'AZ', '12345', 8);

--Act

IF (@IsITVF = 0)
BEGIN
    SELECT td.AddressLine1,
           td.AddressLine2,
           td.City,
           td.StateAbbr,
           td.ZipCode,
           dbo.fnCipValidateCustomerAddress(td.AddressLine1, td.AddressLine2, td.City, td.StateAbbr, td.ZipCode) AS CipExceptionReasonId
    INTO zzfnCipValidateCustomerAddress.Actual
    FROM zzfnCipValidateCustomerAddress.TestData AS td;
END;
ELSE
BEGIN
    SELECT fcvcat.AddressLine1,
           fcvcat.AddressLine2,
           fcvcat.City,
           fcvcat.StateAbbr,
           fcvcat.ZipCode,
           fcvcat.CipExceptionReasonId
    INTO zzfnCipValidateCustomerAddress.Actual
    FROM zzfnCipValidateCustomerAddress.TestData AS td
        CROSS APPLY dbo.fnCipValidateCustomerAddress(
                                                        td.AddressLine1,
                                                        td.AddressLine2,
                                                        td.City,
                                                        td.StateAbbr,
                                                        td.ZipCode
                                                    ) AS fcvcat;
END;


--Assert

EXEC tSQLt.AssertEqualsTable @Expected = N'zzfnCipValidateCustomerAddress.Expected',
                             @Actual = N'zzfnCipValidateCustomerAddress.Actual',
                             @Message = N'',
                             @FailMsg = N'Zip with value of ''12345'' did not generate a CipExceptionReasonId as expected';

END;

【问题讨论】:

我需要比这更多的信息才能开始故障排除。首先,失败的测试是什么样的。是否有一个特定的陈述失败了?测试是否涉及任何形式的事务管理? - 或者在那个环境中每个测试都失败了? 我添加了一个按要求失败的测试示例。我没有在测试中使用任何事务管理。此外,所有测试都失败了。被测试的函数是一个表值函数,也是一个表值函数并且不涉及任何事务管理。如果您需要任何其他信息,请告诉我。 所以,我不明白为什么您处于启用始终在线的环境中。测试(尤其是 tSQLt)永远不应该在生产环境中运行。你能告诉我更多关于你的设置吗? @sebastian-meine 我们的 DBA 也将我们的开发环境用作他们的“开发”区域,因此他们首先将他们想要在生产环境中实现的所有功能设置到我们的开发环境中,然后再推广这些功能.这允许他们首先验证他们的 AlwaysOn 配置是否在 Dev 中工作,并且他们可以验证当他们在 UAT 和生产中实现它时它不会中断。他们对其他功能/配置也做同样的事情。我们的目标是使我们的每个环境之间的差异尽可能小。 如果您创建一个空测试(仅包含 RETURN 语句的测试),然后只执行该测试,您是否仍会收到相同的错误? 【参考方案1】:

tSQLt 在内部依赖于事务保存点。保存点与分布式事务不兼容。

目前在 tSQLt 中没有办法改变对事务的处理。

我的建议是设置一个专用的 CI(持续集成)环境,专门用于执行您的自动化测试套件(因此不需要启用 AlwaysOn)。无论您的具体情况如何,这都是行业最佳实践。

但是,这并不能解决专门处理您的 AlwaysOn 设置的测试程序。您自然必须在启用 AlwaysOn 的环境中进行测试。在这种情况下,您可以使用tSQLt.NewConnection 命令,该命令在单独的连接上执行传入的命令,并在 tSQLt 事务之外执行。

但请记住,在这种情况下,您将负责所有清理操作,因为通过 tSQLt.NewConnection 执行的任何操作显然不会被 tSQLt 回滚。

【讨论】:

如果测试尝试访问链接服务器的同义词(并且您忘记模拟它),我们认为也可能遇到此错误。

以上是关于启用 DTC 的 SQL Server 2017 AG 上的 tSQLt的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 开启DTC分布式事务

MS SQL Server:事务处理概念和 MS DTC 概述 (简述)

已禁用对分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具启用 DTC 以便在 MSDTC 安全配置中进行网络访问。

已禁用对分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具启用 DTC 以便在 MSDTC 安全配置中进行网络访问。

Python和SQL Server 2017的强大功能

Python和SQL Server 2017的强大功能