-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPointServiceConcurrencyIntegrationTest.java
More file actions
138 lines (111 loc) Β· 5.39 KB
/
PointServiceConcurrencyIntegrationTest.java
File metadata and controls
138 lines (111 loc) Β· 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package com.example.ecommerceapi.point.application.service;
import com.example.ecommerceapi.common.AbstractIntegrationTest;
import com.example.ecommerceapi.common.exception.PointException;
import com.example.ecommerceapi.point.application.dto.PointResult;
import com.example.ecommerceapi.user.application.service.UserService;
import com.example.ecommerceapi.user.domain.entity.User;
import com.example.ecommerceapi.user.domain.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@DisplayName("PointService λμμ± ν΅ν© ν
μ€νΈ")
class PointServiceConcurrencyIntegrationTest extends AbstractIntegrationTest {
@Autowired
private PointService pointService;
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
private static final int THREAD_COUNT = 10;
private static final int CHARGE_AMOUNT = 10000;
@BeforeEach
void setUp() {
userService.init();
pointService.init();
}
@Test
@DisplayName("λμμ μ¬λ¬ λ² ν¬μΈνΈ μΆ©μ μμ² μ μ±κ³΅ νμ, λκ΄μ λ½ μ¬μλ μ€ν¨ νμ κ²μ¦")
void chargePoint_ShouldShowRetryLogsAndRecover_WhenConcurrent() throws InterruptedException {
// given
Integer userId = 1;
User user = userRepository.findById(userId);
Integer initialBalance = user.getPointBalance();
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
AtomicInteger recoverCalls = new AtomicInteger(0);
AtomicInteger successCalls = new AtomicInteger(0);
for (int i = 0; i < THREAD_COUNT; i++) {
int threadNumber = i + 1;
executor.submit(() -> {
try {
PointResult result = pointService.chargePoint(userId, CHARGE_AMOUNT);
successCalls.incrementAndGet();
System.out.println("[Thread " + threadNumber + "] μ±κ³΅: μμ‘=" + result.pointBalance());
} catch (PointException e) {
recoverCalls.incrementAndGet();
System.out.println("[Thread " + threadNumber + "] Recover νΈμΆ: " + e.getMessage());
} catch (ObjectOptimisticLockingFailureException e) {
System.out.println("[Thread " + threadNumber + "] μ¬μλ μ€ μΆ©λ λ°μ: " + e.getClass().getSimpleName());
} finally {
latch.countDown();
}
});
}
latch.await();
executor.shutdown();
User updatedUser = userRepository.findById(userId);
Integer expectedMinBalance = initialBalance + (CHARGE_AMOUNT * successCalls.get());
System.out.println("\n--- ν
μ€νΈ μμ½ ---");
System.out.println("μ΄κΈ° μμ‘: " + initialBalance);
System.out.println("μ±κ³΅ νΈμΆ μ: " + successCalls.get());
System.out.println("Recover νΈμΆ μ: " + recoverCalls.get());
System.out.println("μ΅μ’
μμ‘: " + updatedUser.getPointBalance());
// μ΅μ’
μμ‘ κ²μ¦
assertThat(updatedUser.getPointBalance()).isEqualTo(expectedMinBalance);
assertThat(recoverCalls.get()).isGreaterThanOrEqualTo(0);
}
/* [deprecated] λΉκ΄μ λ½μΌ λ μ±κ³΅νλ ν
μ€νΈ
@Test
@DisplayName("λμμ μ¬λ¬ λ² ν¬μΈνΈ μΆ©μ μμ² μ λͺ¨λ μμ²μ΄ μ²λ¦¬λλ€")
void chargePoint_ShouldProcessAllRequestsSequentially_WhenConcurrent() throws InterruptedException {
// given
Integer userId = 1;
User user = userRepository.findById(userId);
Integer initialBalance = user.getPointBalance();
int threads = 10; // λμμ 10λ² μΆ©μ μμ²
CountDownLatch latch = new CountDownLatch(threads);
ExecutorService executor = Executors.newFixedThreadPool(threads);
// when
for (int i = 0; i < threads; i++) {
executor.submit(() -> {
try {
pointService.chargePoint(userId, CHARGE_AMOUNT);
} catch (Exception e) {
System.out.println("μμΈ νμ
: " + e.getClass().getSimpleName());
System.out.println("μμΈ μ 체 ν΄λμ€: " + e.getClass().getName());
System.out.println("μμΈ λ©μΈμ§: " + e.getMessage());
} finally {
latch.countDown();
}
});
}
latch.await();
executor.shutdown();
// then
User updatedUser = userRepository.findById(userId);
Integer expectedBalance = initialBalance + (CHARGE_AMOUNT * threads);
System.out.println("μ΄κΈ° μμ‘: " + initialBalance);
System.out.println("μ΅μ’
μμ‘: " + updatedUser.getPointBalance());
assertThat(updatedUser.getPointBalance()).isEqualTo(expectedBalance);
}
*/
}