개발/DB

[prisma] Transaction API error

prpn97 2023. 12. 6. 13:20

발단

  prisma 를 통해 api를 구현하는 중 acid한 구조를 만들기 위해 트랜잭션을 통해 처리하는 기능을 구현했다. 

구현하다가 에러가 발생했고, 어떤 문제인지 찾아보려 한다. 

 

PrismaClientKnownRequestError: Transaction API error: Transaction already closed: A commit cannot be executed on an expired transaction. The timeout for this transaction was 5000 ms, however 5066 ms passed since the start of the transaction. Consider increasing the interactive transaction timeout or doing less work in the transaction.

 

 

  해당 오류는 트랜잭션이 실행되는 동안 지정된 시간(기본적으로 5000ms)을 초과하여 트랜잭션이 종료되었기 때문에 발생한다는 것이다. 이는 트랜잭션 내에서 수행하는 작업이 너무 오래 걸리거나, 트랜잭션의 타임아웃 설정이 너무 짧기 때문에 발생할 수 있다고 하는데, 기존에 트랜잭션 처리하면서는 에러를 본 적이 없었고, 트랜잭션 안에서 작업하는 내용이 많지는 않았다. 

 

해결 과정

  물론 해당 문제는 위에 언급한 에러 메세지처럼 트랜잭션의 부하에 관련된 내용으로 보인다. 어떤 점이 문제였을까 하고 찾아보았는데, 이번 문제의 경우에는 트랜잭션을 중첩해서 생기는 문제였다. 문제 자체는 트랜잭션을 내부적으로 새로 만들어 중첩하지 않고 기존 트랜잭션을 매개변수로 전달하자마자 바로 해결되었다. 

  async checkIn(userId: bigint) {
    const now = new Date();

    return await this.prismaService.$transaction(async (prisma) => {
      // 출석 체크 기록 생성
      const checkInData = await prisma.user_check_in.create({
        data: { user_id: userId, checked_at: now },
        select: { id: true, checked_at: true },
      });

      // 사용자 퀘스트 상태 가져오기
      const userQuest = await this.getOneUserQuestStatus(
        userId,
        Quest.CHECK_IN
      );

      // 사용자 퀘스트 상태 업데이트
      await this.updateUserQuestSucceedStatus(userQuest.id, prisma);
      return checkInData;
    });
  }
  
  
  
  async updateUserQuestSucceedStatus(
    userQuestId: bigint,
    prisma: Prisma.TransactionClient
  ) {
    const now = new Date();
    const userQuest = await prisma.user_quests.update({
      where: { id: userQuestId },
      data: { succeed_status: true, last_succeed_at: now },
    });
    await this.createUserQuestHistory(userQuest.id, prisma);

    return userQuest;
  }

 

 

  현재 service에서 바로 prisma를 통해 데이터를 가져오지 않고, 별도로 repository 레이어에서 prisma를 통해 데이터를 가져오고 있다. service에서는 repository에서 가져온 데이터에 로직, 에러처리를 더하게 된다.

  updateUserQuestSucceedStatus 를 보면 지금은 받아온 트랜잭션의 prisma에서 업데이트문을 실행하지만, 이 안에서 새로운 트랜잭션을 불러일으킨다면 기존에 acid함을 무시하고 새로운 것을 만들어내려고 하니 문제가 생기는 것으로 파악된다. 

 

 

728x90