[ShedLock 추가 및 멤버 더미데이터 생성 로직 구현현] 분산 스케줄링을 위한 ShedLock 라이브러리를 추가하고, 멤버 추가(더미데이터) 스케줄러를 구현
This commit is contained in:
14
README.md
14
README.md
@@ -355,14 +355,20 @@ public class Member extends BaseEntity {
|
||||
|
||||
### 11. 데이터베이스 스키마
|
||||
|
||||
**데이터베이스 테이블 구조는 `ddl/schema.sql`에 정의되어 있습니다.**
|
||||
**데이터베이스 테이블 구조는 `ddl/schema_entity.sql`에 정의되어 있습니다.**
|
||||
|
||||
#### 스키마 파일
|
||||
|
||||
- **위치**: `ddl/schema.sql`
|
||||
- **내용**: 모든 테이블의 CREATE TABLE DDL 스크립트
|
||||
- **위치**: `ddl/schema_entity.sql`
|
||||
- **내용**: 모든 엔티티 테이블의 CREATE TABLE DDL 스크립트
|
||||
|
||||
#### 초기화 스크립트
|
||||
|
||||
- **위치**: `src/main/resources/schema_initial.sql`
|
||||
- **내용**: 서버 부팅 시 자동 실행되는 초기화 스크립트 (예: shedlock 테이블 등)
|
||||
|
||||
#### 사용 방법
|
||||
|
||||
- **자동 생성**: 애플리케이션 시작 시 `schema.sql`로 테이블 자동 생성
|
||||
- **자동 생성**: 애플리케이션 시작 시 `schema_entity.sql`로 엔티티 테이블 자동 생성
|
||||
- **초기화**: 서버 부팅 시 `schema_initial.sql`로 시스템 테이블 자동 생성
|
||||
- **설정**: `spring.jpa.hibernate.ddl-auto=none`으로 Hibernate 자동 스키마 생성 비활성화
|
||||
|
@@ -73,6 +73,10 @@ dependencies {
|
||||
|
||||
// SpringDoc OpenAPI (Swagger)
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'
|
||||
|
||||
// ShedLock for distributed scheduling
|
||||
implementation 'net.javacrumbs.shedlock:shedlock-spring:5.10.2'
|
||||
implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.10.2'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
@@ -0,0 +1,105 @@
|
||||
package com.bio.bio_backend.domain.base.member.scheduler;
|
||||
|
||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||
import com.bio.bio_backend.domain.base.member.service.MemberService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 멤버 동기화 스케줄러
|
||||
* ShedLock을 사용하여 분산 환경에서 중복 실행을 방지합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MemberSyncScheduler {
|
||||
|
||||
private final MemberService memberService;
|
||||
|
||||
/**
|
||||
* 1시간마다 멤버 동기화를 실행합니다.
|
||||
* 현재는 더미 데이터를 생성하지만, 추후 실제 조직도 연동으로 변경 예정입니다.
|
||||
*/
|
||||
@Scheduled(cron = "0 0 * * * *") // 1시간마다
|
||||
@SchedulerLock(name = "memberSync", lockAtMostFor = "50m", lockAtLeastFor = "5m")
|
||||
public void syncMembersAtTopOfHour() {
|
||||
log.info("1시간마다 멤버 동기화 시작");
|
||||
try {
|
||||
int createdCount = createDummyMembers(5); // 1시간마다 5명씩 생성
|
||||
log.info("1시간마다 멤버 동기화 완료: {}명 생성", createdCount);
|
||||
} catch (Exception e) {
|
||||
log.error("1시간마다 멤버 동기화 실패: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 매일 새벽 2시에 대량 동기화를 실행합니다.
|
||||
*/
|
||||
@Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시
|
||||
@SchedulerLock(name = "memberBulkSync", lockAtMostFor = "30m", lockAtLeastFor = "5m")
|
||||
public void bulkSyncMembers() {
|
||||
log.info("일일 대량 멤버 동기화 시작");
|
||||
try {
|
||||
int createdCount = createDummyMembers(20); // 새벽에 20명씩 생성
|
||||
log.info("일일 대량 멤버 동기화 완료: {}명 생성", createdCount);
|
||||
} catch (Exception e) {
|
||||
log.error("일일 대량 멤버 동기화 실패: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 더미 멤버를 생성합니다.
|
||||
* 추후 실제 조직도 API 연동으로 변경될 예정입니다.
|
||||
*
|
||||
* @param count 생성할 멤버 수
|
||||
* @return 실제 생성된 멤버 수
|
||||
*/
|
||||
private int createDummyMembers(int count) {
|
||||
int createdCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
try {
|
||||
String randomSuffix = String.valueOf(System.currentTimeMillis() + i);
|
||||
|
||||
MemberDto memberDto = MemberDto.builder()
|
||||
.userId("user_" + randomSuffix)
|
||||
.password("password123") // 실제로는 더 복잡한 패스워드 생성
|
||||
.name(generateRandomName())
|
||||
.email("user_" + randomSuffix + "@company.com")
|
||||
.build();
|
||||
|
||||
memberService.createMember(memberDto);
|
||||
createdCount++;
|
||||
|
||||
log.debug("더미 멤버 생성 완료: {}", memberDto.getUserId());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("더미 멤버 생성 실패 ({}번째): {}", i + 1, e.getMessage());
|
||||
// 개별 멤버 생성 실패는 전체 프로세스를 중단하지 않음
|
||||
}
|
||||
}
|
||||
|
||||
return createdCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 랜덤한 이름을 생성합니다.
|
||||
* 실제 환경에서는 조직도에서 가져온 실제 이름을 사용합니다.
|
||||
*
|
||||
* @return 생성된 랜덤 이름
|
||||
*/
|
||||
private String generateRandomName() {
|
||||
String[] surnames = {"김", "이", "박", "최", "정", "강", "조", "윤", "장", "임"};
|
||||
String[] givenNames = {"민수", "지영", "현우", "수진", "태현", "은지", "동훈", "예린", "준호", "서연"};
|
||||
|
||||
String surname = surnames[ThreadLocalRandom.current().nextInt(surnames.length)];
|
||||
String givenName = givenNames[ThreadLocalRandom.current().nextInt(givenNames.length)];
|
||||
|
||||
return surname + givenName;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package com.bio.bio_backend.global.config;
|
||||
|
||||
import net.javacrumbs.shedlock.core.LockProvider;
|
||||
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
|
||||
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* 스케줄러 및 분산 락 설정을 위한 Configuration 클래스
|
||||
* ShedLock을 사용하여 분산 환경에서 스케줄러 중복 실행을 방지합니다.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
|
||||
public class SchedulerConfig {
|
||||
|
||||
/**
|
||||
* ShedLock용 LockProvider Bean을 생성합니다.
|
||||
* JDBC 기반으로 분산 락을 관리합니다.
|
||||
*
|
||||
* @param dataSource 데이터소스
|
||||
* @return JdbcTemplateLockProvider 인스턴스
|
||||
*/
|
||||
@Bean
|
||||
public LockProvider lockProvider(DataSource dataSource) {
|
||||
return new JdbcTemplateLockProvider(
|
||||
JdbcTemplateLockProvider.Configuration.builder()
|
||||
.withJdbcTemplate(new JdbcTemplate(dataSource))
|
||||
.usingDbTime() // DB 시간 사용으로 서버 간 시간차 문제 해결
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@ spring.devtools.restart.additional-paths=src/main/java
|
||||
# ========================================
|
||||
# 데이터베이스 설정
|
||||
# ========================================
|
||||
spring.datasource.url=jdbc:postgresql://stam.kr:15432/imas
|
||||
spring.datasource.url=jdbc:postgresql://stam.kr:15432/imas?options=-c%20TimeZone=Asia/Seoul
|
||||
spring.datasource.username=imas_user
|
||||
spring.datasource.password=stam1201
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
@@ -24,6 +24,10 @@ spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
# spring.datasource.username=${DB_USERNAME:}
|
||||
# spring.datasource.password=${DB_PASSWORD:}
|
||||
|
||||
# 항상 schema_initial.sql, data.sql 실행
|
||||
spring.sql.init.mode=always
|
||||
spring.sql.init.schema-locations=classpath:schema_initial.sql
|
||||
|
||||
# ========================================
|
||||
# JPA/Hibernate 설정
|
||||
# ========================================
|
||||
@@ -43,7 +47,7 @@ spring.jpa.properties.hibernate.order_updates=true
|
||||
|
||||
# 스키마 생성 설정
|
||||
spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
|
||||
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=ddl/schema.sql
|
||||
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=ddl/schema_entity.sql
|
||||
spring.jpa.properties.hibernate.hbm2ddl.schema-generation.script.append=false
|
||||
|
||||
# ========================================
|
||||
|
7
src/main/resources/schema_initial.sql
Normal file
7
src/main/resources/schema_initial.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS shedlock (
|
||||
name varchar(64) NOT NULL,
|
||||
lock_until timestamptz(3) NOT NULL,
|
||||
locked_at timestamptz(3) NOT NULL,
|
||||
locked_by varchar(255) NOT NULL,
|
||||
CONSTRAINT shedlock_pkey PRIMARY KEY (name)
|
||||
);
|
Reference in New Issue
Block a user