Skip to content

Commit

Permalink
feat: 트랜잭셔널 타임아웃 추가 (#346)
Browse files Browse the repository at this point in the history
- 분산락을 사용하는 트랜잭션 메서드에 타임아웃을 추가한다.
  • Loading branch information
chhs2131 authored Aug 29, 2024
1 parent 66f011c commit 6dcc7ad
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wootecam.luckyvickyauction.service;

import com.wootecam.luckyvickyauction.aop.DistributedLock;
import com.wootecam.luckyvickyauction.aop.TransactionalTimeout;
import com.wootecam.luckyvickyauction.domain.entity.Receipt;
import com.wootecam.luckyvickyauction.domain.entity.type.ReceiptStatus;
import com.wootecam.luckyvickyauction.domain.entity.type.Role;
Expand Down Expand Up @@ -36,7 +37,7 @@ public class BasicAuctioneer implements Auctioneer {
* 성공하면 -> Receipt 저장 및 구매자, 판매자 업데이트 적용
*/
@Override
@Transactional
@TransactionalTimeout
@DistributedLock("#message.auctionId + ':auction:lock'")
public void process(AuctionPurchaseRequestMessage message, Runnable... postProcesses) {
AuctionInfo auctionInfo = auctionService.getAuction(message.getAuctionId());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.wootecam.luckyvickyauction.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionalTimeout {
// int timeoutMillis() default 60000;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.wootecam.luckyvickyauction.aop;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
@Order(2)
public class TransactionalTimeoutAspect {

private static final int TIMEOUT_MARGIN = 100;
private final TransactionTemplate transactionTemplate;

@Value("${lock.redisson.lease_time: 500}")
private int leaseTime; // TODO: [시간을 초기화하고 보관하는 Bean을 별도로 만들어 관리하기] [writeAt: 2024/08/29/17:27] [writeBy: chhs2131]

@Around("@annotation(transactionalTimeout)")
public Object handleCustomTransaction(ProceedingJoinPoint joinPoint, TransactionalTimeout transactionalTimeout) {
long startTime = System.currentTimeMillis();
long timeoutMillis = leaseTime - TIMEOUT_MARGIN;

return transactionTemplate.execute((TransactionStatus status) -> {
try {
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - startTime;

if (elapsedTime > timeoutMillis) {
log.debug("트랜잭션 타임아웃을 초과했습니다. 초과시간: {}ms", elapsedTime - timeoutMillis);
status.setRollbackOnly();
throw new RuntimeException(
"Transaction timed out after " + elapsedTime + " ms. Timeout was set to " + timeoutMillis
+ " ms.");
}

return result; // 정상 수행한 결과 반환
} catch (RuntimeException ex) {
status.setRollbackOnly();
throw ex;
} catch (Throwable e) {
log.error("message={}", e.getMessage(), e);
throw new RuntimeException("처리할 수 없습니다.");
}
});
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wootecam.luckyvickyauction.service.payment;

import com.wootecam.luckyvickyauction.aop.DistributedLock;
import com.wootecam.luckyvickyauction.aop.TransactionalTimeout;
import com.wootecam.luckyvickyauction.domain.entity.Member;
import com.wootecam.luckyvickyauction.domain.repository.MemberRepository;
import com.wootecam.luckyvickyauction.dto.member.info.SignInInfo;
Expand Down Expand Up @@ -31,7 +32,7 @@ public void chargePoint(SignInInfo memberInfo, long chargePoint) {
memberRepository.save(member);
}

@Transactional
@TransactionalTimeout
@DistributedLock("#recipientId + ':point:lock'")
public void pointTransfer(long senderId, long recipientId, long amount) {
Member sender = findMemberObject(senderId);
Expand Down

0 comments on commit 6dcc7ad

Please sign in to comment.