|
@eaglerainbow here's a different take on #1300, that leverages the refreshTokens concurrent hashmap as a signal for "currently refreshing a token". If you could test this code with your own scenarios, that'd be great. I've tested it against your AbstractUaaTokenProviderConcurrencyTest tests for #1300, and it passes, but unfortunately AbstractUaaTokenProviderConcurrencyTest passes on my machine with the code that is in 5.14 (no changes), so it does not provide extra confidence. I've spent some time with a Project Reactor contributor discussing #1146 . Note that this is NOT perfect, but achieving perfect here would require a significant rework of the entire Operator architecture. |
Sorry, something went wrong.
|
Affirm. |
Sorry, something went wrong.
|
@eaglerainbow please take your time, it took me over a month to get to this issue 😅 |
Sorry, something went wrong.
|
@eaglerainbow please take your time, it took me over a month to get to this issue 😅 I just built a first new version of the main project where cf-java-client gets used. Having built a second version that does no longer contain my workaround, I observe even slightly higher CPU loads (all other environmental things are exactly the same). #1146 will not surface right away in my environment. I keep the installed second version running for now. It will take at least a week of runtime until we can be confident enough that we have caught the bug. @Kehrlann I keep you posted on the results. |
Sorry, something went wrong.
|
If I understand correctly:
Could you please take a look at the CPU consumption with a build using 5.14, but not our fixes? |
Sorry, something went wrong.
|
That's correct - at least this was my initial impression. For clarity: Item 1 as of your list was with a cf-java-client:5.12, with my fix, without your fix here. Could you please take a look at the CPU consumption with a build using 5.14, but not our fixes? Will do. I will only do that towards the end of this week. Background is that I don't want to interrupt the ongoing experiment by stopping the application (and for comparable values, I would have to do). Intermediate report: No major outage yet. 2025-10-27T12:11:46.060Z ERROR 9 --- [-client-epoll-3] cloudfoundry-client.token : Refresh token grant error.
org.cloudfoundry.uaa.UaaException: invalid_token: The token expired, was revoked, or the token ID is incorrect.
at org.cloudfoundry.reactor.util.ErrorPayloadMappers.lambda$uaa$8(ErrorPayloadMappers.java:104) ~[cloudfoundry-client-reactor-5.15.0.BUILD-fix-1146.jar!/:na]
at org.cloudfoundry.reactor.util.ErrorPayloadMappers.lambda$mapToError$11(ErrorPayloadMappers.java:140) ~[cloudfoundry-client-reactor-5.15.0.BUILD-fix-1146.jar!/:na]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:129) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:224) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onNext(FluxDoFinally.java:113) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onNext(FluxHandleFuseable.java:194) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2097) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:118) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onComplete(FluxDoFinally.java:128) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.6.15.jar!/:3.6.15]
at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:413) ~[reactor-netty-core-1.1.28.jar!/:1.1.28]
at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:455) ~[reactor-netty-core-1.1.28.jar!/:1.1.28]
at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:509) ~[reactor-netty-core-1.1.28.jar!/:1.1.28]
at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:815) ~[reactor-netty-http-1.1.28.jar!/:1.1.28]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:115) ~[reactor-netty-core-1.1.28.jar!/:1.1.28]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:107) ~[netty-codec-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280) ~[netty-handler-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1519) ~[netty-handler-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1377) ~[netty-handler-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1428) ~[netty-handler-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530) ~[netty-codec-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:469) ~[netty-codec-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) ~[netty-codec-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868) ~[netty-transport-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:799) ~[netty-transport-classes-epoll-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501) ~[netty-transport-classes-epoll-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399) ~[netty-transport-classes-epoll-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998) ~[netty-common-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.119.Final.jar!/:4.1.119.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.119.Final.jar!/:4.1.119.Final]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
It first appeared a little after 24hrs of the start - and the next instance was another 24hrs later (which is the one I have copied here). |
Sorry, something went wrong.
|
thanks for the detailed report! I’m surprised at the CPU usage, as the only change I’ve added triggers when we invalidate a token, so, worst case scenario, it should only impact the app every X hours on token refresh. I’ll think about it. This log error is probably because the refresh token does not rotate in the UAA, and expires after 1 day. |
Sorry, something went wrong.
|
@Kehrlann I keep you posted on the results. It's been a whole week since the installation. I did not have a single hanging of the test-promregator during that time frame. That makes me confident that this PR here really caught the issue. I’m surprised at the CPU usage, as the only change I’ve added triggers when we invalidate a token, so, worst case scenario, it should only impact the app every X hours on token refresh. I’ll think about it. If you started, don't continue on this. The yellow bar splits the values at the point in time when I installed the test version (i.e. to the left, it's the "old version", to the right it's the version under test). What I might have seen before is within the block of the blue box in the diagram above. Zooming in there, you observe quite a series of unusual peaks (note that the way of calculating the diagram implies a certain "moving average effect", so individual readings may even be significant higher). In my scenario these peaks could have all kind of different root causes. I would not bet a dollar to a doughnut that this even originates from cf-java-client. Instead I could imagine dozens of other effects that are more likely to produce this (in particular as it was "right after" starting the application). This log error is probably because the refresh token does not rotate in the UAA, and expires after 1 day. Yep, sounds like that. As you can auto-recover from there using the Password Grant again, the error message in the log is a little ugly, but essentially harmless. Given all this, I suggest to merge this PR. I am looking forward to a new release so that I also can officially bring this into promregator so that all its users can benefit from it. Thanks for your support! |
Sorry, something went wrong.
|
If merged, this will closes #1300 |
Sorry, something went wrong.
|
Thank you very much for running a real, live experiment; and for thoroughly reporting the results and observations 🙇 Fixing some pipelines issues on my side, and then I'll merge this. I have a couple of PR to get merged in as well before I cut a release, but it shouldn't take too long. |
Sorry, something went wrong.
|
before I cut a release, but it shouldn't take too long. Take your time. The issue has already celebrated its third anniversary, so a couple of days or weeks more or less doesn't make a big difference anymore 😝 |
Sorry, something went wrong.
Description
Fix for #1146. Tested with the "load test" application below ; using logging.level.cloudfoundry-client.token=DEBUG to observe which tokens are being requested.
Details in the commit message.
Commit message
Appendix: load test app
This is not optimal and does too many things, but it showcases the bug. It does the following:
Run with logging.level.cloudfoundry-client.token=DEBUG to observe when tokens are requested.