InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
9.08k stars 719 forks source link

ktor plugin ocassionally throws ScopeAlreadyCreatedException when trying to initialize RequestScope #1902

Closed raphael-inglin-ergon closed 2 months ago

raphael-inglin-ergon commented 4 months ago

Describe the bug Every few days we see exceptions in the logs of our productive system when trying to handling a request, resulting in the request failing.

First, koin fails to initialize the RequestScope:

org.koin.core.error.ScopeAlreadyCreatedException: Scope with id 'org.koin.ktor.plugin.RequestScope@1857368319' is already created
    at org.koin.core.registry.ScopeRegistry.createScope(ScopeRegistry.kt:64)
    at org.koin.core.Koin.createScope(Koin.kt:184)
    at org.koin.core.component.KoinScopeComponentKt.createScope(KoinScopeComponent.kt:45)
    at org.koin.core.component.KoinScopeComponentKt.createScope$default(KoinScopeComponent.kt:44)
    at org.koin.ktor.plugin.RequestScope.<init>(RequestScope.kt:29)
    at org.koin.ktor.plugin.KoinPluginKt$setupKoinScope$1.invokeSuspend(KoinPlugin.kt:69)
    at org.koin.ktor.plugin.KoinPluginKt$setupKoinScope$1.invoke(KoinPlugin.kt)
    at org.koin.ktor.plugin.KoinPluginKt$setupKoinScope$1.invoke(KoinPlugin.kt)
    at io.ktor.server.application.hooks.CallSetup$install$1.invokeSuspend(CommonHooks.kt:26)
        ...

Then when trying to finish the request, probably as a result of failing to create the scope initially:

java.lang.IllegalStateException: No instance for key AttributeKey: KOIN_SCOPE
    at io.ktor.util.Attributes$DefaultImpls.get(Attributes.kt:62)
    at io.ktor.util.AttributesJvmBase.get(AttributesJvm.kt:15)
    at org.koin.ktor.plugin.KoinPluginKt$setupKoinScope$2.invoke(KoinPlugin.kt:73)
    at org.koin.ktor.plugin.KoinPluginKt$setupKoinScope$2.invoke(KoinPlugin.kt:72)
    at io.ktor.server.application.hooks.ResponseSent$install$1.invokeSuspend(CommonHooks.kt:112)
        ...

It seems that there is a collision in the scopeId, that is automatically generated in KoinScopeComponent:

fun <T : Any> T.getScopeId() = this::class.getFullName() + "@" + this.hashCode()

To Reproduce We were unable to reproduce the issue. We see the same pattern occur every few days in the logs of our production system, randomly causing a request to fail.

Expected behavior

Koin module and version:

Snippet or Sample project to help reproduce See above

Gosunet commented 3 months ago

Hello,

We have the same version of koin: 3.5.6, and we got the same random problem in production, which also caused some requests to fail.

arnaudgiuliani commented 3 months ago

getScopeId() is useful in android, but clashes here with Ktor. Can you ensure there is a clash with the scope id?

raphael-inglin-ergon commented 2 months ago

HI. As previously mentioned, we cannot reproduce the issue. However the stack trace does clearly indicate some kind of clash.

The exception is thrown in the createScopefunction of ScopeRegistry if there is already a Scope with this Id present. Since the RequestScope and its scopeId are automatically created by Koin when a request happens, this indicates that a new RequestScope just happens to have the same scopeId / hashCode as an already active Scope.

The scopeId is generated in KoinScopeComponent

It might also be that old Scopes are not removed correctly in some situations, increasing the likelihood of a collision but this is just speculation from my part.

Generally, hashCode() does not require two Objects to have different results, so it is plausible that collisions may occur.

arnaudgiuliani commented 2 months ago

would mean that hashcode is too close then? 🤔

arnaudgiuliani commented 2 months ago

I've added UUID scope id to allow wider generation. Should help 👍 See PR https://github.com/InsertKoinIO/koin/pull/1976