Skip to content
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

RabbitMQ: spring.cloud.stream.rabbit.bindings.* properties not recognized in Native/AOT builds #2938

Closed
mjbingen opened this issue Apr 22, 2024 · 5 comments
Assignees
Labels
Milestone

Comments

@mjbingen
Copy link

Describe the issue
When building a Spring Boot app with Spring Cloud Stream as a native image with GraalVM, the properties scoped under spring.cloud.stream.rabbit.bindings.* do not appear to be recognized in the native image and are defaulted.

This is problematic as properties like exchangeType, routingKeyExpression, and bindingRoutingKey produce defaults that lead to unexpected behaviors. For example, the exchangeType will always be topic and the routing keys default to #.

It is important to note that properties scoped under spring.cloud.function.definition, spring.cloud.stream.output-bindings, and spring.cloud.stream.bindings seem to function as desired.

To Reproduce
A simple reproducible example can be found in full here: https://github.com/mjbingen/spring-cloud-stream-sandbox/tree/native

Example:

application.yml

spring:
  cloud:
    function:
      definition: onFoo;onBar
    stream:
      output-bindings: the-exchange
      bindings:
        onFoo-in-0:
          destination: the-exchange
        onBar-in-0:
          destination: the-exchange
      rabbit:
        bindings:
          the-exchange:
            producer:
              exchangeType: direct
              routingKeyExpression: '''the.foo.key'''
          onFoo-in-0:
            consumer:
              exchangeType: direct
              bindingRoutingKey: the.foo.key
          onBar-in-0:
            consumer:
              exchangeType: direct
              bindingRoutingKey: the.bar.key

Code - expect to self-consume messages when REST endpoint is hit only from onFoo method that is bound to the exchange with matching routing key. However, when application is run as a native image, all bounding keys default to #, so the YAML-specified routing key is ignored and both methods consume the message.

package com.mjb.spring.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.time.OffsetDateTime;
import java.util.function.Consumer;

@SpringBootApplication
@RestController
public class Main
{
    public static void main(String... args)
    {
        SpringApplication.run(Main.class, args);
    }

    @Autowired
    private StreamBridge streamBridge;

    @RequestMapping
    @ResponseStatus(HttpStatus.ACCEPTED)
    public String sendToBridge()
    {
        // Publish a timestamp
        final var now = OffsetDateTime.now().toString();
        streamBridge.send("the-exchange", now);
        return now;
    }

    @Bean
    public Consumer<String> onFoo()
    {
        // Receive from exchange with key 'the.foo.key'
        return received -> System.out.println("Received from FOO: " + received);
    }

    @Bean
    public Consumer<String> onBar()
    {
        // Not expected to receive from exchange with key 'the.bar.key'
        return received -> System.out.println("ERROR: Received from BAR: " + received);
    }
}

Version of the framework
Spring Boot 3.2.5
Spring Cloud Stream 4.1.1
Oracle GraalVM 21

Expected behavior
Properties defined in application.yml and present at compile time should be reflected in the native image and behave just like the JVM version.

Screenshots

Additional context
Similar to prior issue with Kafka streams. However, RabbitMQ binder documentation suggests properties must be defined in the prefix above.

@olegz olegz self-assigned this Apr 26, 2024
@olegz olegz added this to the 4.1.2 milestone Apr 26, 2024
@sobychacko sobychacko self-assigned this Apr 30, 2024
@sobychacko
Copy link
Contributor

@mjbingen We are investigating this issue. Can you please add instructions for how exactly to reproduce the issue? Your sample is a great start, and I see a native branch there. How do you build, run, and test the app? Also, how do you verify that things don't work as expected? Once you clarify these things, we will continue looking at this issue. Thanks!

@sobychacko
Copy link
Contributor

@mjbingen Any updates?

@mjbingen
Copy link
Author

Hi @sobychacko - sorry for the delay. Here is the full set of instructions to build the sample application to highlight the issue.

  • Assure your current Java version is a GraalVM compatible build that works with org.graalvm.buildtools.native Gradle plugin (I tested against Oracle GraalVM 21)

  • Build the native branch of the example as a bootable Jar and a native image: ./gradlew bootJar nativeCompile

  • Assure a RabbitMQ broker is running on your localhost (quick startup via docker docker run -it --rm -p 15672:15672 -p 5672:5672 rabbitmq:3-management)

  • Run the application as a Jar to see the expected behavior

    • java -jar build/libs/spring-cloud-stream-sandbox.jar
    • Confirm that the settings in application.yml match the exchange/queue details on the RabbitMQ broker (via management panel in the browser via http://localhost:15672/ -> admin:admin)
    • This include a direct exchange named the-exchange and two queues, both bound to the exchange but with routing keys the.bar.key and the.foo.key.
    • Pasted Graphic
    • Pasted Graphic 1
    • Pasted Graphic 2
    • Hit the service GET REST endpoint (localhost:8080) to trigger a pub/sub action
    • Confirm only the FOO channel is receiving the message as the application is configured to publish with only this routing key. Example: Received from FOO: 2024-05-21T10:46:00.322937+02:00
  • Run the application as the native image to see the undesired behavior of the application.yml properties being ignored and the RabbitMQ settings defaulting.

    • Delete the the-exchange exchange from the RabbitMQ broker to assure it is re-created on re-start of the service
    • Run the native image: build/native/nativeCompile/spring-cloud-stream-sandbox
    • Verify the RabbitMQ broker settings are not the same as when run as a Jar and the properties are defaulted. Exchange type is topic and binding keys are all (#)
    • Pasted Graphic 3
    • Pasted Graphic 4
    • Pasted Graphic 5
    • When the REST endpoint is hit, the application will receive on both channels because the routing keys are defaulted and not respected. Example: ERROR: Received from BAR: 2024-05-21T10:50:04.930574+02:00

Let me know if this helps or you need any other details/tests to help with the investigation.

@sobychacko
Copy link
Contributor

@mjbingen Sorry for the delay. This turned out to be a bug in the binder. We just addressed it via the above-referenced commit. It should be available as part of the 4.1.2 release.

@sobychacko sobychacko added the bug label May 30, 2024
@mjbingen
Copy link
Author

mjbingen commented Jun 6, 2024

Many thanks @sobychacko ! I confirmed the 4.1.2 release fixes the issue, the native image is working as expected. Thanks for the great work in Spring Cloud Stream and its compatibility with Spring Native!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants