Skip to content

WebFlux leaks buffers when server request is cancelled #36438

@kzander91

Description

@kzander91

Spring WebFlux 7.0.5

With a controller like this:

@RestController
class TestController {

    @GetMapping
    Set<String> endpoint() {
        return Set.of("SET ELEMENT");
    }

}

And this config:

spring.netty.leak-detection=paranoid
logging.level.root=debug

We occasionally get leak reports from Netty, happening when the client cancelled the request before receiving the body. We are not creating/handling DataBuffer anywhere in our application, so this appears to be an issue with the framework.
Here's the logs associated with a request and the subsequent leak report:

{"@timestamp":"2026-03-10T12:34:02.308692871Z","message":"[ec2939a0, L:/172.21.10.15:8080 - R:/172.21.10.120:41258] New http connection, requesting read","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"reactor-http-epoll-7","level":"DEBUG"}
{"@timestamp":"2026-03-10T12:34:02.308783621Z","message":"[ec2939a0, L:/172.21.10.15:8080 - R:/172.21.10.120:41258] Initialized pipeline DefaultChannelPipeline{(reactor.left.httpCodec = io.netty.handler.codec.http.HttpServerCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.server.HttpTrafficHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}","logger_name":"reactor.netty.transport.TransportConfig","thread_name":"reactor-http-epoll-7","level":"DEBUG"}
{"@timestamp":"2026-03-10T12:34:02.308986932Z","message":"[ec2939a0, L:/172.21.10.15:8080 - R:/172.21.10.120:41258] Increasing pending responses count: 1","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"reactor-http-epoll-7","level":"DEBUG"}
{"@timestamp":"2026-03-10T12:34:02.309015114Z","message":"[ec2939a0-1, L:/172.21.10.15:8080 - R:/172.21.10.120:41258] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@1398a3c4","logger_name":"reactor.netty.http.server.HttpServer","thread_name":"reactor-http-epoll-7","level":"DEBUG"}
{"@timestamp":"2026-03-10T12:34:02.309044789Z","message":"[ec2939a0-28160] HTTP GET \"/my/request/path\"","logger_name":"org.springframework.web.server.adapter.HttpWebHandlerAdapter","thread_name":"reactor-http-epoll-7","level":"DEBUG","tags":["COMMONS-LOGGING"]}
{"@timestamp":"2026-03-10T12:34:02.312145169Z","message":"[ec2939a0-28160] Mapped to my.company.ControllerName#controllerMethod()","logger_name":"org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping","thread_name":"parallel-1","level":"DEBUG","traceId":"edbef23c90b1f5d0e1eef76c6fea79d3","spanId":"621b56e814ce2053","tags":["COMMONS-LOGGING"]}
{"@timestamp":"2026-03-10T12:34:02.312448804Z","message":"[ec2939a0-28160] Using 'application/json' given [*/*] and supported [application/json]","logger_name":"org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler","thread_name":"task-13623","level":"DEBUG","traceId":"edbef23c90b1f5d0e1eef76c6fea79d3","spanId":"621b56e814ce2053","tags":["COMMONS-LOGGING"]}
{"@timestamp":"2026-03-10T12:34:02.312510228Z","message":"[ec2939a0-28160] 0..1 [java.util.Set<java.lang.String>]","logger_name":"org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler","thread_name":"task-13623","level":"DEBUG","traceId":"edbef23c90b1f5d0e1eef76c6fea79d3","spanId":"621b56e814ce2053","tags":["COMMONS-LOGGING"]}
{"@timestamp":"2026-03-10T12:34:02.312700699Z","message":"[ec2939a0-28160] Encoding [[SET ELEMENT]]","logger_name":"org.springframework.http.codec.json.JacksonJsonEncoder","thread_name":"task-13623","level":"DEBUG","traceId":"edbef23c90b1f5d0e1eef76c6fea79d3","spanId":"621b56e814ce2053","tags":["COMMONS-LOGGING"]}
{"@timestamp":"2026-03-10T12:34:02.313039046Z","message":"[ec2939a0-1, L:/172.21.10.15:8080 ! R:/172.21.10.120:41258] [HttpServer] Channel inbound receiver cancelled (channel disconnected).","logger_name":"reactor.netty.channel.ChannelOperations","thread_name":"reactor-http-epoll-7","level":"DEBUG"}
{"@timestamp":"2026-03-10T12:34:02.313089093Z","message":"[ec2939a0-28160] Completed 200 OK","logger_name":"org.springframework.web.server.adapter.HttpWebHandlerAdapter","thread_name":"task-13623","level":"DEBUG","traceId":"edbef23c90b1f5d0e1eef76c6fea79d3","spanId":"1c5dac71e20cfa03","tags":["COMMONS-LOGGING"]}
{"@timestamp":"2026-03-10T12:35:04.378519737Z","message":"LEAK: ByteBuf.release() was not called before it's garbage-collected. See https://netty.io/wiki/reference-counted-objects.html for more information.\nRecent access records: \n#1:\n\tHint: Channel id: ec2939a0-1, L:/172.21.10.15:8080 - R:/172.21.10.120:41258\n\torg.springframework.core.io.buffer.NettyDataBuffer.touch(NettyDataBuffer.java:360)\n\torg.springframework.core.io.buffer.NettyDataBuffer.touch(NettyDataBuffer.java:40)\n\torg.springframework.core.io.buffer.DataBufferUtils.touch(DataBufferUtils.java:583)\n\torg.springframework.http.server.reactive.ReactorServerHttpResponse.touchDataBuffer(ReactorServerHttpResponse.java:140)\n\torg.springframework.http.server.reactive.AbstractServerHttpResponse.lambda$writeWith$0(AbstractServerHttpResponse.java:175)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132)\n\treactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:109)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:138)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)\n\treactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:142)\n\treactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:138)\n\treactor.core.publisher.MonoFlatMap$FlatMapInner.onSubscribe(MonoFlatMap.java:292)\n\treactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:103)\n\treactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:155)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)\n\treactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:103)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:75)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)\n\treactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:75)\n\treactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2858)\n\treactor.core.publisher.MonoSingle$SingleSubscriber.onComplete(MonoSingle.java:181)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onComplete(MonoFlatMapMany.java:261)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onComplete(FluxContextWriteRestoringThreadLocals.java:150)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:351)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2566)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:361)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.request(FluxContextWriteRestoringThreadLocals.java:164)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:151)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onSubscribe(MonoFlatMapMany.java:246)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onSubscribe(FluxContextWriteRestoringThreadLocals.java:105)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:83)\n\treactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:58)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals.subscribe(FluxContextWriteRestoringThreadLocals.java:45)\n\treactor.core.publisher.Flux.subscribe(Flux.java:8888)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:197)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:114)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.request(FluxMap.java:294)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:142)\n\treactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2047)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:86)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onSubscribe(FluxMap.java:194)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:56)\n\treactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:75)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:147)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:203)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:297)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:191)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxSubscribeOnValue$ScheduledScalar.run(FluxSubscribeOnValue.java:181)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\treactor.core.scheduler.ExecutorScheduler$ExecutorPlainRunnable.run(ExecutorScheduler.java:135)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\tjava.base/java.lang.VirtualThread.run(Unknown Source)\n#2:\n\tHint: [ec2939a0-28160] \n\torg.springframework.core.io.buffer.NettyDataBuffer.touch(NettyDataBuffer.java:360)\n\torg.springframework.core.io.buffer.NettyDataBuffer.touch(NettyDataBuffer.java:40)\n\torg.springframework.core.io.buffer.DataBufferUtils.touch(DataBufferUtils.java:583)\n\torg.springframework.core.codec.Hints.touchDataBuffer(Hints.java:165)\n\torg.springframework.http.codec.EncoderHttpMessageWriter.lambda$write$1(EncoderHttpMessageWriter.java:133)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132)\n\treactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:75)\n\treactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2858)\n\treactor.core.publisher.MonoSingle$SingleSubscriber.onComplete(MonoSingle.java:181)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onComplete(MonoFlatMapMany.java:261)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onComplete(FluxContextWriteRestoringThreadLocals.java:150)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:351)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2566)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:361)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.request(FluxContextWriteRestoringThreadLocals.java:164)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:151)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onSubscribe(MonoFlatMapMany.java:246)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onSubscribe(FluxContextWriteRestoringThreadLocals.java:105)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:83)\n\treactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:58)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals.subscribe(FluxContextWriteRestoringThreadLocals.java:45)\n\treactor.core.publisher.Flux.subscribe(Flux.java:8888)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:197)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:114)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.request(FluxMap.java:294)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:142)\n\treactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2047)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:86)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onSubscribe(FluxMap.java:194)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:56)\n\treactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:75)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:147)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:203)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:297)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:191)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxSubscribeOnValue$ScheduledScalar.run(FluxSubscribeOnValue.java:181)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\treactor.core.scheduler.ExecutorScheduler$ExecutorPlainRunnable.run(ExecutorScheduler.java:135)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\tjava.base/java.lang.VirtualThread.run(Unknown Source)\n#3:\n\tHint: [ec2939a0-28160] \n\torg.springframework.core.io.buffer.NettyDataBuffer.touch(NettyDataBuffer.java:360)\n\torg.springframework.core.io.buffer.NettyDataBuffer.touch(NettyDataBuffer.java:40)\n\torg.springframework.core.io.buffer.DataBufferUtils.touch(DataBufferUtils.java:583)\n\torg.springframework.core.codec.Hints.touchDataBuffer(Hints.java:165)\n\torg.springframework.http.codec.AbstractJacksonEncoder.encodeValue(AbstractJacksonEncoder.java:246)\n\torg.springframework.http.codec.AbstractJacksonEncoder.lambda$encode$1(AbstractJacksonEncoder.java:151)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:284)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:361)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.request(FluxContextWriteRestoringThreadLocals.java:164)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:151)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onSubscribe(MonoFlatMapMany.java:246)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onSubscribe(FluxContextWriteRestoringThreadLocals.java:105)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:83)\n\treactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:58)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals.subscribe(FluxContextWriteRestoringThreadLocals.java:45)\n\treactor.core.publisher.Flux.subscribe(Flux.java:8888)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:197)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:114)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.request(FluxMap.java:294)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:142)\n\treactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2047)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:86)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onSubscribe(FluxMap.java:194)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:56)\n\treactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:75)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:147)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:203)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:297)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:191)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxSubscribeOnValue$ScheduledScalar.run(FluxSubscribeOnValue.java:181)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\treactor.core.scheduler.ExecutorScheduler$ExecutorPlainRunnable.run(ExecutorScheduler.java:135)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\tjava.base/java.lang.VirtualThread.run(Unknown Source)\n#4:\n\tio.netty.buffer.AdvancedLeakAwareByteBuf.writeBytes(AdvancedLeakAwareByteBuf.java:617)\n\torg.springframework.core.io.buffer.NettyDataBuffer.write(NettyDataBuffer.java:180)\n\torg.springframework.core.io.buffer.NettyDataBuffer.write(NettyDataBuffer.java:40)\n\torg.springframework.http.codec.AbstractJacksonEncoder.encodeValue(AbstractJacksonEncoder.java:245)\n\torg.springframework.http.codec.AbstractJacksonEncoder.lambda$encode$1(AbstractJacksonEncoder.java:151)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:284)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:361)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.request(FluxContextWriteRestoringThreadLocals.java:164)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:151)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onSubscribe(MonoFlatMapMany.java:246)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onSubscribe(FluxContextWriteRestoringThreadLocals.java:105)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:83)\n\treactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:58)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals.subscribe(FluxContextWriteRestoringThreadLocals.java:45)\n\treactor.core.publisher.Flux.subscribe(Flux.java:8888)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:197)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:114)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.request(FluxMap.java:294)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:142)\n\treactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2047)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:86)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onSubscribe(FluxMap.java:194)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:56)\n\treactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:75)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:147)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:203)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:297)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:191)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxSubscribeOnValue$ScheduledScalar.run(FluxSubscribeOnValue.java:181)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\treactor.core.scheduler.ExecutorScheduler$ExecutorPlainRunnable.run(ExecutorScheduler.java:135)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\tjava.base/java.lang.VirtualThread.run(Unknown Source)\nCreated at:\n\tio.netty.buffer.AdaptiveByteBufAllocator.newDirectBuffer(AdaptiveByteBufAllocator.java:67)\n\tio.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:168)\n\tio.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:159)\n\tio.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:96)\n\torg.springframework.core.io.buffer.NettyDataBufferFactory.allocateBuffer(NettyDataBufferFactory.java:72)\n\torg.springframework.core.io.buffer.NettyDataBufferFactory.allocateBuffer(NettyDataBufferFactory.java:39)\n\torg.springframework.http.codec.AbstractJacksonEncoder.encodeValue(AbstractJacksonEncoder.java:244)\n\torg.springframework.http.codec.AbstractJacksonEncoder.lambda$encode$1(AbstractJacksonEncoder.java:151)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:284)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:361)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.request(FluxContextWriteRestoringThreadLocals.java:164)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:151)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onSubscribe(MonoFlatMapMany.java:246)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals$ContextWriteRestoringThreadLocalsSubscriber.onSubscribe(FluxContextWriteRestoringThreadLocals.java:105)\n\treactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:83)\n\treactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:58)\n\treactor.core.publisher.FluxContextWriteRestoringThreadLocals.subscribe(FluxContextWriteRestoringThreadLocals.java:45)\n\treactor.core.publisher.Flux.subscribe(Flux.java:8888)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:197)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:114)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224)\n\treactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2564)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.request(FluxMap.java:294)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.request(FluxFilter.java:186)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)\n\treactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98)\n\treactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:142)\n\treactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2047)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)\n\treactor.core.publisher.FluxFilter$FilterSubscriber.onSubscribe(FluxFilter.java:86)\n\treactor.core.publisher.FluxMap$MapConditionalSubscriber.onSubscribe(FluxMap.java:194)\n\treactor.core.publisher.MonoJust.subscribe(MonoJust.java:56)\n\treactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:56)\n\treactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:75)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:147)\n\treactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:80)\n\treactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:203)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:297)\n\treactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:191)\n\treactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)\n\treactor.core.publisher.FluxSubscribeOnValue$ScheduledScalar.run(FluxSubscribeOnValue.java:181)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\treactor.core.scheduler.ExecutorScheduler$ExecutorPlainRunnable.run(ExecutorScheduler.java:135)\n\tio.micrometer.context.ContextSnapshot.lambda$wrap$0(ContextSnapshot.java:91)\n\tjava.base/java.lang.VirtualThread.run(Unknown Source)","logger_name":"io.netty.util.ResourceLeakDetector","thread_name":"reactor-http-epoll-8","level":"ERROR"}

The most recent place that touched the leaked buffer (see leak report above) is touchDataBuffer(buffer) here:

if (body instanceof Mono) {
return ((Mono<? extends DataBuffer>) body)
.flatMap(buffer -> {
touchDataBuffer(buffer);
AtomicBoolean subscribed = new AtomicBoolean();
return doCommit(
() -> {
try {
return writeWithInternal(Mono.fromCallable(() -> buffer)
.doOnSubscribe(s -> subscribed.set(true))
.doOnDiscard(DataBuffer.class, DataBufferUtils::release));
}
catch (Throwable ex) {
return Mono.error(ex);
}
})
.doOnError(ex -> DataBufferUtils.release(buffer))
.doOnCancel(() -> {
if (!subscribed.get()) {
DataBufferUtils.release(buffer);
}
});
})
.doOnError(t -> getHeaders().clearContentHeaders())
.doOnDiscard(DataBuffer.class, DataBufferUtils::release);

Commit 25101fb (see #26232) added the release in doOnCancel(), but only if no subscription took place 🤔 Perhaps that's the culprit?

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)status: feedback-providedFeedback has been providedstatus: waiting-for-triageAn issue we've not yet triaged or decided on

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions