Read Concern
The readConcern option allows you to control the consistency and isolation properties of the data read from replica sets and replica set shards.
Through the effective use of write concerns and read concerns, you can adjust the level of consistency and availability guarantees as appropriate, such as waiting for stronger consistency guarantees, or loosening consistency requirements to provide higher availability.
Read Concern Levels
local
The query returns data from the instance with no guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
Default for:
- reads against primary
- reads against secondaries if the reads are associated with causally consistent sessions.
Read concern local is available for use with causally consistent sessions and transactions.
available
The query returns data from the instance with no guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
Default for reads against secondaries if the reads are not associated with causally consistent sessions.
For sharded collections, "available" read concern provides the lowest latency reads possible among the various read concerns but at the expense of consistency as "available" read concern can return orphaned documents.
Read concern available is unavailable for use with causally consistent sessions.
majority
The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure.
To use read concern level of "majority", replica sets must use WiredTiger storage engine.
Read concern majority is available for use with causally consistent sessions and transactions.
For operations in multi-document transactions, read concern "majority" provides its guarantees only if the transaction commits with write concern “majority”. Otherwise, the "majority" read concern provides no guarantees about the data read in transactions.
linearizable
The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results.
If a majority of your replica set members crash and restart after the read operation, documents returned by the read operation are durable if writeConcernMajorityJournalDefault is set to the default state of true.
With writeConcernMajorityJournalDefault set to false, MongoDB does not wait for w: "majority" writes to be written to the on-disk journal before acknowledging the writes. As such, majority write operations could possibly roll back in the event of a transient loss (e.g. crash and restart) of a majority of nodes in a given replica set.
You can specify linearizable read concern for read operations on the primary only.
Read concern linearizable is unavailable for use with causally consistent sessions.
Linearizable read concern guarantees only apply if read operations specify a query filter that uniquely identifies a single document.
Always use
maxTimeMSwith linearizable read concern in case a majority of data bearing members are unavailable.maxTimeMSensures that the operation does not block indefinitely and instead ensures that the operation returns an error if the read concern cannot be fulfilled.
snapshot
Only available for operations within multi-document transactions.
- If the transaction is not part of a causally consistent session, upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data.
- If the transaction is part of a causally consistent session, upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data that provides causal consistency with the operation immediately preceding the transaction start.
Regardless of the read concern level, the most recent data on a node may not reflect the most recent version of the data in the system.
Examples
Deploy a Replica Set for Testing and Development
- Create the necessary data directories for each member by issuing a command similar to the following:
mkdir -p /srv/mongodb/rs0-0 /srv/mongodb/rs0-1 /srv/mongodb/rs0-2
- Start your
mongodinstances in their own shell windows by issuing the following commands:
First member:
sudo service mongod stop
mongod --replSet rs0 --port 27017 --bind_ip localhost --dbpath /srv/mongodb/rs0-0 --smallfiles --oplogSize 128
Second member:
mongod --replSet rs0 --port 27018 --bind_ip localhost --dbpath /srv/mongodb/rs0-1 --smallfiles --oplogSize 128
Third member:
mongod --replSet rs0 --port 27019 --bind_ip localhost --dbpath /srv/mongodb/rs0-2 --smallfiles --oplogSize 128
- Connect to one of your mongod instances through the mongo shell.
mongo --port 27017
- In the mongo shell, use
rs.initiate()to initiate the replica set. You can create a replica set configuration object in the mongo shell environment, as in the following example:
rsconf = {
_id: "rs0",
members: [
{
_id: 0,
host: "localhost:27017"
},
{
_id: 1,
host: "localhost:27018"
},
{
_id: 2,
host: "localhost:27019"
}
]
}
{
"_id": "rs0",
"members": [
{
"_id": 0,
"host": "localhost:27017"
},
{
"_id": 1,
"host": "localhost:27018"
},
{
"_id": 2,
"host": "localhost:27019"
}
]
}
rs.initiate( rsconf )
{
"ok" : 1,
"operationTime" : Timestamp(1551425599, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1551425599, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
rs0:SECONDARY>
- Display the current replica configuration by issuing the following command:
rs.conf()
{
"_id": "rs0",
"version": 1,
"protocolVersion": NumberLong(1),
"writeConcernMajorityJournalDefault": true,
"members": [
{
"_id": 0,
"host": "localhost:27017",
"arbiterOnly": false,
"buildIndexes": true,
"hidden": false,
"priority": 1,
"tags": {},
"slaveDelay": NumberLong(0),
"votes": 1
},
{
"_id": 1,
"host": "localhost:27018",
"arbiterOnly": false,
"buildIndexes": true,
"hidden": false,
"priority": 1,
"tags": {},
"slaveDelay": NumberLong(0),
"votes": 1
},
{
"_id": 2,
"host": "localhost:27019",
"arbiterOnly": false,
"buildIndexes": true,
"hidden": false,
"priority": 1,
"tags": {},
"slaveDelay": NumberLong(0),
"votes": 1
}
],
"settings": {
"chainingAllowed": true,
"heartbeatIntervalMillis": 2000,
"heartbeatTimeoutSecs": 10,
"electionTimeoutMillis": 10000,
"catchUpTimeoutMillis": -1,
"catchUpTakeoverDelayMillis": 30000,
"getLastErrorModes": {},
"getLastErrorDefaults": {
"w": 1,
"wtimeout": 0
},
"replicaSetId": ObjectId("5c78e03f1a60bab484754eda")
}
}
Check the status of your replica set at any time with the rs.status() operation.
Import restaurants collection
wget -nc https://www.dropbox.com/s/iovpcb7hc3gc4n4/primer-dataset.json
mongoimport --collection restaurants --drop --file primer-dataset.json
2019-03-01T07:35:42.460+0000 connected to: localhost
2019-03-01T07:35:42.461+0000 dropping: test.restaurants
2019-03-01T07:35:44.542+0000 imported 25359 documents
mongo
rs0:PRIMARY>
local
db.restaurants.find( { restaurant_id: "40356731" } ).readConcern("local")
{
"_id": ObjectId("5c78e0ce1a60bab48475509d"),
"address": {
"building": "1839",
"coord": [-73.9482609, 40.6408271],
"street": "Nostrand Avenue",
"zipcode": "11226"
},
"borough": "Brooklyn",
"cuisine": "Ice Cream, Gelato, Yogurt, Ices",
"grades": [
{ "date": ISODate("2014-07-14T00:00:00Z"), "grade": "A", "score": 12 },
{ "date": ISODate("2013-07-10T00:00:00Z"), "grade": "A", "score": 8 },
{ "date": ISODate("2012-07-11T00:00:00Z"), "grade": "A", "score": 5 },
{ "date": ISODate("2012-02-23T00:00:00Z"), "grade": "A", "score": 8 }
],
"name": "Taste The Tropics Ice Cream",
"restaurant_id": "40356731"
}
available
db.restaurants.find( { restaurant_id: "40356731" } ).readConcern("available")
{
"_id": ObjectId("5c78e0ce1a60bab48475509d"),
"address": {
"building": "1839",
"coord": [-73.9482609, 40.6408271],
"street": "Nostrand Avenue",
"zipcode": "11226"
},
"borough": "Brooklyn",
"cuisine": "Ice Cream, Gelato, Yogurt, Ices",
"grades": [
{ "date": ISODate("2014-07-14T00:00:00Z"), "grade": "A", "score": 12 },
{ "date": ISODate("2013-07-10T00:00:00Z"), "grade": "A", "score": 8 },
{ "date": ISODate("2012-07-11T00:00:00Z"), "grade": "A", "score": 5 },
{ "date": ISODate("2012-02-23T00:00:00Z"), "grade": "A", "score": 8 }
],
"name": "Taste The Tropics Ice Cream",
"restaurant_id": "40356731"
}
majority
db.restaurants.find( { restaurant_id: "40356731" } ).readConcern("majority")
{
"_id": ObjectId("5c78e0ce1a60bab48475509d"),
"address": {
"building": "1839",
"coord": [-73.9482609, 40.6408271],
"street": "Nostrand Avenue",
"zipcode": "11226"
},
"borough": "Brooklyn",
"cuisine": "Ice Cream, Gelato, Yogurt, Ices",
"grades": [
{ "date": ISODate("2014-07-14T00:00:00Z"), "grade": "A", "score": 12 },
{ "date": ISODate("2013-07-10T00:00:00Z"), "grade": "A", "score": 8 },
{ "date": ISODate("2012-07-11T00:00:00Z"), "grade": "A", "score": 5 },
{ "date": ISODate("2012-02-23T00:00:00Z"), "grade": "A", "score": 8 }
],
"name": "Taste The Tropics Ice Cream",
"restaurant_id": "40356731"
}
linearizable
Unlike "majority", "linearizable" read concern confirms with secondary members that the read operation is reading from a primary that is capable of confirming writes with { w: "majority" } write concern. As such, reads with linearizable read concern may be significantly slower than reads with "majority" or "local" read concerns.
Always use maxTimeMS with linearizable read concern in case a majority of data bearing members are unavailable. maxTimeMS ensures that the operation does not block indefinitely and instead ensures that the operation returns an error if the read concern cannot be fulfilled.
db.restaurants.find( { restaurant_id: "40356731" } ).readConcern("linearizable").maxTimeMS(10000)
{
"_id": ObjectId("5c78e0ce1a60bab48475509d"),
"address": {
"building": "1839",
"coord": [-73.9482609, 40.6408271],
"street": "Nostrand Avenue",
"zipcode": "11226"
},
"borough": "Brooklyn",
"cuisine": "Ice Cream, Gelato, Yogurt, Ices",
"grades": [
{ "date": ISODate("2014-07-14T00:00:00Z"), "grade": "A", "score": 12 },
{ "date": ISODate("2013-07-10T00:00:00Z"), "grade": "A", "score": 8 },
{ "date": ISODate("2012-07-11T00:00:00Z"), "grade": "A", "score": 5 },
{ "date": ISODate("2012-02-23T00:00:00Z"), "grade": "A", "score": 8 }
],
"name": "Taste The Tropics Ice Cream",
"restaurant_id": "40356731"
}
db.runCommand( {
find: "restaurants",
filter: { restaurant_id: "40356731" },
readConcern: { level: "linearizable" },
maxTimeMS: 10000
} )
{
"cursor": {
"firstBatch": [
{
"_id": ObjectId("5c78e0ce1a60bab48475509d"),
"address": {
"building": "1839",
"coord": [-73.9482609, 40.6408271],
"street": "Nostrand Avenue",
"zipcode": "11226"
},
"borough": "Brooklyn",
"cuisine": "Ice Cream, Gelato, Yogurt, Ices",
"grades": [
{
"date": ISODate("2014-07-14T00:00:00Z"),
"grade": "A",
"score": 12
},
{
"date": ISODate("2013-07-10T00:00:00Z"),
"grade": "A",
"score": 8
},
{
"date": ISODate("2012-07-11T00:00:00Z"),
"grade": "A",
"score": 5
},
{
"date": ISODate("2012-02-23T00:00:00Z"),
"grade": "A",
"score": 8
}
],
"name": "Taste The Tropics Ice Cream",
"restaurant_id": "40356731"
}
],
"id": NumberLong(0),
"ns": "test.restaurants"
},
"ok": 1,
"operationTime": Timestamp(1551425974, 1),
"$clusterTime": {
"clusterTime": Timestamp(1551425974, 1),
"signature": {
"hash": BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId": NumberLong(0)
}
}
}
snapshot
db.restaurants.find( { restaurant_id: "40356731" } ).readConcern("snapshot")
Error: error: {
"operationTime" : Timestamp(1551426201, 1),
"ok" : 0,
"errmsg" : "readConcern level snapshot is only valid in multi-statement transactions",
"code" : 72,
"codeName" : "InvalidOptions",
"$clusterTime" : {
"clusterTime" : Timestamp(1551426201, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
session { "id" : UUID("b9b59ead-0bab-4334-b4ec-7d68d9bbf2f7") }
session.startTransaction( { readConcern: { level: "snapshot" } } );
session.getDatabase("test").restaurants.find( { restaurant_id: "40356731" } ).readConcern("snapshot")
{
"_id": ObjectId("5c78e0ce1a60bab48475509d"),
"address": {
"building": "1839",
"coord": [-73.9482609, 40.6408271],
"street": "Nostrand Avenue",
"zipcode": "11226"
},
"borough": "Brooklyn",
"cuisine": "Ice Cream, Gelato, Yogurt, Ices",
"grades": [
{ "date": ISODate("2014-07-14T00:00:00Z"), "grade": "A", "score": 12 },
{ "date": ISODate("2013-07-10T00:00:00Z"), "grade": "A", "score": 8 },
{ "date": ISODate("2012-07-11T00:00:00Z"), "grade": "A", "score": 5 },
{ "date": ISODate("2012-02-23T00:00:00Z"), "grade": "A", "score": 8 }
],
"name": "Taste The Tropics Ice Cream",
"restaurant_id": "40356731"
}