feat: Add a wait strategy that waits until the ADO.NET database is available#1401
Conversation
✅ Deploy Preview for testcontainers-dotnet ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
src/Testcontainers/Configurations/WaitStrategies/IWaitForContainerOS.cs
Outdated
Show resolved
Hide resolved
HofmeisterAn
left a comment
There was a problem hiding this comment.
We need to update the Db2 tests as well. If you're busy, I can take care of both tasks.
|
Now using a |
|
Now using a And here something extremely interesting happened! The test executes in 2 seconds when using the
|
27610b2 to
ca8a178
Compare
HofmeisterAn
left a comment
There was a problem hiding this comment.
Thanks for the adjustments.
Unfortunately, I'm running into an issue locally when I run the Cassandra tests multiple times.
The ConnectionStateReturnsOpen() test fails for CassandraContainerDefaultConfiguration when I run all the Cassandra tests together (including CassandraFixtureWaitForDatabase). But if I run just the CassandraContainerDefaultConfiguration tests on their own, everything passes.
All hosts tried for query failed (tried 127.0.0.1:52448: SocketException 'A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.')
I believe this is a connection pooling issue. I think I've seen something similar before.
There was a problem hiding this comment.
I've updated the remaining test projects as well. They now all use the same structure and test at least the default and overridden wait strategy configurations.
I'll update the module documentation later today. At least, we need to review it, as it might no longer be accurate.
IMO, the PR looks good. We just need to figure out why Cassandra is failing and check whether other modules are experiencing similar issues.
Edit:
On CI Cassandra failed with a similar issue:
Cassandra.NoHostAvailableException : All hosts tried for query failed (tried 127.0.0.1:32769: SocketException 'Transport endpoint is not connected')
Raw output
Of course, it works on my machine. 🤷🏻♂️ I tried something in 3d22bf5 for FirebirdSql. This could be applied to all other db modules too. The default wait strategy automatically switches to the new What do you think? Good idea or not? Note that applying the same mechanism to the PostgreSQL module would automatically add support for older images where |
I've tried it on two different machines and it's failing in CI. I'll take a look at it after the public holidays.
I'd prefer to discuss this in a separate PR. The PR looks good overall, except for the necessary Cassandra fix. I'm not a huge reflection fan (I guess no one is 😆), but it's an interesting idea. It could help support more configurations and might even be faster in some cases. But we definitely need a way to opt out or fallback. |
d1a6578 to
aa2d3a9
Compare
OK, I removed the commits that use reflection to get the |
d1a6578 to
aa2d3a9
Compare
|
For Postgres a specific hint, relying on a single SQL query (or even on |
For |
|
I had some time to look into the Cassandra issue. It looks like a connection pooling issue. The test that opens the DB connection fails after a few runs. The connection string in the exception message doesn't match the one that's actually configured and used by/for the container:
The only way I found to configure pooling is through Not sure how to move forward from here. Maybe we can ask for guidance in the upstream repo (I believe this is a general issue with the client implementation). WDYT? |
|
I was finally able to reproduce the problem by changing the public CassandraContainerTest(CassandraDefaultFixture fixture)
{
this.fixture = fixture;
if (GetType().Name.Contains("CassandraDefaultConfiguration"))
{
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}I see two possible solutions to this problem that occurs because the
diff --git a/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs b/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs
index d351eab..62ebcb6 100644
--- a/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs
+++ b/tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs
@@ -8,7 +8,8 @@ public abstract class CassandraContainerTest(CassandraContainerTest.CassandraDef
public void ConnectionStateReturnsOpen()
{
// Given
- using DbConnection connection = fixture.CreateConnection();
+ var connectionString = new CassandraConnectionStringBuilder(fixture.ConnectionString) { ClusterName = GetType().Name }.ConnectionString;
+ using DbConnection connection = new CqlConnection(connectionString);
// When
connection.Open();
diff --git a/src/Testcontainers.Cassandra/CassandraContainer.cs b/src/Testcontainers.Cassandra/CassandraContainer.cs
index 3e44d61..2709655 100644
--- a/src/Testcontainers.Cassandra/CassandraContainer.cs
+++ b/src/Testcontainers.Cassandra/CassandraContainer.cs
@@ -18,7 +18,9 @@ public sealed class CassandraContainer : DockerContainer, IDatabaseContainer
{
var properties = new Dictionary<string, string>();
properties.Add("Contact Points", Hostname);
- properties.Add("Port", GetMappedPublicPort(CassandraBuilder.CqlPort).ToString());
+ var port = GetMappedPublicPort(CassandraBuilder.CqlPort).ToString();
+ properties.Add("Port", port);
+ properties.Add("Cluster Name", $"{Hostname}:{port}");
return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
}Both strategies work. I think the second one is a bit better but I'm not a Cassandra user so I'm not 100% sure. |
Because the `CqlConnection` class uses a static concurrent dictionary of clusters keyed by cluster name. Using multiple containers (which actually happens in the tests) can fail because the `CqlConnection` will try to connect to a potentially disposed container.
|
I just pushed solution #2 in ac743ce. ✅ The tests are now all passing! |
|
Thanks for the adjustments. I prefer setting the cluster name property too. Leaving it unset prevents Testcontainers users from running multiple containers. If any issues come up afterward, we can deal with them as they arise. I also prepared an issue I'd like to share with DataStax. Unfortunately, I can't create a GitHub or Jira issue at the moment, I'll send the following to their mailing list later: We're running into a race-condition with the DataStax C# driver for Apache Cassandra, and I believe this is a general issue with the client implementation. We're running multiple Apache Cassandra containers on the same host. It looks like the client caches connections based on the cluster name. If the connection string doesn't specify a cluster name, it uses the default cluster name. Here's what happens:
|

What does this PR do?
This pull request introduces a new wait strategy for containers implementing the
IDatabaseContainerinterface. It tries to open a connection with the container connection string until it works or until it times out.Why is it important?
This new wait strategy is quite powerful as it can be used for all database containers. It could have been used to workaround #1383 and it could be used as an alternative to #1392.
Related issues
Mentioned above.
How to test this PR
New tests have been added to use this new wait strategy for database containers in addition to the existing wait strategies.
Follow-ups
Explicitly supporting PostgreSQL < 9.3 should be done in a follow-up pull request by using this new wait strategy.