diff --git a/README.md b/README.md
index 69a61ac..6fd94cc 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-  
+  

**TPS(6,000)** on my Macbook air m2(default options). _[link](#Test1-TPS)_
diff --git a/gradle/test.gradle b/gradle/test.gradle
index c07246f..7c910c7 100644
--- a/gradle/test.gradle
+++ b/gradle/test.gradle
@@ -14,6 +14,7 @@ dependencies {
testImplementation "org.awaitility:awaitility:${awaitilityVersion}"
testImplementation "io.jsonwebtoken:jjwt-api:0.12.5"
+ testImplementation "org.springframework.boot:spring-boot-starter-web"
runtimeOnly "io.jsonwebtoken:jjwt-impl:0.12.5"
runtimeOnly "io.jsonwebtoken:jjwt-jackson:0.12.5"
}
diff --git a/src/main/kotlin/org/rooftop/netx/api/Exceptions.kt b/src/main/kotlin/org/rooftop/netx/api/Exceptions.kt
index 4e6b9c3..c29cf7a 100644
--- a/src/main/kotlin/org/rooftop/netx/api/Exceptions.kt
+++ b/src/main/kotlin/org/rooftop/netx/api/Exceptions.kt
@@ -1,5 +1,7 @@
package org.rooftop.netx.api
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties
+
class EncodeException(message: String, throwable: Throwable) : RuntimeException(message, throwable)
class DecodeException(message: String, throwable: Throwable) : RuntimeException(message, throwable)
@@ -16,4 +18,5 @@ class FailedAckSagaException(message: String) : RuntimeException(message)
class ResultTimeoutException(message: String, throwable: Throwable) :
RuntimeException(message, throwable)
+@JsonIgnoreProperties(ignoreUnknown = true)
class ResultException(message: String) : RuntimeException(message)
diff --git a/src/main/kotlin/org/rooftop/netx/redis/RedisResultHolder.kt b/src/main/kotlin/org/rooftop/netx/redis/RedisResultHolder.kt
index 66c299b..aa0a248 100644
--- a/src/main/kotlin/org/rooftop/netx/redis/RedisResultHolder.kt
+++ b/src/main/kotlin/org/rooftop/netx/redis/RedisResultHolder.kt
@@ -2,9 +2,10 @@ package org.rooftop.netx.redis
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
-import org.rooftop.netx.core.Codec
import org.rooftop.netx.api.Result
+import org.rooftop.netx.api.ResultException
import org.rooftop.netx.api.ResultTimeoutException
+import org.rooftop.netx.core.Codec
import org.rooftop.netx.engine.ResultHolder
import org.rooftop.netx.engine.logging.info
import org.springframework.data.redis.core.ReactiveRedisTemplate
@@ -73,10 +74,20 @@ internal class RedisResultHolder(
}
override fun setFailResult(id: String, result: T): Mono {
- val error = Error(
- objectMapper.writeValueAsString(result::class.java),
- objectMapper.writeValueAsString(result)
- )
+ val error = runCatching {
+ Error(
+ type = objectMapper.writeValueAsString(result::class.java),
+ error = objectMapper.writeValueAsString(result),
+ )
+ }.getOrElse {
+ Error(
+ type = objectMapper.writeValueAsString(ResultException::class.java),
+ error = objectMapper.writeValueAsString(
+ ResultException("Cannot encode fail result to json cause \"${it.message}\"")
+ ),
+ )
+ }
+
val encodedError = objectMapper.writeValueAsString(error)
return reactiveRedisTemplate.opsForList()
.leftPush(
diff --git a/src/main/kotlin/org/rooftop/netx/redis/RedisSagaConfigurer.kt b/src/main/kotlin/org/rooftop/netx/redis/RedisSagaConfigurer.kt
index c8e8903..02c4544 100644
--- a/src/main/kotlin/org/rooftop/netx/redis/RedisSagaConfigurer.kt
+++ b/src/main/kotlin/org/rooftop/netx/redis/RedisSagaConfigurer.kt
@@ -1,9 +1,6 @@
package org.rooftop.netx.redis
-import com.fasterxml.jackson.annotation.JsonAutoDetect
-import com.fasterxml.jackson.annotation.JsonCreator
-import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.PropertyAccessor
+import com.fasterxml.jackson.annotation.*
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
diff --git a/src/test/kotlin/org/rooftop/netx/engine/OrchestratorConfigurer.kt b/src/test/kotlin/org/rooftop/netx/engine/OrchestratorConfigurer.kt
index 8e73487..2d8ed38 100644
--- a/src/test/kotlin/org/rooftop/netx/engine/OrchestratorConfigurer.kt
+++ b/src/test/kotlin/org/rooftop/netx/engine/OrchestratorConfigurer.kt
@@ -10,6 +10,8 @@ import org.rooftop.netx.engine.OrchestratorTest.Companion.rollbackOrchestratorRe
import org.rooftop.netx.engine.OrchestratorTest.Companion.upChainResult
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
+import org.springframework.http.HttpStatus
+import org.springframework.web.client.HttpClientErrorException
import reactor.core.publisher.Mono
import java.time.Instant
@@ -317,6 +319,16 @@ internal class OrchestratorConfigurer(
.commit { "" }
}
+ @Bean(name = ["throwHttpClientErrorExceptionOnStartOrchestrator"])
+ fun throwHttpClientErrorExceptionOnStartOrchestrator(): Orchestrator {
+ return OrchestratorFactory.instance()
+ .create("throwHttpClientErrorExceptionOnStartOrchestrator")
+ .startWithContext({ _, _ ->
+ throw HttpClientErrorException(HttpStatus.UNAUTHORIZED)
+ })
+ .commitWithContext({ _, _ -> "" })
+ }
+
fun interface ListOrchestrate :
ContextOrchestrate, List> {
diff --git a/src/test/kotlin/org/rooftop/netx/engine/OrchestratorTest.kt b/src/test/kotlin/org/rooftop/netx/engine/OrchestratorTest.kt
index 8d922d3..a0faa90 100644
--- a/src/test/kotlin/org/rooftop/netx/engine/OrchestratorTest.kt
+++ b/src/test/kotlin/org/rooftop/netx/engine/OrchestratorTest.kt
@@ -2,18 +2,21 @@ package org.rooftop.netx.engine
import io.jsonwebtoken.JwtException
import io.kotest.assertions.nondeterministic.eventually
+import io.kotest.assertions.throwables.shouldThrowExactly
import io.kotest.assertions.throwables.shouldThrowWithMessage
import io.kotest.core.annotation.DisplayName
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.equality.shouldBeEqualToComparingFields
import io.kotest.matchers.equals.shouldBeEqual
import org.rooftop.netx.api.Orchestrator
+import org.rooftop.netx.api.ResultException
import org.rooftop.netx.api.TypeReference
import org.rooftop.netx.meta.EnableSaga
import org.rooftop.netx.redis.RedisContainer
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.TestPropertySource
+import org.springframework.web.client.HttpClientErrorException
import java.time.Instant
import kotlin.time.Duration.Companion.seconds
@@ -45,6 +48,7 @@ internal class OrchestratorTest(
@Qualifier("throwOnJoinWithContextOrchestrator") private val throwOnJoinWithContextOrchestrator: Orchestrator, List>,
@Qualifier("throwOnCommitWithContextOrchestrator") private val throwOnCommitWithContextOrchestrator: Orchestrator, List>,
@Qualifier("throwJwtExceptionOnStartOrchestrator") private val throwJwtExceptionOnStartOrchestrator: Orchestrator,
+ @Qualifier("throwHttpClientErrorExceptionOnStartOrchestrator") private val throwHttpClientErrorExceptionOnStartOrchestrator: Orchestrator,
) : DescribeSpec({
describe("numberOrchestrator 구현채는") {
@@ -259,7 +263,7 @@ internal class OrchestratorTest(
it("해당 예외를 Result에서 throw한다.") {
shouldThrowWithMessage("Throw error for test.") {
throwOnStartWithContextOrchestrator.sagaSync(listOf())
- .decodeResultOrThrow(object: TypeReference>(){})
+ .decodeResultOrThrow(object : TypeReference>() {})
}
}
}
@@ -270,7 +274,7 @@ internal class OrchestratorTest(
it("해당 예외를 Result에서 throw한다.") {
shouldThrowWithMessage("Throw error for test.") {
throwOnJoinWithContextOrchestrator.sagaSync(listOf())
- .decodeResultOrThrow(object: TypeReference>(){})
+ .decodeResultOrThrow(object : TypeReference>() {})
}
}
}
@@ -281,7 +285,7 @@ internal class OrchestratorTest(
it("해당 예외를 Result에서 throw한다.") {
shouldThrowWithMessage("Throw error for test.") {
throwOnCommitWithContextOrchestrator.sagaSync(listOf())
- .decodeResultOrThrow(object: TypeReference>(){})
+ .decodeResultOrThrow(object : TypeReference>() {})
}
}
}
@@ -297,6 +301,17 @@ internal class OrchestratorTest(
}
}
}
+
+ describe("throwHttpClientErrorExceptionOnStartOrchestrator 구현채는") {
+ context("처리할 수 없는 HttpClientErrorException이 던져지면") {
+ it("ResultException을 Result에 담고 timeout시간안에 예외를 반환한다") {
+ shouldThrowExactly {
+ throwHttpClientErrorExceptionOnStartOrchestrator.sagaSync("")
+ .decodeResultOrThrow(String::class)
+ }
+ }
+ }
+ }
}) {
data class Home(
val address: String,