From 8bdea75d27597e96c1bb4fc0d8d06cdc452c6e0e Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 17:15:55 +0900 Subject: [PATCH 01/19] feat: updated testing docker file --- docker/testing/docker-compose.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docker/testing/docker-compose.yml diff --git a/docker/testing/docker-compose.yml b/docker/testing/docker-compose.yml new file mode 100644 index 00000000..8f853538 --- /dev/null +++ b/docker/testing/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + controller: + image: ngrinder/controller + restart: unless-stopped + ports: + - "9000:80" + - "16001:16001" + - "12000-12009:12000-12009" + volumes: + - ./ngrinder-controller:/opt/ngrinder-controller + platform: linux/amd64 + + agent: + image: ngrinder/agent + restart: unless-stopped + links: + - controller + platform: linux/amd64 From b649a9e8634758992af43332d701b945acf4c5da Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 19:39:38 +0900 Subject: [PATCH 02/19] fix: added log.error, lock for cache stampede --- piikii-output-cache/redis/build.gradle.kts | 3 ++ .../piikii/output/redis/config/RedisConfig.kt | 18 +++++++++ piikii-output-web/tmap/build.gradle.kts | 7 ++++ .../web/tmap/adapter/TmapNavigationAdapter.kt | 40 +++++++++++++++---- 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/piikii-output-cache/redis/build.gradle.kts b/piikii-output-cache/redis/build.gradle.kts index 094b29d2..a59832c8 100644 --- a/piikii-output-cache/redis/build.gradle.kts +++ b/piikii-output-cache/redis/build.gradle.kts @@ -5,4 +5,7 @@ plugins { dependencies { implementation(project(":piikii-application")) implementation(libs.bundles.adaptor.cache.redis) + + // redisson + implementation ("org.redisson:redisson-spring-boot-starter:3.27.0") } diff --git a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt index 4e21f4e3..aaf9c75b 100644 --- a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt +++ b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt @@ -1,6 +1,9 @@ package com.piikii.output.redis.config import com.fasterxml.jackson.databind.ObjectMapper +import org.redisson.Redisson +import org.redisson.api.RedissonClient +import org.redisson.config.Config import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.cache.CacheManager @@ -72,6 +75,21 @@ class RedisConfig { .cacheDefaults(redisCacheConfiguration) .build() } + + @Bean + fun redissonClient(redisProperties: RedisProperties): RedissonClient? { + val config = Config() + val address = REDISSON_HOST_PREFIX + redisProperties.host + ":" + redisProperties.port + config.useSingleServer() + .setAddress(address) + .setPassword(redisProperties.password) + .setConnectionMinimumIdleSize(1) + return Redisson.create(config) + } + + companion object { + const val REDISSON_HOST_PREFIX = "rediss://" + } } @ConfigurationProperties(prefix = "redis") diff --git a/piikii-output-web/tmap/build.gradle.kts b/piikii-output-web/tmap/build.gradle.kts index a262c7ea..67c0d9d8 100644 --- a/piikii-output-web/tmap/build.gradle.kts +++ b/piikii-output-web/tmap/build.gradle.kts @@ -5,4 +5,11 @@ plugins { dependencies { implementation(project(":piikii-application")) implementation(libs.spring.web) + + // lombok + compileOnly("org.projectlombok:lombok") + annotationProcessor("org.projectlombok:lombok") + + // redisson + implementation ("org.redisson:redisson-spring-boot-starter:3.27.0") } diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index 01ec2324..2484eb22 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -4,17 +4,23 @@ import com.piikii.application.domain.course.Coordinate import com.piikii.application.domain.course.Distance import com.piikii.application.domain.place.Place import com.piikii.application.port.output.web.NavigationPort -import com.piikii.common.exception.ExceptionCode -import com.piikii.common.exception.PiikiiException +import org.redisson.api.RLock +import org.redisson.api.RedissonClient +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Component import org.springframework.web.client.RestClient import org.springframework.web.client.body +import java.util.concurrent.TimeUnit @Component class TmapNavigationAdapter( private val tmapApiClient: RestClient, + private val redissonClient: RedissonClient, ) : NavigationPort { + private val logger: Logger = LoggerFactory.getLogger(TmapNavigationAdapter::class.java) + @Cacheable( value = ["Distance"], key = "#startPlace.id + '_' + #endPlace.id", @@ -27,7 +33,22 @@ class TmapNavigationAdapter( val startCoordinate = startPlace.getCoordinate() val endCoordinate = endPlace.getCoordinate() return if (startCoordinate.isValid() && endCoordinate.isValid()) { - getDistanceFromTmap(startCoordinate, endCoordinate) + val lockKey = "distance-lock-${startPlace.id}_${endPlace.id}" + val lock: RLock = redissonClient.getLock(lockKey) + + try { + // 락 획득 시도, 10초 동안 기다리고 5초 동안 락을 유지 + if (lock.tryLock(10, 5, TimeUnit.SECONDS)) { + getDistanceFromTmap(startCoordinate, endCoordinate) + } else { + // 락을 획득하지 못한 경우 대기&재시도 또는 기본 값 반환 + Distance.EMPTY + } + } finally { + if (lock.isHeldByCurrentThread) { + lock.unlock() + } + } } else { Distance.EMPTY } @@ -50,9 +71,14 @@ class TmapNavigationAdapter( .retrieve() .body() ?.toDistance() - ?: throw PiikiiException( - exceptionCode = ExceptionCode.ROUTE_PROCESS_ERROR, - detailMessage = "fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})", - ) + ?: getEmptyDistanceOfError(start, end) + } + + private fun getEmptyDistanceOfError( + start: Coordinate, + end: Coordinate, + ): Distance { + logger.error("fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})") + return Distance.EMPTY } } From d272c4e534e6906c939cfb4814bf926fc47b67ea Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 19:46:05 +0900 Subject: [PATCH 03/19] delete: deleted blank --- piikii-output-cache/redis/build.gradle.kts | 2 +- piikii-output-web/tmap/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/piikii-output-cache/redis/build.gradle.kts b/piikii-output-cache/redis/build.gradle.kts index a59832c8..2a0a078d 100644 --- a/piikii-output-cache/redis/build.gradle.kts +++ b/piikii-output-cache/redis/build.gradle.kts @@ -7,5 +7,5 @@ dependencies { implementation(libs.bundles.adaptor.cache.redis) // redisson - implementation ("org.redisson:redisson-spring-boot-starter:3.27.0") + implementation("org.redisson:redisson-spring-boot-starter:3.27.0") } diff --git a/piikii-output-web/tmap/build.gradle.kts b/piikii-output-web/tmap/build.gradle.kts index 67c0d9d8..a86b3686 100644 --- a/piikii-output-web/tmap/build.gradle.kts +++ b/piikii-output-web/tmap/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { annotationProcessor("org.projectlombok:lombok") // redisson - implementation ("org.redisson:redisson-spring-boot-starter:3.27.0") + implementation("org.redisson:redisson-spring-boot-starter:3.27.0") } From bbb1f2c5dae8601e02f96e4f2c37e7151cfc0c8b Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 22:22:34 +0900 Subject: [PATCH 04/19] fix: updated ddl --- .../src/main/resources/database-config/application-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml index e1469cbe..ebd41cba 100644 --- a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml +++ b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml @@ -19,7 +19,7 @@ spring: database-platform: org.hibernate.dialect.PostgreSQLDialect database: postgresql hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate: temp.use_jdbc_metadata_defaults: false From c9f2f614fbf0c0eec47e56d2bb1274214f8badd9 Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 22:27:34 +0900 Subject: [PATCH 05/19] fix: rollback --- .../src/main/resources/database-config/application-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml index ebd41cba..e1469cbe 100644 --- a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml +++ b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml @@ -19,7 +19,7 @@ spring: database-platform: org.hibernate.dialect.PostgreSQLDialect database: postgresql hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate: temp.use_jdbc_metadata_defaults: false From bb5a5bb4db7eee80103f696041d3245f8339f1f1 Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 22:47:35 +0900 Subject: [PATCH 06/19] test: deleted testing dockerfile --- docker/testing/docker-compose.yml | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docker/testing/docker-compose.yml b/docker/testing/docker-compose.yml index 8f853538..6dcdee05 100644 --- a/docker/testing/docker-compose.yml +++ b/docker/testing/docker-compose.yml @@ -1,20 +1,20 @@ -version: '3.8' - -services: - controller: - image: ngrinder/controller - restart: unless-stopped - ports: - - "9000:80" - - "16001:16001" - - "12000-12009:12000-12009" - volumes: - - ./ngrinder-controller:/opt/ngrinder-controller - platform: linux/amd64 - - agent: - image: ngrinder/agent - restart: unless-stopped - links: - - controller - platform: linux/amd64 +#version: '3.8' +# +#services: +# controller: +# image: ngrinder/controller +# restart: unless-stopped +# ports: +# - "9000:80" +# - "16001:16001" +# - "12000-12009:12000-12009" +# volumes: +# - ./ngrinder-controller:/opt/ngrinder-controller +# platform: linux/amd64 +# +# agent: +# image: ngrinder/agent +# restart: unless-stopped +# links: +# - controller +# platform: linux/amd64 From 2f1a87ae93c5e0b570749d3dc119fa64ad662ee1 Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 22:50:45 +0900 Subject: [PATCH 07/19] fix: rollback --- docker/testing/docker-compose.yml | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docker/testing/docker-compose.yml b/docker/testing/docker-compose.yml index 6dcdee05..8f853538 100644 --- a/docker/testing/docker-compose.yml +++ b/docker/testing/docker-compose.yml @@ -1,20 +1,20 @@ -#version: '3.8' -# -#services: -# controller: -# image: ngrinder/controller -# restart: unless-stopped -# ports: -# - "9000:80" -# - "16001:16001" -# - "12000-12009:12000-12009" -# volumes: -# - ./ngrinder-controller:/opt/ngrinder-controller -# platform: linux/amd64 -# -# agent: -# image: ngrinder/agent -# restart: unless-stopped -# links: -# - controller -# platform: linux/amd64 +version: '3.8' + +services: + controller: + image: ngrinder/controller + restart: unless-stopped + ports: + - "9000:80" + - "16001:16001" + - "12000-12009:12000-12009" + volumes: + - ./ngrinder-controller:/opt/ngrinder-controller + platform: linux/amd64 + + agent: + image: ngrinder/agent + restart: unless-stopped + links: + - controller + platform: linux/amd64 From b410d50261cfda4419054e6c623376dfcfa4ab2c Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 23:06:10 +0900 Subject: [PATCH 08/19] test --- .github/workflows/ci-pull-request.yml | 14 ++++++++++++++ .../resources/database-config/application-test.yml | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pull-request.yml b/.github/workflows/ci-pull-request.yml index 54efe833..aff5be61 100644 --- a/.github/workflows/ci-pull-request.yml +++ b/.github/workflows/ci-pull-request.yml @@ -9,6 +9,20 @@ jobs: name: ready for launch (CI) runs-on: ubuntu-latest if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '출격준비')}} + services: + postgres: + image: postgres:16.3 + ports: + - 5432:5432 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + options: > + --health-cmd="pg_isready" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + steps: - name: Get PR branch id: get_branch diff --git a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml index e1469cbe..9e23409d 100644 --- a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml +++ b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml @@ -5,9 +5,9 @@ spring: datasource: driver-class-name: org.postgresql.Driver - url: ${DB_URL} - username: ${DB_USER} - password: ${DB_PASSWORD} + url: jdbc:postgresql://localhost:5432/piikii + username: postgres + password: postgres hikari: pool-name: Hikari-PostgreSQL maximum-pool-size: 20 From f6a27b22a7098d7ad680de392177f6be3467d4fc Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 23:12:32 +0900 Subject: [PATCH 09/19] rollback --- .github/workflows/ci-pull-request.yml | 13 ------------- .../resources/database-config/application-test.yml | 6 +++--- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-pull-request.yml b/.github/workflows/ci-pull-request.yml index aff5be61..66493d2c 100644 --- a/.github/workflows/ci-pull-request.yml +++ b/.github/workflows/ci-pull-request.yml @@ -9,19 +9,6 @@ jobs: name: ready for launch (CI) runs-on: ubuntu-latest if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '출격준비')}} - services: - postgres: - image: postgres:16.3 - ports: - - 5432:5432 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - options: > - --health-cmd="pg_isready" - --health-interval=10s - --health-timeout=5s - --health-retries=5 steps: - name: Get PR branch diff --git a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml index 9e23409d..e1469cbe 100644 --- a/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml +++ b/piikii-output-persistence/postgresql/src/main/resources/database-config/application-test.yml @@ -5,9 +5,9 @@ spring: datasource: driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://localhost:5432/piikii - username: postgres - password: postgres + url: ${DB_URL} + username: ${DB_USER} + password: ${DB_PASSWORD} hikari: pool-name: Hikari-PostgreSQL maximum-pool-size: 20 From f782d570923fcdc65909029cc04a05cc84dbea4f Mon Sep 17 00:00:00 2001 From: thguss Date: Thu, 10 Oct 2024 23:13:01 +0900 Subject: [PATCH 10/19] rollback --- .github/workflows/ci-pull-request.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-pull-request.yml b/.github/workflows/ci-pull-request.yml index 66493d2c..54efe833 100644 --- a/.github/workflows/ci-pull-request.yml +++ b/.github/workflows/ci-pull-request.yml @@ -9,7 +9,6 @@ jobs: name: ready for launch (CI) runs-on: ubuntu-latest if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '출격준비')}} - steps: - name: Get PR branch id: get_branch From bf9747ba9005497aca52c6012c3ef32b02173e08 Mon Sep 17 00:00:00 2001 From: thguss Date: Fri, 11 Oct 2024 11:03:48 +0900 Subject: [PATCH 11/19] fix: deleted lombok, used common logger --- piikii-output-web/tmap/build.gradle.kts | 4 ---- .../output/web/tmap/adapter/TmapNavigationAdapter.kt | 7 ++----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/piikii-output-web/tmap/build.gradle.kts b/piikii-output-web/tmap/build.gradle.kts index a86b3686..64acdd61 100644 --- a/piikii-output-web/tmap/build.gradle.kts +++ b/piikii-output-web/tmap/build.gradle.kts @@ -6,10 +6,6 @@ dependencies { implementation(project(":piikii-application")) implementation(libs.spring.web) - // lombok - compileOnly("org.projectlombok:lombok") - annotationProcessor("org.projectlombok:lombok") - // redisson implementation("org.redisson:redisson-spring-boot-starter:3.27.0") } diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index 2484eb22..7a6e95cd 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -4,10 +4,9 @@ import com.piikii.application.domain.course.Coordinate import com.piikii.application.domain.course.Distance import com.piikii.application.domain.place.Place import com.piikii.application.port.output.web.NavigationPort +import com.piikii.common.logutil.SystemLogger.logger import org.redisson.api.RLock import org.redisson.api.RedissonClient -import org.slf4j.Logger -import org.slf4j.LoggerFactory import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Component import org.springframework.web.client.RestClient @@ -19,8 +18,6 @@ class TmapNavigationAdapter( private val tmapApiClient: RestClient, private val redissonClient: RedissonClient, ) : NavigationPort { - private val logger: Logger = LoggerFactory.getLogger(TmapNavigationAdapter::class.java) - @Cacheable( value = ["Distance"], key = "#startPlace.id + '_' + #endPlace.id", @@ -78,7 +75,7 @@ class TmapNavigationAdapter( start: Coordinate, end: Coordinate, ): Distance { - logger.error("fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})") + logger.error { "fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})" } return Distance.EMPTY } } From 9c7456abc6cd333ee22aa4c8d5a21cc982675ca5 Mon Sep 17 00:00:00 2001 From: thguss Date: Fri, 11 Oct 2024 11:27:44 +0900 Subject: [PATCH 12/19] add: added SlackLogger --- .../piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index 7a6e95cd..2973ec54 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -4,6 +4,7 @@ import com.piikii.application.domain.course.Coordinate import com.piikii.application.domain.course.Distance import com.piikii.application.domain.place.Place import com.piikii.application.port.output.web.NavigationPort +import com.piikii.common.logutil.SlackHookLogger import com.piikii.common.logutil.SystemLogger.logger import org.redisson.api.RLock import org.redisson.api.RedissonClient @@ -17,6 +18,7 @@ import java.util.concurrent.TimeUnit class TmapNavigationAdapter( private val tmapApiClient: RestClient, private val redissonClient: RedissonClient, + private val hookLogger: SlackHookLogger, ) : NavigationPort { @Cacheable( value = ["Distance"], @@ -75,7 +77,9 @@ class TmapNavigationAdapter( start: Coordinate, end: Coordinate, ): Distance { - logger.error { "fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})" } + val message = "fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})" + logger.error { message } + hookLogger.send(message) return Distance.EMPTY } } From 11efcbb6027efb66a61d729eb4dc0e666515f589 Mon Sep 17 00:00:00 2001 From: KimDoubleB Date: Sun, 13 Oct 2024 17:32:26 +0900 Subject: [PATCH 13/19] feat: apply version catalog for redisson: --- gradle/libs.versions.toml | 4 +++- piikii-output-cache/redis/build.gradle.kts | 3 --- piikii-output-web/tmap/build.gradle.kts | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69a1451e..464e3792 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,9 @@ spring-boot-starter-validation = { group = "org.springframework.boot", name = "s spring-boot-starter-cache = { group = "org.springframework.boot", name = "spring-boot-starter-cache" } spring-boot-starter-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa" } spring-boot-starter-redis = { group = "org.springframework.boot", name = "spring-boot-starter-data-redis" } + spring-boot-docs = { group = "org.springdoc", name = "springdoc-openapi-starter-webmvc-ui", version = "2.5.0" } +spring-boot-starter-redisson = { group = "org.redisson", name = "redisson-spring-boot-starter", version = "3.27.0" } postgresql = { group = "org.postgresql", name = "postgresql" } caffeine = { group = "com.github.ben-manes.caffeine", name = "caffeine" } @@ -63,5 +65,5 @@ domain-application = ["spring-boot-docs", "spring-transaction"] adaptor-input-http = ["spring-boot-starter-web", "spring-boot-starter-aop", "spring-boot-docs", "spring-boot-starter-validation"] adaptor-persistence-postgresql = ["spring-boot-starter-jpa", "postgresql"] adaptor-storage = ["spring-web", "aws-sdk-s3", "jaxb-api", "jaxb-runtime"] -adaptor-cache-redis = ["spring-boot-starter-cache", "spring-boot-starter-redis"] +adaptor-cache-redis = ["spring-boot-starter-cache", "spring-boot-starter-redis", "spring-boot-starter-redisson"] adaptor-cache-caffeine = ["spring-boot-starter-cache", "caffeine"] diff --git a/piikii-output-cache/redis/build.gradle.kts b/piikii-output-cache/redis/build.gradle.kts index 2a0a078d..094b29d2 100644 --- a/piikii-output-cache/redis/build.gradle.kts +++ b/piikii-output-cache/redis/build.gradle.kts @@ -5,7 +5,4 @@ plugins { dependencies { implementation(project(":piikii-application")) implementation(libs.bundles.adaptor.cache.redis) - - // redisson - implementation("org.redisson:redisson-spring-boot-starter:3.27.0") } diff --git a/piikii-output-web/tmap/build.gradle.kts b/piikii-output-web/tmap/build.gradle.kts index 64acdd61..d108f81e 100644 --- a/piikii-output-web/tmap/build.gradle.kts +++ b/piikii-output-web/tmap/build.gradle.kts @@ -5,7 +5,5 @@ plugins { dependencies { implementation(project(":piikii-application")) implementation(libs.spring.web) - - // redisson - implementation("org.redisson:redisson-spring-boot-starter:3.27.0") + implementation(libs.spring.boot.starter.redisson) } From d24a92f2d29aec55ec46107a3688171a57e8c599 Mon Sep 17 00:00:00 2001 From: thguss Date: Mon, 14 Oct 2024 14:09:33 +0900 Subject: [PATCH 14/19] fix: (code review) updated method name, deleted final const --- .../kotlin/com/piikii/output/redis/config/RedisConfig.kt | 6 +----- .../piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt index aaf9c75b..2251aebc 100644 --- a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt +++ b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt @@ -79,17 +79,13 @@ class RedisConfig { @Bean fun redissonClient(redisProperties: RedisProperties): RedissonClient? { val config = Config() - val address = REDISSON_HOST_PREFIX + redisProperties.host + ":" + redisProperties.port + val address = "rediss://${redisProperties.host}:${redisProperties.port}" config.useSingleServer() .setAddress(address) .setPassword(redisProperties.password) .setConnectionMinimumIdleSize(1) return Redisson.create(config) } - - companion object { - const val REDISSON_HOST_PREFIX = "rediss://" - } } @ConfigurationProperties(prefix = "redis") diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index 2973ec54..bba74fe8 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -70,10 +70,10 @@ class TmapNavigationAdapter( .retrieve() .body() ?.toDistance() - ?: getEmptyDistanceOfError(start, end) + ?: getEmptyDistanceWithError(start, end) } - private fun getEmptyDistanceOfError( + private fun getEmptyDistanceWithError( start: Coordinate, end: Coordinate, ): Distance { From b879dbdc746f9638c5b19e3a5f89333dda686f86 Mon Sep 17 00:00:00 2001 From: thguss Date: Mon, 14 Oct 2024 14:35:57 +0900 Subject: [PATCH 15/19] fix: added retry logic --- .../web/tmap/adapter/TmapNavigationAdapter.kt | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index bba74fe8..609193c7 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -31,26 +31,47 @@ class TmapNavigationAdapter( ): Distance { val startCoordinate = startPlace.getCoordinate() val endCoordinate = endPlace.getCoordinate() - return if (startCoordinate.isValid() && endCoordinate.isValid()) { - val lockKey = "distance-lock-${startPlace.id}_${endPlace.id}" - val lock: RLock = redissonClient.getLock(lockKey) + if (!startCoordinate.isValid() || !endCoordinate.isValid()) { + return Distance.EMPTY + } + + val lockKey = "distance-lock-${startPlace.id}_${endPlace.id}" + val lock: RLock = redissonClient.getLock(lockKey) + + return try { + if (tryLockWithRetry(lock, 3, 1000)) { + getDistanceFromTmap(startCoordinate, endCoordinate) + } else { + Distance.EMPTY + } + } finally { + if (lock.isHeldByCurrentThread) { + lock.unlock() + } + } + } + + private fun tryLockWithRetry( + lock: RLock, + maxRetries: Int, + retryDelayMs: Long + ): Boolean { + var attempts = 0 + while (attempts < maxRetries) { try { - // 락 획득 시도, 10초 동안 기다리고 5초 동안 락을 유지 if (lock.tryLock(10, 5, TimeUnit.SECONDS)) { - getDistanceFromTmap(startCoordinate, endCoordinate) + return true } else { - // 락을 획득하지 못한 경우 대기&재시도 또는 기본 값 반환 - Distance.EMPTY - } - } finally { - if (lock.isHeldByCurrentThread) { - lock.unlock() + attempts++ + Thread.sleep(retryDelayMs) } + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + return false } - } else { - Distance.EMPTY } + return false } private fun getDistanceFromTmap( From c3258a9b12c75aa0177ad5b4b1dd7393c55c58f7 Mon Sep 17 00:00:00 2001 From: thguss Date: Mon, 14 Oct 2024 14:49:46 +0900 Subject: [PATCH 16/19] add: added configuration options --- .../main/kotlin/com/piikii/output/redis/config/RedisConfig.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt index 2251aebc..325a798a 100644 --- a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt +++ b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt @@ -84,6 +84,8 @@ class RedisConfig { .setAddress(address) .setPassword(redisProperties.password) .setConnectionMinimumIdleSize(1) + .setRetryAttempts(3) // 재시도 횟수 + .setRetryInterval(1500) // 재시도 간격 (ms) return Redisson.create(config) } } From 34b8aa5acf31e02d60d93311586944f0b4b4d9a2 Mon Sep 17 00:00:00 2001 From: thguss Date: Mon, 14 Oct 2024 14:52:15 +0900 Subject: [PATCH 17/19] add: added comment --- .../com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index 609193c7..ce91d323 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -60,6 +60,7 @@ class TmapNavigationAdapter( var attempts = 0 while (attempts < maxRetries) { try { + // 락 획득 시도: 10초 대기, 5초 동안 락을 유지 if (lock.tryLock(10, 5, TimeUnit.SECONDS)) { return true } else { From d04384fb49e7d853f552295e5d155b77fbb3249e Mon Sep 17 00:00:00 2001 From: thguss Date: Fri, 18 Oct 2024 00:14:22 +0900 Subject: [PATCH 18/19] add: [code review] added @retry --- gradle/libs.versions.toml | 2 + piikii-output-web/tmap/build.gradle.kts | 1 + .../web/tmap/adapter/TmapNavigationAdapter.kt | 49 ++++++++++--------- .../output/web/tmap/config/TmapConfig.kt | 2 + 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 464e3792..b4311bc5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,6 +35,8 @@ spring-boot-starter-cache = { group = "org.springframework.boot", name = "spring spring-boot-starter-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa" } spring-boot-starter-redis = { group = "org.springframework.boot", name = "spring-boot-starter-data-redis" } +spring-boot-starter-retry = { group = "org.springframework.retry", name = "spring-retry", version = "1.3.3" } + spring-boot-docs = { group = "org.springdoc", name = "springdoc-openapi-starter-webmvc-ui", version = "2.5.0" } spring-boot-starter-redisson = { group = "org.redisson", name = "redisson-spring-boot-starter", version = "3.27.0" } diff --git a/piikii-output-web/tmap/build.gradle.kts b/piikii-output-web/tmap/build.gradle.kts index d108f81e..621ee479 100644 --- a/piikii-output-web/tmap/build.gradle.kts +++ b/piikii-output-web/tmap/build.gradle.kts @@ -6,4 +6,5 @@ dependencies { implementation(project(":piikii-application")) implementation(libs.spring.web) implementation(libs.spring.boot.starter.redisson) + implementation(libs.spring.boot.starter.retry) } diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index ce91d323..100acfd5 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -5,10 +5,12 @@ import com.piikii.application.domain.course.Distance import com.piikii.application.domain.place.Place import com.piikii.application.port.output.web.NavigationPort import com.piikii.common.logutil.SlackHookLogger -import com.piikii.common.logutil.SystemLogger.logger import org.redisson.api.RLock import org.redisson.api.RedissonClient import org.springframework.cache.annotation.Cacheable +import org.springframework.retry.annotation.Backoff +import org.springframework.retry.annotation.Recover +import org.springframework.retry.annotation.Retryable import org.springframework.stereotype.Component import org.springframework.web.client.RestClient import org.springframework.web.client.body @@ -25,6 +27,11 @@ class TmapNavigationAdapter( key = "#startPlace.id + '_' + #endPlace.id", unless = "#result == T(com.piikii.application.domain.course.Distance).EMPTY", ) + @Retryable( + value = [InterruptedException::class], + maxAttempts = 3, + backoff = Backoff(delay = 1000) + ) override fun getDistance( startPlace: Place, endPlace: Place, @@ -40,10 +47,11 @@ class TmapNavigationAdapter( val lock: RLock = redissonClient.getLock(lockKey) return try { - if (tryLockWithRetry(lock, 3, 1000)) { + // 락 획득 시도: 10초 대기, 5초 동안 락을 유지 + if (lock.tryLock(10, 5, TimeUnit.SECONDS)) { getDistanceFromTmap(startCoordinate, endCoordinate) } else { - Distance.EMPTY + throw InterruptedException("락 획득 실패") } } finally { if (lock.isHeldByCurrentThread) { @@ -52,27 +60,21 @@ class TmapNavigationAdapter( } } - private fun tryLockWithRetry( - lock: RLock, - maxRetries: Int, - retryDelayMs: Long - ): Boolean { - var attempts = 0 - while (attempts < maxRetries) { - try { - // 락 획득 시도: 10초 대기, 5초 동안 락을 유지 - if (lock.tryLock(10, 5, TimeUnit.SECONDS)) { - return true - } else { - attempts++ - Thread.sleep(retryDelayMs) - } - } catch (e: InterruptedException) { - Thread.currentThread().interrupt() - return false - } + @Recover + fun recoverFromInterruptedException( + exception: InterruptedException, + startPlace: Place, + endPlace: Place + ): Distance { + hookLogger.send(exception.message?:"[redis] 알 수 없는 에러") + val startCoordinate = startPlace.getCoordinate() + val endCoordinate = endPlace.getCoordinate() + return if (startCoordinate.isValid() && endCoordinate.isValid()) { + // 락 획득 실패 시 바로 외부 API 호출 + getDistanceFromTmap(startCoordinate, endCoordinate) + } else { + Distance.EMPTY } - return false } private fun getDistanceFromTmap( @@ -100,7 +102,6 @@ class TmapNavigationAdapter( end: Coordinate, ): Distance { val message = "fail to request tmap api call, start(${start.x}, ${start.y}), end(${end.x}, ${end.y})" - logger.error { message } hookLogger.send(message) return Distance.EMPTY } diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/config/TmapConfig.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/config/TmapConfig.kt index 8efce5a5..1e13c824 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/config/TmapConfig.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/config/TmapConfig.kt @@ -6,10 +6,12 @@ import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.HttpHeaders import org.springframework.http.MediaType +import org.springframework.retry.annotation.EnableRetry import org.springframework.web.client.RestClient // https://skopenapi.readme.io/reference/%EB%B3%B4%ED%96%89%EC%9E%90-%EA%B2%BD%EB%A1%9C%EC%95%88%EB%82%B4 @Configuration +@EnableRetry @EnableConfigurationProperties(TmapProperties::class) class TmapConfig { @Bean From 5bd81e8dd9c66ae597777824a6b077e4a537101e Mon Sep 17 00:00:00 2001 From: thguss Date: Fri, 18 Oct 2024 00:41:58 +0900 Subject: [PATCH 19/19] fix: followed convention --- .../kotlin/com/piikii/output/redis/config/RedisConfig.kt | 4 ++-- .../piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt index 325a798a..9c1602e1 100644 --- a/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt +++ b/piikii-output-cache/redis/src/main/kotlin/com/piikii/output/redis/config/RedisConfig.kt @@ -84,8 +84,8 @@ class RedisConfig { .setAddress(address) .setPassword(redisProperties.password) .setConnectionMinimumIdleSize(1) - .setRetryAttempts(3) // 재시도 횟수 - .setRetryInterval(1500) // 재시도 간격 (ms) + .setRetryAttempts(3) + .setRetryInterval(1500) return Redisson.create(config) } } diff --git a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt index 100acfd5..81a2f8a0 100644 --- a/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt +++ b/piikii-output-web/tmap/src/main/kotlin/com/piikii/output/web/tmap/adapter/TmapNavigationAdapter.kt @@ -30,7 +30,7 @@ class TmapNavigationAdapter( @Retryable( value = [InterruptedException::class], maxAttempts = 3, - backoff = Backoff(delay = 1000) + backoff = Backoff(delay = 1000), ) override fun getDistance( startPlace: Place, @@ -64,9 +64,9 @@ class TmapNavigationAdapter( fun recoverFromInterruptedException( exception: InterruptedException, startPlace: Place, - endPlace: Place + endPlace: Place, ): Distance { - hookLogger.send(exception.message?:"[redis] 알 수 없는 에러") + hookLogger.send(exception.message ?: "[redis] 알 수 없는 에러") val startCoordinate = startPlace.getCoordinate() val endCoordinate = endPlace.getCoordinate() return if (startCoordinate.isValid() && endCoordinate.isValid()) {