💭

[MongoDB]mongoshでBEGIN...COMMIT

2022/08/03に公開

概要

RDBで直接SQLを実行してデータを修正するようなケースにおいて、
BEGINを実行 -> データ修正クエリ -> データ確認クエリ -> 問題なければCOMMITを実行 
のようなトランザクションを利用したオペレーションを手動で行うこともあると思います。
MongoDBに対し、mongoshで同等の実行ができるかやってみました。

下記のドキュメントなど参考にしつつ、いくらか試してみてそれっぽくできたので備忘として記録します。

https://www.mongodb.com/docs/manual/reference/method/Mongo.startSession/

環境

MongoDB Atlas 4.4.15

やってみた

mongoshで、usersコレクションにinsertする処理をAbort/Commitしてみます。
実行はCompassのmongoshターミナルで実施しています。

セッション生成~データ登録

セッションオブジェクトの生成

> var session = db.getMongo().startSession()

トランザクション開始

> session.startTransaction({
    "readConcern": { "level": "snapshot" },
    "writeConcern": { "w": "majority" }
})

操作するコレクションを取得

> var users = session.getDatabase('test').getCollection('users')

インサート

> users.insertOne({name: "Yuto"})
// Result:
// { acknowledged: true,
//   insertedId: ObjectId("62ea2800b5612e419f621fda") }

インサートしたデータ確認

> users.findOne({name: "Yuto"})
// Result:
// { _id: ObjectId("62ea2800b5612e419f621fda"), name: 'Yuto' }

Abort

トランザクションをAbort

> session.abortTransaction()

インサートしたデータ確認(Abortされたのでヒットしない)

> users.findOne({name: "Yuto"})
// Result:
// null

Commit

トランザクションをコミット

> session.commitTransaction()
// Result:
// {
//   ok: 1,
//   '$clusterTime': {
//     clusterTime: Timestamp({ t: 1659513236, i: 109 }),
//     signature: {
//       hash: Binary(Buffer.from("df95ffba0897860cac7eb22e9604e6c14fbd53c5", "hex"), 0),
//       keyId: Long("7080911735359537154")
//     }
//   },
//   operationTime: Timestamp({ t: 1659513236, i: 109 })
// }

インサートしたデータ確認

> users.findOne({name: "Yuto"})
// Result:
// { _id: ObjectId("62ea2989b5612e419f621fdb"), name: 'Yuto' }

タイムアウト考慮

今回のような手動オペレーションでトランザクション内でコマンドを実行する場合、トランザクションのタイムアウト値(transactionLifetimeLimitSeconds, デフォルト60秒)によって途中でabortされてしまう可能性があります。

https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.transactionLifetimeLimitSeconds

この値はAtlasの場合はユーザ側では変更できず、サポートに変更をリクエストする必要があるようです。

https://www.mongodb.com/community/forums/t/transactions-that-are-taking-more-than-60-seconds-on-atlas/13643

なお、現在のタイムアウト値は下記のコマンドで確認できます。

command
db.adminCommand({getParameter: 1, "transactionLifetimeLimitSeconds": 1})
result
{
  transactionLifetimeLimitSeconds: 60,
  ok: 1,
  ...
}

まとめ

BEGIN...COMMIT的なオペレーションが可能なことを確認できました。
需要のない記事とは思いますが、逆にすぐに情報出てこなかったので、どなたかの参考になれば幸いです。

Discussion