使用回滚的 Postgres、Npgsql 和集成测试
Postgres, Npgsql and Integration tests using rollbacks
在我们的 SQL 服务器集成测试中,我们将测试包装在 TransactionScope 中,然后我们在每次测试后回滚以保持数据库处于一致状态。
使用 Postgres(或者可能特别是 Npgsql),这似乎是不可能的,因为在单个连接之外的选择无法读取未提交的数据(即使在未提交的事务范围内)。
基本场景简化如下:
[Test]
public void ImplicitEnlist()
{
var connectionString = ConnectionString + ";enlist=true";
using (var scope = new TransactionScope())
{
using (var conn = new NpgsqlConnection(connectionString))
{
conn.Open();
Assert.That(conn.ExecuteNonQuery(@"INSERT INTO data (name) VALUES('test')"), Is.EqualTo(1));
}
using (var conn = new NpgsqlConnection(connectionString))
{
// -> this is false
Assert.That(conn.ExecuteScalar(@"SELECT COUNT(*) FROM data"), Is.EqualTo(1));
}
scope.Rollback();
}
}
任何人都可以分享人们如何使用 Postgres 数据库来处理这个问题吗?
从 github issue:
复制粘贴答案
我真的不认为事情应该按照你想的那样运作。当您在同一个 TransactionScope 中打开两个连接时,您正在执行分布式事务。 PostgreSQL 有 2 个不同的连接,每个连接都有一个 prepared transaction。现在,这并不意味着这两个连接相互了解,或者这两个准备好的事务在某种程度上是相关联的。由于它们尚未提交,事务隔离适用并且每个连接都看不到另一个未提交的更改。
总而言之,分布式事务意味着当您在 TransactionScope 上调用 Complete() 时,将使用两阶段提交协议来确保两个事务都提交,或者 none。它不保证参与交易以某种方式相互了解。
虽然我很确定这就是工作原理,但我不是分布式事务或 PostgreSQL 准备事务方面的专家 - 所以我可能是错的。我将关闭它,但如果您发现任何与我所说的相反的信息,将重新打开。
进一步深入研究后,我们发现直到现在我们一直依赖 SQL 服务器的精确度。 SQL Server 2008(和 .NET 3.5+)可以在一个事务中处理多个连接而无需 MSDTC 提升,前提是它们不是同时打开的 - https://msdn.microsoft.com/en-us/library/ms172070(VS.90).aspx。 Postgres 在这种情况下没有相同的行为。
我们最终消除了对回滚测试状态的事务的依赖(无论如何它都有自己的问题),而是使用 Respawn (https://github.com/jbogard/Respawn) 为我们将数据库重置回空状态。
在我们的 SQL 服务器集成测试中,我们将测试包装在 TransactionScope 中,然后我们在每次测试后回滚以保持数据库处于一致状态。
使用 Postgres(或者可能特别是 Npgsql),这似乎是不可能的,因为在单个连接之外的选择无法读取未提交的数据(即使在未提交的事务范围内)。
基本场景简化如下:
[Test]
public void ImplicitEnlist()
{
var connectionString = ConnectionString + ";enlist=true";
using (var scope = new TransactionScope())
{
using (var conn = new NpgsqlConnection(connectionString))
{
conn.Open();
Assert.That(conn.ExecuteNonQuery(@"INSERT INTO data (name) VALUES('test')"), Is.EqualTo(1));
}
using (var conn = new NpgsqlConnection(connectionString))
{
// -> this is false
Assert.That(conn.ExecuteScalar(@"SELECT COUNT(*) FROM data"), Is.EqualTo(1));
}
scope.Rollback();
}
}
任何人都可以分享人们如何使用 Postgres 数据库来处理这个问题吗?
从 github issue:
复制粘贴答案我真的不认为事情应该按照你想的那样运作。当您在同一个 TransactionScope 中打开两个连接时,您正在执行分布式事务。 PostgreSQL 有 2 个不同的连接,每个连接都有一个 prepared transaction。现在,这并不意味着这两个连接相互了解,或者这两个准备好的事务在某种程度上是相关联的。由于它们尚未提交,事务隔离适用并且每个连接都看不到另一个未提交的更改。
总而言之,分布式事务意味着当您在 TransactionScope 上调用 Complete() 时,将使用两阶段提交协议来确保两个事务都提交,或者 none。它不保证参与交易以某种方式相互了解。
虽然我很确定这就是工作原理,但我不是分布式事务或 PostgreSQL 准备事务方面的专家 - 所以我可能是错的。我将关闭它,但如果您发现任何与我所说的相反的信息,将重新打开。
进一步深入研究后,我们发现直到现在我们一直依赖 SQL 服务器的精确度。 SQL Server 2008(和 .NET 3.5+)可以在一个事务中处理多个连接而无需 MSDTC 提升,前提是它们不是同时打开的 - https://msdn.microsoft.com/en-us/library/ms172070(VS.90).aspx。 Postgres 在这种情况下没有相同的行为。
我们最终消除了对回滚测试状态的事务的依赖(无论如何它都有自己的问题),而是使用 Respawn (https://github.com/jbogard/Respawn) 为我们将数据库重置回空状态。