Write Concerns: Durability vs Throughput
Write Concerns
Write concern controls how many replica set members must acknowledge a write before the driver considers it successful. A weaker write concern returns faster but risks data loss. A stronger write concern returns slower but guarantees durability.
Write Concern Levels
| Level | Acknowledgment | Durability on primary crash | Latency |
|---|---|---|---|
w: 0 (unacknowledged) | None. Fire and forget. | Data may be lost | ~0ms |
w: 1 (default) | Primary acknowledges | Data may be lost (not yet replicated) | ~2ms |
w: 1, j: true | Primary journal flush | Durable on primary disk | ~5ms |
w: "majority" | Majority of voting members | Durable (survives primary failure) | ~8ms |
w: "majority", j: true | Majority + journal flush | Maximum durability | ~12ms |
// Configure write concern at the client level
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(uri))
.writeConcern(WriteConcern.MAJORITY.withJournal(true))
.build();
// Override per-collection
MongoCollection<Document> readings = database.getCollection("readings")
.withWriteConcern(WriteConcern.W1);
// Override per-operation
collection.withWriteConcern(WriteConcern.MAJORITY)
.insertOne(criticalDocument);
What Happens When the Primary Crashes
With w: 1, the primary acknowledges the write after storing it in memory (and eventually flushing to the journal). If the primary crashes before replicating to a secondary:
- A secondary is elected as the new primary.
- The new primary does not have the unreplicated write.
- When the old primary comes back online, it has a write that the new primary does not. This write is rolled back (written to a rollback file for manual recovery).
- The application received a success acknowledgment for a write that no longer exists.
With w: "majority", the primary waits until a majority of members (including itself) have the write. If the primary crashes after the majority acknowledgment, the write exists on at least one other member. The new primary will have the write.
The Cost of Durability
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class WriteConcernBenchmark {
@Benchmark
public void w0() {
collection.withWriteConcern(WriteConcern.UNACKNOWLEDGED)
.insertOne(reading);
}
@Benchmark
public void w1() {
collection.withWriteConcern(WriteConcern.W1)
.insertOne(reading);
}
@Benchmark
public void wMajority() {
collection.withWriteConcern(WriteConcern.MAJORITY)
.insertOne(reading);
}
@Benchmark
public void wMajorityJournal() {
collection.withWriteConcern(WriteConcern.MAJORITY.withJournal(true))
.insertOne(reading);
}
}
// Results (3-member replica set, same data center):
// w:0 -> 48,000 ops/sec
// w:1 -> 22,000 ops/sec
// w:majority -> 14,000 ops/sec
// w:majority,j -> 11,000 ops/sec
Each step up in durability costs approximately 30-50% throughput. The cost comes from waiting for replication (network round trip to secondaries) and journal flushes (disk I/O).