In MongoDB, multi-document atomic-transactions are not supported. However atomic operations on a single document are supported. This mean the atomicity will have been maintained at the document level.
For you to maintain atomicity, it is recommended that you keep all related information in a single document and in the form of an embedded document.
Consider the document given below:
{
"_id":1,
"product_name":"Samsung Galaxy",
"category":"mobiles",
"total_products":20,
"available_product":12,
"product_bought_by":[ {"customer":"joel",
"date":"7-Dec-2015"
},
{
"customer":"mercy",
"date":"10-Dec-2015"
}
]
}
The field "product_bought_by" has been used for embedding information about a customer who buys a product. If a new customer comes to buy a product, the field "available_product" will be used for checking whether the product is available or not. If there are some products, then the value of the field will be reduced. At the same time, a new customer will be added to the embedded document in the field "product_bought_by" field. The command "findAndModify" can be used since it finds and updates the document at a go. This is shown below:
> db.products.findAndModify({query:{_id:3, available_product:{$gt:0}}, update:{$inc:{available_products:-1},
$push:{product_bought_by: {customer:"gideon",date:"10-Dec-2015"}}
}
})
The above two mechanism are for ensuring that the update of the document is only done when the product is available. This will ensure that the transaction is atomic in nature.
Consider a situation in which the information the customer who purchased the device had been kept separately. In this case, the first query will be used for checking on the availability of the product. The second query will then be used for updating the information about the purchase. Suppose there was only one product available. Between the above two transactions, another customer may come in and purchased the product. This will mean that there will be no product available. Despite this, our second query will update the information about the purchased based on the information from the first query. We will then have sold a product that is not available. The database will then be in an inconsistent state.
When a single write operation modifies multiple documents, the modification of each document is atomic, but the operation as a whole is not atomic and other operations may interleave. However, you can isolate a single write operation that affects multiple documents using the $isolated operator.
$isolated Operator
Using the $isolated operator, a write operation that affect multiple documents can prevent other processes from interleaving once the write operation modifies the first document. This ensures that no client sees the changes until the write operation completes or errors out.
Isolated write operation does not provide “all-or-nothing” atomicity. That is, an error during the write operation does not roll back all its changes that preceded the error.
Transaction-Like Semantics
Since a single document can contain multiple embedded documents, single-document atomicity is sufficient for many practical use cases. For cases where a sequence of write operations must operate as if in a single transaction, you can implement a two-phase commit in your application.
However, two-phase commits can only offer transaction-like semantics. Using two-phase commit ensures data consistency, but it is possible for applications to return intermediate data during the two-phase commit or rollback.
For more information on two-phase commit and rollback, see Perform Two Phase Commits.
Concurrency Control
Concurrency control allows multiple applications to run concurrently without causing data inconsistency or conflicts.
One approach is to create a unique index on a field that can only have unique values. This prevents insertions or updates from creating duplicate data. Create a unique index on multiple fields to force uniqueness on that combination of field values. For examples of use cases, see update() and Unique Index and findAndModify() and Unique Index.
Another approach is to specify the expected current value of a field in the query predicate for the write operations. For an example, see Update if Current.
The two-phase commit pattern provides a variation where the query predicate includes the application identifier as well as the expected state of the data in the write operation.