Use of StorageManagers inside the EventListenerProvider takes exactly 7 attempts after keycloak startup to finally not throw an SQLException and succeed!

Running this code snippet below which implements an EventListenerProvider and attempts to read realm, user, and group info inside an event, causes java.sql.SQLException: ResultSet is closed when an admin attempts to update a user. After the 7th attempt, it works perfectly until the server is restarted. The only difference between this code and my actual running instance is that I read the user id used in this code from the admin event’s included representation as well as the actual database backend being a postgres instance to store the users.

Update:
I can swear the number of failures has something to do with the number of groups the user is assigned to. at the time of this writing the user was assigned to 6 groups. I confirmed that reducing the number of user’s assigned groups to 4 caused the number of necessary attempts to drop to 5!

Any guesses what the Issue is, or better yet, how I can modify this code to fix the issue:

package ca.example.keycloak.provider

import org.keycloak.models.KeycloakSession
import org.keycloak.events.EventListenerProvider
import org.keycloak.events.Event
import org.keycloak.events.admin.AdminEvent
import org.keycloak.models.AbstractKeycloakTransaction
import scala.jdk.CollectionConverters.*

class MyEventListenerProvider(session: KeycloakSession)
    extends EventListenerProvider {

  override def onEvent(event: Event) = ()

  override def onEvent(
      adminEvent: AdminEvent,
      includeRepresentation: Boolean
  ) =
    session.getTransactionManager().enlist(new AbstractKeycloakTransaction {
      protected def commitImpl(): Unit = {
        val realm = session.realms().getRealm(adminEvent.getRealmId())
        println(s"realm -> ${realm.getId()}")
        val user = session.userStorageManager().getUserById(realm, "03002dd3-cfe8-4e35-8dc9-7fb8af0e4301")
        println(s"user -> ${user.getId()}")
        val groups = user.getGroups().asScala.map(_.getName()).toSeq
        println(s"groups -> ${groups}")
      }

      protected def rollbackImpl(): Unit = ()
    })

  override def close() = {
  }
}

Here’s is the Stacktrace:

Could not update user!: org.hibernate.exception.GenericJDBCException: could not advance using next()
        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
        at org.hibernate.internal.ScrollableResultsImpl.convert(ScrollableResultsImpl.java:71)
        at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:106)
        at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33)
        at java.base/java.util.Iterator.forEachRemaining(Iterator.java:132)
        at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at org.hibernate.query.spi.StreamDecorator.collect(StreamDecorator.java:209)
        at org.keycloak.utils.ClosingStream.collect(ClosingStream.java:182)
        at org.keycloak.models.UserModel$Streams.getGroups(UserModel.java:337)
        at ca.growwithtreehouse.keycloak.provider.XylemEventListenerProvider$$anon$1.commitImpl(XylemEventListenerProvider.scala:25)
        at org.keycloak.models.AbstractKeycloakTransaction.commit(AbstractKeycloakTransaction.java:48)
        at org.keycloak.services.DefaultKeycloakTransactionManager.commit(DefaultKeycloakTransactionManager.java:136)
        at org.keycloak.services.resources.admin.UserResource.updateUser(UserResource.java:194)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:152)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:183)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:152)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:183)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:152)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:183)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:380)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:358)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:71)
        at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
        at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
        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(Thread.java:829)
Caused by: java.sql.SQLException: ResultSet is closed
        at io.agroal.pool.wrapper.ResultSetWrapper$1.invoke(ResultSetWrapper.java:52)
        at com.sun.proxy.$Proxy85.next(Unknown Source)
        at io.agroal.pool.wrapper.ResultSetWrapper.next(ResultSetWrapper.java:88)
        at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:101)
        ... 70 more