ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Commit Count 확장 - 로그(TIL) 카운트 EventListener Error
    devRace 2024. 11. 26. 18:46

     

     

    커밋카운트 로직을 바탕으로 알고리즘 카운트, 로그(TIL) 카운트 를 만들었다.

     

        @EventListener
        @Async
        public void handleLogSubmitEvent(LogSubmitEvent event) {
            logCountService.updateLogCountOnSubmit(event.getUserId());
        }
    }

     

    이 곳에는 비동기를 적용하여 

    사용자가 제출한 이후 카운트를 따로 처리하게 함으로써  제출 이후 바로 다른 작업을 할 수 있도록 하였다.

     

     

     

    테스트코드를 작성하여  간단히 로그 제출 -> 제출하며 카운트 이벤트 호출 로직으로 구성하였다.

     

    하지만 테스트에서 문제가 발생하였고,  비동기가 제대로 적용되지 않은것이라 생각해

     

     Awaitility.await()
                    .atMost(5, TimeUnit.SECONDS)
                    .untilAsserted(() ->

     

    여기에 5초를 주고 다음엔 30초씩 줘가며 확인을 하였지만 실패하였고,

     

    트랜잭션이 제대로 돌아가지 않는게 문제인가 해서 트랜잭션을 삭제하고 afterEach로 레포지토리를 비워주었다.

    이때 테스트는 성공하였으나 테스트 내부에서 에러가 발생하였고,

    깔끔한 테스트 성공을 위해 testConfig를 하나 더 만들어서 적용하였다.

     

     

    org.springframework.transaction.UnexpectedRollbackException: 
    Transaction silently rolled back because it has been marked as rollback-only

     

     

    테스트 코드에서 트랜잭션을 없앴지만 서비스코드에 남아있어서 이런 에러가 발생한다고 판단하였고,

     

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    
    public void updateLogCountOnSubmit(Long userId)

     

    트랜잭션의 전파 속성을 변경해보았지만 결과는 같았다.

    그래서 다시 일반 트랜잭션으로 되돌리고

     

     

        @EventListener
        @Async
        public void handleLogSubmitEvent(LogSubmitEvent event) {
            logCountService.updateLogCountOnSubmit(event.getUserId());
        }
    }
    
        @TransactionalEventListener
        @Async
        public void handleLogSubmitEvent(LogSubmitEvent event) {
            logCountService.updateLogCountOnSubmit(event.getUserId());
        }
    }

     

    이벤트에서 이벤트리스너를 트랜잭션이벤트리스너로 변경하였다.

     

    로직 상 제출이 완료 된 후 카운트를 체크하는 것이 맞기에 제출 트랜잭션이 끝난 후 카운트 트랜잭션이 실행되도록

    하나의 트랜잭션을 두 개로 분리하였다.

     

    기존에는 submitLog 테스트는 성공했으나

    Log saveLog = logRepository.findByAddressAndUserId(submitLogDto.getAddress(), user.getId())
    .orElseThrow(() -> new AssertionError("로그 못찾음"));

     

    이 에러가 계속 발생하였다.

    그래서 비동기쪽 문제라고 생각하여 이것저것 건드려보았는데 이벤트 관련 트랜잭션을 나누는 것이 정답이었다.

     

     

    요약

    처음에는 @Async와 @EventListener 를 조합해서 이벤트리스너가 비동기적으로 동작하도록 설정했는데

    이렇게하면 로그 제출작업이 완료되기 전에 로그 카운트 증가작업이 시작될 수 있었다.

     

    그래서 로그 제출 트랜잭션이 아직 완료되지 않아 DB에 저장되지 않은 상태에서 이벤트가 실행될 경우

    카운트 증가를 위해 로그를 조회할 때 해당로그를 찾기 못했던 것이었다.

     

    그래서 애너테이션을 변경하여 제출 트랜잭션이 커밋된 이후에 실행되도록 하였고,

    이를 통해 로그가 성공적으로 제출된 이후에 이벤트 리스너가 실행되므로 계속해서 발생하던 에러를 해결할 수 있었다.

     

    이 성공 이후 위의 config를 삭제한 후 실행했음에도 문제가 발생하지 않았고, 성공적으로 실행되는 것으로 보아 

    트랜잭션 분리가 답이었다.

     

    비동기 이벤트 처리와 트랜잭션 타이밍이 불일치하는 것이 원인이었고,

    애너테이션 변경을 통해 이벤트 실행 시점을 트랜잭션 완료 후로 조정하여 문제를 해결하였다.

     

     

Designed by Tistory.