feat: add Redis/Kafka boilerplate for distributed environment migration - #53
Conversation
|
Warning Review limit reached
Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughRedis와 Kafka 기반 인프라를 추가했습니다. 배치 작업에 Redis 분산 락을 적용했습니다. API CORS와 서비스 연결 설정을 갱신했습니다. Problem-AI k6 테스트 대상과 실행 결과를 업데이트했습니다. Changes플랫폼 인프라 구성
배치 스케줄러 분산 락
Problem-AI 부하 테스트 갱신
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (4)
momogo-core/src/main/java/com/momogo/core/common/config/KafkaConfig.java (1)
10-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win재시도 횟수와 DLT 이름 설명을 실제 설정과 일치시키세요.
maxAttempts(4)는 최초 처리 1회를 포함한 총 4회 처리입니다. 따라서 지연 재시도는 1초, 2초, 4초의 3회입니다. 기본 DLT 접미사는".DLT"가 아니라"-dlt"입니다.4회의 지연 재시도가 요구사항이면
maxAttempts(5)로 변경하세요. 총 4회 처리가 요구사항이면 JavaDoc을 수정하세요. Spring Kafka 관리 버전에서 동작을 확인하세요. (docs.spring.io)Also applies to: 20-22
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@momogo-core/src/main/java/com/momogo/core/common/config/KafkaConfig.java` around lines 10 - 12, KafkaConfig의 공통 재시도/DLT 정책 JavaDoc을 실제 설정과 일치시키세요. maxAttempts(4)가 최초 처리 포함 총 4회 처리인지 확인해 지연 재시도 횟수를 3회로 설명하고, 기본 DLT 접미사를 ".DLT"가 아닌 "-dlt"로 수정하세요. 요구사항이 지연 재시도 4회라면 관련 설정을 maxAttempts(5)로 변경하고, 해당 Spring Kafka 버전의 동작과 일치하는지 검증하세요.momogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.java (3)
18-20: 🗄️ Data Integrity & Integration | 🔵 Trivial운영 Redis의 장애 모델을 확인하세요.
RedisLockProvider는 Redis 장애나 master failover 이후 잠금 상태를 잃을 수 있습니다. 이 경우 여러 배치 인스턴스가 동시에 실행될 수 있습니다. 운영 Redis 토폴로지를 확인하세요. 중복 실행이 허용되지 않으면 작업의 멱등성 또는 fencing 전략을 추가하고, 필요하면 더 강한 일관성의 잠금 저장소를 검토하세요. (github.com)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@momogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.java` around lines 18 - 20, Review the Redis topology and failure behavior used by lockProvider, including master failover and lock-state loss scenarios. If duplicate batch execution is not acceptable, add an appropriate idempotency or fencing strategy to the scheduled jobs, or replace RedisLockProvider with a lock store that provides the required consistency guarantees.
18-20: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy liftRedis 기반 분산 락 통합 테스트를 추가하세요.
동일한 Redis와 잠금 이름을 사용하는 두 개의 batch ApplicationContext를 테스트하세요. 한 인스턴스만 작업 부수효과를 수행하고 다른 인스턴스는 건너뛰는지 검증하세요. 잠금 만료, Redis 재시작, 스케줄러 프록시 활성화도 검증하세요. ShedLock은 동일한 잠금 이름이 보유 중이면 다른 실행을 대기시키지 않고 건너뜁니다. (github.com)
PR 목표의
Test code is not yet completed항목을 반영한 제안입니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@momogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.java` around lines 18 - 20, Redis 기반 분산 락의 통합 테스트를 추가하세요. ShedLockConfig의 lockProvider가 반환하는 동일한 Redis 연결과 잠금 이름을 공유하도록 두 개의 batch ApplicationContext를 구성하고, 한 인스턴스만 작업 부수효과를 수행하며 다른 인스턴스는 건너뛰는지 검증하세요. 잠금 만료와 Redis 재시작 후 재획득, 스케줄러 프록시 활성화 동작도 테스트하고, 잠금 보유 중 실행은 대기하지 않고 건너뛰는지 확인하세요.
10-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win분산 락의 보장 범위를 정확히 문서화하세요.
현재 문구의
한 번만 실행은 exactly-once 보장처럼 읽힙니다. ShedLock은 동일한 이름의 작업이 동시에 실행되는 것을 제한합니다. 잠금 만료나 Redis 장애 이후 재실행은 가능하므로 작업은 멱등해야 합니다. (github.com)제안 수정
/** - * 배치 인스턴스가 여러 대로 늘어나도 `@Scheduled` 작업이 한 번만 실행되도록 하는 분산 락 설정. + * 동일한 잠금 이름의 `@Scheduled` 작업이 여러 인스턴스에서 동시에 실행되지 않도록 조정하는 분산 락 설정. + * 잠금 만료 또는 Redis 장애 시 재실행될 수 있으므로 작업은 멱등해야 한다. * 스케줄러 메서드에 `@SchedulerLock`(name = "...")을 붙이면 이 락이 적용된다. */🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@momogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.java` around lines 10 - 12, ShedLockConfig의 클래스 주석에서 스케줄 작업이 “한 번만 실행”된다는 표현을 제거하고, 동일한 락 이름의 작업에 대해 동시 실행만 제한한다고 명시하세요. 락 만료나 Redis 장애 후 재실행될 수 있으므로 작업이 멱등해야 한다는 요구사항도 함께 문서화하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docker-compose.yml`:
- Around line 16-17: Update the Kafka listener configuration in
docker-compose.yml to separate host and Docker-network endpoints: retain a
localhost listener for host clients and add an internal broker listener
advertised with the Kafka service hostname. Configure Kafka UI to use the
internal listener rather than localhost, and keep KAFKA_LISTENERS aligned with
both advertised endpoints.
In `@k6/problem-ai/config.js`:
- Around line 16-17: Update the BASE_URL configuration to use the valid
certificate-backed HTTPS endpoint instead of the public HTTP address, ensuring
load-test.js sends Authorization, X-XSRF-TOKEN, and Cookie headers only over
HTTPS.
In `@k6/problem-ai/load-result.json`:
- Around line 163-164: Remove the serialized setup_data authentication fields
authToken and csrfToken from k6/problem-ai/load-result.json lines 163-164,
k6/problem-ai/soak-result.json lines 132-134, k6/problem-ai/spike-result.json
lines 159-161, and k6/problem-ai/stress-result.json lines 166-168; update the
common result-generation flow to exclude or mask these sensitive values, and
revoke any exposed tokens and remove them from repository history if necessary.
In `@momogo-api/src/main/java/com/momogo/api/auth/config/SecurityConfig.java`:
- Around line 197-199: Remove the HTTP production origin "http://momogo.kro.kr"
from the credentialed CORS origins in SecurityConfig, retaining only
"https://momogo.kro.kr" for production while preserving the localhost origin.
In `@momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java`:
- Line 37: 각 스케줄러의 ShedLock 어노테이션에 업무 주기보다 충분히 짧은 lockAtLeastFor를 추가하세요.
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java 37-37의
dailyReportJob에는 예시로 PT5M을 설정하고, 51-51의 weeklyReportJob과
momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java
24-24의 expiredUserCleanupJob에도 적절한 최소 잠금 시간을 설정하되 기존 lockAtMostFor는 유지하세요.
- Line 37: Update the ShedLock TTLs on BatchScheduler.dailyReportJob
(BatchScheduler.java:37-37), BatchScheduler.weeklyReportJob
(BatchScheduler.java:51-51), and
ExpiredUserCleanupScheduler.expiredUserCleanupJob
(ExpiredUserCleanupScheduler.java:24-24) individually, replacing the fixed PT30M
values with durations exceeding each job’s measured P99 execution time plus
safety margin; base report jobs on createReports() and cleanup on
deleteExpiredUsers().
---
Nitpick comments:
In `@momogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.java`:
- Around line 18-20: Review the Redis topology and failure behavior used by
lockProvider, including master failover and lock-state loss scenarios. If
duplicate batch execution is not acceptable, add an appropriate idempotency or
fencing strategy to the scheduled jobs, or replace RedisLockProvider with a lock
store that provides the required consistency guarantees.
- Around line 18-20: Redis 기반 분산 락의 통합 테스트를 추가하세요. ShedLockConfig의 lockProvider가
반환하는 동일한 Redis 연결과 잠금 이름을 공유하도록 두 개의 batch ApplicationContext를 구성하고, 한 인스턴스만 작업
부수효과를 수행하며 다른 인스턴스는 건너뛰는지 검증하세요. 잠금 만료와 Redis 재시작 후 재획득, 스케줄러 프록시 활성화 동작도 테스트하고,
잠금 보유 중 실행은 대기하지 않고 건너뛰는지 확인하세요.
- Around line 10-12: ShedLockConfig의 클래스 주석에서 스케줄 작업이 “한 번만 실행”된다는 표현을 제거하고, 동일한
락 이름의 작업에 대해 동시 실행만 제한한다고 명시하세요. 락 만료나 Redis 장애 후 재실행될 수 있으므로 작업이 멱등해야 한다는 요구사항도
함께 문서화하세요.
In `@momogo-core/src/main/java/com/momogo/core/common/config/KafkaConfig.java`:
- Around line 10-12: KafkaConfig의 공통 재시도/DLT 정책 JavaDoc을 실제 설정과 일치시키세요.
maxAttempts(4)가 최초 처리 포함 총 4회 처리인지 확인해 지연 재시도 횟수를 3회로 설명하고, 기본 DLT 접미사를 ".DLT"가
아닌 "-dlt"로 수정하세요. 요구사항이 지연 재시도 4회라면 관련 설정을 maxAttempts(5)로 변경하고, 해당 Spring Kafka
버전의 동작과 일치하는지 검증하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: fc7ae5ed-1278-4bc4-a381-6399930bae90
📒 Files selected for processing (18)
docker-compose.ymlk6/problem-ai/config.jsk6/problem-ai/load-result.jsonk6/problem-ai/soak-result.jsonk6/problem-ai/spike-result.jsonk6/problem-ai/stress-result.jsonmomogo-api/src/main/java/com/momogo/api/auth/config/SecurityConfig.javamomogo-api/src/main/resources/application.yamlmomogo-batch/build.gradlemomogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.javamomogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.javamomogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.javamomogo-batch/src/main/resources/application.yamlmomogo-core/build.gradlemomogo-core/src/main/java/com/momogo/core/common/config/KafkaConfig.javamomogo-core/src/main/java/com/momogo/core/common/config/KafkaTopics.javamomogo-core/src/main/java/com/momogo/core/common/config/RedisConfig.javamomogo-realtime/src/main/resources/application.yaml
| KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093 | ||
| KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
[중대] 컨테이너용 Kafka 리스너를 분리하세요.
KAFKA_ADVERTISED_LISTENERS가 localhost:9092만 반환합니다. Kafka UI는 메타데이터를 받은 뒤 자기 컨테이너의 localhost:9092에 연결합니다. 따라서 Kafka UI가 브로커에 연결하지 못합니다.
호스트용 리스너와 Docker 네트워크용 리스너를 분리하세요. Kafka UI는 내부 리스너를 사용해야 합니다.
수정 예시
- KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
+ KAFKA_LISTENERS: HOST://:9092,INTERNAL://:29092,CONTROLLER://:9093
+ KAFKA_ADVERTISED_LISTENERS: HOST://localhost:9092,INTERNAL://kafka:29092
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
- KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,HOST:PLAINTEXT,INTERNAL:PLAINTEXT
+ KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
...
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
+ KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docker-compose.yml` around lines 16 - 17, Update the Kafka listener
configuration in docker-compose.yml to separate host and Docker-network
endpoints: retain a localhost listener for host clients and add an internal
broker listener advertised with the Kafka service hostname. Configure Kafka UI
to use the internal listener rather than localhost, and keep KAFKA_LISTENERS
aligned with both advertised endpoints.
There was a problem hiding this comment.
현재 보일러 플레이트용 구성을 한 상태입니다. 추후 kafka 리스너를 구현하고 적용하면서 수정하도록 하겠습니다.
There was a problem hiding this comment.
@SungHuii, 확인했습니다. 현재 PR의 보일러플레이트 범위에서는 이 항목을 보류하겠습니다.
Kafka 리스너를 적용하는 작업 시점에는 호스트용 엔드포인트와 Docker 네트워크용 엔드포인트를 분리해야 합니다. 그렇지 않으면 Kafka UI와 같은 컨테이너 클라이언트가 localhost:9092로 연결을 시도할 수 있습니다.
이 작업을 추적할 GitHub 후속 이슈를 생성할까요?
🐇
You are interacting with an AI system.
| // 1. [서버 주소] EC2 + Nginx (momogo.kro.kr) | ||
| export const BASE_URL = 'http://momogo.kro.kr'; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
[심각] 인증 요청에 HTTPS를 사용하세요.
load-test.js의 요청은 Authorization, X-XSRF-TOKEN, Cookie 헤더를 전송합니다. 현재 공개 HTTP 주소를 사용하므로 네트워크 구간에서 인증 정보가 노출될 수 있습니다.
유효한 인증서가 설정된 HTTPS 주소를 사용하세요.
🔐 변경 예시
-export const BASE_URL = 'http://momogo.kro.kr';
+export const BASE_URL = 'https://momogo.kro.kr';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 1. [서버 주소] EC2 + Nginx (momogo.kro.kr) | |
| export const BASE_URL = 'http://momogo.kro.kr'; | |
| // 1. [서버 주소] EC2 + Nginx (momogo.kro.kr) | |
| export const BASE_URL = 'https://momogo.kro.kr'; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@k6/problem-ai/config.js` around lines 16 - 17, Update the BASE_URL
configuration to use the valid certificate-backed HTTPS endpoint instead of the
public HTTP address, ensuring load-test.js sends Authorization, X-XSRF-TOKEN,
and Cookie headers only over HTTPS.
There was a problem hiding this comment.
이 부분은 아직 HTTPS 설정이 되어있지 않습니다. 추후 HTTPS를 구성한 후 해결하도록 하겠습니다.
There was a problem hiding this comment.
@SungHuii, 확인했습니다. HTTPS 설정은 별도 작업으로 진행해 주세요. 인프라 구성이 끝나면 BASE_URL 값을 https://momogo.kro.kr로 변경해 주세요. 이 항목은 나중에 처리하는 것으로 남겨두겠습니다.
(๑•̀ㅁ•́๑) 응원합니다, 화이팅!
You are interacting with an AI system.
| "http://localhost:5173", | ||
| "http://momogo.kro.kr", | ||
| "https://momogo.kro.kr" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -i -C 4 \
'momogo\.kro\.kr|allowedOrigins|setAllowCredentials|strict-transport-security|hsts|set-cookie|responsecookie|samesite|setsecure|secure\s*\(' \
.Repository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 16613
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== SecurityConfig security / cors sections =="
sed -n '160,245p' momogo-api/src/main/java/com/momogo/api/auth/config/SecurityConfig.java
echo
echo "== JwtTokenProvider cookie creation section =="
sed -n '240,265p' momogo-api/src/main/java/com/momogo/api/auth/jwt/JwtTokenProvider.java
echo
echo "== application property files with jwt cors / cookie settings =="
rg -n -i 'jwt|cors|refresh-token.cookie|cors' -g 'application*.properties' -g 'application*.yml' .Repository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 3610
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('momogo-api/src/main/java/com/momogo/api/auth/config/SecurityConfig.java')
text = p.read_text()
print('SecurityConfig.java path exists:', p.exists())
for needle in [
'setAllowCredentials(true)',
'"http://localhost:5173"',
'"http://momogo.kro.kr"',
'"https://momogo.kro.kr"',
'strict-transport-security',
'hsts',
]:
print(f'{needle!r}:', needle in text)
# Extract CORS origin/security settings using simple line-scanning.
origins = []
allow_credentials = None
for line in text.splitlines():
stripped = line.strip()
if 'setAllowedOrigins' in stripped:
continue
if 'setAllowCredentials' in stripped:
allow_credentials = 'true' in stripped
if '"' in stripped:
origin = stripped.strip().strip(',').strip('"').strip()
if origin and (origin.startswith('http://') or origin.startswith('https://')):
origins.append(origin)
print('extracted_http_origins:', [o for o in origins if o.startswith('http://')])
print('extracted_https_origins:', [o for o in origins if o.startswith('https://')])
print('allow_credentials:', allow_credentials)
PYRepository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 529
[중대] 자격 증명 허용 CORS에서 HTTP 운영 Origin을 제거하세요.
http://momogo.kro.kr는 암호화되지 않은 외부 Origin입니다. setAllowCredentials(true)가 있는 CORS 설정에서 HTTP 운영 Origin을 허용하면, 브라우저가 자격 증명을 포함할 때 공격자가 스나이핑한 HTTP 트래픽에서 스크립트가 운영 도메인 API 요청을 실행할 수 있습니다.
운영 CORS에는 https://momogo.kro.kr만 유지하세요. HTTP에서 HTTPS로의 리다이렉트와 HSTS는 배포 프록시/TLS 종료 지점에서 적용하고, 인증 쿠키의 Secure 및 SameSite 속성도 함께 확인하세요.
수정 예시
configuration.setAllowedOrigins(List.of(
"http://localhost:5173",
- "http://momogo.kro.kr",
"https://momogo.kro.kr"
));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "http://localhost:5173", | |
| "http://momogo.kro.kr", | |
| "https://momogo.kro.kr" | |
| "http://localhost:5173", | |
| "https://momogo.kro.kr" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@momogo-api/src/main/java/com/momogo/api/auth/config/SecurityConfig.java`
around lines 197 - 199, Remove the HTTP production origin "http://momogo.kro.kr"
from the credentialed CORS origins in SecurityConfig, retaining only
"https://momogo.kro.kr" for production while preserving the localhost origin.
There was a problem hiding this comment.
이 부분은 아직 HTTPS 설정이 되어있지 않습니다. 추후 HTTPS를 구성한 후 해결하도록 하겠습니다.
There was a problem hiding this comment.
이 부분은 팀원의 말처럼 아직 HTTPS 설정이 되어있지 않습니다. 추후 HTTPS를 구성한 후 해결하도록 하겠습니다.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
@coderabbitai 그냥 나중에 해당 부분은 HTTPS가 구성되면 적용토록 하겠습니다.
There was a problem hiding this comment.
@Junkov0 확인했습니다. HTTPS 구성 전까지 이 보안 위험은 남아 있습니다.
HTTPS 구성 시 SecurityConfig에서 http://momogo.kro.kr를 제거하고, https://momogo.kro.kr만 자격 증명 허용 CORS Origin으로 유지해야 합니다. TLS 종료 지점의 HTTP→HTTPS 리다이렉트와 인증 쿠키의 Secure/SameSite 설정도 함께 확인하세요.
이 작업을 추적할 GitHub 후속 이슈를 생성할까요?
🐰 …
You are interacting with an AI system.
|
|
||
| // 매일 00:10 - 어제 하루치 개인 일간 리포트 생성 | ||
| @Scheduled(cron = "0 10 0 * * *") | ||
| @SchedulerLock(name = "dailyReportJob", lockAtMostFor = "PT30M") |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -a 'BatchScheduler\.java|ExpiredUserCleanupScheduler\.java' . || true
echo
echo "== imports and SchedulerLock usages =="
rg -n "SchedulerLock|shedlock|lockAtMostFor|lockAtLeastFor|CronTrigger|`@Scheduled`" -S . || true
echo
echo "== relevant source snippets =="
for f in $(fd 'BatchScheduler\.java|ExpiredUserCleanupScheduler\.java' .); do
echo "--- $f"
wc -l "$f"
sed -n '1,120p' "$f" | cat -n
done
echo
echo "== dependency references for shedlock =="
fd -a 'build.gradle|build.gradle.kts|pom.xml|gradle.properties|settings.gradle|settings.gradle.kts' . | while read -r f; do
echo "--- $f"
rg -n "shedlock|ShedLock|scheduleLock|lockAtLeastFor|lockProvider|lock-store|mongodb|redis|consul|jdbc" "$f" || true
doneRepository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 7409
🌐 Web query:
ShedLock lockAtLeastFor prevents repeated executions after job finishes lockAtMostFor duration
💡 Result:
Yes, that is correct. In ShedLock, the lock is normally released as soon as the task finishes [1][2]. However, if you specify the lockAtLeastFor attribute, ShedLock will explicitly hold the lock for at least that duration, preventing other nodes from executing the same task even if the original task has already completed [3][4][5]. The primary purpose of lockAtLeastFor is to ensure that a task is executed at most once within a given period [3][4]. This is particularly useful for short-running tasks where, due to clock synchronization differences between nodes, one node might otherwise perceive the lock as released and immediately trigger the task again [1][6][2]. By contrast, lockAtMostFor serves as a safety mechanism (a fallback) to ensure that if a node crashes or the JVM dies before the task completes, the lock will eventually be released after the specified duration, allowing other nodes to take over [3][1][2]. You should always set lockAtMostFor to a value significantly longer than the maximum expected execution time of your task; if a task runs longer than this duration, the behavior becomes unpredictable, potentially leading to multiple processes holding the lock simultaneously [1][6][2].
Citations:
- 1: https://github.com/lukas-krecan/ShedLock?tab=readme-ov-file
- 2: https://github.com/lukas-krecan/ShedLock/blob/master/README.md
- 3: https://javadoc.io/static/net.javacrumbs.shedlock/shedlock-core/4.34.0/net/javacrumbs/shedlock/core/SchedulerLock.html
- 4: https://javadoc.io/static/net.javacrumbs.shedlock/shedlock-spring/4.0.0/net/javacrumbs/shedlock/spring/annotation/SchedulerLock.html
- 5: The lock is NOT released as soon the tasks finishes lukas-krecan/ShedLock#1542
- 6: https://github.com/lukas-krecan/ShedLock/
🌐 Web query:
ShedLock SchedulerLock annotation lockAtLeastFor default lockAtMostFor release immediately after task done
💡 Result:
In ShedLock, the lock is released as soon as the task finishes, with one notable exception involving the lockAtLeastFor attribute [1][2][3]. Here is how the attributes function regarding lock release: 1. lockAtMostFor: This serves as a safety net [4][5]. If the node executing the task crashes or dies, the lock is guaranteed to be released after this duration [4][1][2]. You should set this to a value significantly longer than your task's maximum expected execution time to avoid premature expiration [1][2][3]. If a task takes longer than this duration, the behavior becomes unpredictable, as the lock might be acquired by another node [1][2][3]. If not specified in the @SchedulerLock annotation, the default value defined in @EnableSchedulerLock is used [6][2][3]. 2. lockAtLeastFor: This attribute mandates that the lock be held for at least the specified duration, even if the task completes earlier [4][1][2]. Its primary purpose is to prevent the task from being executed more than once in a given period in scenarios where task execution is very short and there is clock drift between nodes [1][2][3]. If this is set, the lock is not released immediately upon task completion; it is held until this minimum time has elapsed [7][8][9]. Under normal circumstances (without lockAtLeastFor), the library attempts to release the lock immediately upon the conclusion of the task [4][5][9]. There is no native configuration to override this to force an immediate release if lockAtLeastFor is active, as that would contradict the purpose of the setting [7][8].
Citations:
- 1: https://github.com/lukas-krecan/ShedLock/
- 2: https://github.com/lukas-krecan/ShedLock/blob/master/README.md
- 3: https://github.com/lukas-krecan/ShedLock?tab=readme-ov-file
- 4: https://javadoc.io/static/net.javacrumbs.shedlock/shedlock-core/4.34.0/net/javacrumbs/shedlock/core/SchedulerLock.html
- 5: https://github.com/lukas-krecan/ShedLock/blob/master/spring/shedlock-spring/src/main/java/net/javacrumbs/shedlock/spring/annotation/SchedulerLock.java
- 6: Difference between defaultLockAtMostFor, lockAtMostFor and lockAtLeastFor? lukas-krecan/ShedLock#198
- 7: The lock is NOT released as soon the tasks finishes lukas-krecan/ShedLock#1542
- 8: Feature Request - Manual Lock Release lukas-krecan/ShedLock#2336
- 9: https://github.com/lukas-krecan/ShedLock/blob/master/shedlock-core/src/main/java/net/javacrumbs/shedlock/core/DefaultLockingTaskExecutor.java
각 스케줄러에 lockAtLeastFor와 함께 최소 잠금 시간을 설정하세요.
lockAtMostFor는 장애 시 잠금이 자동 해제되도록 하는 상한 보호 설정입니다. 이 설정만 사용되면 작업이 끝나면 잠금이 바로 해제되므로, 짧은 실행 시간이나 노드 간 clock drift 때문에 같은 @Scheduled 크론 스팀의 재호출이 같은 잠금 이름으로 다시 실행될 수 있습니다. 이 작업들은 "하루/일주 단건 실행"이 목적이라 각 어노테이션에业务주기보다 충분히 짧은 최소 잠금 시간을 추가해 중복 실행을 막는 편이 안전합니다.
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java#L37:dailyReportJob에lockAtLeastFor = "PT5M"처럼 최소 잠금 시간을 추가하세요.momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java#L51:weeklyReportJob에lockAtLeastFor를 추가하세요.momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java#L24:expiredUserCleanupJob에도 최소 잠금 시간을 추가하세요.
📍 Affects 2 files
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java#L37-L37(this comment)momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java#L51-L51momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java#L24-L24
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java` at
line 37, 각 스케줄러의 ShedLock 어노테이션에 업무 주기보다 충분히 짧은 lockAtLeastFor를 추가하세요.
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java 37-37의
dailyReportJob에는 예시로 PT5M을 설정하고, 51-51의 weeklyReportJob과
momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java
24-24의 expiredUserCleanupJob에도 적절한 최소 잠금 시간을 설정하되 기존 lockAtMostFor는 유지하세요.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 'runDailyReportJob|runWeeklyReportJob|runExpiredUserCleanupJob|deleteExpiredUsers|`@Timed`|Timer|Observation' . --glob '*.java'Repository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 10124
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Locate and inspect the relevant scheduler/service implementations and any shedlock configuration references.
for f in \
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java \
momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java \
momogo-core/src/main/java/com/momogo/core/domain/user/service/UserServiceImpl.java \
momogo-batch/src/main/java/com/momogo/batch/job \
.
do
if [ -d "$f" ]; then
echo "== files under $f =="
git ls-files "$f" | sed -n '1,120p'
elif [ -f "$f" ]; then
echo "== outline $f =="
ast-grep outline "$f" || true
echo "== contents $f =="
nl -ba "$f" | sed -n '1,220p'
fi
done
echo "== shedlock mentions across repo =="
rg -n -i "shedlock|LockProvider|`@SchedulerLock`|lockAtMostFor|schedule" --glob '*.java' --glob 'pom.xml' --glob '*.yml' --glob '*.yaml' --glob '*.properties' .Repository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 519
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
for f in \
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java \
momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java \
momogo-core/src/main/java/com/momogo/core/domain/user/service/UserServiceImpl.java \
momogo-core/src/main/java/com/momogo/core/domain/user/service/UserHardDeleteProcessor.java \
momogo-batch/src/main/java/com/momogo/batch/job \
.
do
if [ -d "$f" ]; then
echo "== files under $f =="
git ls-files "$f" | sed -n '1,160p'
elif [ -f "$f" ]; then
echo "== outline $f =="
ast-grep outline "$f" || true
echo "== contents $f =="
awk '{printf "%6d\t%s\n", NR, $0}' "$f" | sed -n '1,240p'
fi
done
echo "== shedlock mentions across repo =="
rg -n -i "shedlock|LockProvider|`@SchedulerLock`|lockAtMostFor|schedule|batch" --glob '*.java' --glob 'pom.xml' --glob '*.yml' --glob '*.yaml' --glob '*.properties' . || trueRepository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 36769
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== PersonalReportBatchJobConfig =="
awk '{printf "%6d\t%s\n", NR, $0}' momogo-batch/src/main/java/com/momogo/batch/job/PersonalReportBatchJobConfig.java | sed -n '1,260p'
echo "== ShedLockConfig =="
awk '{printf "%6d\t%s\n", NR, $0}' momogo-batch/src/main/java/com/momogo/batch/config/ShedLockConfig.java | sed -n '1,80p'
echo "== batch deps =="
awk '{printf "%6d\t%s\n", NR, $0}' momogo-batch/build.gradle | sed -n '1,160p'
echo "== repo files under batch job =="
git ls-files momogo-batch/src/main/java/com/momogo/batch/job | sed -n '1,120p'Repository: MoMoGo-QuizPlatform/MoMoGo
Length of output: 8439
고정된 PT30M을 각 배치 작업의 P99 실행 시간보다 길게 검증하세요.
ShedLock의 락 TTL은 작업 종료보다 먼저 만료되면 분산 환경에서 같은 스케줄을 가진 작업이 다시 실행될 수 있습니다. dailyReportJob, weeklyReportJob, expiredUserCleanupJob은 각각 집계/저장과 만료 회원 목록 조회 + 개별 삭제 경로를 모두 포함하므로 데이터 규모에 따라 30분이 부족할 수 있습니다. PT30M 대신 createReports() 또는 deleteExpiredUsers()의 P99 실행 시간 + 여유 시간을 반영한 값으로 개별 설정하세요.
📍 Affects 2 files
momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java#L37-L37(this comment)momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java#L51-L51momogo-batch/src/main/java/com/momogo/batch/scheduler/ExpiredUserCleanupScheduler.java#L24-L24
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@momogo-batch/src/main/java/com/momogo/batch/scheduler/BatchScheduler.java` at
line 37, Update the ShedLock TTLs on BatchScheduler.dailyReportJob
(BatchScheduler.java:37-37), BatchScheduler.weeklyReportJob
(BatchScheduler.java:51-51), and
ExpiredUserCleanupScheduler.expiredUserCleanupJob
(ExpiredUserCleanupScheduler.java:24-24) individually, replacing the fixed PT30M
values with durations exceeding each job’s measured P99 execution time plus
safety margin; base report jobs on createReports() and cleanup on
deleteExpiredUsers().
There was a problem hiding this comment.
추후 도메인 담당자가 수정하도록 하겠습니다.
There was a problem hiding this comment.
@SungHuii, 확인했습니다. 현재 PR에서는 해당 검토 사항을 보류하겠습니다.
후속 작업에서는 각 배치 작업의 lockAtLeastFor 설정과 lockAtMostFor 적정 시간을 함께 검토해야 합니다. 필요하면 도메인 담당자에게 할당하는 GitHub 이슈를 생성하겠습니다. 생성할까요?
🐇 …
You are interacting with an AI system.
|
✅ Action performedReview finished.
|
작업 내용
서버를 여러 대로 늘리는 순간 JWT 세션, SSE 알림, 배치 스케줄러가 인스턴스별로
따로 동작해 깨지는 문제가 있음. 이번 작업은 그 해결에 필요한 Redis/Kafka
인프라를 팀 공용으로 미리 잡아둔 보일러플레이트임 (실제 비즈니스 로직 교체는 별도 작업).
변경 사항
momogo-core에 Redis/Kafka 의존성 추가 → api/realtime/batch 전체에 자동 전파RedisConfig: 공용RedisTemplate<String, Object>빈 추가 (JSON 직렬화)KafkaConfig: 전체@KafkaListener기본 재시도(4회, exponential backoff) + DLT 격리 정책 추가KafkaTopics: 토픽명 상수 클래스 추가 (하드코딩으로 인한 발행-구독 불일치 방지)momogo-batch에 ShedLock(Redis 기반) 추가,BatchScheduler/ExpiredUserCleanupSchedule @SchedulerLock적용 → 배치 인스턴스가 여러 대여도 스케줄 중복 실행 안 되게docker-compose.yml추가 (Redis, Kafka, Kafka UI) — 로컬 개발 환경 통일application.yaml에spring.data.redis.*,spring.kafka.*설정 추가(전부 env var 기반, 로컬 기본값 포함이라 별도 설
체크리스트
참고 사항
관련 이슈
Summary by CodeRabbit
새 기능
개선 사항
momogo.kro.kr접속을 지원하고 서비스 간 통신 안정성을 높였습니다.테스트