diff --git a/src/main/kotlin/org/rooftop/netx/engine/OrchestrateChain.kt b/src/main/kotlin/org/rooftop/netx/engine/OrchestrateChain.kt index ba243ba..1bb376f 100644 --- a/src/main/kotlin/org/rooftop/netx/engine/OrchestrateChain.kt +++ b/src/main/kotlin/org/rooftop/netx/engine/OrchestrateChain.kt @@ -84,25 +84,27 @@ class OrchestrateChain private constructor( val nextCommitOrchestrateListener = getCommitOrchestrateListener(function) val nextRollbackOrchestrateListener = getRollbackOrchestrateListener(rollback) - val nextOrchestrateChain = OrchestrateChain( - orchestratorId, - orchestrateSequence + 1, - chainContainer, - nextCommitOrchestrateListener, - nextRollbackOrchestrateListener, - this, - ) - this.nextOrchestrateChain = nextOrchestrateChain - val firstOrchestrateChain = nextOrchestrateChain.initOrchestrateListeners() + return OrchestratorCache.cache(orchestratorId) { + val nextOrchestrateChain = OrchestrateChain( + orchestratorId, + orchestrateSequence + 1, + chainContainer, + nextCommitOrchestrateListener, + nextRollbackOrchestrateListener, + this, + ) + this.nextOrchestrateChain = nextOrchestrateChain + val firstOrchestrateChain = nextOrchestrateChain.initOrchestrateListeners() - return OrchestratorManager( - transactionManager = chainContainer.transactionManager, - codec = chainContainer.codec, - orchestratorId = orchestratorId, - resultHolder = chainContainer.resultHolder, - orchestrateListener = firstOrchestrateChain.orchestrateListener, - rollbackOrchestrateListener = firstOrchestrateChain.rollbackOrchestrateListener, - ) + return@cache OrchestratorManager( + transactionManager = chainContainer.transactionManager, + codec = chainContainer.codec, + orchestratorId = orchestratorId, + resultHolder = chainContainer.resultHolder, + orchestrateListener = firstOrchestrateChain.orchestrateListener, + rollbackOrchestrateListener = firstOrchestrateChain.rollbackOrchestrateListener, + ) + } } private fun getCommitOrchestrateListener(function: OrchestrateFunction) = @@ -136,26 +138,28 @@ class OrchestrateChain private constructor( val nextJoinOrchestrateListener = getMonoCommitOrchestrateListener(function) val nextRollbackOrchestrateListener = getMonoRollbackOrchestrateListener(rollback) - val nextOrchestrateChain = OrchestrateChain( - orchestratorId, - orchestrateSequence + 1, - chainContainer, - nextJoinOrchestrateListener, - nextRollbackOrchestrateListener, - this, - ) - this.nextOrchestrateChain = nextOrchestrateChain + return OrchestratorCache.cache(orchestratorId) { + val nextOrchestrateChain = OrchestrateChain( + orchestratorId, + orchestrateSequence + 1, + chainContainer, + nextJoinOrchestrateListener, + nextRollbackOrchestrateListener, + this, + ) + this.nextOrchestrateChain = nextOrchestrateChain - val firstOrchestrateChain = nextOrchestrateChain.initOrchestrateListeners() + val firstOrchestrateChain = nextOrchestrateChain.initOrchestrateListeners() - return OrchestratorManager( - transactionManager = chainContainer.transactionManager, - codec = chainContainer.codec, - orchestratorId = orchestratorId, - resultHolder = chainContainer.resultHolder, - orchestrateListener = firstOrchestrateChain.orchestrateListener, - rollbackOrchestrateListener = firstOrchestrateChain.rollbackOrchestrateListener, - ) + return@cache OrchestratorManager( + transactionManager = chainContainer.transactionManager, + codec = chainContainer.codec, + orchestratorId = orchestratorId, + resultHolder = chainContainer.resultHolder, + orchestrateListener = firstOrchestrateChain.orchestrateListener, + rollbackOrchestrateListener = firstOrchestrateChain.rollbackOrchestrateListener, + ) + } } @Suppress("UNCHECKED_CAST") diff --git a/src/main/kotlin/org/rooftop/netx/engine/OrchestratorCache.kt b/src/main/kotlin/org/rooftop/netx/engine/OrchestratorCache.kt new file mode 100644 index 0000000..eca5990 --- /dev/null +++ b/src/main/kotlin/org/rooftop/netx/engine/OrchestratorCache.kt @@ -0,0 +1,25 @@ +package org.rooftop.netx.engine + +import org.rooftop.netx.api.Orchestrator + +internal object OrchestratorCache { + + private val cache: MutableMap> = mutableMapOf() + + @Suppress("UNCHECKED_CAST") + internal fun cache( + key: String, + behavior: () -> Orchestrator<*, *>, + ): Orchestrator { + if (cache.contains(key)) { + return cache[key] as Orchestrator + } + synchronized(key) { + if (cache.contains(key)) { + return cache[key] as Orchestrator + } + cache[key] = behavior.invoke() + return cache[key] as Orchestrator + } + } +} diff --git a/src/test/kotlin/org/rooftop/netx/engine/OrchestratorCacheTest.kt b/src/test/kotlin/org/rooftop/netx/engine/OrchestratorCacheTest.kt new file mode 100644 index 0000000..1a932e6 --- /dev/null +++ b/src/test/kotlin/org/rooftop/netx/engine/OrchestratorCacheTest.kt @@ -0,0 +1,54 @@ +package org.rooftop.netx.engine + +import io.kotest.core.annotation.DisplayName +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.equals.shouldBeEqual +import io.kotest.matchers.equals.shouldNotBeEqual +import org.rooftop.netx.api.Orchestrator +import org.rooftop.netx.factory.OrchestratorFactory +import org.rooftop.netx.meta.EnableDistributedTransaction +import org.rooftop.netx.redis.RedisContainer +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.TestPropertySource + +@EnableDistributedTransaction +@DisplayName("OrchestratorCache 클래스의") +@ContextConfiguration(classes = [RedisContainer::class]) +@TestPropertySource("classpath:application.properties") +internal class OrchestratorCacheTest( + private val orchestratorFactory: OrchestratorFactory, +) : DescribeSpec({ + + describe("cache 메소드는") { + context("같은 orchestratorId를 가진 Orchestrator 를 생성하려하면,") { + val expected = orchestratorFactory.createIntOrchestrator("same") + + it("동일한 Orchestrator 를 반환한다.") { + val sameOrchestrator = orchestratorFactory.createIntOrchestrator("same") + + expected shouldBeEqual sameOrchestrator + } + } + + context("다른 orchestratorId를 가진 Orchestrator 를 생성하려하면,") { + val expected = orchestratorFactory.createIntOrchestrator("same") + + it("동일하지 않은 Orchestrator 를 반환한다.") { + val notSameOrchestrator = orchestratorFactory.createIntOrchestrator("notSame") + + expected shouldNotBeEqual notSameOrchestrator + } + } + } + +}) { + + companion object { + + private fun OrchestratorFactory.createIntOrchestrator(orchestratorId: String): Orchestrator { + return this.create(orchestratorId) + .start({ it + 1 }) + .commit({ it + 1 }) + } + } +}