diff --git a/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java b/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java index 345806a7..e55a2059 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java +++ b/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java @@ -13,7 +13,6 @@ import io.micrometer.core.instrument.Timer; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.List; @@ -37,7 +36,7 @@ public class ApiScheduler { private final MeterRegistry meterRegistry; private String ticker; - @Scheduled(fixedRate = 5000) +// @Scheduled(fixedRate = 5000) public void MarketAllRequest() throws InterruptedException { Timer timer = meterRegistry.timer("apischeduler.request.duration"); timer.record(() -> { diff --git a/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java b/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java new file mode 100644 index 00000000..74c08104 --- /dev/null +++ b/src/main/java/com/cleanengine/coin/realitybot/api/H2UnitPriceRefresher.java @@ -0,0 +1,23 @@ +package com.cleanengine.coin.realitybot.api; + + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@Profile("h2-mem") +@RequiredArgsConstructor +public class H2UnitPriceRefresher implements ApplicationRunner { + private final UnitPriceRefresher unitPriceRefresher; + + public void run(ApplicationArguments args){ + log.info("Running Unit Price Refresher (h2-mem)..."); + unitPriceRefresher.initializeUnitPrices(); + } + +} diff --git a/src/main/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresher.java b/src/main/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresher.java index eafe3a3a..e50a1384 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresher.java +++ b/src/main/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresher.java @@ -7,9 +7,6 @@ import com.cleanengine.coin.realitybot.vo.UnitPricePolicy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.List; @@ -19,19 +16,13 @@ @Slf4j @Component @RequiredArgsConstructor -public class UnitPriceRefresher implements ApplicationRunner { +public class UnitPriceRefresher { private final UnitPricePolicy unitPricePolicy; private final AssetRepository assetRepository; private final BithumbAPIClient bithumbAPIClient; private final OpeningPriceParser openingPriceParser; private final Map unitPriceCache = new ConcurrentHashMap<>(); - @Override - public void run(ApplicationArguments args){ - log.info("Running Unit Price Refresher..."); - initializeUnitPrices(); - } - public void initializeUnitPrices() { List tickers = assetRepository.findAll(); for (Asset ticker : tickers){ @@ -40,16 +31,6 @@ public void initializeUnitPrices() { } } - @Scheduled(cron = "${bot-handler.corn}") - public void refreshUnitPrices() { - initializeUnitPrices(); -// List tickers = assetRepository.findAll(); -// for (Asset ticker : tickers){ -// double unitPrice = fetchOpeningPriceFromAPI(ticker.getTicker()); -// unitPriceCache.put(ticker.getTicker(),unitPrice); -// } - - } private double fetchOpeningPriceFromAPI(String ticker) { String rawJson = bithumbAPIClient.getOpeningPrice(ticker); //api raw데이터 diff --git a/src/main/java/com/cleanengine/coin/realitybot/config/SchedulerConfig.java b/src/main/java/com/cleanengine/coin/realitybot/config/SchedulerConfig.java index c26118a1..86aa2a3a 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/config/SchedulerConfig.java +++ b/src/main/java/com/cleanengine/coin/realitybot/config/SchedulerConfig.java @@ -1,6 +1,7 @@ package com.cleanengine.coin.realitybot.config; import com.cleanengine.coin.realitybot.api.ApiScheduler; +import com.cleanengine.coin.realitybot.api.UnitPriceRefresher; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; @@ -18,17 +19,31 @@ public class SchedulerConfig implements SchedulingConfigurer { // @Autowired // private TaskScheduler apiScheduler; private final ApiScheduler apiScheduler; + private final UnitPriceRefresher unitPriceRefresher; + @Value("${bot-handler.cron}") + private final String cron; @Value("${bot-handler.fixed-rate}") private final Duration fixedRate; - protected SchedulerConfig(ApiScheduler apiScheduler, @Value("${bot-handler.fixed-rate}") Duration fixedRate) { + protected SchedulerConfig(ApiScheduler apiScheduler, @Value("${bot-handler.fixed-rate}") Duration fixedRate, UnitPriceRefresher unitPriceRefresher, + @Value("${bot-handler.cron}")String cron) { this.apiScheduler = apiScheduler; this.fixedRate = fixedRate; + this.unitPriceRefresher = unitPriceRefresher; + this.cron = cron; } @Override public void configureTasks(ScheduledTaskRegistrar registrar) { -// registrar.setScheduler(apiScheduler); //멀티 쓰레드 x + unitPriceRefresher.initializeUnitPrices(); //선반영 + + registrar.addCronTask(() -> { + try { + unitPriceRefresher.initializeUnitPrices(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, cron); registrar.addFixedRateTask(() -> { try { apiScheduler.MarketAllRequest(); diff --git a/src/main/java/com/cleanengine/coin/trade/application/TradeQueueManager.java b/src/main/java/com/cleanengine/coin/trade/application/TradeQueueManager.java index 0b198e55..29e564ff 100644 --- a/src/main/java/com/cleanengine/coin/trade/application/TradeQueueManager.java +++ b/src/main/java/com/cleanengine/coin/trade/application/TradeQueueManager.java @@ -3,9 +3,7 @@ import com.cleanengine.coin.order.application.event.OrderInsertedToQueue; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionalEventListener; @Slf4j @Component diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c67b4de9..e7402301 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -47,5 +47,5 @@ server: bot-handler: fixed-rate: 5000 # 5초마다 실행 - corn : "0 0 0 * * *" # 매일 자정마다 호가 + cron : "0 0 0 * * *" # 매일 자정마다 호가 order-level : 1,2,3,4,5 #오더북 단계 설정 - 주문량 증가 diff --git a/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java b/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java index 74bad746..57fde90d 100644 --- a/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java +++ b/src/test/java/com/cleanengine/coin/realitybot/RealitybotCoreTestSuite.java @@ -2,10 +2,7 @@ import com.cleanengine.coin.realitybot.api.ApiSchedulerTest; import com.cleanengine.coin.realitybot.api.BithumbAPIClientTest; -import com.cleanengine.coin.realitybot.api.RefresherRunnerTest; -import com.cleanengine.coin.realitybot.api.UnitPriceRefresherTest; import com.cleanengine.coin.realitybot.config.ApiClientConfigTest; -import com.cleanengine.coin.realitybot.config.SchedulerConfigTest; import com.cleanengine.coin.realitybot.domain.APIVWAPStateTest; import com.cleanengine.coin.realitybot.domain.PlatformVWAPStateTest; import com.cleanengine.coin.realitybot.domain.VWAPCalculatorTest; @@ -24,13 +21,10 @@ @Suite @SelectClasses({ - RefresherRunnerTest.class, BithumbAPIClientTest.class, - UnitPriceRefresherTest.class, OpeningPriceTest.class, PlatformVWAPStateTest.class, UnitPricePolicyTest.class, - SchedulerConfigTest.class, ApiSchedulerTest.class, ApiClientConfigTest.class, PlatformVWAPServiceTest.class, diff --git a/src/test/java/com/cleanengine/coin/realitybot/api/RefresherRunnerTest.java b/src/test/java/com/cleanengine/coin/realitybot/api/RefresherRunnerTest.java deleted file mode 100644 index ef27e0bc..00000000 --- a/src/test/java/com/cleanengine/coin/realitybot/api/RefresherRunnerTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.cleanengine.coin.realitybot.api; - -import com.cleanengine.coin.order.adapter.out.persistentce.asset.AssetRepository; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.boot.ApplicationArguments; - -import java.util.Collections; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -public class RefresherRunnerTest { - - @Spy - @InjectMocks - private UnitPriceRefresher unitPriceRefresher; - @Mock - private AssetRepository assetRepository; - @Mock - private ApplicationArguments applicationArguments; - - @BeforeEach - public void setUp(){ - when(assetRepository.findAll()).thenReturn(Collections.emptyList()); - } - - @DisplayName("어플리케이션 실행 시 호가 단위 수집") - @Test - public void runwithrefrecher() { - unitPriceRefresher.run(applicationArguments); - - verify(unitPriceRefresher,times(1)).run(any(ApplicationArguments.class)); - verify(unitPriceRefresher,times(1)).initializeUnitPrices(); - } - -// @DisplayName(" ") -} diff --git a/src/test/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresherTest.java b/src/test/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresherTest.java deleted file mode 100644 index 38fcb970..00000000 --- a/src/test/java/com/cleanengine/coin/realitybot/api/UnitPriceRefresherTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.cleanengine.coin.realitybot.api; - -import com.cleanengine.coin.order.adapter.out.persistentce.asset.AssetRepository; -import com.cleanengine.coin.order.domain.Asset; -import com.cleanengine.coin.realitybot.dto.OpeningPrice; -import com.cleanengine.coin.realitybot.parser.OpeningPriceParser; -import com.cleanengine.coin.realitybot.vo.UnitPricePolicy; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -public class UnitPriceRefresherTest { - @InjectMocks - private UnitPriceRefresher unitPriceRefresher; - - @Mock - private AssetRepository assetRepository; - - @Mock - private BithumbAPIClient bithumbAPIClient; - - @Mock - private OpeningPriceParser openingPriceParser; - - @Mock - private UnitPricePolicy unitPricePolicy; - - @DisplayName("run 시작시 호가 단위를 불러오는 지 여부") - @Test - void testRefresherUnitPrice() { - //given - String ticker = "BTC"; - Asset btc = new Asset(ticker,"비트코인",null); - - String json = "[{\"market\": \"KRW-BTC\", \"opening_price\": 1000000, \"trade_price\": 1010000}]"; - OpeningPrice parsed = new OpeningPrice(); - parsed.setOpening_price(1000000); - - when(assetRepository.findAll()).thenReturn(List.of(btc)); - when(bithumbAPIClient.getOpeningPrice(ticker)).thenReturn(json); - when(openingPriceParser.parseGson(json)).thenReturn(parsed); - when(unitPricePolicy.getUnitPrice(1000000)).thenReturn(100.0); - //when - unitPriceRefresher.refreshUnitPrices(); - - //then - double unitPrice = unitPriceRefresher.getUnitPriceByTicker(ticker); - assertEquals(100.0, unitPrice); - } - -} \ No newline at end of file diff --git a/src/test/java/com/cleanengine/coin/realitybot/config/SchedulerConfigTest.java b/src/test/java/com/cleanengine/coin/realitybot/config/SchedulerConfigTest.java deleted file mode 100644 index 3b573a5a..00000000 --- a/src/test/java/com/cleanengine/coin/realitybot/config/SchedulerConfigTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.cleanengine.coin.realitybot.config; - -import com.cleanengine.coin.realitybot.api.ApiScheduler; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; - -import java.time.Duration; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; - -@ExtendWith(MockitoExtension.class) -public class SchedulerConfigTest { - - @Mock - ApiScheduler apiScheduler; - - @Mock - ScheduledTaskRegistrar scheduledTaskRegistrar; - - @InjectMocks - SchedulerConfig schedulerConfig; - - @BeforeEach - void setUp() { - schedulerConfig = new SchedulerConfig(apiScheduler,Duration.ofMillis(500)); - } - - - @DisplayName("fixedrate를 적용 후 정상 작동하는 지") - @Test - void testConfigureTasksOnFixedRate() throws InterruptedException { - //when - schedulerConfig.configureTasks(scheduledTaskRegistrar); - - //then - ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(Runnable.class);//스케줄러에 등록된 작업 - ArgumentCaptor intervalCaptor = ArgumentCaptor.forClass(Duration.class);//실행 주기 - //mock에게 전달 된 인자를 캡처해서 확인가능하게 해줌 - //내부 속성을 확인할 때, 동적으로 생성된 값을 검증 할 때 - - verify(scheduledTaskRegistrar).addFixedRateTask(taskCaptor.capture(), intervalCaptor.capture()); - //addFixedRateTask를 실행 시 인자 두개를 캡쳐함 - - Runnable task = taskCaptor.getValue(); - //그 캡처된 인자중 task는 실제 실행하는 작업 (schedulerconfig에 구현한 것 -> apiScheduler.MarketAllRequest();) - task.run(); //가져와서 동적으로 실행할 수 있게 됨 -> apiScheduler.MarketAllRequest(); - - verify(apiScheduler).MarketAllRequest(); //작동 검증 - - Duration interval = intervalCaptor.getValue(); - assertEquals(Duration.ofMillis(500), interval); - } - @DisplayName("marketallrequest가 예외 발생 시 에러를 던지는 지 확인") - @Test - void testCheckErrorbyMarketallRequest() throws InterruptedException { - //given - doThrow(new InterruptedException()).when(apiScheduler).MarketAllRequest(); - //메서드 실행 시 에러 던지도록 셋팅 - - //when - schedulerConfig.configureTasks(scheduledTaskRegistrar); - - //then - ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(Runnable.class);//스케줄러에 등록된 작업 - verify(scheduledTaskRegistrar).addFixedRateTask(taskCaptor.capture(), any(Duration.class)); - - Runnable task = taskCaptor.getValue(); - assertThrows(RuntimeException.class, () -> task.run()); - } - -} \ No newline at end of file