quarkus: Quarkus cache does not work with kotlin suspend functions

Describe the bug

Quarkus-cache does not work correctly with kotlin suspend functions. The following code snippet

@ApplicationScoped
class StationAmbassador(
    @RestClient
    private val stationConfigClient: StationConfigClient
) {

    suspend fun stationConfig(stationId: String): StationConfig? {
        return stationConfig()[stationId]
    }

    @CacheResult(cacheName = "station-config")
    suspend fun stationConfig(): Map<String, StationConfig> {
        val snapshot = stationConfigClient.loadStationConfig()

        Log.infof(
            "Fetched {} stations (hd: {}, sd: {}, newTv: {}) from product-configuration.",
            snapshot.stationsCount,
            snapshot.hdStationsCount,
            snapshot.sdStationsCount,
            snapshot.newTvStationsCount
        )
        return snapshot.stations.associateBy { it.id }
    }
}

results in

2022-02-15 19:50:11,168 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-3) HTTP Request to /api/station-config failed, error id: 2e427033-ad2e-4129-9391-a8fd872be97f-1: java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-3
	at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:30)
	at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
	at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
	at io.quarkus.cache.runtime.CacheResultInterceptor.intercept(CacheResultInterceptor.java:115)
	at io.quarkus.cache.runtime.CacheResultInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at stationconfig.StationAmbassador_Subclass.stationConfig(Unknown Source)
	at stationconfig.StationAmbassador.stationConfig$suspendImpl(StationAmbassador.kt:17)
	at stationconfig.StationAmbassador.stationConfig(StationAmbassador.kt)
	at stationconfig.StationAmbassador_Subclass.stationConfig$$superforward1(Unknown Source)
	at stationconfig.StationAmbassador_Subclass$$function$$1.apply(Unknown Source)

We can workaround this behaviour by returning a CompletionStage from the rest client and work with unis instead.

Expected behavior

Quarkus-cache should work with suspend functions as well.

Actual behavior

2022-02-15 19:50:11,168 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-3) HTTP Request to /api/station-config failed, error id: 2e427033-ad2e-4129-9391-a8fd872be97f-1: java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-3
	at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:30)
	at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
	at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
	at io.quarkus.cache.runtime.CacheResultInterceptor.intercept(CacheResultInterceptor.java:115)
	at io.quarkus.cache.runtime.CacheResultInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at stationconfig.StationAmbassador_Subclass.stationConfig(Unknown Source)
	at stationconfig.StationAmbassador.stationConfig$suspendImpl(StationAmbassador.kt:17)
	at stationconfig.StationAmbassador.stationConfig(StationAmbassador.kt)
	at stationconfig.StationAmbassador_Subclass.stationConfig$$superforward1(Unknown Source)
	at stationconfig.StationAmbassador_Subclass$$function$$1.apply(Unknown Source)

How to Reproduce?

No response

Output of uname -a or ver

Darwin MB-07-P15.fritz.box 19.6.0 Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64 x86_64

Output of java -version

openjdk version “11.0.13” 2021-10-19 OpenJDK Runtime Environment GraalVM CE 20.3.4 (build 11.0.13+7-jvmci-20.3-b24) OpenJDK 64-Bit Server VM GraalVM CE 20.3.4 (build 11.0.13+7-jvmci-20.3-b24, mixed mode, sharing)

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.7.0.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Gradle 7.3.3

Additional information

No response

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 10
  • Comments: 21 (14 by maintainers)

Most upvoted comments

Is someone working on this? If not, I can pick it up

Any progress on this?

I can help with that @evanchooly.

A possible workaround for this issue is to wrap the cached method and to use the kotlin-mutiny lib:

@ApplicationScoped
class StationAmbassador(
    @RestClient
    private val stationConfigClient: StationConfigClient
) {

    suspend fun stationConfig(stationId: String): StationConfig? {
        return stationConfig().awaitSuspending()[stationId]
    }

    @CacheResult(cacheName = "station-config")
    fun stationConfig(): Uni<Map<String, StationConfig>> {
        return stationConfigClient
            .loadStationConfig()
            .onItem()
            .transform { snapshot ->
                Log.infof(
                    "Fetched {} stations (hd: {}, sd: {}, newTv: {}) from product-configuration.",
                    snapshot.stationsCount,
                    snapshot.hdStationsCount,
                    snapshot.sdStationsCount,
                    snapshot.newTvStationsCount
                )
                snapshot.stations.associateBy { it.id }
            }
    }
}