Transaction

Bonggus·2024년 7월 29일
0

db

목록 보기
9/9

상황

Loop를 돌면서 Transaction 처리를 하는 로직이 있다. 여기에 추가적인 로직이 들어가야하는데, 문제는 외부 API를 호출해야하는 로직이 포함되어있다.

Loop를 통해 영속화된 데이터를 기반으로 API 호출이 필요한 상황인데, 그냥 가벼운 생각으로 로직 중간중간 마다 API 호출을 시도하고, 호출 결과를 다시 업데이트 해줄 생각으로 작업을 시작했다. 하지만 그 생각이 고통의 길로 이끌줄은 몰랐다.(여전히 진행중)

일단, 트렌젝션 중간에 API 호출을 한다? 이거부터 다시 생각을 해볼 필요가 있을거 같다. 보기만해도 안티패턴 같은데, 왜 그럴까?

Trsanction

사용이유

트랜잭션을 사용하는 이유는 데이터 정합성을 위함이다. 다수의 요청이 들어왔을 때 DBMS는 그 순서를 정확하게 보장할 수 없다. 그럴 경우, 데이터의 정확성에 문제가 생김. 이 때 논리적으로 한 번에 처리될 필요가 있는 하나 이상의 쿼리를 단일 작업으로 묶어 데이터 정합성을 확보한다.

특성(ACID)

원자성(Atomicity)

트랜잭션에 묶인 작업 단위는 모두 실행되어야 한다. 트랜잭션 처리 과정 중 문제가 생긴다면, 이전상태로 롤백한다. 이를 위해 DBMS는 이전 상태를 저장해놓는데, 이전상태를 저장하는 임시 영역을 롤백 세그먼트라고 한다.

일관성(Consistency)

트랜잭션의 일관성 보장은, 테이블에 이벤트에 자동으로 반응하는 트리거(Trigger)를 통해 보장하는데, 이 트리거는 해당 작업 전・후에 모든 제약조건과 만족하는지 확인하여 일관성을 유지한다.

독립성(Isolation)

트랜잭션은 서로 영향울 주면 안된다. 트랜잭션 작업 중 다른 트랜잭션이 끼어들지 못하도록 보장해야 한다.

트랜잭션은 공통된 데이터를 동시 처리하는 과정에서 갱신분실, 오손판독, 반복불가능, 팬텀문제등 여러 문제가 발생할 수 있다.

DBMS는 Lock & excute unlock을 통해 고립성을 보장한다

  • Lock 종류
    - 공유
    - 다른 트랜잭션의 변경 작업은 막고, 읽기는 허용한다.
    • 단독
      • 다른 트랜잭션의 모든 작업을 막고 단독적인 소유권을 획득한다.
        - 서비스 전체적으로 작업이 멈출 수 있음.
        - 동시성이 매우 필요한 부분+ 작은 범위에서 사용해야되지 않을까?

지속성(Durability)

트랜잭션이 성공(Commit)하면 하드웨어 혹은 소프트웨어 오류등 어떤 이유로도 그 결과가 유실 되서는 안되고 영원히 보장되어야 한다. 트랜잭션이 성공하면, 트랜잭션에 대한 로그가 남아있어야 한다.

트랜잭션 중간의 API호출

API호출은 언제나 실패 가능성이 있다. 호출 결과를 가지고 DB 작업을 해야되는 상황이라면, 안정적으로 데이터를 처리하는것에 영향을 줄 수 있다(외부요인으로). 게다가 트랜잭션 사용중에는 락이 걸리기때문에 서비스 전체적으로 영향이 갈 수 있다. API 호출이 너무 오래걸릴 경우, 이러한 부분도 충분히 문제가 될 수 있겠다 싶음.
일단, 원자성이 깨진다? 원자성이 너무쉽게 깨진다?고 생각하는걸로 하고, 다시 공부해야지

다음은 GPT의 대답이다

트랜잭션 시간 증가: 트랜잭션은 데이터베이스 리소스를 잠금 상태로 유지합니다. 외부 API 호출은 네트워크 상태와 응답 시간에 따라 다를 수 있으므로 트랜잭션 시간이 불필요하게 길어질 수 있습니다. 이는 데이터베이스 리소스의 비효율적인 사용으로 이어질 수 있습니다.

트랜잭션 롤백의 어려움: 외부 API 호출은 트랜잭션과 독립적으로 수행됩니다. 만약 트랜잭션이 실패하여 롤백되더라도, 이미 수행된 API 호출을 취소하거나 되돌릴 방법이 없습니다. 이는 데이터 일관성 문제를 초래할 수 있습니다.

신뢰성 문제: 외부 API는 다양한 이유로 실패할 수 있습니다 (예: 네트워크 문제, 서버 다운타임). 트랜잭션 내에서 이러한 실패를 처리하는 것은 복잡성을 증가시키고, 트랜잭션의 신뢰성을 낮출 수 있습니다.

Transaction is not started yet, start transaction before committing or rolling it back.

TypeORM을 사용하고 있는데, 작업중 발생한 에러이다. 이 에러의 원인은 뭘까?

typeORM githiub이슈에 올라온 글을 보면

I found the solution, might be useful for anyone : when using transaction, you mustn't mix up save and find queries with the transaction manager. Otherwise, i suppose mysql doesn't know what to rollback, therefore it just commits everything. So just use the repository manager for find requests, and use the queryRunner.manager or transactionManager only for saving the transaction entities.

트랜잭션 관리자와 save, find와 같은 쿼리를 섞어서 생기는 문제? typeorm 문서에도

트랜잭션에서 작업할 때 가장 중요한 제한 사항은 제공된 엔티티 관리자 인스턴스를 항상 transactionalEntityManager 사용해야 한다는 것입니다 . 이 예에서는 GLOBAL ENTITY MANAGER를 사용하지 마십시오. 모든 작업은 제공된 트랜잭션 엔티티 관리자를 사용하여 실행 해야 합니다.

결국 내 로직 내에서 quneryRunner.manager를 사용하지 않는 부분이 있다는걸까? 일단 의심가는 부분은 외부

  1. API 호출을 위해 DB에서 값을 가져와 사용하는 것
    • 여기서 가져오는 값은 직전에 저장한 데이터를 가져오는 것
    • 즉. commit 이전 데이터인데, 이럴경우 문제가 있을 수 있을까?
  2. API 호출 후 DB작업을 하는것,

그래서 TODO

  • API 호출부를 외부로 트랜잭션 외부로 빼자
  • 1차적으로 데이터 커밋이 다된 후, 트랜잭션 외부에서 외부 API 로직을 다시 처리하자

이렇게해도 안된다면...다른 방법을 시도해야지 아자자

참고자료

profile
프론트엔드

0개의 댓글