Problem: @ReadOperation on a custom actuator endpoint returning Flux cancels the request after first element, the same code wrapped as a @GetMapping of a @RestController returns full response as expected. The workaround is to return Mono but its a bit weird.

Spring Boot Version 2.6.4 created via initializr with Webflux and Actuator

demo.zip

Code: Actuator Endpoint

@Endpoint(id = "demo")
@Component
public class DemoEndpoint {
    @ReadOperation
    public Flux<String> test() {
        return Flux.range(0, 10).map(String::valueOf).log();
    }
}

Demonstration Controller:

@RestController
public class DemoRestController {
    @GetMapping
    public Flux<String> test(){
        return Flux.range(0,10).map(String::valueOf).log();
    }
}

See sample project attached.

curl localhost:8080 -> 0123456789 curl localhost:8080/actuator/demo -> 0

Expected behavior: 2nd call delivers 0123456789

Log:

2022-03-07 14:35:55.185  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onSubscribe([Fuseable] FluxMapFuseable.MapFuseableSubscriber)
2022-03-07 14:35:55.186  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | request(1)
2022-03-07 14:35:55.187  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(0)
2022-03-07 14:35:55.201  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | request(127)
2022-03-07 14:35:55.201  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(1)
2022-03-07 14:35:55.201  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(2)
2022-03-07 14:35:55.202  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(3)
2022-03-07 14:35:55.202  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(4)
2022-03-07 14:35:55.202  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(5)
2022-03-07 14:35:55.202  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(6)
2022-03-07 14:35:55.203  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(7)
2022-03-07 14:35:55.203  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(8)
2022-03-07 14:35:55.203  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onNext(9)
2022-03-07 14:35:55.203  INFO 42052 --- [ctor-http-nio-2] reactor.Flux.MapFuseable.1               : | onComplete()
2022-03-07 14:36:45.169  INFO 42052 --- [ctor-http-nio-3] reactor.Flux.MapFuseable.2               : | onSubscribe([Fuseable] FluxMapFuseable.MapFuseableSubscriber)
2022-03-07 14:36:45.169  INFO 42052 --- [ctor-http-nio-3] reactor.Flux.MapFuseable.2               : | request(unbounded)
2022-03-07 14:36:45.169  INFO 42052 --- [ctor-http-nio-3] reactor.Flux.MapFuseable.2               : | onNext(0)
2022-03-07 14:36:45.169  INFO 42052 --- [ctor-http-nio-3] reactor.Flux.MapFuseable.2               : | cancel()

Comment From: wilkinsona

I spent a bit of time looking at this following the discussion on Gitter. Only a single element is returned due to this code:

https://github.com/spring-projects/spring-boot/blob/5960d2dba153e2550359ccb0551e10c34c0ad393/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java#L365

Mono.from(Publisher) is documented as follows:

Expose the specified Publisher with the Mono API, and ensure it will emit 0 or 1 item. The source emitter will be cancelled on the first onNext.

There also appears to be problems with returning Flux when using Spring MVC and Jersey as well.

I can't remember if it was a conscious decision to only support Mono or if it's an oversight. If it was intentional, we should improve the documentation as we don't seem to say anything about supported return types at the moment. If it was an oversight, we'll have to decide if we want to support returning a Flux and, if we do, if we consider adding support to be a bug fix or an enhancement.