🙂2024.08.14
일일 회고 20회차
Keep
일일 회고를 꾸준히 하면서 성장하기 위해 노력하는 것
Problem
명확한 원인을 파악하지 않고 문제를 해결하는 것
여러 개의 방법 중에 최선의 방법이 아닌 빠른 방법을 선택하는 것
Try
일일 회고를 통해 원인을 명확하게 파악하고 문제를 해결했는지 검토
일일 회고를 통해 최선의 방법을 선택했는지 검토
오늘 할 일
사이드 프로젝트
알림 읽음 상태 변경 테스트 구현
경험 및 배움
회사 업무
LWLcok 발생 원인 및 해결 방법 분석
29CM 팀의 PostgreSQL Autovacuum 장애 대응기 글을 보고 LWLock 발생 원인을 유추할 수 있게 됨
회사 시스템에 특정 테이블을 주기적으로 초기화하고 데이터를 다시 넣는 스케줄링이 있음
테이블이 초기화될 경우 dead tuple이 발생하여 Autovacuum이 동작함
vacuum이 실행되면서 block된 row들이 생겨 LWLock이 발생하게 됨
위 내용은 추측이므로 문제가 재현될 경우 명확한 원인 파악 필요
주기적으로 실행하는 로직에 Timer 적용
스케줄링을 10초에 한 번씩 실행되어 시스템이 전반적으로 느려지는 문제 발생
스케줄링을 하루에 한 번씩 실행되도록 수정이 요구됨
기존의 스케줄링 로직은 서버가 실행될 경우 바로 스케줄링을 실행하는 로직이였음
while (true) 의 실행문에서 delay를 추가한 로직
사용자가 사용하는 시간과 스케줄링이 수행되는 시간을 완전히 분리하기 위해 자정 시간에 실행되도록 수정
java.util.Timer 객체를 활용하여 자정 시간에 실행되도록 수정
private const val SCENE_LABEL_VIEW_CACHE_TIME = 24 * 60 * 60 * 1000L
private fun cacheSceneLabelView() {
val timer = Timer()
val midnight = getNextMidnight()
logger.info { "SceneLabelView 업데이트 스케줄링 시작 (한국 시간 기준 매일 자정에 수행)" }
timer.scheduleAtFixedRate(midnight, SCENE_LABEL_VIEW_CACHE_TIME) {
CoroutineScope(Dispatchers.IO).launch {
logger.info { "SceneLabelView 업데이트 시작" }
runCatching {
injector().get<SceneLabelViewRepository>().use { it.refresh() }
}.onSuccess {
logger.info { "SceneLabelView 업데이트 완료" }
}.onFailure {
logger.error(it) { "SceneLabelView 업데이트 실패" }
}
}
}
}
private fun getNextMidnight(): Date {
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Seoul"))
calendar.set(Calendar.HOUR_OF_DAY, 0)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)
if (calendar.time.before(Date())) {
calendar.add(Calendar.DAY_OF_MONTH, 1)
}
return calendar.time
}
사이드 프로젝트
알림 읽음 상태 변경 테스트 구현
하나의 API로 단일 상태 변경과 전체 상태 변경을 수행하려 했으나 테스트를 실패함
API를 /notifications/{notificationId}/checked 로 정의함
/notifications//checked 와 /notification/{UUID}/checked로 두 가지의 경우를 처리하려 했음
PatchMapping에 path 값으로 /notifications/{notificationId}/checked 와 /notifications/checked로 두 가지의 경우를 처리할 수 있게 수정함
테스트 코드로 정상 동작하는 것 확인함
@PatchMapping(path = ["/notifications/{notificationId}/checked", "/notifications/checked"])
suspend fun checkNotifications() {}
// 읽지 않은 모든 알림의 읽음 상태 변경 테스트
val count = webTestClient
.patch()
.uri("/notifications/checked")
// ...
// 읽지 않은 특정 알림의 읽음 상태 변경 테스트
val count = webTestClient
.patch()
.uri("/notifications/${expected.id}/checked")
// ...
앞으로 할 일
회사 업무
LWLock 발생 원인 및 해결 방법 분석
Github Actions 추가 개선 (ref. 백엔드 Github Actions 개선)
주기적으로 실행하는 로직에 Timer 적용
개인 학습
AOP의 Joinpoint 분석
@Transactional 동작원리 학습
사이드 프로젝트
Swagger Docs 보완
@Profile 적용
알림 기능 구현
uuid v7 적용
에러 로깅 적용
Last updated