-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Hazelcast Example #6117
Add Hazelcast Example #6117
Conversation
I clicked the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @tomazfernandes ! Can you add a link here, please?
examples/hazelcast/build.gradle
Outdated
} | ||
|
||
dependencies { | ||
testImplementation 'org.testcontainers:testcontainers:1.17.5' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
testImplementation 'org.testcontainers:testcontainers:1.17.5' | |
testImplementation 'org.testcontainers:testcontainers' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works without version since we are doing a Gradle composite build with the examples
.
also, please do not forget the |
1940c01
to
10ff2c2
Compare
Though the |
Ok, so there were two issues with the The first is that sometimes the client would connect to the cluster The other issue is a The problem is that once the client connects to the first node, it fetches the cluster's There's this logic executed by the client:
public void tryConnectToAllClusterMembers(boolean sync) {
if (!isSmartRoutingEnabled) {
return;
}
if (sync) {
for (Member member : client.getClientClusterService().getMemberList()) {
try {
getOrConnectToMember(member, false);
} catch (Exception e) {
EmptyStatement.ignore(e);
}
}
}
executor.scheduleWithFixedDelay(new ConnectToAllClusterMembersTask(), 1, 1, TimeUnit.SECONDS);
} It'll basically try to connect to all members every second, and just log if it can't:
try {
if (!client.getLifecycleService().isRunning()) {
return;
}
getOrConnectToMember(member, false);
} catch (Exception e) {
if (logger.isFineEnabled()) {
logger.warning("Could not connect to member " + uuid, e);
} else {
logger.warning("Could not connect to member " + uuid + ", reason " + e);
}
} finally {
connectingAddresses.remove(uuid);
} I'm not sure what's the purpose of this attempt to connect to the members and ignore if it fails. Nevertheless, it doesn't affect the actual functionality of the test. (EDIT: Well, probably the purpose is to log if it can't connect 😄) But it might affect more complex scenarios where the client needs to make a request to a node that isn't the original one we configured the client with, since the client won't know the address exposed by the container for that node. |
A simple Hazelcast example using both a single container and a cluster with two nodes. Fixes testcontainers#6115
Sometimes the client would connect to the first container before the second one registers in the cluster. Add a condition to wait for both nodes to be registered when starting the containers.
f5809dc
to
0ca87ee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomazfernandes thanks for looking at it! I found a very interesting setup here but I didn't make it work. Also, found this and makes me think it is not that bad :D
clientConfig | ||
.setClusterName(TEST_CLUSTER_NAME) | ||
.getNetworkConfig() | ||
.addAddress(container1.getHost() + HOST_PORT_SEPARATOR + container1.getFirstMappedPort()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to register both instances so if one of them are down the client can switch, just as a reference given that the example will make sure there are 2 members in the cluster.
.addAddress(container1.getHost() + HOST_PORT_SEPARATOR + container1.getFirstMappedPort()); | |
.addAddress( | |
container1.getHost() + HOST_PORT_SEPARATOR + container1.getFirstMappedPort(), | |
container2.getHost() + HOST_PORT_SEPARATOR + container2.getFirstMappedPort() | |
); |
|
||
private static final int DEFAULT_EXPOSED_PORT = 5701; | ||
|
||
private static final String CLUSTER_STARTUP_LOG_MESSAGE_REGEX = ".*Members \\{size:2, ver:2\\}.*"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size
and ver
are interesting. According to the docs
Here, you can see the size of your cluster (size) and member list version (ver). The member list version is incremented when changes happen to the cluster, e.g., a member leaving from or joining to the cluster.
I will really only on Members \\{size:2
Source: https://docs.hazelcast.com/imdg/4.2/starting-members-clients
examples/hazelcast/src/test/java/org/testcontainers/examples/HazelcastContainerTest.java
Outdated
Show resolved
Hide resolved
@tomazfernandes Regarding:
No need to do rebase in PRs in most cases, since we do a squash when merging 🙂 |
No worries, thank you both for the suggestions!
If I understand correctly this is a way of programmatically creating the cluster, so it would be an alternative to using a container, rather than something we can use along, right?
Nice! As far as I understand the way I've checked and indeed setting smart routing to false removes the warning - but since users might prefer having the Tests are always passing here - let's see if with the REGEX change it'll pass on the CI as well. |
You can do it via env vars too but didn't succeed.
I think it is safe. Thanks again! |
Cluster test is failing in the CI, attempt to fix it by starting containers sequentially instead of in parallel.
Just to register, I've also looked into some alternatives to make Hazelcast's An alternative to use automatic discovery without knowing the ports is to use the IMHO the solution is robust as is with the last changes, and in the future if a user asks for something this example doesn't cover we can look into that. Let's see if the Thanks! |
examples/hazelcast/src/test/java/org/testcontainers/examples/HazelcastContainerTest.java
Outdated
Show resolved
Hide resolved
I just was doing some hacky things last night but got other issue related to protocols 🤷🏽♂️ static class HazelcastContainer extends GenericContainer<HazelcastContainer> {
private static final String CLUSTER_STARTUP_LOG_MESSAGE_REGEX = ".*Members \\{size:2.*";
private static final String STARTER_SCRIPT = "/testcontainers_start.sh";
HazelcastContainer(DockerImageName image) {
super(image);
addExposedPort(5701);
withCreateContainerCmdModifier(cmd -> {
cmd.withEntrypoint("sh");
});
withCommand("-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT);
waitingFor(Wait.forLogMessage(CLUSTER_STARTUP_LOG_MESSAGE_REGEX, 1));
}
@Override
protected void containerIsStarting(InspectContainerResponse containerInfo) {
super.containerIsStarting(containerInfo);
String host;
try {
host = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
String networkAlias = getNetworkAliases().get(0);
String ip = getContainerInfo().getNetworkSettings().getNetworks().get(((Network.NetworkImpl) getNetwork()).getName()).getIpAddress();
String command = "#!/bin/bash\n";
// command += "export HZ_NETWORK_PUBLICADDRESS=" + host + ":" + getMappedPort(5701) + "\n";
command += "export HZ_ADVANCEDNETWORK_ENABLED=true\n";
// command += "export HZ_NETWORK_PORT_AUTOINCREMENT=false\n";
// command += "export HZ_ADVANCEDNETWORK_MEMBERSERVERSOCKETENDPOINTCONFIG_PUBLICADDRESS=" + ip + "\n";
command += "export HZ_ADVANCEDNETWORK_MEMBERSERVERSOCKETENDPOINTCONFIG_PORT_PORT=5701\n";
command += "export HZ_ADVANCEDNETWORK_CLIENTSERVERSOCKETENDPOINTCONFIG_PUBLICADDRESS=" + host + "\n";
command += "export HZ_ADVANCEDNETWORK_CLIENTSERVERSOCKETENDPOINTCONFIG_PORT_PORT=" + getMappedPort(5701) + "\n";
command += "/opt/hazelcast/bin/hz start";
copyFileToContainer(Transferable.of(command, 0777), STARTER_SCRIPT);
}
} So far looks good and the comment will cover it. No need to rush on this. I am pretty sure people with more experience on Hazelcast will contribute later |
Cool stuff!
Nice! Regarding the I had also looked into waiting for |
Run |
Seems like even running the containers sequentially the second one still emits the log before joining the cluster in the Unless you have any other suggestions, I'll look for a different approach. |
I would suggest to use the same wait strategy for server 2 |
I totally sent the wrong message. Like that is ok. I wonder if it is related to the startup timeout and should be increased. |
Change strategy back to parallel
Might be. I changed back to starting the containers in parallel and now they both wait for Seems I might have broken the first test though, I'm checking. |
I think it's fine - let's see the next CI execution. Hopefully it'll pass this time! 🤞🏼 |
I can try to increase the timeout even further, or maybe go back to starting the containers sequentially. But I wonder if maybe something in the Also, since this is an example and not production code test, perhaps for now we can remove the wait and not assert the cluster. |
I think we might have something along these lines. In the CI we have these logs:
While locally:
I'll investigate this further. |
weird! we can try with an env var |
Are you sure about this name? Couldn't find anything online 😄 |
reference here Use HZ for server and HZ_CLIENT for client :D All things we can learn in one night 🥱 |
|
Oh, I see, you can use the yaml / xml configuration as environment variables. Cool! I had run spotless, but changed the code before committing 🤦🏻♂️ |
more info about env vars here |
No change:
|
https://hazelcast.com/blog/hazelcast-discovery-auto-detection/ now, it makes sense ☝🏽 I think we are close, can we do one last test with |
🥳 |
can we remove the custom startup? I think the default one will work. Also, can we add a comment about the flags are needed because are running on GHA which use a Azure VM -.- |
You mean the log waiting?
Sure! |
remove this, please |
Remove custom wait timeout
Thanks @tomazfernandes ! this is now in |
Thanks @eddumelendez !
It's available already, cool! 🥳 |
A simple Hazelcast example with both single container and cluster tests that I created with @eddumelendez.
Please let me know if there's anything you'd like me to change.
Thanks!
Closes #6115