Troubleshooting:Lock Time Out - takeoff-26/logistics-service GitHub Wiki
์ฑ๋ฅ ํ ์คํธ ์ค DB์ ๋ถํ๊ฐ ๋ง์ ์ฒ๋ฆฌ๊ฐ ์ง์ฐ๋ ๋ timeOut ์์ธ๊ฐ ๋ฐ์ํ์ง ์๋ ์ ์์ ์ธ์
ํ์์์ ์ ํ - 1000ms ํธ๋์ญ์ ๋ด ์๋์ ์ผ๋ก 3000ms์ผ๋ก ์ค์ ํด ํ์ธ
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({
@QueryHint(name = "jakarta.persistence.lock.timeout", value = "1000"),
@QueryHint(name = "org.hibernate.dialect.lock.timeout", value = "1000")
})
@Query("SELECT s FROM Stock s WHERE s.id = :id AND s.deletedAt is null")
Optional<Stock> findByIdWithLock(@Param("id") StockId id);
@Override
@Transactional
public void prepareStock(PrepareStockRequestDto requestDto) {
try {
getSortedStocks(requestDto.stocks())
.forEach(stockItem ->
getStockWithLock(stockItem.stockId()).decreaseStock(stockItem.quantity()));
sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Caused by: org.hibernate.PessimisticLockException: JDBC exception executing SQL [select s1_0.hub_id,s1_0.product_id,s1_0.created_at,s1_0.created_by,s1_0.deleted_at,s1_0.deleted_by,s1_0.quantity,s1_0.updated_at,s1_0.updated_by from p_stock s1_0 where (s1_0.hub_id,s1_0.product_id)=(?,?) and s1_0.deleted_at is null for update] [Timeout trying to lock table "P_STOCK"; SQL statement:
select s1_0.hub_id,s1_0.product_id,s1_0.created_at,s1_0.created_by,s1_0.deleted_at,s1_0.deleted_by,s1_0.quantity,s1_0.updated_at,s1_0.updated_by from p_stock s1_0 where (s1_0.hub_id,s1_0.product_id)=(?,?) and s1_0.deleted_at is null for update [50200-232]] [n/a]
at org.hibernate.dialect.H2Dialect.lambda$buildSQLExceptionConversionDelegate$3(H2Dialect.java:771) ~[hibernate-core-6.6.8.Final.jar:6.6.8.Final]
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:58) ~[hibernate-core-6.6.8.Final.jar:6.6.8.Final]
ํ์ ์์ ์์ธ์ฌ ๋ฐ์ํ์ง ์์๊ณ ํ ์คํธ ์๊ฐ์ด 30์ด๊ฐ ๊ฑธ๋ฆฐ ๊ฒ์ ํ์ธ.
javax.persistence.lock.timeout์ด๋ jakarta.persistence.lock.timeout ์ค์ ์ ํ๋๋ผ๋, ์ฌ์ฉ ์ค์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ JDBC ๋๋ผ์ด๋ฒ๊ฐ ์ด ๊ธฐ๋ฅ์ ์ง์ํ์ง ์์ผ๋ฉด ์ค์ ์ด ์ ์ฉ๋์ง ์์ ์ ์๋ค.
์๋ฅผ ๋ค์ด, MySQL, Oracle ๋ฑ์ ๋๋ผ์ด๋ฒ๋ ์ด ๊ธฐ๋ฅ์ ์ง์ํ์ง๋ง, ์ผ๋ถ ๋ค๋ฅธ ๋๋ผ์ด๋ฒ๋ ์ง์ํ์ง ์์ ์ ์๋ค.
๋ค์ดํฐ๋ธ ์ฟผ๋ฆฌ๋ฅผ aop๋ก ์ ์ฉํด ํด๊ฒฐ
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LockTimeout {
int timeout() default 3000;
}
public interface LockManager {
void setLockTimeout(int timeout);
}
h2 ๊ตฌํ์ฒด
@Slf4j
@Profile("Test")
@Component
@RequiredArgsConstructor
public class H2LockManager implements LockManager {
@Override
public void setLockTimeout(int timeout) {
log.info("ํ
์คํธ ํ๊ฒฝ์์๋ ๋ฝ ํ์์์ ์ค์ ์ด ๋ฌด์๋ฉ๋๋ค: {}ms", timeout);
}
}
postgres ๊ตฌํ์ฒด
@Profile("!Test")
@Component
@RequiredArgsConstructor
public class PostgresLockManager implements LockManager {
private final EntityManager em;
public void setLockTimeout(int timeout) {
Query query = em.createNativeQuery("SET LOCAL lock_timeout = '" + timeout + "ms'");
query.executeUpdate();
}
}
@Aspect
@Component
@RequiredArgsConstructor
public class LockTimeoutAspect {
private final LockManager lockManager;
@Before("@annotation(takeoff.logistics_service.msa.product.stock.infrastructure.persistence"
+ ".aspect.LockTimeout)")
public void beforeLockTimeout(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
LockTimeout annotation = signature.getMethod().getAnnotation(LockTimeout.class);
lockManager.setLockTimeout(annotation.timeout());
}
}
@LockTimeout(timeout = 1000)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT s FROM Stock s WHERE s.id = :id AND s.deletedAt is null")
Optional<Stock> findByIdWithLock(@Param("id") StockId id);
findByIdWithLock ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด, @LockTimeout ์ด๋
ธํ
์ด์
๋๋ฌธ์ AOP๊ฐ ๋์ํจ
AOP๋ LockTimeoutAspect์ beforeLockTimeout ๋ฉ์๋๋ฅผ ์คํํจ
์ด ๋ฉ์๋๋ LockManager.setLockTimeout()์ ํธ์ถํจ
PostgreSQL ํ๊ฒฝ์์๋ PostgresLockManager์ ๊ตฌํ์ด ์ฌ์ฉ๋๊ณ ,
์ด ๊ตฌํ์ em.createNativeQuery("SET LOCAL lock_timeout = '1000ms'")๋ฅผ ์คํํจ
๊ทธ ํ์ ์ค์ ์ฟผ๋ฆฌ(@Query("SELECT s FROM Stock s WHERE s.id = :id AND s.deletedAt is null"))๊ฐ ์คํ๋๋๋ฐ, ์ด ์ฟผ๋ฆฌ๋ @Lock(LockModeType.PESSIMISTIC_WRITE) ๋๋ฌธ์ for update ๋๋ for no key update ๊ตฌ๋ฌธ์ด ์ถ๊ฐ๋จ