Transaction은 복수개의 작업들을 고립된 상태로 진행하게 해줍니다. 쉽게 말하면, 복수개의 작업이 모두 성공했을 때만 성공하게 되고, 그중 하나라도 실패하게 되면 모두가 실패하게 됩니다. 실패된 뒤에는 transaction 작업 전의 상태로 되돌아가게 됩니다.
Transaction은 MongoDB sessions에 구성되어 있습니다. startSession()
호출한뒤 startTransaction()
function을 호출하여야 트렌젝션을 시작할 수 있습니다. 트렌젝션 안에서 작업을 실행하려면 session을 옵션으로 전달해주어야만 합니다.
const Account = db.model('Account', new Schema({
onwer: String,
money: Number,
}));
await Account.create({ name: 'kim', money: 1000 });
await Account.create({ name: 'choi', money: 1000 });
await Account.insertMany([
{ name: 'kim', money: 1000 },
{ name: 'choi', money: 1000 }
]
);
const session = await db.startSession();
session.startTransaction();
// kim의 계좌에서 인출
await Account.update({ name: 'kim' }, { $inc : { money: 100 } }, { new: true }).session(session);
// choi의 계좌에 송금
await Account.update({ name: 'choi' }, { $inc: { money: -100 } }, { new: true }).session(session);
await session.commitTransaction();
session.endSession();
위 코드에서 체크해야할 것은 몇가지 없습니다.
1. startSession()
으로 세션을 생성합니다.
2. session.startTransaction()
을 통해 트렌젝션을 시작합니다.
3. DB 명령어들에 .session(session)
을 붙여줍니다.(sessio 옵션을 설정합니다.)
4. session.commitTransaction()
모든 작업들을 commit(save changes)합니다.
5. session.endSession()
을 통해 세션을 종료합니다.
const session = await Account.startSession();
session.startTransaction();
await Account.create([{ name: 'Test' }], { session: session });
await Account.create([{ name: 'Test2' }], { session: session });
// abortTransaction() 메서드를 통해 rollback 시킵니다.
await session.abortTransaction();
const count = await Account.countDocuments();
assert.strictEqual(count, 0);
session.endSession();
abortTransaction()
메서드를 통해 트랜젝션내의 모든 작업을 rollback시킬 수 있습니다.
실패시 재시도
transaction에 에러가 발생하면, MongoDB 드라이버가 알아서 트렌젝션을단 한번
재시도합니다.
세션을 이용할 때 좀더 편하게 쓰려면 withTransaction()
를 쓰세요 !
const session = await db.startSession();
session.startTransaction();
// kim의 계좌에서 인출
await Account.update({ name: 'kim' }, { $inc : { money: 100 } }, { new: true }).session(session);
// choi의 계좌에 송금
await Account.update({ name: 'choi' }, { $inc: { money: -100 } }, { new: true }).session(session);
await session.commitTransaction();
session.endSession();
위 코드를 아래코드로 줄여줍니다. startTransaction()과 commitTransaction() 코드를 한줄로 줄여줍니다.
const session = await db.startSession();
await session.withTransaction(() => {
// kim의 계좌에서 인출
await Account.update({ name: 'kim' }, { $inc : { money: 100 } }, { new: true }).session(session);
// choi의 계좌에 송금
await Account.update({ name: 'choi' }, { $inc: { money: -100 } }, { new: true }).session(session);
});
session.endSession();
save()
메서드는 자동으로 관련된 session을 이용하도록 만들어져 있습니다.