Support service connections with RabbitMQ Streams and Testcontainers#42443
Support service connections with RabbitMQ Streams and Testcontainers#42443eddumelendez wants to merge 3 commits into
Conversation
Add `RabbitStreamConnectionDetails` and support from `RabbitMQContainer` when `rabbitmq_stream` plugin is enabled.
There was a problem hiding this comment.
Hey @eddumelendez, thanks for the PR. I've left some comments for your consideration.
| private static RabbitMQContainer getRabbitMqStreamContainer() { | ||
| RabbitMQContainer container = TestImage.container(RabbitMQContainer.class); | ||
| container.addExposedPorts(RABBITMQ_STREAMS_PORT); | ||
| var enabledPlugins = "[rabbitmq_stream,rabbitmq_prometheus]."; |
There was a problem hiding this comment.
We don't use var in the codebase.
wilkinsona
left a comment
There was a problem hiding this comment.
when rabbitmq_stream plugin is enabled
Apologies if I've missed it, but I can't see any code in the connection details factory that checks that the stream plugin is enabled. What'll happen if the factory kicks in on a container without streams enabled? I'm wondering if it may break somehow, perhaps by overriding the app's Rabbit Streams configuration with connection details that won't work.
|
@Bean
EnvironmentBuilderCustomizer environmentBuilderCustomizer() {
return env -> {
Address entrypoint = new Address(rabbit.getHost(), rabbit.getMappedPort(RABBITMQ_STREAMS_PORT));
env.addressResolver(address -> entrypoint);
};
} |
|
PR has been updated |
| import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; | ||
|
|
||
| /** | ||
| * Details required to establish a connection to a RabbitMQ service. |
There was a problem hiding this comment.
Why do we need this? Especially given the javadoc, it looks like to me that the existing RabbitConnectionDetails should be enough, isn't it?
There was a problem hiding this comment.
rabbitmq stream uses a different port and in order to do that the plugin should be enabled. Having two implementations of RabbitConnectionDetails will lead to having an exception with 2 beans unless other beans or configuration are disabled when stream are enabled.
I think would be possible to have a different virtual host for stream in a single RabbitMQ instance, so, having a specific ConnectionDetails would make sense.
There was a problem hiding this comment.
I think there's a bug in the current code as we're not taking RabbitConnectionDetails into account when falling back from stream-specific configuration to general Rabbit configuration. I've opened #42489 for that. When fixed, we'll fallback from spring.rabbit.stream.* properties to RabbitConnectionDetails. With the changes here in place, we'd then fallback from RabbitStreamConnectionDetails (a replacement for some of the spring.rabbit.stream.* properties) to RabbitConnectionDetails (a replacement for some of the general spring.rabbit.* properties).
There was a problem hiding this comment.
Thinking about this some more, Rabbit Streams support is deceptively complicated (to me at least) as I think it's the first time where different users may want different things. One user may want RabbitConnectionDetails and RabbitStreamConnectionDetails to be sourced from the same container as their app is using the same Rabbit broker for both streaming and regular messaging. Another user may want RabbitConnectionDetails and RabbitStreamConnectionDetails to be sourced from different containers as their app is using different Rabbit brokers for streaming and regular messaging.
With only RabbitMQContainer to look at, even if we could detect in the container connection details factory that the streams plugin is enabled, we wouldn't know if the user wanted this container to be used only for streams or for both streams and regular messaging. It feels like we may need some other signal to indicate how the container should be used as a source for connection details. This reminds me a bit of the exploratory work being done for SSL and the possibility of having SSL-related annotations on the container field. I'll flag this one for a meeting so I can see what the rest of the team thinks.
There was a problem hiding this comment.
We think we may be able to check to see if the default streams port is exposed by the container. If it isn't, the factory should return null.
We should also think about Docker Compose as, ideally, we'd keep the supported services in sync across Compose and Testcontainers.
There was a problem hiding this comment.
Upon further thought, I'm not sure that this will work for all cases. Those cases are:
- One container to be used for both streams and "standard" messaging
- Two containers: one for streams and one for standard messaging
In the two container case, RabbitContainerConnectionDetailsFactory will match a container with the streams port exposed and I think we'll end up with a duplicate connection details failure. If we made RabbitContainerConnectionDetailsFactory ignore containers with the streams port exposed it would fix the two container case but it would break the one container case.
Some experimentation's needed to see if the above's accurate.
Add `RabbitStreamConnectionDetails` and support from `RabbitMQContainer` when `rabbitmq_stream` plugin is enabled. See spring-projectsgh-42443
|
I've rebased and polished the proposal in this branch.
This wasn't possible. The connection details factory can't access the container to examine its exposed port configuration as the container's only available to the connection details bean and one then once the bean has been initialized.
I've done some experimentation and we don't end up with a duplicate connection details failure, instead any connection details of the same type after the first are ignored: Given the two behaviors described above, the polished proposal requires the use of the
In the two cases where This opt-in behavior and the need for the If we are happy, the next step will be to consider Docker Compose support and how things might work there. |
|
We discussed this today and wondered if we could overload |
I don't think this really helps. You either have to implement some logic in The former leaves a split in the code where checking if a source accepts a particular connection is split across the factory and the source. The latter is public API so overloading it doesn't avoid an increase in the public API. I think making |
Add `RabbitStreamConnectionDetails` and support from `RabbitMQContainer` when `rabbitmq_stream` plugin is enabled. See gh-42443
|
Thanks very much, @eddumelendez. |
Add
RabbitStreamConnectionDetailsand support fromRabbitMQContainerwhen
rabbitmq_streamplugin is enabled.