--- title: mongodb transaction tags: mongodb, transaction, yoctol, kurator --- # mongodb transaction # set up replica set Download the docker-compose settings `$ curl -sSL https://raw.githubusercontent.com/bitnami/bitnami-docker-mongodb/master/docker-compose-replicaset.yml > docker-compose.yml` ``` version: '2' services: mongodb-primary: image: 'bitnami/mongodb:latest' environment: - MONGODB_ADVERTISED_HOSTNAME=mongodb-primary - MONGODB_REPLICA_SET_MODE=primary - MONGODB_ROOT_PASSWORD=password123 - MONGODB_REPLICA_SET_KEY=replicasetkey123 volumes: - 'mongodb_master_data:/bitnami' mongodb-secondary: image: 'bitnami/mongodb:latest' depends_on: - mongodb-primary environment: - MONGODB_ADVERTISED_HOSTNAME=mongodb-secondary - MONGODB_REPLICA_SET_MODE=secondary - MONGODB_INITIAL_PRIMARY_HOST=mongodb-primary - MONGODB_INITIAL_PRIMARY_PORT_NUMBER=27017 - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=password123 - MONGODB_REPLICA_SET_KEY=replicasetkey123 mongodb-arbiter: image: 'bitnami/mongodb:latest' depends_on: - mongodb-primary environment: - MONGODB_ADVERTISED_HOSTNAME=mongodb-arbiter - MONGODB_REPLICA_SET_MODE=arbiter - MONGODB_INITIAL_PRIMARY_HOST=mongodb-primary - MONGODB_INITIAL_PRIMARY_PORT_NUMBER=27017 - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=password123 - MONGODB_REPLICA_SET_KEY=replicasetkey123 volumes: mongodb_master_data: driver: local ``` and run docker-compose using: `$ docker-compose up --detach` # Mongodb transaction transaction before mongodb 4.0: https://juejin.im/post/5ace2f935188255566700f19 transaction in mongodb 4.0 or later version: ``` db.accounts.insert( [ { _id: "A", balance: 1000, pendingTransactions: [] }, { _id: "B", balance: 1000, pendingTransactions: [] } ] ) db.transactionRecords.insert( { _id: 1, source: "A", destination: "B", value: 100, state: "initial", lastModified: new Date() } ) // Start a session. session = db.getMongo().startSession( { readPreference: { mode: "primary" } } ); accountsCollection = session.getDatabase("test").accounts; transactionRecordsCollection = session.getDatabase("test").transactionRecords; // Start a transaction session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } ); // Operations inside the transaction try { accountsCollection.updateOne( { _id: "A", }, { $set: { balance: 900 } } ); accountsCollection.updateOne( { _id: "B", }, { $set: { balance: 1100 } } ); transactionRecordsCollection.updateOne( { _id: 1, }, { $set: { state: "done" }, $currentDate: { lastModified: true } }); } catch (error) { // Abort transaction on error session.abortTransaction(); throw error; } // Commit the transaction using write concern set at transaction start session.commitTransaction(); session.endSession(); ``` mongodb 4.0 有了新的 Multi-document transactions,在這之前 mongodb 只保證單一檔案的寫入 atomicity,既使是 batchInsert 也沒有保證寫入是同時完成的。所以使用者只能自行實作 transaction 機制,讓存取 db 的邏輯變得更加複雜。 利用新的 Multi-document transactions 可以像 relational db 一樣輕鬆做到保證跨 document、跨 collection、甚至是跨 db 的操作能夠一併完成,並且可以定義 read preference、read concern、write concern(類似 relational db 的 isolation level)。 在 mongodb 4.2 更是支援了 sharded clusters 的 transaction。 # Read Concern 在 transaction 過程中,僅適用 transaction level read concern,所有設定在 collection 以及 database level 的 read concern 設定都會被忽略;你可以在 startTransaction 時設定 readConcern,若未設定則會自動套用 session-level read concern;若 startSession 時未設定,則會自動套用 client-level 的 read concern,[預設情況下 client-level 的 read concern 是 local](https://docs.mongodb.com/manual/reference/mongodb-defaults/#read-concern) - local - Read concern "local" returns the most recent data available from the node but can be rolled back. - 接近 Read Uncommitted Isolation,可能會造成 Dirty Read 等等問題 - majority - Read concern "majority" returns data that has been acknowledged by a majority of the replica set members (i.e. data cannot be rolled back) if the transaction commits with write concern “majority”. - If the transaction does not use write concern “majority” for the commit, the "majority" read concern provides no guarantees that read operations read majority-committed data. - snapshot (is only available for multi-document transactions) - Read concern "snapshot" returns data from a snapshot of majority committed data if the transaction commits with write concern “majority”. - If the transaction does not use write concern “majority” for the commit, the "snapshot" read concern provides no guarantee that read operations used a snapshot of majority-committed data. - 可以避免類似 Phantom read 的問題 # Write Concern Transaction 的 write concern 只適用在 commit write operation 的時候,transaction 過程中的所有 write operation 無視 write concern(對 write operation 設定 write concern 會噴錯,因為 commit 時才算是真正寫入) 你可以在 startTransaction 時設定 writeConcern,若未設定則會自動套用 session-level write concern;若 startSession 時未設定,則會自動套用 client-level 的 write concern,[預設情況下 client-level 的 write concern 是 w: 1](https://docs.mongodb.com/manual/reference/mongodb-defaults/#write-concern)。 - w: 1 - Write concern w: 1 returns acknowledgement after the commit has been applied to the primary. - When you commit with w: 1, your transaction can be rolled back if there is a failover. - When you commit with w: 1 write concern, transaction-level "majority" read concern provides no guarantees that read operations in the transaction read majority-committed data. - When you commit with w: 1 write concern, transaction-level "snapshot" read concern provides no guarantee that read operations in the transaction used a snapshot of majority-committed data. - w: "majority" - Write concern w: "majority" returns acknowledgement after the commit has been applied to a majority (M) of voting members; i.e. the commit has been applied to the primary and (M-1) voting secondaries. - When you commit with w: "majority" write concern, transaction-level "majority" read concern guarantees that operations have read majority-committed data. - When you commit with w: "majority" write concern, transaction-level "snapshot" read concern guarantees that operations have from a synchronized snapshot of majority-committed data. # Read Preference - primary refs: https://docs.mongodb.com/manual/core/causal-consistency-read-write-concerns/ https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/ https://www.mongodb.com/blog/post/quick-start-nodejs--mongodb--how-to-implement-transactions