InsertKoinIO / koin

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

How to share singletons between isolated Koin contexts in Ktor? #1783

Closed volkert-fastned closed 7 months ago

volkert-fastned commented 7 months ago

@arnaudgiuliani What if I want to continue sharing the same Koin context between multiple embeddedServer instances in the same Ktor application, when using Koin version 3.5.3 and onwards? I don't want separate singleton instances in different contexts, since that would be a waste of resources. Thanks.

Originally posted by @volkert-fastned in https://github.com/InsertKoinIO/koin/issues/1738#issuecomment-1939294193

volkert-fastned commented 7 months ago

Answering my own question at https://github.com/InsertKoinIO/koin/issues/1738#issuecomment-1939301262 below:

@arnaudgiuliani Oh wait. If I pass the same Koin module with singletons to different isolated Koin contexts, will those separate Koin contexts share the same singleton instances?

I just performed a test and apparently my worry was unwarranted: when the same module is supplied to different isolated Koin contexts, the singletons will be shared between them, i.e. they will still be singletons.

I tested something similar to the following code to confirm that no duplicate singletons are instantiated, even among multiple KoinIsolated contexts:

import mu.KotlinLogging

private var instanceCounter = 0

class SingletonTestService {

    private val log = KotlinLogging.logger { }

    init {
        instanceCounter++
        log.error { "=>\n\n*** Instantiated instance $instanceCounter of ${this::class.simpleName}. ***\n\n" }
        check(instanceCounter <= 1) {
            "There should never be more than one instance of ${this::class.simpleName}, but there are now " +
                    "$instanceCounter of them"
        }
    }

    fun getNumberOfInstances(): Int {
        val currentNumberOfInstances = instanceCounter
        return currentNumberOfInstances
    }
}

val commonModule = module {
    single { SingletonTestService() }
}

embeddedServer(Netty, port = 8080) {

    install(KoinIsolated) {
        SLF4JLogger()
        modules(commonModule)
    }

    val singletonTestService: SingletonTestService by inject()
    log.warn {
        "=>\n\n*** There are currently ${singletonTestService.getNumberOfInstances()} instances of " +
                "${SingletonTestService::class.simpleName}. ***\n\n"
    }

    routing {
        get("/") {
            call.respondText("Hello, world, from server 1!")
        }
    }
}.start(wait = false)

embeddedServer(Netty, port = 8081) {

    install(KoinIsolated) {
        SLF4JLogger()
        modules(commonModule)
    }

    val singletonTestService: SingletonTestService by inject()
    log.warn {
        "=>\n\n*** There are currently ${singletonTestService.getNumberOfInstances()} instances of " +
                "${SingletonTestService::class.simpleName}. ***\n\n"
    }

    routing {
        get("/") {
            call.respondText("Hello, world, from server 2!")
        }
    }
}.start(wait = true)