57P01:按顺序运行多个测试时由于管理员命令而终止连接

问题描述 投票:0回答:2

我有一个在 Docker 环境中运行的 Postgres 数据库,该环境是在 Xunit 测试中使用

FluentDocker
设置的。

Xunit 配置为串行运行测试。

运行单个测试时,一切正常。

但是,当添加另一个测试(实际上只是复制第一个测试)并使用

dotnet test
在一次调用中运行这两个测试时,第二个测试总是失败并显示
57P01: terminating connection due to administrator command

我尝试关闭数据库连接,在每次测试运行后停止容器等,但错误仍然存在,并且总是发生在同一行代码中。

这是测试代码:

[Fact]
public async Task ShouldProjectUserRegistration()
{
  var file = Path.Combine(
    Directory.GetCurrentDirectory(),
    (TemplateString)"Resources/docker-compose.yml"
  );

  var service = new Builder()
    .UseContainer()
    .UseCompose()
    .FromFile(file)
    .RemoveOrphans()
    .ForceRecreate()
    .WaitForPort(
      "database",
      "5432/tcp",
      30000 /*30s*/
    )
    .Build();
  var container = service.Start();

  var PgTestConnectionString =
    "PORT = 5432; HOST = localhost; TIMEOUT = 15; POOLING = True; MINPOOLSIZE = 1; MAXPOOLSIZE = 100; COMMANDTIMEOUT = 20; DATABASE = 'marten'; PASSWORD = '123456'; USER ID = 'marten'";
  using var store = DocumentStore.For(
    options =>
    {
      options.Connection(PgTestConnectionString);
      options.AutoCreateSchemaObjects = AutoCreate.All;
      options.Projections.SelfAggregate<User>(ProjectionLifecycle.Inline);
    }
  );

  var id = Guid.NewGuid();
  var username = "[email protected]";
  var userRegistered = new UserRegistered(
    id,
    username
  );

  await using var session = store.OpenSession();
  session.Events.StartStream(
    id,
    userRegistered
  );

  await session.SaveChangesAsync();


  var user = session.Load<User>(id);
  await session.Connection?.CloseAsync();
  service.Stop();
  Assert.Equal(
    username,
    user?.Username
  );

这是

docker-compose.yml

version: "3"

services:
  database:
    image: library/postgres:14
    environment:
      POSTGRES_USER: 'marten'
      POSTGRES_PASSWORD: '123456'
      POSTGRES_DB: 'marten'
    ports:
      - "5432:5432"

例外:

Npgsql.PostgresException
57P01: terminating connection due to administrator command
   at Npgsql.Internal.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|213_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
   at Weasel.Core.CommandBuilderBase`6.ExecuteReaderAsync(TConnection conn, CancellationToken cancellation, TTransaction tx)
   at Weasel.Core.SchemaMigration.Determine(DbConnection conn, ISchemaObject[] schemaObjects)
   at Weasel.Core.Migrations.DatabaseBase`1.executeMigration(ISchemaObject[] schemaObjects, CancellationToken token)
   at Weasel.Core.Migrations.DatabaseBase`1.executeMigration(ISchemaObject[] schemaObjects, CancellationToken token)
   at Weasel.Core.Migrations.DatabaseBase`1.generateOrUpdateFeature(Type featureType, IFeatureSchema feature, CancellationToken token)
   at Weasel.Core.Migrations.DatabaseBase`1.ensureStorageExists(IList`1 types, Type featureType, CancellationToken token)
   at Weasel.Core.Migrations.DatabaseBase`1.ensureStorageExists(IList`1 types, Type featureType, CancellationToken token)
   at Weasel.Core.Migrations.DatabaseBase`1.ensureStorageExists(IList`1 types, Type featureType, CancellationToken token)
   at Marten.Events.EventGraph.ProcessEventsAsync(DocumentSessionBase session, CancellationToken token)
   at Marten.Internal.Sessions.DocumentSessionBase.SaveChangesAsync(CancellationToken token)
   at Marten.Internal.Sessions.DocumentSessionBase.SaveChangesAsync(CancellationToken token)
   at MartenFluentDockerNpsql57P01Repro.Tests.UserProjectionTests.ShouldProjectUserRegistrationSecond() in /Users/alexzeitler/src/MartenFluentDockerNpsql57P01Repro/MartenFluentDockerNpsql57P01Repro.Tests/UserProjectionTests.cs:line 120
   at MartenFluentDockerNpsql57P01Repro.Tests.UserProjectionTests.ShouldProjectUserRegistrationSecond() in /Users/alexzeitler/src/MartenFluentDockerNpsql57P01Repro/MartenFluentDockerNpsql57P01Repro.Tests/UserProjectionTests.cs:line 126
   at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_1.<<InvokeTestMethodAsync>b__1>d.MoveNext() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestInvoker.cs:line 264
--- End of stack trace from previous location ---
   at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:line 48
   at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 90

我创建了一个重现,可以在 on GitHub 上找到。

postgresql docker .net-core docker-compose marten
2个回答
3
投票

您的连接字符串中有生成连接池的选项:

POOLING = True; MINPOOLSIZE = 1; MAXPOOLSIZE = 100;

生成此池并运行测试逻辑后,您仅在调用

CloseAsync
时关闭池中的当前连接。

由于池的最小大小为 1,因此在关闭当前连接后,池将立即添加新连接。

看起来关闭数据库会在连接生成的新连接上触发错误。因此要解决问题并销毁所有连接相关资源,您需要使用

DisposeAsync

await session.Connection?.DisposeAsync();

您可能还应该设置

POOLING = False
,因为您的测试似乎只需要单个连接。

session
似乎通过
using
正确关闭/处置连接,因为连接是由 Marten 创建/管理的。这里的问题是,这不会正确清理生成的连接池,而只会将连接返回到保持空闲状态的池中。因此,只要
PgTestConnectionString
ShouldProjectUserRegistration
中的
ShouldProjectUserRegistrationSecond
相同,池就会尝试提供您在
ShouldProjectUserRegistration
中使用的相同连接。问题是,当连接在池中闲置时,您会终止它所连接的数据库。因此,当您从池中获取此连接并尝试对其执行操作时,您会收到您所描述的错误消息。

可以通过几种不同的方式来防止这种情况:

  • 在两种测试方法中设置
    POOLING = False;
    ,因为您不需要连接池,因为您在测试之间会销毁数据库。
  • 如果你想保留
    POOLING = True;
    ,你可以摧毁 每个
    Fact
    末端的泳池与
    NpgsqlConnection.ClearPool(session.Connection);
    NpgsqlConnection.ClearAllPools();

0
投票

对我来说,问题是我通过

.WithPortBinding(“5433”, “5432”)
中的
PostgreSqlBuilder
为多个测试容器打开相同的硬编码端口。

每个容器(实际上是每个测试类)它运行时都会终止前一个容器以获取端口绑定,从而导致

terminating connection due to administrator command
错误

© www.soinside.com 2019 - 2024. All rights reserved.