Conversation
* fix: 피버타임 검증 시 점수가 두배가 되지 않는 문제 해결 * refactor: 바뀐 dto에 맞게 BIZ 및 test 수정
* fix: 피버타임 검증 시 점수가 두배가 되지 않는 문제 해결 * refactor: 바뀐 dto에 맞게 BIZ 및 test 수정 * fix: 피버타임중 일시정지 시 피버타임도 멈추게 한다 * refactor: 인터페이스로 묶는다
* fix: 점수 증가 로직 오류 해결 * fix: 스트릭 발생 시각을 받는다 * refactor: 네이밍 수정 * fix(test): 스트릭 발생 시각 추가
* fix: 치팅 및 네트워크 지연 해결 * comment: 디버깅을 위한 로그 추가 * fix: session과 fevertime 일시정지 동기화
* fix: 치팅 및 네트워크 지연 해결 * comment: 디버깅을 위한 로그 추가 * fix: session과 fevertime 일시정지 동기화 * fix: 세션 예외 오류 해결 * feat: multipart 예외 핸들링 * refactor: CD 파이프라인 OCI로 마이그레이션 * fix: end response 분리
There was a problem hiding this comment.
Code Review
This pull request introduces a "Fever Time" mechanic, featuring a new domain model, event-driven state management for pausing and resuming fever periods, and updated scoring logic. It also adds deployment scripts, Jib containerization, and refactors domain models to support inheritance and proxying. Feedback identifies a critical bug in the percentile calculation query, significant code duplication across business logic classes, and opportunities to improve deployment script robustness and test stability.
| SELECT percent_rank() over (order by score desc) as percentile | ||
| FROM snackgame | ||
| WHERE expires_at <= now() + INTERVAL 1 SECOND | ||
| AND session_id = :sessionId | ||
|
|
||
| UNION ALL | ||
|
|
||
| SELECT 0.0 as percentile | ||
| FROM snackgame | ||
| WHERE session_id = :sessionId | ||
| AND expires_at > now() + INTERVAL 1 SECOND | ||
| LIMIT 1 | ||
| """, |
There was a problem hiding this comment.
현재 findPercentileOf의 네이티브 쿼리는 WHERE 절에 session_id = :sessionId 조건이 포함되어 있어 percent_rank() 함수가 항상 단일 행에 대해 실행됩니다. 이로 인해 백분위수는 항상 0으로 계산되는 버그가 있습니다. 올바른 백분위수를 계산하려면, 먼저 모든 세션에 대해 순위를 계산한 후 특정 세션의 결과를 필터링해야 합니다.
SELECT percentile FROM (
SELECT session_id, percent_rank() over (order by score desc) as percentile
FROM snackgame
WHERE expires_at <= now() + INTERVAL 1 SECOND
) as ranked_sessions
WHERE session_id = :sessionId
UNION ALL
SELECT 0.0 as percentile
FROM snackgame
WHERE session_id = :sessionId
AND expires_at > now() + INTERVAL 1 SECOND
LIMIT 1
| fun remove(streakWithFever: StreakWithFever) { | ||
| val streak = streakWithFever.streak | ||
| val removedSnacks = board.removeSnacksIn(streak) | ||
|
|
||
| val multiplier = calculateMultiplier(streakWithFever) | ||
| increaseScore(streak.length * multiplier) | ||
|
|
||
| if (removedSnacks.any(Snack::isGolden)) { | ||
| this.board = board.reset() | ||
| } | ||
| } | ||
|
|
||
| private fun calculateMultiplier(streakWithFever: StreakWithFever): Int { | ||
| val serverFever = feverTime ?: return NORMAL_MULTIPLIER | ||
|
|
||
| if (streakWithFever.clientIsFever && | ||
| serverFever.isFeverTime(streakWithFever.occurredAt) && | ||
| serverFever.canApplyFeverMultiplier() | ||
| ) { | ||
| serverFever.incrementFeverStreak() | ||
| return FEVER_MULTIPLIER | ||
| } | ||
| return NORMAL_MULTIPLIER | ||
| } |
| image = "ghcr.io/snack-game/server" | ||
| auth { | ||
| username = "snack-game" | ||
| password = System.getenv("GHCR_PASSWORD") ?: "" |
| local http_code="000" | ||
|
|
||
| while [ "$elapsed" -lt "$HEALTH_CHECK_TIMEOUT" ]; do | ||
| http_code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" 2>/dev/null || echo "000") |
There was a problem hiding this comment.
cURL 요청 시 -f 또는 --fail 옵션을 추가하여 HTTP 상태 코드가 200-299 범위가 아닐 경우 스크립트가 즉시 실패하도록 하는 것이 좋습니다. 이렇게 하면 http_code를 수동으로 확인하는 것보다 더 안정적으로 오류를 처리할 수 있습니다. set -e가 설정되어 있으므로 cURL이 0이 아닌 종료 코드를 반환하면 스크립트가 중단됩니다.
| http_code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" 2>/dev/null || echo "000") | |
| http_code=$(curl -s -o /dev/null -w "%{http_code}" -f "$HEALTH_CHECK_URL" 2>/dev/null || echo "000") |
| echo "[헬스체크] 컨테이너가 종료됨, 즉시 롤백" | ||
| return 1 | ||
| fi | ||
| http_code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" 2>/dev/null || echo "000") |
There was a problem hiding this comment.
cURL 요청 시 -f 또는 --fail 옵션을 추가하여 HTTP 상태 코드가 200-299 범위가 아닐 경우 스크립트가 즉시 실패하도록 하는 것이 좋습니다. 이렇게 하면 http_code를 수동으로 확인하는 것보다 더 안정적으로 오류를 처리할 수 있습니다. set -e가 설정되어 있으므로 cURL이 0이 아닌 종료 코드를 반환하면 스크립트가 중단됩니다.
| http_code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL" 2>/dev/null || echo "000") | |
| http_code=$(curl -s -o /dev/null -w "%{http_code}" -f "$HEALTH_CHECK_URL" 2>/dev/null || echo "000") |
| var paused: Boolean? = false, | ||
| var feverStreakCount: Int? = 0 |
| } | ||
|
|
||
| fun pause(at: LocalDateTime) { | ||
| if (paused != true) { |
| snackgameService.useFeverTime(땡칠().id, game.sessionId) | ||
|
|
||
| snackgameService.pause(game.ownerId, game.sessionId) | ||
| Thread.sleep(1000) |
No description provided.