---
# System prepended metadata

title: mongodb transaction
tags: [' yoctol', mongodb, ' kurator', ' transaction']

---

---
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