Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,22 @@ private IClusterExtension<? extends IInstance> provisionClusterWithRetries(TestV
}
catch (RuntimeException rte)
{
if (rte.getMessage() != null && (rte.getMessage().contains("Address already in use") ||
rte.getMessage().contains("is in use by another")))
// The BindException ("Address already in use") is several levels deep in the cause
// chain when thrown through the reflection-based cluster provisioning path, so we
// must walk the full chain rather than checking only rte.getMessage().
boolean isBindFailure = false;
for (Throwable cause = rte; cause != null; cause = cause.getCause())
{
String message = cause.getMessage();
if (message != null && (message.contains("Address already in use") ||
message.contains("is in use by another") ||
message.contains("Failed to bind port")))
{
isBindFailure = true;
break;
}
Comment on lines +226 to +233
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor:
If "Address already in use" and "is in use by another" originate from BindException (OS/JVM), a cause instanceof BindException check inside the loop would be more robust than string matching, which can vary across OS, JVM vendor, or locale. If they come from application-level messages, the string matching is fine as-is.

Suggested change
String message = cause.getMessage();
if (message != null && (message.contains("Address already in use") ||
message.contains("is in use by another") ||
message.contains("Failed to bind port")))
{
isBindFailure = true;
break;
}
String message = cause.getMessage();
if (cause instanceof java.net.BindException || (cause.getMessage() != null && cause.getMessage().contains("Failed to bind port")))
{
isBindFailure = true;
break;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comes from our friend Server.java#start in Cassandra:

        if (!bindFuture.awaitUninterruptibly().isSuccess())
            throw new IllegalStateException(String.format("Failed to bind port %d on %s.", socket.getPort(), socket.getAddress().getHostAddress()),
                                            bindFuture.cause());

Certainly if that changes we could have a bad time. But the reality is that we're a) throwing an IllegalStateException in C*, and b) it's going through layers of indirection (C* inside in-jvm dtest API inside classloaders) that make it pretty hard to durably determine what exception type is going to show up in the test level since various tiers can catch and rethrow as different types.

Hence the throwing my hands in the air and just strcmp'ing it for now. On the plus side, that string has been stable since at least 2018 so seems stable.

Famous last words.

}
if (isBindFailure)
{
logger.warn("Failed to provision cluster after {} retries", retry, rte);
}
Expand Down
Loading