Data integrity means keeping stored data coherent and protected from unintended changes. In MongoDB, the first safeguard is single-document atomicity: an insert, update, or delete of one document completes in full or does not take effect. When related changes span documents, a multidocument transaction can make them succeed or fail together. section 8.2 section 8.0
Avoid a partial booking
A travel booking needs both a flight and a hotel. If the flight reservation succeeds but the hotel reservation fails, aborting the transaction also undoes the flight reservation. The database is not left showing half a booking. section 8.0
Prefer storing related data together when the application permits it: single-document atomicity often avoids the need for a distributed transaction. When updates must cross documents, put all dependent operations in one transaction. For example, a transaction can update an account, update its customer record, and insert a transaction record; if an operation fails, the group is aborted. section 8.5 section 8.4.3
Listing 8.2 Executing a multidocument transaction with Node.js and Callback API
const { MongoClient } = require('mongodb')
// Replace with your actual connection string
const uri = "your_mongodb_connection_string"
async function run(accountId) {
const client = new MongoClient(uri)
try {
await client.connect()
const session = client.startSession()
const transactionOptions = {
readConcern: { level: 'snapshot' },
writeConcern: { w: 'majority' },
readPreference: 'primary'
}
await session.withTransaction(async () => {
const accounts = client.db('sample_analytics').collection('accounts')
const customers = client.db('sample_analytics').collection('customers')
const transactions = client.db('sample_analytics').collection('transactions')
const currentDate = new Date()
const account = await accounts.findOne(
{ account_id: parseInt(accountId) },
{ session }
)
if (!account) throw new Error('Account not found')
const accountsUpdateResult = await accounts.updateOne(
{ account_id: parseInt(accountId) },
{
$set: { limit: 12000, last_transaction_date: currentDate },
$inc: { transaction_count: 1 }
},
{ session }
)
const customersUpdateResult = await customers.updateMany(
{ accounts: { $in: [parseInt(accountId)] } },
{
$inc: { transaction_count: 1 },
$set: { last_transaction_date: currentDate }
},
{ session }
)
const transactionsInsertResult = await transactions.insertOne(
{
account_id: parseInt(accountId),
transaction_count: account.transaction_count + 1,
bucket_start_date: currentDate,
bucket_end_date: currentDate,
transactions: [
{
date: currentDate,
amount: 1500,
transaction_code: 'buy',
symbol: 'amzn',
price: '125.00',
total: '187500.00'
}
]
},
{ session }
)
console.log("Transaction committed.")
console.log("Accounts updated:", accountsUpdateResult.modifiedCount)
console.log("Customers updated:", customersUpdateResult.modifiedCount)
console.log("New transaction inserted:", transactionsInsertResult.insertedId)
}, transactionOptions)
session.endSession()
} catch (error) {
console.error("Transaction aborted due to error:", error)
} finally {
await client.close()
}
}
// Get accountId from command line arguments
const accountId = process.argv[2]
if (!accountId) {
console.error("Please provide an account ID as an argument.")
process.exit(1)
}
run(accountId).catch(console.dir)
The Node.js listing shows dependent account, customer, and transaction operations inside a session’s withTransaction callback, with transaction read and write concerns configured. section 8.4.3
Keep the integrity mechanism practical to operate:
- Keep transactions short, use indexes for their operations, and configure appropriate read and write concerns. Handle transient failures with retries; transactions across shards add performance overhead. section 8.5
- Maintain backups separately from Atlas Online Archive. Archived data remains queryable, but the archive is not a recovery substitute for data loss or corruption. section 17.0
- Protect against changes made without permission through security controls. Atlas SQL takes a different, narrower approach: its federated interface is read-only, avoiding writes and synchronization across queried sources. section 20.0 section 18.1

















