Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,4 +63,4 @@ public Account findById(Long id) {
public void handleAssetLockFallback(Long userId, Long accountId) {
log.warn("분산락 획득 실패 - userId: {}, accountId: {}", userId, accountId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
/**
* 락 임대 시간 (default - 3s) 락을 획득한 이후 leaseTime 이 지나면 락을 해제한다
*/
long leaseTime() default 3L;
int leaseTime() default 3;

String fallbackMethod() default "";

Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,37 +30,34 @@ 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);

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);
}
}
}
}

Expand Down Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Loading