@@ -138,92 +138,6 @@ void clean() {
138138 redissonClient .getKeys ().flushall ();
139139 }
140140
141- @ Nested
142- @ DisplayName ("카트 담기, 주문 동시성 통합 테스트" )
143- class TestClass {
144-
145- @ Test
146- @ DisplayName ("N명이 동시에 주문했을 때 음식 상품 메뉴의 재고수가 N개 감소한다." )
147- void concurrentOrderTest () throws InterruptedException {
148- menus = List .of (saveMenu (store , menuCategory , "메뉴1" , 5000L , 10L ));
149-
150- // given
151- int totalCustomerCount = customers .size (); // 고객 수만큼 멀티스레딩
152- ExecutorService executorService = Executors .newFixedThreadPool (totalCustomerCount );
153- CountDownLatch latch = new CountDownLatch (totalCustomerCount );
154-
155- Menu targetMenu = menus .get (0 ); // 첫 번째 메뉴를 대상으로 테스트
156- Long initialStock = targetMenu .getStockCount ();
157-
158- // when
159- for (int customerCount = 0 ; customerCount < totalCustomerCount ; customerCount ++) {
160- Customer customer = customers .get (customerCount );
161- executorService .submit (() -> executeAddCartAndOrderCreation (latch , customer , targetMenu ));
162- }
163- latch .await (10 , TimeUnit .SECONDS );
164- executorService .shutdown ();
165-
166- // then: Redis Cache 정합성
167- assertThat (redissonClient .getAtomicLong (
168- RedisCacheConstants .MENU_STOCK_PREFIX + targetMenu .getId ()).get ())
169- .isEqualTo (initialStock - totalCustomerCount );
170-
171- // then: verify RDB
172- Menu findMenu = getStockCountFromDB (targetMenu .getId ());
173- assertThat (findMenu .getStockCount ()).isEqualTo (initialStock - totalCustomerCount );
174- }
175-
176- /**
177- * 시나리오:
178- * 고객1: [메뉴1:1개, 메뉴2:1개]
179- * 고객2: [메뉴1:1개, 메뉴2:1개]
180- * 음식 상품: [메뉴1:2개, 메뉴2:1개]
181- */
182- @ Test
183- @ DisplayName ("여러 고객이 동시에 주문할 때, 재고 부족 시 롤백되어야 한다." )
184- void concurrentOrderWithLimitedStockTest () throws InterruptedException {
185- // given
186- menus = List .of (
187- saveMenu (store , menuCategory , "메뉴1" , 5000L , 2L ),
188- saveMenu (store , menuCategory , "메뉴2" , 5000L , 1L ) // 여기서 터진다.
189- );
190-
191- for (int index = 0 ; index < customers .size (); index ++) {
192- Customer customer = customers .get (index );
193- for (Menu menu : menus ) {// 각 메뉴를 1개씩 담음
194- cartService .addMenu (new AddCartCommand (customer .getId ().toString (), menu .getId ()));
195- }
196- }
197-
198- int numberOfThreads = 2 ;
199- ExecutorService executorService = Executors .newFixedThreadPool (numberOfThreads );
200- CountDownLatch latch = new CountDownLatch (numberOfThreads );
201- Menu menu1 = menus .get (0 );
202- Menu menu2 = menus .get (1 );
203-
204- // when
205- for (int personCount = 0 ; personCount < numberOfThreads ; personCount ++) {
206- final int index = personCount ;
207- executorService .submit (() -> executeOrderCreation (latch , customers .get (index )));
208- }
209- latch .await (10 , TimeUnit .MINUTES );
210- executorService .shutdown ();
211-
212- // then: Redis Cache 정합성
213- assertThat (redissonClient .getAtomicLong (
214- RedisCacheConstants .MENU_STOCK_PREFIX + menu1 .getId ()).get ()).isEqualTo (1 );
215- assertThat (redissonClient .getAtomicLong (
216- RedisCacheConstants .MENU_STOCK_PREFIX + menu2 .getId ()).get ()).isEqualTo (0 );
217-
218- // then: verify RDB
219- Menu updatedMenu1 = getStockCountFromDB (menu1 .getId ());
220- Menu updatedMenu2 = getStockCountFromDB (menu2 .getId ());
221-
222- assertThat (updatedMenu1 .getStockCount ()).isEqualTo (1 );
223- assertThat (updatedMenu2 .getStockCount ()).isEqualTo (0 );
224- }
225- }
226-
227141 /**
228142 * Test Scenario: 계좌 잔액 부족으로 인한 롤백 시나리오
229143 * 고객1: [메뉴1:3개, 메뉴2:3개, 메뉴3:3개] 잔액: 100000원
@@ -298,13 +212,14 @@ void insufficientBalanceExceptionThenRollback() throws Throwable {
298212 executorService .shutdown ();
299213
300214 // then: verify RDB
301- Menu updatedMenu1 = getStockCountFromDB (menu1 .getId ());
302- Menu updatedMenu2 = getStockCountFromDB (menu2 .getId ());
303- Menu updatedMenu3 = getStockCountFromDB (menu3 .getId ());
304-
305- assertThat (updatedMenu1 .getStockCount ()).isEqualTo (menu1ExpectedCount );
306- assertThat (updatedMenu2 .getStockCount ()).isEqualTo (menu2ExpectedCount );
307- assertThat (updatedMenu3 .getStockCount ()).isEqualTo (menu3ExpectedCount );
215+ // DB 싱크 스케줄링 변경으로 인한 주석 처리
216+ // Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
217+ // Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
218+ // Menu updatedMenu3 = getStockCountFromDB(menu3.getId());
219+ //
220+ // assertThat(updatedMenu1.getStockCount()).isEqualTo(menu1ExpectedCount);
221+ // assertThat(updatedMenu2.getStockCount()).isEqualTo(menu2ExpectedCount);
222+ // assertThat(updatedMenu3.getStockCount()).isEqualTo(menu3ExpectedCount);
308223
309224 // then: verify Redis
310225 assertThat (redissonClient .getAtomicLong (
@@ -399,13 +314,14 @@ void minOrderPriceExceptionThenRollback() throws Throwable {
399314 executorService .shutdown ();
400315
401316 // then: verify RDB
402- Menu updatedMenu1 = getStockCountFromDB (menu1 .getId ());
403- Menu updatedMenu2 = getStockCountFromDB (menu2 .getId ());
404- Menu updatedMenu3 = getStockCountFromDB (menu3 .getId ());
405-
406- assertThat (updatedMenu1 .getStockCount ()).isEqualTo (menu1ExpectedCount );
407- assertThat (updatedMenu2 .getStockCount ()).isEqualTo (menu2ExpectedCount );
408- assertThat (updatedMenu3 .getStockCount ()).isEqualTo (menu3ExpectedCount );
317+ // DB 싱크 스케줄링 변경으로 인한 주석 처리
318+ // Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
319+ // Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
320+ // Menu updatedMenu3 = getStockCountFromDB(menu3.getId());
321+ //
322+ // assertThat(updatedMenu1.getStockCount()).isEqualTo(menu1ExpectedCount);
323+ // assertThat(updatedMenu2.getStockCount()).isEqualTo(menu2ExpectedCount);
324+ // assertThat(updatedMenu3.getStockCount()).isEqualTo(menu3ExpectedCount);
409325
410326 // then: verify Redis
411327 assertThat (redissonClient .getAtomicLong (
@@ -416,6 +332,94 @@ void minOrderPriceExceptionThenRollback() throws Throwable {
416332 RedisCacheConstants .MENU_STOCK_PREFIX + menu3 .getId ()).get ()).isEqualTo (menu3ExpectedCount );
417333 }
418334
335+ @ Nested
336+ @ DisplayName ("카트 담기, 주문 동시성 통합 테스트" )
337+ class TestClass {
338+
339+ @ Test
340+ @ DisplayName ("N명이 동시에 주문했을 때 음식 상품 메뉴의 재고수가 N개 감소한다." )
341+ void concurrentOrderTest () throws InterruptedException {
342+ menus = List .of (saveMenu (store , menuCategory , "메뉴1" , 5000L , 10L ));
343+
344+ // given
345+ int totalCustomerCount = customers .size (); // 고객 수만큼 멀티스레딩
346+ ExecutorService executorService = Executors .newFixedThreadPool (totalCustomerCount );
347+ CountDownLatch latch = new CountDownLatch (totalCustomerCount );
348+
349+ Menu targetMenu = menus .get (0 ); // 첫 번째 메뉴를 대상으로 테스트
350+ Long initialStock = targetMenu .getStockCount ();
351+
352+ // when
353+ for (int customerCount = 0 ; customerCount < totalCustomerCount ; customerCount ++) {
354+ Customer customer = customers .get (customerCount );
355+ executorService .submit (() -> executeAddCartAndOrderCreation (latch , customer , targetMenu ));
356+ }
357+ latch .await (10 , TimeUnit .SECONDS );
358+ executorService .shutdown ();
359+
360+ // then: Redis Cache 정합성
361+ assertThat (redissonClient .getAtomicLong (
362+ RedisCacheConstants .MENU_STOCK_PREFIX + targetMenu .getId ()).get ())
363+ .isEqualTo (initialStock - totalCustomerCount );
364+
365+ // then: verify RDB
366+ // DB 싱크 스케줄링 변경으로 인한 주석 처리
367+ // Menu findMenu = getStockCountFromDB(targetMenu.getId());
368+ // assertThat(findMenu.getStockCount()).isEqualTo(initialStock - totalCustomerCount);
369+ }
370+
371+ /**
372+ * 시나리오:
373+ * 고객1: [메뉴1:1개, 메뉴2:1개]
374+ * 고객2: [메뉴1:1개, 메뉴2:1개]
375+ * 음식 상품: [메뉴1:2개, 메뉴2:1개]
376+ */
377+ @ Test
378+ @ DisplayName ("여러 고객이 동시에 주문할 때, 재고 부족 시 롤백되어야 한다." )
379+ void concurrentOrderWithLimitedStockTest () throws InterruptedException {
380+ // given
381+ menus = List .of (
382+ saveMenu (store , menuCategory , "메뉴1" , 5000L , 2L ),
383+ saveMenu (store , menuCategory , "메뉴2" , 5000L , 1L ) // 여기서 터진다.
384+ );
385+
386+ for (int index = 0 ; index < customers .size (); index ++) {
387+ Customer customer = customers .get (index );
388+ for (Menu menu : menus ) {// 각 메뉴를 1개씩 담음
389+ cartService .addMenu (new AddCartCommand (customer .getId ().toString (), menu .getId ()));
390+ }
391+ }
392+
393+ int numberOfThreads = 2 ;
394+ ExecutorService executorService = Executors .newFixedThreadPool (numberOfThreads );
395+ CountDownLatch latch = new CountDownLatch (numberOfThreads );
396+ Menu menu1 = menus .get (0 );
397+ Menu menu2 = menus .get (1 );
398+
399+ // when
400+ for (int personCount = 0 ; personCount < numberOfThreads ; personCount ++) {
401+ final int index = personCount ;
402+ executorService .submit (() -> executeOrderCreation (latch , customers .get (index )));
403+ }
404+ latch .await (10 , TimeUnit .MINUTES );
405+ executorService .shutdown ();
406+
407+ // then: Redis Cache 정합성
408+ assertThat (redissonClient .getAtomicLong (
409+ RedisCacheConstants .MENU_STOCK_PREFIX + menu1 .getId ()).get ()).isEqualTo (1 );
410+ assertThat (redissonClient .getAtomicLong (
411+ RedisCacheConstants .MENU_STOCK_PREFIX + menu2 .getId ()).get ()).isEqualTo (0 );
412+
413+ // then: verify RDB
414+ // DB 싱크 스케줄링 변경으로 인한 주석 처리
415+ // Menu updatedMenu1 = getStockCountFromDB(menu1.getId());
416+ // Menu updatedMenu2 = getStockCountFromDB(menu2.getId());
417+ //
418+ // assertThat(updatedMenu1.getStockCount()).isEqualTo(1);
419+ // assertThat(updatedMenu2.getStockCount()).isEqualTo(0);
420+ }
421+ }
422+
419423 void setupMenuToCart (Customer customer , Menu menu , int addCount ) {
420424 for (int count = 0 ; count < addCount ; count ++) {
421425 cartService .addMenu (new AddCartCommand (customer .getId ().toString (), menu .getId ()));
0 commit comments