Stuart,
I recently upgraded to 2.8.2 which forced the switch from quarkus-undertow-websockets to quarkus-websockets so I took some time to look at this @OnClose issue again. The issue does still exist but the underlying cause now seems apparent.
I did the upgrade and library switch, removed the OrderedExecutor workaround and set quarkus.websocket.dispatch-to-worker=true.
The @OnClose was then not called some of the time (I don't have a formal reproducer but our nightly test suite reliably triggers this).
The following appeared in our logs:
2022-04-28 02:10:19,368 ERROR [org.jboss.threads.errors] 'executor-thread-10' Thread Thread[executor-thread-10,5,main] threw an uncaught exception: java.lang.RuntimeException: java.lang.IllegalStateException: Instance already destroyed
at io.undertow.websockets.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:534)
at io.undertow.websockets.ServerWebSocketContainer$6.run(ServerWebSocketContainer.java:514)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:553)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalStateException: Instance already destroyed
at io.quarkus.arc.impl.AbstractInstanceHandle.get(AbstractInstanceHandle.java:41)
at io.quarkus.arc.runtime.BeanContainerImpl$1$1.get(BeanContainerImpl.java:40)
at io.quarkus.websockets.client.runtime.WebsocketCoreRecorder$1$1$1.getInstance(WebsocketCoreRecorder.java:136)
at io.undertow.websockets.annotated.AnnotatedEndpoint$5.run(AnnotatedEndpoint.java:225)
at io.undertow.websockets.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:143)
at io.undertow.websockets.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:140)
at io.quarkus.websockets.client.runtime.WebsocketCoreRecorder$4$1.call(WebsocketCoreRecorder.java:181)
at io.undertow.websockets.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:532)
... 8 more
I think that was during a shutdown. It doesn't appear for every missed @OnClose but executors often silently swallow these things, so it got me thinking.
Our @ServerEndpoint had the default scope so I added a @PreDestroy that effectively did the same cleanup work as the @OnClose if it hadn't already been called - problem solved. I was also able to convert it to @ApplicationScoped and remove the
@PreDestroy - problem also solved.
So it looks like a race condition between the ARC destroy of the Endpoint and the @OnClose callback.
We are proceeding with the @ApplicationScoped approach. Anyone using the default scope and requiring some cleanup will need the @PreDestroy workaround until/unless you can resolve the underlying race condition.
Regards, James,
P.S. the memory leak referred to in the original subject line does not exist as the channel is closed even if the @OnClose isn't called.