트랜잭션 범위가 길어질땐? 범위를 좁혀라

Alex·2024년 11월 14일
0

Plaything

목록 보기
15/118
post-thumbnail

Real MySQL을 공부할 때 나왔던 내용 중 하나가
"트랜잭션을 길게 가져가지 않도록 해야 한다" 였다.

트랜잭션이 정말 필요한 부분에서만 트랜잭션을 걸어야 한다는 것이다.

생각해보면

1)더티체킹을 할 때
2)롤백을 할 때

이런 경우에 나는 트랜잭션을 썼다.

정말 필요한 곳에 트랜잭션을 붙이고 있을까?
그렇지 않으면, 트랜잭션으로 인한 커넥션이 너무 길어진다.
커넥션이 부족해지면 대기시간도 길어질 수밖에 없다.

보통 커넥션은 커넥션 풀에 만들어서 저장한다. 클라이언트 요청이 오면 풀에 있는 커넥션을 사용한다. 커넥션을 획득하지 못한 요청은 다른 요청이 끝나서 커넥션이 반납될 때까지 기다려야 한다. 그래서 외부 API를 호출하는 경우에는, 트랜잭션을 불필요하게 길게 잡고 있을 수 있어 분리하는 게 좋다.

참고:[Spring] 트랜잭션(Transaction) 우아하게 분리하기

트랜잭션을 짧게 가져가는 건 언두로그 때문이기도 하다.
언두로그는 insert, update, delete로 데이터를 변경할 때 변경하기 전의 상태를 기록해놓은 것으로, 롤백할 때 활용한다.

언두로그는 인덱스를 기준으로 트랜잭션이 중첩되면 로그 도한 중첩돼서 쌓이고, 해당 컬럼에 대해 트랜잭션이 없어질 때까지 정리되지 않는다.
언두로그가 쌓이면 잠금없는 읽기를 위해서 탐색 비용이 발생해 읽기 성능이 떨어진다.

언루로그란?에 관련 내용을 정리해두었다.

코드를 확인해보자.

지금은 이렇게 전체 프로세스에 트랜잭션과 롤백을 걸어주고 있다.
한번 코드를 자세히 살펴보자.

1)jwt 토큰을 통해서 받은 name으로 user를 찾는다.
2)key를 생성한다.

1번 부분은 굳이 트랜잭션을 걸어줄 필요가 없지 않을까?

user와 관련해서 더티체킹을 하는 부분이 없다.
단지 연관관계를 맺어주기 위해서 매개변수로 전달하고 있다.
마지막에 adView와 관련된 로그를 넣는것도 분리해도 되지 않을까? 싶었지만 롤백을 위해서는 이것들을 함께 캡슐화하는 것이 맞는 것같다.

트랜잭션 범위를 조정해도 정상적으로 연관관계가 맵핑된다.

롤백도 제대로 되는지 확인해보자.

체크 예외가 터지면 롤백이 되는 것을 알 수 있다.

호출하는 곳에서 예외가 터질 땐?

로그인 보상을 주는 곳을 봐보자.
그날의 첫 로그인이면 포인트 키 보상을 주는 코드가 있다.

그 다음에 체크 예외를 터뜨려보자.

이 경우엔 호출한 곳에서 예외가 터지면 롤백이 발생하지 않았다.

이걸 해결하려고 복잡하게 클래스를 나누고 이것저것 해봤는데...
근본적으로 login이 정말 복잡한 로직인가? 하는 고민이 필요해보였다.

그냥

이렇게 해도 괜찮지 않을까? 싶다.
외부 api를 연동한다거나 어떤 복잡한 db작업이 있는 게 아니니
그냥 트랜잭션을 여기에 걸고 예외가 터지면 롤백하게끔 하는 게 좀더 안전하다고 생각했다.

포인트 사용 로직은 어때?

여기도 전체에 트랜잭션을 걸어두었다.
그런데 위에서 유효성 검사를 하는 부분이 트랜잭션과 같이 걸려 있을 필요는 없을 거 같다.

실제 DB에 insert를 하면서, 롤백이 원자적으로 돼야 하는 단위를 묶어서 트랜잭션을 걸었다.

키 사용 전

키 사용 후 체크 예외 발생

체크 예외가 터졌을 때도 사용했던 Point_key가 복구됐다.

profile
답을 찾기 위해서 노력하는 사람

0개의 댓글