Post List

2015년 8월 5일 수요일

MongoDB Study #09 Transaction

1. MongoDB Transaction

RDBMS는 Transaction 위주의 작업에 적합한 DB 입니다.
MongoDB 같은 NoSQL DB는 반면 대량의 Data의 빠른 입출력에 적합합니다.
RDBMS의 Transacion의 모든 기능을 완벽하게 제공해주지는 못하더라도,
유사하게 동작하게 구현이 가능합니다.

Programming 경험이 좀 있으신 분들은
설마... 임시 Table 하나 만들어 놓고, 원래 정보가 있는 Table에 Flag를 하나 만들어서 그 값을 보고
비교해서 고치고 그런식으로 하라는 건 아니겠지 ? 라고 생각할 수 있으시겠죠 ?
네. 그거 맞습니다. ;;; 맞고요...
그렇게 라도 필요하면 해야죠.
그렇게 구현하고 나중에 배울 lock을 같이 접목시키면 RDBMS의 Transaction 같은 작업 처리가 가능합니다.

2개의 작업을 한번에 commit 하는 것과 rollback 하는 방법에 대해서 소개해 드리겠습니다.

2. Two Task Commit

먼저 2명의 계좌 정보를 입력합니다.

db.bank.save( { name : "Luna" , balance : 2500, pending: [] } )
db.bank.save( { name : "Star" , balance : 1300 , pending: [] } )
 

이체 작업을 위한 정보를 입력합니다.

db.transfer.save( { src : "Luna" , dst : "Star" , value : 100 , state : "init" } ) 

해야 할 작업이 있는지 보고 있다면, 그중 하나를 가져옵니다.
그리고, 일단 변수에 담아둡니다.

t = db.transfer.findOne( { state : "init" } )
{
        "_id" : ObjectId("55c14f8a2a1c4137302b1809"),
        "src" : "Luna",
        "dst" : "Star",
        "value" : 100,
        "state" : "init"
} 

그 정보를 이용하여 계좌 정보의 값을 변경합니다.
pending field 값을 이용하여 작업중을 판단할수 있도록 기록합니다.

db.transfer.update ( { _id : t._id } , { $set : { state : "pending" } } )
db.bank.update( { name : t.src , pending : { $ne : t._id } } , { $inc : { balance : - t.value} , $push : { pending : t._id } } )
db.bank.update( { name : t.dst , pending : { $ne : t._id } } , { $inc : { balance : t.value} , $push : { pending : t._id } } )
 

db.bank.find()
{ "_id" : ObjectId("55c14f2b2a1c4137302b1807"), "name" : "Luna", "balance" : 2400, "pending" : [ ObjectId("55c14f8a2a1c4137302b1809") ] }
{ "_id" : ObjectId("55c14f4d2a1c4137302b1808"), "name" : "Star", "balance" : 1400, "pending" : [ ObjectId("55c14f8a2a1c4137302b1809") ] }
db.transfer.find()
{ "_id" : ObjectId("55c14f8a2a1c4137302b1809"), "src" : "Luna", "dst" : "Star", "value" : 100, "state" : "pending" } 

이제 모든 작업이 끝났으니 commit 작업을 해줍니다.

db.transfer.update( { _id : t._id } , { $set : { state: "commited" } } )
db.bank.update( { name : t.src } , { $pull : { pending : t._id } } )
db.bank.update( { name : t.dst } , { $pull : { pending : t._id } } )
db.transfer.update( { _id : t._id } , { $set : { state : "done" } } )
 

db.bank.find()
{ "_id" : ObjectId("55c14f2b2a1c4137302b1807"), "name" : "Luna", "balance" : 2400, "pending" : [ ] }
{ "_id" : ObjectId("55c14f4d2a1c4137302b1808"), "name" : "Star", "balance" : 1400, "pending" : [ ] }
db.transfer.find()
{ "_id" : ObjectId("55c14f8a2a1c4137302b1809"), "src" : "Luna", "dst" : "Star", "value" : 100, "state" : "done" }

어때요. 참~ 쉽죠 ?
어렵진 않죠. ㅎ 다만 좀 귀찮을 뿐이죠.

3. Two Task Rollback

현재 상태에서 다시 이체 작업을 하나 등록한 뒤,
작업을 위해서 등록한 작업 중 하나를 찾습니다.

db.transfer.save( { src : "Luna" , dst : "Star" , value : 100 , state : "init" } )
t = db.transfer.findOne( { state : "init" } )
{
        "_id" : ObjectId("55c1550f2a1c4137302b180a"),
        "src" : "Luna",
        "dst" : "Star",
        "value" : 100,
        "state" : "init"
}

​앞의 작업에서 처럼 일단 해당 작업을 수행하겠습니다.


db.transfer.update ( { _id : t._id } , { $set : { state : "pending" } } )
db.bank.update( { name : t.src , pending : { $ne : t._id } } , { $inc : { balance : - t.value} , $push : { pending : t._id } } )
db.bank.update( { name : t.dst , pending : { $ne : t._id } } , { $inc : { balance : t.value} , $push : { pending : t._id } } )
 

db.bank.find()
{ "_id" : ObjectId("55c14f2b2a1c4137302b1807"), "name" : "Luna", "balance" : 2300, "pending" : [ ObjectId("55c1550f2a1c4137302b180a") ] }
{ "_id" : ObjectId("55c14f4d2a1c4137302b1808"), "name" : "Star", "balance" : 1500, "pending" : [ ObjectId("55c1550f2a1c4137302b180a") ] }
db.transfer.find()
{ "_id" : ObjectId("55c14f8a2a1c4137302b1809"), "src" : "Luna", "dst" : "Star", "value" : 100, "state" : "done" }
{ "_id" : ObjectId("55c1550f2a1c4137302b180a"), "src" : "Luna", "dst" : "Star", "value" : 100, "state" : "pending" }


​이까지는 앞 단계랑 똑같죠 ?
이제 작업을 취소하고 rollback 시키는 과정을 수행하겠습니다.


db.transfer.update( { _id : t._id } , { $set : { state : "canceling" } } ) 
db.bank.update( { name : t.src , pending : t._id } , { $inc : { balance : t.value } , $pull : { pending : t._id } } )
db.bank.update( { name : t.dst , pending : t._id } , { $inc : { balance : -t.value } , $pull : { pending : t._id } } )

db.transfer.update ( { _id : t._id } , { $set : { state : "cancelled" } } )

db.bank.find()
{ "_id" : ObjectId("55c14f2b2a1c4137302b1807"), "name" : "Luna", "balance" : 2400, "pending" : [ ] }
{ "_id" : ObjectId("55c14f4d2a1c4137302b1808"), "name" : "Star", "balance" : 1400, "pending" : [ ] }
db.transfer.find()
{ "_id" : ObjectId("55c14f8a2a1c4137302b1809"), "src" : "Luna", "dst" : "Star", "value" : 100, "state" : "done" }
{ "_id" : ObjectId("55c1550f2a1c4137302b180a"), "src" : "Luna", "dst" : "Star", "value" : 100, "state" : "cancelled" }

댓글 없음:

댓글 쓰기