2개의 API 요청이 같은 row 삭제 시도하여 생긴 충돌 문제(트랜잭션 낙관적 동시성 실패) 해결
문제 상황
프론트 측에서 개발 중인 토스트 메세지 기능과 충돌하여 동시성 문제 발생
프론트에서 토스트 메세지 기능 수정 후, 앞으로 개발 완료될 UI에서는 이와 같은 문제 일어날 확률이 적다고 합니다. 그래도 일단 예외처리는 해두었는데 불필요 시 예외처리 코드는 삭제예정입니다.
delete
from
favorite
where
id=?
2023-04-07 01:16:56.385 TRACE 1 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [86]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_] : [BIGINT]) - [86]
Hibernate:
update
shop
set
create_date=?,
modify_date=?,
favorite_cnt=?,
place_name=?,
review_cnt=?,
road_address_name=?,
star_rating_avg=?
where
id=?
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [TIMESTAMP] - [2023-03-16T10:56:09]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [TIMESTAMP] - [2023-04-07T01:16:56.386509453]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [INTEGER] - [1]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [VARCHAR] - [인생네컷]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [INTEGER] - [0]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [VARCHAR] - [서울 용산구 이태원로 171]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [7] as [DOUBLE] - [0.0]
2023-04-07 01:16:56.386 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [8] as [BIGINT] - [4157]
Hibernate:
delete
from
favorite
where
id=?
2023-04-07 01:16:56.388 TRACE 1 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [86]
2023-04-07 01:16:56.390 INFO 1 --- [nio-8080-exec-6] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2023-04-07 01:16:56.395 ERROR 1 --- [nio-8080-exec-6] c.i.f.g.error.GlobalExceptionHandler : Exception
org.springframework.orm.ObjectOptimisticLockingFailureException:
Batch update returned unexpected row count from update [0];
actual row count: 0; expected: 1; statement executed: delete from favorite where id=?;
nested exception is org.hibernate.StaleStateException:
Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1;
statement executed: delete from favorite where id=?
## 작업 상세 내용
- [x] 찜 추가 및 삭제 트랜잭션 커밋 단위 축소
- 찜 수 갱신 코드 ShopService로 분리
- Shop 엔티티에 `@DynamicUpdate` 적용하여 수정된 컬럼만 업데이트 반영
- [x] 찜 삭제 `ObjectOptimisticLockingFailureException` 예외 처리
- exists로 검사 후 테이블에 존재하면 찜 삭제 재시도, 재실패하거나 존재하지 않을 시 오류 처리
## 참고 사항
- [ObjectOptimisticLockingFailureException 발생 시 예외처리](https://www.inflearn.com/questions/266817/objectoptimisticlockingfailureexception)
목적
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: delete from favorite where id=?; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: delete from favorite where id=?