트랜잭션 동작 테스트확인

깡통·2024년 2월 11일
0
post-thumbnail

@Transactional + @Async

@Slf4j
@Service
@RequiredArgsConstructor
public class AsyncService {

	private final MemberRepository memberRepository;

	@Transactional
	public void saveMember() {
		logTransactionStatus("saveMember");
		memberRepository.save(Member.factory());
		Events.raise(new MailSentEvent(Member.factory()));
	}

	private void logTransactionStatus(String methodName) {
		log.info("in {} method: name={}, active={}, synchronizationActive={}",
			methodName,
			TransactionSynchronizationManager.getCurrentTransactionName(),
			TransactionSynchronizationManager.isActualTransactionActive(),
			TransactionSynchronizationManager.isSynchronizationActive());
	}

}

@Slf4j
@RequiredArgsConstructor
@Service
public class MailSentEventHandler {

    private final MailService mailService;
    private final MemberRepository memberRepository;

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) // 트랜잭션이 커밋된후에 이벤트가 실행된다.
    public void handle(MailSentEvent event) {
        logTransactionStatus("handle");
        memberRepository.save(Member.factory());
        mailService.send(event.getMessage());
    }

    private void logTransactionStatus(String methodName) {
        log.info("in {} method: name={}, active={}, synchronizationActive={}",
            methodName,
            TransactionSynchronizationManager.getCurrentTransactionName(),
            TransactionSynchronizationManager.isActualTransactionActive(),
            TransactionSynchronizationManager.isSynchronizationActive());
    }
}

  • 총 2개의 스레드에서 동작하게된다.
  • 당연하지만 Async쪽엔 트랜잭션이 없다. 트랜잭션은 단하나만 존재한다.
  • 응답이 1초내에 온다.
  • 만약, send메서드에서 repository 코드를 써야한다면 아래와같이 동작한다.
    saveMember에서 트랜잭션을 열고 엔티티매니저를 연후 현재 존재하는 트랜잭션에 참여한다. 그후 트랜잭션을 얻어와서 끝낸후 커밋하고 JPA 트랜잭션에 커밋한다.
    메일전송에 와서 트랜잭션을 새로 만들고 엔티티매니저를 닫은다음 새 엔티티매니저를 열고 커밋한다. 그후 엔티티매니저를 닫는다.

@Transactional + REQUIRES_NEW

@Slf4j
@Service
@RequiredArgsConstructor
public class AsyncService {

	private final MemberRepository memberRepository;

	@Transactional
	public void saveMember() {
		logTransactionStatus("saveMember");
		memberRepository.save(Member.factory());
		Events.raise(new MailSentEvent(Member.factory()));
	}

	private void logTransactionStatus(String methodName) {
		log.info("in {} method: name={}, active={}, synchronizationActive={}",
			methodName,
			TransactionSynchronizationManager.getCurrentTransactionName(),
			TransactionSynchronizationManager.isActualTransactionActive(),
			TransactionSynchronizationManager.isSynchronizationActive());
	}

}

@Slf4j
@RequiredArgsConstructor
@Service
public class MailSentEventHandler {

    private final MailService mailService;
    private final MemberRepository memberRepository;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) // 트랜잭션이 커밋된후에 이벤트가 실행된다.
    public void handle(MailSentEvent event) {
        logTransactionStatus("handle");
        memberRepository.save(Member.factory());
        mailService.send(event.getMessage());
    }

    private void logTransactionStatus(String methodName) {
        log.info("in {} method: name={}, active={}, synchronizationActive={}",
            methodName,
            TransactionSynchronizationManager.getCurrentTransactionName(),
            TransactionSynchronizationManager.isActualTransactionActive(),
            TransactionSynchronizationManager.isSynchronizationActive());
    }
}

  • 하나의 스레드에서 2개의 논리트랜잭션으로 동작한다.

앞 트랜잭션 커밋 , 뒤 트랜잭션 롤백

앞 트랜잭션 롤백 , 뒤 트랜잭션 커밋되는경우

뒤 트랜잭션은 실행 되지도 않는다

급 궁금한점, @Transactional이 없으면 엔티티매니저 어떻게 동작하나?

@Slf4j
@Service
@RequiredArgsConstructor
public class AsyncService {

	private final MemberRepository memberRepository;

	// @Transactional
	public void saveMember() {
		logTransactionStatus("saveMember");
		memberRepository.save(Member.factory());
		Events.raise(new MailSentEvent(Member.factory()));
	}

	private void logTransactionStatus(String methodName) {
		log.info("in {} method: name={}, active={}, synchronizationActive={}",
			methodName,
			TransactionSynchronizationManager.getCurrentTransactionName(),
			TransactionSynchronizationManager.isActualTransactionActive(),
			TransactionSynchronizationManager.isSynchronizationActive());
	}

}

메일전송은 실행되지않았고, 엔티티매니저에선 새롭게 트랜잭션을 생성해서 내부적으로 커밋을 완료시킨후 서비스로직으로 돌아온걸 확인할 수 있다.

@Transactional+REQUIRES_NEW+Async

  • 1초안에 응답이 온다.
  • 2개의 스레드와 2개의 서로다른 트랜잭션에서 동작

0개의 댓글

관련 채용 정보