diff --git a/Tradin-Core/src/main/java/com/tradin/core/account/service/AccountFacadeService.java b/Tradin-Core/src/main/java/com/tradin/core/account/service/AccountFacadeService.java index 2f11135..e83d333 100644 --- a/Tradin-Core/src/main/java/com/tradin/core/account/service/AccountFacadeService.java +++ b/Tradin-Core/src/main/java/com/tradin/core/account/service/AccountFacadeService.java @@ -7,8 +7,6 @@ import com.tradin.core.common.annotation.DistributedLock; import com.tradin.core.strategy.domain.CoinType; import com.tradin.core.account.domain.Account; -import com.tradin.core.strategy.domain.Position; -import com.tradin.core.strategy.domain.Strategy; import com.tradin.core.users.domain.Users; import com.tradin.core.users.service.UsersService; import java.math.BigDecimal; @@ -65,4 +63,4 @@ public Account findById(Long id) { public void handleAssetLockFallback(Long userId, Long accountId) { log.warn("분산락 획득 실패 - userId: {}, accountId: {}", userId, accountId); } -} +} \ No newline at end of file diff --git a/Tradin-Core/src/main/java/com/tradin/core/common/annotation/DistributedLock.java b/Tradin-Core/src/main/java/com/tradin/core/common/annotation/DistributedLock.java index 8b1c5ef..7298893 100644 --- a/Tradin-Core/src/main/java/com/tradin/core/common/annotation/DistributedLock.java +++ b/Tradin-Core/src/main/java/com/tradin/core/common/annotation/DistributedLock.java @@ -28,7 +28,7 @@ /** * 락 임대 시간 (default - 3s) 락을 획득한 이후 leaseTime 이 지나면 락을 해제한다 */ - long leaseTime() default 3L; + int leaseTime() default 3; String fallbackMethod() default ""; diff --git a/Tradin-Core/src/main/java/com/tradin/core/common/aop/AopForTransaction.java b/Tradin-Core/src/main/java/com/tradin/core/common/aop/AopForTransaction.java index 2369a44..37245a5 100644 --- a/Tradin-Core/src/main/java/com/tradin/core/common/aop/AopForTransaction.java +++ b/Tradin-Core/src/main/java/com/tradin/core/common/aop/AopForTransaction.java @@ -1,14 +1,21 @@ package com.tradin.core.common.aop; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; @Component +@RequiredArgsConstructor @Slf4j public class AopForTransaction { + private final PlatformTransactionManager transactionManager; @Transactional(propagation = Propagation.REQUIRES_NEW) public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { @@ -22,8 +29,36 @@ public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { throwable ); throw throwable; - } finally { -// log.info("🔓 트랜잭션 종료 - method: {}", joinPoint.getSignature().getName()); + } + } + + /** + * 트랜잭션을 새로 생성하고, 지정된 시간(초) 내에 완료되지 않으면 롤백합니다. + */ + public Object proceedWithTimeout(final ProceedingJoinPoint joinPoint, int timeoutSeconds) throws Throwable { + DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); + transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + + if (timeoutSeconds > 0) { + transactionDefinition.setTimeout(timeoutSeconds); + } + + TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); + + try { + Object result = joinPoint.proceed(); + transactionManager.commit(transactionStatus); + return result; + } catch (Throwable throwable) { + transactionManager.rollback(transactionStatus); + log.error( + "🚫 트랜잭션 처리 중 오류 발생 - method: {}, timeout: {}s, error: {}", + joinPoint.getSignature().getName(), + timeoutSeconds, + throwable.getMessage(), + throwable + ); + throw throwable; } } } \ No newline at end of file diff --git a/Tradin-Core/src/main/java/com/tradin/core/common/aop/DistributedLockAop.java b/Tradin-Core/src/main/java/com/tradin/core/common/aop/DistributedLockAop.java index eed3f67..ff81cc4 100644 --- a/Tradin-Core/src/main/java/com/tradin/core/common/aop/DistributedLockAop.java +++ b/Tradin-Core/src/main/java/com/tradin/core/common/aop/DistributedLockAop.java @@ -30,7 +30,6 @@ public class DistributedLockAop { @Around("@annotation(distributedLock)") public Object handleDistributedLock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - Method method = signature.getMethod(); String key = createKey(joinPoint, distributedLock, signature); RLock lock = redissonClient.getLock(key); @@ -38,29 +37,27 @@ public Object handleDistributedLock(ProceedingJoinPoint joinPoint, DistributedLo boolean isLocked = false; try { - isLocked = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), distributedLock.timeUnit()); + isLocked = lock.tryLock(distributedLock.waitTime(), -1L, distributedLock.timeUnit()); if (!isLocked) { log.warn("🔒 락 획득 실패 - key: {}", key); return invokeFallback(joinPoint, distributedLock.fallbackMethod()); } -// log.info("🔐 락 획득 성공 - key: {}", key); - return aopForTransaction.proceed(joinPoint); + try { + Object result = aopForTransaction.proceedWithTimeout(joinPoint, distributedLock.leaseTime()); + releaseLock(lock, key); + return result; + } catch (Throwable throwable) { + releaseLock(lock, key); + throw throwable; + } } catch (InterruptedException e) { - Thread.currentThread().interrupt(); log.error("🚫 락 처리 중 인터럽트 발생 - key: {}", key, e); + Thread.currentThread().interrupt(); + releaseLock(lock, key); return invokeFallback(joinPoint, distributedLock.fallbackMethod()); - } finally { - if (isLocked && lock.isHeldByCurrentThread()) { - try { - lock.unlock(); -// log.info("🔓 락 해제 - key: {}", key); - } catch (IllegalMonitorStateException e) { -// log.warn("⚠️ 락 이미 해제됨 - method: {}, key: {}", method.getName(), key); - } - } } } @@ -106,4 +103,18 @@ private Method findFallbackMethod(ProceedingJoinPoint joinPoint, String fallback } return null; } + + private void releaseLock(RLock lock, String key) { + if (lock.isHeldByCurrentThread()) { + try { + lock.unlock(); + } catch (IllegalMonitorStateException e) { + log.warn("⚠️ 락 이미 해제됨 - key: {}", key); + } catch (Exception e) { + log.error("🚫 락 해제 실패 - key: {}, error: {}", key, e.getMessage()); + } + } else { + log.warn("⚠️ 현재 스레드가 락을 소유하지 않음 - key: {}", key); + } + } } \ No newline at end of file diff --git a/Tradin-Core/src/main/java/com/tradin/core/futuresOrder/service/FuturesOrderFacadeService.java b/Tradin-Core/src/main/java/com/tradin/core/futuresOrder/service/FuturesOrderFacadeService.java index f1b2a3d..f66159a 100644 --- a/Tradin-Core/src/main/java/com/tradin/core/futuresOrder/service/FuturesOrderFacadeService.java +++ b/Tradin-Core/src/main/java/com/tradin/core/futuresOrder/service/FuturesOrderFacadeService.java @@ -1,5 +1,7 @@ package com.tradin.core.futuresOrder.service; +import static java.lang.Thread.sleep; + import com.tradin.core.account.domain.Account; import com.tradin.core.balance.domain.Balance; import com.tradin.core.balance.domain.vo.Amount; @@ -29,7 +31,6 @@ public class FuturesOrderFacadeService { key = "'asset-lock:' + #account.id + ':USDT'", fallbackMethod = "handleAssetLockFallback" ) - @Transactional public void autoTrade(Strategy strategy, Account account, Position position) { try { closeExistingPositionIfExists(strategy, account);