spring-integration icon indicating copy to clipboard operation
spring-integration copied to clipboard

Future<Void> method with @Gateway causes thread leak

Open toyopilgrim opened this issue 4 years ago • 2 comments

In what version(s) of Spring Integration are you seeing this issue?

org.springframework.boot:spring-boot-starter-integration: '2.2.6.RELEASE'

Describe the bug

If response type is Future<Void>, the method with @Gateway causes thread leak. New threads are constantly spawned without reusing or disposing. It was confirmed on thread dump I collected in my application. Even though each memory usage is quite small, threads being racked up will eventually lead OOM. I assume that this is happening because of no acknowledgement with Void.

However, there's a solution. It simply can be avoided by replacing it with void send along with ExecutorService in order to keep it asynchronous. (Non-blocking messaging is what I wanna do) e.g.

@Bean
public MessageChannel sampleChannel() {
    return new PublishSubscribeChannel(Executors.newCachedThreadPool());
}

This is maybe a bug or should be described clearly in the docs if not a bug imhp.

To Reproduce

  • Register bean at a config class
@Bean
public MessageChannel sampleChannel() {
    return MessageChannels.publishSubscribe().get();
}
  • Make MessagingGateway have a method returns Future<Void> as following
@MessagingGateway
public interface SampleMessagingGateway { 
  @Gateway(requestChannel = "sampleChannel")  
  Future<Void> send(Payload payload);
}
  • send(payload) at a producer class.

  • Have a consumer class which contains method like

@ServiceActivator(inputChannel = "sampleChannel")
public void onSample(Payload payload){
  // do something 
}

As a result, threads spawned by that consumers stays in the lifetime of application apparently unless the application is restarted or so. Thus this will eventually lead OOM.

Expected behavior

  • It won't cause thread leak with Future<Void>

Or

  • Described in doc well. This behavior seems not described explicitly at asyn-gateway doc. If you wanna deal with it void type, the solution using ExecutorService should be recommended.

Sample

See above.

Thx in advance.

toyopilgrim avatar Sep 22 '21 09:09 toyopilgrim

See similar discussion about Mono<Void> on Gitter: https://gitter.im/spring-projects/spring-integration?at=614aef15f428f97a9f9d0077.

I think this is that part which is missed from the logic and therefore it is not described in the docs.

At a glance it does not make much sense to have a Future<Void>, but if you build an async pipe-line and just rely on the completion of the Future as a fact, it really could be handy since even void services could be called from executors. See more explanation here: https://stackoverflow.com/questions/50567120/what-is-futurevoid

artembilan avatar Sep 22 '21 14:09 artembilan

For now I schedule this for the next version since we never advertised support for Future<Void>, so this could be treated as a new feature, but we always can change our mind and with enough justification back-port the fix to the current 5.5.x generation.

artembilan avatar Sep 22 '21 14:09 artembilan

Looks like Kotlin suspend functions without return type will suffer from the same problem.

Will try to fix this shortly.

artembilan avatar Sep 30 '22 19:09 artembilan