MongoDB (Mongoose) Transaction 사용 방법

2023. 2. 26. 17:15MongoDB, Mongoose

MongoDB가 4.0, Mongoose는 5.0 버전 부터 트랙젝션이 지원이 되었다.

 

트랜잭션 사용 시에는 트랜잭션 자체가 원자적으로 동작해야 때문에 비동기로 요청을 하면 안된다.

 

만약 비동기로 여러개의 데이터가 업데이트가 진행되면 마지막 데이터만 업데이트가 되는 현상이 발생 할 수 있다.

1. Mongoose 연결 시 필수 설정

import mongoose from 'mongoose';

mongoose.connect('mongodb://localhost/mydatabase', {
  useNewUrlParser: true,
  useCreateIndex: false,
  useFindAndModify: false,
  useUnifiedTopology: true
});

const session = mongoose.startSession();

2. withTransaction 메소드 사용

whithTransaction 함수 안에서 작동 해야하는 쿼리를 넣어 준다.

const session = mongoose.startSession();

session.withTransaction(async () => {
 const book = await this.bookModel.findOne({ title }).session(session);
 const updatedBook = await this.bookModel.findOneAndUpdate({ title }, { $inc: { copies: increment } }, 
 { new: true }).session(session);
});

* .session()를 사용 하는 이유 :

쿼리가 외부에서 실행이 되어 Transaction 실행 시 일관성이 깨지는 위험성이 있기 때문이다.
그래서 동일한 Session 으로 Transaction이 진행 할 수 있게 사용해야 한다.

withTransaction 는 자동으로 세션이 종료 된다.

 


3. session.endSession 메소드 사용  [ Session 종료 ]

* withTransaction  함수 경우에는 함수 종료 시 자동으로 세션이 종료된다

mongoose.startSession() 로 Session 을 시작 했으니 종료를 넣어 주어야 한다.

const session = mongoose.startSession();

const book = await this.bookModel.findOne({ title }).session(session);
const updatedBook = await this.bookModel.findOneAndUpdate({ title }, { $inc: { copies: increment } }, 
{ new: true }).session(session);
 
session.endSession();

 

* Session 을 종료해야 하는 이유

1. Resource management  : 작업이 끝났는데도 자원을 계속 잡고 있으면 연결 자원이 떨어져 성능 저하가 일어나거나 또는 충돌이 일어날 수 있다.

2. Transaction management : Transaction 를 진행 하면 Data Lock를 시키고 변경이 된다. 그 때 Session을 종료하지 않으면 Data Lock이 풀리지 않아 다른 Transaction 이나, 다른 Query에 동일한 Data를 사용 할 수 없게 된다.

3. Memory management : Memory를 사용하여 상태를 확인하는데 그 때 Session 종료가 되지 않으면 메모리 부족현상이 일어난다.


4. 에러 처리

const session = mongoose.startSession();

try {
  await session.withTransaction(async () => {
    const book = await this.bookModel.findOne({ title }).session(session);

    if (!book) {
      throw new Error('404 Error');
    }

    const updatedBook = await this.bookModel
      .findOneAndUpdate({ title }, { $inc: { copies: increment } }, { new: true })
      .session(session);

    if (!updatedBook) {
      throw new Error('500 Error');
    }
  });
} catch (error) {
  throw new Error('500 Error');
}

 

728x90
반응형