Skip to content

Commit cddfdcf

Browse files
authored
[Test]hikariCP 멀티스레드 환경 테스트 (#37)
* [Fix]Test 코드 수정 * [Refactor]CafeteriaServiceV2 단일 책임 원칙 적용하여 클래스 분리 * Update Java CI-CD.yml dependency submission code removed * [Refactor]CampusServiceV2 클래스 분리 * [Refactor]DietServiceV2 서비스 클래스 분리 * [fix]Cafeteria Cacheable 주석처리 * [Refactor]캐시 적용 및 중복로직 최적화 * [fix]userService에서 발생한 hikariCP connection error 찾는 중 * [Test]HikariCP 멀티스레드 환경 테슽
1 parent 05b8cc3 commit cddfdcf

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ dependencies {
5555
compileOnly 'org.projectlombok:lombok'
5656
annotationProcessor 'org.projectlombok:lombok'
5757
testImplementation 'org.springframework.boot:spring-boot-starter-test'
58+
// 테스트 코드에서 Lombok 사용할 경우 필요
59+
testCompileOnly 'org.projectlombok:lombok'
60+
testAnnotationProcessor 'org.projectlombok:lombok'
5861
compileOnly 'javax:javaee-api:8.0.1'
5962

6063
// 테스트를 위한 h2 설정

src/main/java/com/example/Jinus/service/v2/userInfo/UserServiceV2.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.example.Jinus.service.v2.userInfo;
22

33
import com.example.Jinus.repository.v2.userInfo.UserRepositoryV2;
4+
import jakarta.transaction.Transactional;
45
import lombok.RequiredArgsConstructor;
56
import lombok.extern.slf4j.Slf4j;
6-
import org.springframework.data.redis.core.RedisTemplate;
77
import org.springframework.stereotype.Service;
88

9-
import java.time.Duration;
109
import java.util.Map;
1110
import java.util.Optional;
1211

@@ -39,4 +38,12 @@ public int getUserDepartmentId(String userId) {
3938
return parentDepartmentMap.getOrDefault(departmentId, departmentId);
4039
}
4140

41+
// hikariCP test
42+
@Transactional
43+
public void selectCampusIdTest(String userId) throws InterruptedException {
44+
log.info("selectCampusIdTest 시작 - userId: {}", userId);
45+
Thread.sleep(2500); // 커넥션을 5초간 점유 (풀 점유 시 타임아웃 유도)
46+
userRepositoryV2.findCampusIdById(userId);
47+
log.info("selectCampusIdTest 종료 - userId: {}", userId);
48+
}
4249
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.example.Jinus.service.userInfo;
2+
3+
import com.example.Jinus.service.v2.userInfo.UserServiceV2;
4+
import com.zaxxer.hikari.HikariDataSource;
5+
import com.zaxxer.hikari.HikariPoolMXBean;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.boot.test.context.SpringBootTest;
11+
import org.springframework.test.context.ActiveProfiles;
12+
13+
import javax.sql.DataSource;
14+
import java.util.concurrent.CountDownLatch;
15+
import java.util.concurrent.ExecutorService;
16+
import java.util.concurrent.Executors;
17+
18+
@Slf4j
19+
@SpringBootTest
20+
@ActiveProfiles("test")
21+
public class UserServiceHikariCPTest {
22+
23+
@Autowired
24+
private UserServiceV2 userService;
25+
26+
@Autowired
27+
private DataSource dataSource;
28+
29+
@Test
30+
@DisplayName("멀티스레드 10개 생성 후 id 조회")
31+
void hikariConnectionTimeoutTest() throws InterruptedException {
32+
int threadCount = 10;
33+
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
34+
CountDownLatch latch = new CountDownLatch(threadCount);
35+
36+
for (int i = 0; i < threadCount; i++) {
37+
final int userNo = i;
38+
executor.submit(() -> {
39+
try {
40+
log.info(">> [{}] 서비스 호출 시작", userNo);
41+
userService.selectCampusIdTest("user" + userNo);
42+
log.info(">> [{}] 서비스 호출 완료", userNo);
43+
} catch (Exception e) {
44+
log.error(">> [{}] 예외 발생: {}", userNo, e.getMessage());
45+
} finally {
46+
latch.countDown();
47+
}
48+
});
49+
}
50+
51+
// 커넥션 풀 상태 모니터링 (2초마다 찍기)
52+
for (int i = 0; i < 5; i++) {
53+
printHikariStatus();
54+
Thread.sleep(2000);
55+
}
56+
57+
latch.await();
58+
executor.shutdown();
59+
}
60+
61+
private void printHikariStatus() {
62+
if (!(dataSource instanceof HikariDataSource hikariDataSource)) {
63+
log.warn("데이터소스는 Hikari가 아님: {}", dataSource.getClass());
64+
return;
65+
}
66+
67+
HikariPoolMXBean poolStats = hikariDataSource.getHikariPoolMXBean();
68+
log.info("### HikariCP 상태 체크 -> 총: {}, 사용 중: {}, 유휴: {}, 대기중 쓰레드: {}",
69+
poolStats.getTotalConnections(),
70+
poolStats.getActiveConnections(),
71+
poolStats.getIdleConnections(),
72+
poolStats.getThreadsAwaitingConnection()
73+
);
74+
}
75+
}

src/test/resources/application-test.properties

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Datasource ??
22
spring.datasource.driver-class-name=org.h2.Driver
3-
spring.datasource.url=jdbc:h2:mem:test
3+
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
44
spring.datasource.username=sa
55
spring.datasource.password=
6+
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
67

78
# H2 ??????? Hibernate dialect(JPA? ??? ?????? ???? ????)
89
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
@@ -14,4 +15,13 @@ spring.jpa.properties.hibernate.format_sql=true
1415

1516
# H2 ?? ??
1617
spring.h2.console.enabled=true
17-
spring.h2.console.path=/h2
18+
spring.h2.console.path=/h2
19+
20+
spring.datasource.hikari.maximumPoolSize=4
21+
spring.datasource.hikari.connectionTimeout=3000
22+
spring.datasource.hikari.maxLifetime=50000
23+
spring.datasource.hikari.idleTimeout=0
24+
spring.datasource.hikari.keepaliveTime=30000
25+
spring.datasource.hikari.validationTimeout=1000
26+
logging.level.com.zaxxer.hikari=trace
27+
logging.level.com.zaxxer.hikari.HikariConfig=debug

0 commit comments

Comments
 (0)