Causal consistency in MongoDB is about ensuring that once you’ve written something, you’ll always read that write (and any subsequent writes from the same client) on subsequent reads from the same client.
Let’s see it in action. Imagine we have a replica set and we want to read our own writes.
First, we need to configure our client to use causal consistency. This is done by passing a causalConsistency option when creating a MongoClient instance.
const { MongoClient } = require('mongodb');
async function run() {
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri, {
useUnifiedTopology: true,
// Enable causal consistency
causalConsistency: true
});
try {
await client.connect();
const database = client.db("myDatabase");
const collection = database.collection("myCollection");
// --- Write Operation ---
console.log("Inserting a document...");
const insertResult = await collection.insertOne({ item: "apple", quantity: 10 });
console.log(`Inserted document with _id: ${insertResult.insertedId}`);
// --- Read Operation (immediately after write, should see it) ---
console.log("Reading the inserted document...");
const readResult1 = await collection.findOne({ _id: insertResult.insertedId });
console.log("Read 1:", readResult1);
// --- Another Write Operation ---
console.log("Updating the document...");
await collection.updateOne(
{ _id: insertResult.insertedId },
{ $set: { quantity: 15 } }
);
console.log("Document updated.");
// --- Another Read Operation (should see the update) ---
console.log("Reading the updated document...");
const readResult2 = await collection.findOne({ _id: insertResult.insertedId });
console.log("Read 2:", readResult2);
} finally {
await client.close();
}
}
run().catch(console.dir);
When you run this code, you’ll observe that both readResult1 and readResult2 correctly reflect the latest state of the document, even though the operations are happening on a potentially distributed replica set. The causalConsistency: true option tells the MongoDB driver to manage the necessary information to ensure this.
The problem this solves is the "stale read" issue in distributed systems. In a sharded cluster or a replica set without causal consistency, a client might write to a primary, but then its subsequent read request could be routed to a secondary that hasn’t yet received the write from the primary. This results in the client not seeing its own recent write. Causal consistency prevents this by tracking the logical clock of the last write operation performed by a client.
Internally, when causal consistency is enabled and a write operation completes, the driver records the majorityClusterTime from the write concern acknowledgment. For subsequent read operations from the same client session, the driver automatically includes this majorityClusterTime as part of the read preference. This tells the MongoDB server that the read must be performed on a node whose data is at least as recent as the recorded cluster time. If the read is routed to a secondary that is lagging behind this majorityClusterTime, the server will block the read until the secondary catches up, or until a timeout occurs, ensuring that the read is causally consistent with the previous write.
This mechanism is session-based. Each client session that has causal consistency enabled will maintain its own majorityClusterTime. When you perform a write, the driver updates this majorityClusterTime for that session. When you perform a read using the same session, the driver uses the session’s current majorityClusterTime to guarantee that you read at least that state. This is crucial for applications where the order of operations and seeing your own writes immediately is critical, like in a shopping cart or a user profile update.
A common misconception is that causal consistency is a global setting for the entire MongoDB deployment. It’s not. It’s a client-side driver feature, enabled per MongoClient instance, and managed on a per-session basis by the driver. This granular control allows you to enable it only for the parts of your application that absolutely require read-your-writes guarantees, without impacting the performance of other operations that might tolerate eventual consistency.
If you’re using unacknowledged writes (w: 0) and then immediately try to read, you’ll likely hit a read concern error because the server has no information about the write’s completion time to satisfy the causal consistency requirement.