0

I have StreamListener which I would like to replace using the new functional model and Consumer <>. Unfortunately, I don't know how to transfer @Transactional to new model:

@Transactional
@StreamListener(PaymentChannels.PENDING_PAYMENTS_INPUT)
public void executePayments(PendingPaymentEvent event) throws Exception {

    paymentsService.triggerInvoicePayment(event.getInvoiceId());
}

I have tired certain things. Sample code below. I added logging messages to a different queue for tests. Then I throw an exception to trigger a rollback. Unfortunately, messages are queued even though they are not there until the method is completed (I tested this using brakepoints). It seems that the transaction was automatically committed despite the error.

@Transactional
@RequiredArgsConstructor
@Component
public class functionalPayment implements Consumer<PendingPaymentEvent> {
    private final PaymentsService paymentsService;
    private final StreamBridge streamBridge;

    public void accept(PendingPaymentEvent event) {
        paymentsService.triggerInvoicePayment(event.getInvoiceId());

        streamBridge.send("log-out-0",event);
        throw new RuntimeException("Test exception to rollback message from log-out-0");
    }
}

Configuration:

spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.queue-name-group-only=true
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.declare-exchange=true
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.bind-queue=true
spring.cloud.stream.rabbit.bindings.functionalPayment-in-0.consumer.transacted=true

spring.cloud.stream.source=log

spring.cloud.stream.bindings.log-out-0.content-type=application/json
spring.cloud.stream.bindings.log-out-0.destination=log_a
spring.cloud.stream.bindings.log-out-0.group=log_a
spring.cloud.stream.rabbit.bindings.log-out-0.producer.declare-exchange=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.bind-queue=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.queue-name-group-only=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.binding-routing-key=log
spring.cloud.stream.rabbit.bindings.log-out-0.producer.transacted=true
spring.cloud.stream.rabbit.bindings.log-out-0.producer.exchange-type=direct
spring.cloud.stream.rabbit.bindings.log-out-0.producer.routing-key-expression='log'
matmot
  • 163
  • 5

1 Answers1

1

Have you tried something along the lines of

@Transactional
public class ExecutePaymentConsumer implements Consumer<PendingPaymentEvent> {
   public void accept(PendingPaymentEvent event) {
       paymentsService.triggerInvoicePayment(event.getInvoiceId());
   }
}
. . .
@Bean
public ExecutePaymentConsumer executePayments() {
    return new ExecutePaymentConsumer();
}

Oleg Zhurakousky
  • 5,820
  • 16
  • 17
  • Yes, I have tried this option. Unfortunately, it doesn't seem to work. As a test, I tried to send a message to a different queue and then throw an exception. The message wasn't rolled back. Both consumer and publisher had transacted=true in application properties. – matmot Oct 25 '21 at 11:47
  • You need to provide more configuration, such as whether it is transacted or not (see for example Rabbit consumer properties here - https://docs.spring.io/spring-cloud-stream-binder-rabbit/docs/3.1.4/reference/html/spring-cloud-stream-binder-rabbit.html). Also, see this SO answer for additional references - https://stackoverflow.com/questions/50372319/transaction-in-spring-cloud-stream, otherwise I would suggest to provide an example with bare minimum that reproduces the issue and push it to Github somewhere so we can tale a look. – Oleg Zhurakousky Oct 25 '21 at 11:58
  • I've added sample code with application.properties configuration to the problem description. – matmot Oct 25 '21 at 12:42
  • We need to see the full code, not just bits that you believe are relevant. Can you please compile sample project and push it to github so we can look? – Oleg Zhurakousky Oct 25 '21 at 16:10
  • I've just exposed sample project in github https://github.com/matmot/consumerpoc.git – matmot Oct 25 '21 at 18:37
  • Than you @malmot, i'll be looking and will get back to you – Oleg Zhurakousky Oct 26 '21 at 09:20
  • 1
    It appears you are doing a lot of unnecessary things in your example (creating bindings manually etc) as well as things that are missing such as defining transaction manager etc. Here is the bare minimum example that I've just created that you should be able to retrofit into yours - https://github.com/olegz/stream-function-samples/tree/main/stream-rabbit-producer-transaction – Oleg Zhurakousky Oct 26 '21 at 13:13
  • Thank you @Oleg Zhurakousy, I made some mistakes and your example helped me a lot. First, I didn't realize the need to register RabbitTransactionMenager. When testing the StreamListener, I didn't set producer.required-groups=log_queue and misinterpreted that the message was being rolled back but it was simply lost. Secondly, I set the consumer to transacted. This unfortunately causes the message to be saved in the queue despite the exception being thrown. I don't quite understand why this is happening. That's weird. – matmot Oct 27 '21 at 13:18