미팅 완료 버튼을 누르지 않은 사용자들을 위한 스케줄링

땡쿠 서비스는 쿠폰을 통해 예약이 승인되면 미팅이라는 티켓을 생성합니다. 미팅은 언제든지 사용완료를 할 수 있는데요. 사용자라면 보통 미팅을 한 후 미팅 완료 버튼을 누를겁니다. 하지만 까먹기 쉬운 부분이기 때문에 완료 버튼을 누르지 않을 수도 있겠죠?

스크린샷 2022-08-31 오후 3 13 41

그렇게 되면 쿠폰의 상태가 계속 사용 완료된 상태가 아니라 예약된 상태로 머무르게 됩니다. 물론 예약된 쿠폰을 조회하는 경우 문제가 발생할겁니다.

그래서 땡쿠팀은 매일 스케줄 작업을 통해 미팅 날짜가 하루가 지나게 된다면 완료 처리를 해주는 작업을 실행하고 있습니다.

@Component
@RequiredArgsConstructor
public class MeetingScheduleTask {

  public static final Long DAY = 1L;

  private final MeetingService meetingService;

  @Scheduled(cron = "0 0 2 * * *")
  public void executeCompleteMeeting() {
    meetingService.complete(LocalDate.now().minusDays(DAY));
  }
}

@Modifying

@Query어노테이션으로 작성된 메서드에 insert, update, delete 연산을 하게되는 경우 @Modifying 어노테이션을 함께 사용해야 합니다.

@Modifying(clearAutomatically = true)
@Query("UPDATE Meeting m SET m.meetingStatus = :status WHERE m.id IN (:meetingIds)")
void updateMeetingStatus(@Param("status") MeetingStatus status, @Param("meetingIds") List<Long> meetingIds);

@Modifying(clearAutomatically = true)
@Query("UPDATE Coupon c SET c.couponStatus = :status WHERE c.id IN (:couponIds)")
void updateCouponStatus(@Param("status") CouponStatus status, @Param("couponIds") List<Long> couponIds);

하지만 문제가 있습니다. @Query를 사용하는 경우 영속성 컨텍스트를 거쳐 select문을 실행하게 되는데 @Modifying을 사용하게 된다면 영속성 컨텍스트를 거치지 않고 쿼리를 직접 날립니다.

그러면 어떤 문제가 발생하게 될까요?

  1. 미팅을 저장한다. → 영속성 컨텍스트에 해당 미팅이 존재
  2. 미팅을 수정한다. → 영속성 컨텍스트를 거치지 않고 쿼리 실행
    • 데이터 베이스는 update쿼리가 작동하여 데이터가 변경
    • 영속성 컨텍스트에는 1번에서 저장한 미팅이 그대로 존재
  3. 수정된 미팅을 기대하고 미팅을 조회 → 1번에서 저장된 미팅이 반환

이렇게 데이터 정합성이 맞지 않게 되고 테스트 코드가 실패하게 될겁니다. 그래서 @Modifying 어노테이션에는 clearAutomatically를 true로 설정합니다..

영속성 컨텍스트에는 1번이 저장되어 조회할 경우 계속 변경되지 않은 미팅이 조회될 것이므로 영속성 컨텍스트를 비우고 난 뒤 조회를 하게되면 변경된 미팅이 조회될겁니다.

@Modifying(clearAutomatically = true)
@Query(value = "UPDATE Meeting m SET m.status = ? WHERE m.date = ? AND m.status = ?", nativeQuery = true)
void expire(String updatedStatus, LocalDate date, String originStatus);

결론

@Modifying 어노테이션이 영속성 컨텍스트를 거치지 않는다는 점을 모르고 있었고 몇시간 삽질을 하게 되었습니다.

clearAutomatically 을 사용하게 되면 메서드를 실행할 경우 컨텍스트가 비워지기 때문에 더티 체킹이 필요한 곳에 사용하면 안되겠죠?

카테고리:

업데이트: