From fe686e695430f88ce932261d376c1d72e4e76a5d Mon Sep 17 00:00:00 2001 From: mii2026 Date: Thu, 2 Jan 2025 22:19:01 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat=20#108]=20=EA=B3=B5=EC=97=B0=2010?= =?UTF-8?q?=EB=B6=84=20=EC=A0=84=20=EC=A2=8C=EC=84=9D=20=EC=BA=90=EC=8B=B1?= =?UTF-8?q?=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=8B=9C=20=EB=94=94=EC=8A=A4=EC=BD=94=EB=93=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/SeatCacheScheduler.java | 16 ++++---- .../exception/MessageExceptionCase.java | 15 +++++++ .../config/RestTemplateConfig.java | 25 ++++++++++++ .../service/DiscordNotificationService.java | 40 +++++++++++++++++++ .../src/main/resources/application.yml | 2 + 5 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 services/performance/src/main/java/com/ticketPing/performance/common/exception/MessageExceptionCase.java create mode 100644 services/performance/src/main/java/com/ticketPing/performance/infrastructure/config/RestTemplateConfig.java create mode 100644 services/performance/src/main/java/com/ticketPing/performance/infrastructure/service/DiscordNotificationService.java diff --git a/services/performance/src/main/java/com/ticketPing/performance/application/scheduler/SeatCacheScheduler.java b/services/performance/src/main/java/com/ticketPing/performance/application/scheduler/SeatCacheScheduler.java index ee9124f4..18f73255 100644 --- a/services/performance/src/main/java/com/ticketPing/performance/application/scheduler/SeatCacheScheduler.java +++ b/services/performance/src/main/java/com/ticketPing/performance/application/scheduler/SeatCacheScheduler.java @@ -2,6 +2,7 @@ import com.ticketPing.performance.application.service.PerformanceService; import com.ticketPing.performance.domain.model.entity.Performance; +import com.ticketPing.performance.infrastructure.service.DiscordNotificationService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; @@ -17,6 +18,7 @@ public class SeatCacheScheduler { private final PerformanceService performanceService; private final RedissonClient redissonClient; + private final DiscordNotificationService discordNotificationService; private static final String LOCK_KEY = "SchedulerLock"; private static final int LOCK_TIMEOUT = 300; @@ -24,24 +26,24 @@ public class SeatCacheScheduler { @Scheduled(cron = "0 0/10 * * * *") public void runScheduler() { try { - log.info("[SeatCacheScheduler] 스케줄러 실행 시작"); + log.info("Scheduler started"); RLock lock = redissonClient.getLock(LOCK_KEY); - boolean acquired = lock.tryLock(10, LOCK_TIMEOUT, TimeUnit.SECONDS); + boolean acquired = lock.tryLock(0, LOCK_TIMEOUT, TimeUnit.SECONDS); if (acquired) { Performance performance = performanceService.getUpcomingPerformance(); if (performance != null) { performanceService.cacheAllSeatsForPerformance(performance.getId()); - log.info("[SeatCacheScheduler] 캐싱 완료"); + log.info("Caching completed"); } else { - log.info("[SeatCacheScheduler] 예정된 공연이 없음"); + log.info("No upcoming performance"); } } else { - log.warn("[SeatCacheScheduler] 다른 서버에서 스케줄러 실행 중"); + log.warn("Another server is running the scheduler"); } } catch (Exception e) { - log.error("[SeatCacheScheduler] 실행 중 오류 발생: {}", e.getMessage(), e); - // TODO: discord 알림? + log.error("Error occurred during execution: {}", e.getMessage(), e); + discordNotificationService.sendErrorNotification(e.getMessage()); } } } diff --git a/services/performance/src/main/java/com/ticketPing/performance/common/exception/MessageExceptionCase.java b/services/performance/src/main/java/com/ticketPing/performance/common/exception/MessageExceptionCase.java new file mode 100644 index 00000000..0840dbba --- /dev/null +++ b/services/performance/src/main/java/com/ticketPing/performance/common/exception/MessageExceptionCase.java @@ -0,0 +1,15 @@ +package com.ticketPing.performance.common.exception; + +import exception.ErrorCase; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum MessageExceptionCase implements ErrorCase { + MESSAGE_SEND_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "알람을 보낼 수 없습니다."); + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/services/performance/src/main/java/com/ticketPing/performance/infrastructure/config/RestTemplateConfig.java b/services/performance/src/main/java/com/ticketPing/performance/infrastructure/config/RestTemplateConfig.java new file mode 100644 index 00000000..7909ce92 --- /dev/null +++ b/services/performance/src/main/java/com/ticketPing/performance/infrastructure/config/RestTemplateConfig.java @@ -0,0 +1,25 @@ +package com.ticketPing.performance.infrastructure.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.BufferingClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder + .requestFactory(() -> new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())) + .setConnectTimeout(Duration.ofMillis(5000)) + .setReadTimeout(Duration.ofMillis(300000)) + .additionalMessageConverters(new StringHttpMessageConverter(StandardCharsets.UTF_8)) + .build(); + } +} diff --git a/services/performance/src/main/java/com/ticketPing/performance/infrastructure/service/DiscordNotificationService.java b/services/performance/src/main/java/com/ticketPing/performance/infrastructure/service/DiscordNotificationService.java new file mode 100644 index 00000000..a56cdcd4 --- /dev/null +++ b/services/performance/src/main/java/com/ticketPing/performance/infrastructure/service/DiscordNotificationService.java @@ -0,0 +1,40 @@ +package com.ticketPing.performance.infrastructure.service; + +import com.ticketPing.performance.common.exception.MessageExceptionCase; +import exception.ApplicationException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DiscordNotificationService { + + @Value("${discord.webhook-url}") + private String discordWebhookUrl; + + private final RestTemplate restTemplate; + + public void sendErrorNotification(String errorMessage) { + try { + String message = String.format("**Error occurred in SeatCacheScheduler**: %s", errorMessage); + String payload = String.format("{\"content\": \"%s\"}", message); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(payload, headers); + + restTemplate.exchange(discordWebhookUrl, HttpMethod.POST, entity, String.class); + } catch (Exception e) { + log.error("Error occurred during execution: {}", e.getMessage(), e); + throw new ApplicationException(MessageExceptionCase.MESSAGE_SEND_FAIL); + } + } +} diff --git a/services/performance/src/main/resources/application.yml b/services/performance/src/main/resources/application.yml index 7b29e521..3cbf549c 100644 --- a/services/performance/src/main/resources/application.yml +++ b/services/performance/src/main/resources/application.yml @@ -22,3 +22,5 @@ server: seat: pre-reserve-ttl: 300 +discord: + webhook-url: ${GF_DISCORD_WEBHOOK_URL} \ No newline at end of file From f5bd2ff5ef479f3b25be5294aa7489293299efff Mon Sep 17 00:00:00 2001 From: mii2026 Date: Thu, 2 Jan 2025 22:20:09 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[fix=20#108]=20=EC=98=88=EB=A7=A4=2010-0?= =?UTF-8?q?=EB=B6=84=20=EC=A0=84=20=EA=B3=B5=EC=97=B0=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EC=BF=BC=EB=A6=AC=200=EB=B6=84=EC=9D=80=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8=20=EC=95=88=EB=90=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/PerformanceService.java | 2 +- .../domain/repository/PerformanceRepository.java | 2 +- .../repository/PerformanceJpaRepository.java | 9 ++++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/services/performance/src/main/java/com/ticketPing/performance/application/service/PerformanceService.java b/services/performance/src/main/java/com/ticketPing/performance/application/service/PerformanceService.java index 9584949e..51b44381 100644 --- a/services/performance/src/main/java/com/ticketPing/performance/application/service/PerformanceService.java +++ b/services/performance/src/main/java/com/ticketPing/performance/application/service/PerformanceService.java @@ -46,7 +46,7 @@ public Performance getUpcomingPerformance() { LocalDateTime now = LocalDateTime.now(); LocalDateTime tenMinutesLater = now.plusMinutes(10); - return performanceRepository.findFirstByReservationStartDateBetween(now, tenMinutesLater); + return performanceRepository.findUpcomingPerformance(now, tenMinutesLater); } public void cacheAllSeatsForPerformance(UUID performanceId) { diff --git a/services/performance/src/main/java/com/ticketPing/performance/domain/repository/PerformanceRepository.java b/services/performance/src/main/java/com/ticketPing/performance/domain/repository/PerformanceRepository.java index 4cd988d8..0a33bcf0 100644 --- a/services/performance/src/main/java/com/ticketPing/performance/domain/repository/PerformanceRepository.java +++ b/services/performance/src/main/java/com/ticketPing/performance/domain/repository/PerformanceRepository.java @@ -19,5 +19,5 @@ public interface PerformanceRepository { Optional findByIdWithDetails(UUID id); - Performance findFirstByReservationStartDateBetween(LocalDateTime start, LocalDateTime end); + Performance findUpcomingPerformance(LocalDateTime start, LocalDateTime end); } diff --git a/services/performance/src/main/java/com/ticketPing/performance/infrastructure/repository/PerformanceJpaRepository.java b/services/performance/src/main/java/com/ticketPing/performance/infrastructure/repository/PerformanceJpaRepository.java index f1094e4d..8e75cd2f 100644 --- a/services/performance/src/main/java/com/ticketPing/performance/infrastructure/repository/PerformanceJpaRepository.java +++ b/services/performance/src/main/java/com/ticketPing/performance/infrastructure/repository/PerformanceJpaRepository.java @@ -2,13 +2,14 @@ import com.ticketPing.performance.domain.model.entity.Performance; +import java.time.LocalDateTime; import java.util.Optional; import java.util.UUID; import com.ticketPing.performance.domain.repository.PerformanceRepository; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -23,4 +24,10 @@ public interface PerformanceJpaRepository extends PerformanceRepository, JpaRepo "LEFT JOIN FETCH p.seatCosts sc " + "WHERE p.id = :id ") Optional findByIdWithDetails(UUID id); + + @Query("SELECT p " + + "FROM Performance p " + + "WHERE p.reservationStartDate > :now " + + "AND p.reservationStartDate <= :tenMinutesLater") + Performance findUpcomingPerformance(@Param("now") LocalDateTime now, @Param("tenMinutesLater") LocalDateTime tenMinutesLater); } From 9f6128496f5761b2bccf50131f3a01777f35dabb Mon Sep 17 00:00:00 2001 From: mii2026 Date: Thu, 2 Jan 2025 22:31:36 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[fix=20#108]=20docker-compose=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20discord=20=EC=9B=B9=ED=9B=85=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose/docker-compose.yml | 1 + services/performance/src/main/resources/application.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index fc84c865..ed04a46e 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -93,6 +93,7 @@ services: - PERFORMANCE_POSTGRES_URL=${PERFORMANCE_POSTGRES_URL} - PERFORMANCE_POSTGRES_USERNAME=${PERFORMANCE_POSTGRES_USERNAME} - PERFORMANCE_POSTGRES_PASSWORD=${PERFORMANCE_POSTGRES_PASSWORD} + - DISCORD_WEBHOOK_URL=${GF_DISCORD_WEBHOOK_URL} - REDIS_NODE_1=${REDIS_NODE_1} - REDIS_NODE_2=${REDIS_NODE_2} - REDIS_NODE_3=${REDIS_NODE_3} diff --git a/services/performance/src/main/resources/application.yml b/services/performance/src/main/resources/application.yml index 3cbf549c..86470e92 100644 --- a/services/performance/src/main/resources/application.yml +++ b/services/performance/src/main/resources/application.yml @@ -23,4 +23,4 @@ seat: pre-reserve-ttl: 300 discord: - webhook-url: ${GF_DISCORD_WEBHOOK_URL} \ No newline at end of file + webhook-url: ${DISCORD_WEBHOOK_URL} \ No newline at end of file