OPTIONS

Java Driver Replica Set Semantics

The MongoDB Java driver handles failover in replicated setups with tuneable levels of transparency to the user. By default, a MongoClient connection object will ignore failures of secondaries, and reads will only throw a MongoException when the primary node is unavailable.

The level of exception reporting is tuneable however, using a specific WriteConcern ; you can set this on the MongoClient/DB/DBCollection/Method level. Several levels are included as static options:

WriteConcern.UNACKNOWLEDGED
Exceptions are only thrown when the primary node is unreachable for a read, or the full replica set is unreachable.
WriteConcern.ACKNOWLEDGED
Same as the above, but exceptions thrown when there is a server error on writes or reads. Calls getLastError().
WriteConcern.REPLICA_ACKNOWLEDGED
Tries to write to two separate nodes. Same as the above, but will throw an exception if two writes are not possible.
WriteConcern.JOURNALED
Same as WriteConcern.ACKNOWLEDGED, but also waits for write to be written to the journal.

Warning

Additional errors may appear in the log files, these are for reporting purposes and logged based on the logging settings.

Sample code is provided which illustrates some of these options. To quickly initialize a sample replica set, you can use the mongo shell:

var rst = new ReplSetTest({ nodes : 3 })
rst.startSet() // wait for processes to start
rst.initiate() // wait for replica set initialization

Java client code demonstrating error handling is available:

Read Preferences and Tagging

MongoDB’s read preferences and tagging allows application developers to target member nodes within a replica set for read or write operations. Version 2.2 brings several refinements on node tagging that give you greater control over how your data is read or written. Release 2.9.0 of the Java driver has been built in coordination with the release of MongoDB 2.2 and provides full access to these newly available preferences and tagging features.

Read Preferences

A read preference provides client applications with control over which nodes of a replica set are used for reads. A client application defines its read preference by selecting one of the five behavioral modes:

  • PRIMARY

    The default read mode. Read from primary only. Throw an error if primary is unavailable. Cannot be combined with tags.

  • PRIMARY PREFERRED

    Read from primary if available, otherwise a secondary.

  • SECONDARY

    Read from a secondary node if available, otherwise error.

  • SECONDARY PREFERRED

    Read from a secondary if available, otherwise read from the primary.

  • NEAREST

    Read from any member node from the set of nodes which respond the fastest. The responsiveness of a node is measured with pings. Any node whose ping time is within 15 milliseconds of the node with the lowest ping time is considered near.

Implementation

The Java driver implements MongoDB’s read preferences with the ReadPreference class. Client applications allocate the desired read mode by calling one of ReadPreference‘s static factory methods. One factory method exists for each mode.

ReadPreference.primary();
ReadPreference.primaryPreferred();
ReadPreference.secondary();
ReadPreference.secondaryPreferred();
ReadPreference.nearest();

The factory method returns a private inner subclass of ReadPreference that implements the correct behavioral mode. The driver’s use of polymorphism in this manner means that your code only needs to interface with the ReadPreference class alone.

Example

Suppose we are developing an application and we prefer operate on strongly consistent data, (i.e. read requests always return the most recent updates of a given document). In this case, the primary node will handle all reads and writes. Now suppose our application must be able to perform reads even if the primary becomes unavailable for some reason. Even though we prefer data consistency we can tolerate eventual consistency when the primary is down. We therefore use the primary preferred mode as our read preference.

ReadPreference preference = ReadPreference.primaryPreferred();
DBCursor cur = new DBCursor(collection, query, null, preference);

The Java driver maintains knowledge and state of the replica set by periodically pinging the member nodes and can detect the loss of a member. In this example, the driver will automatically detect the loss of the primary and route the read request to a secondary node, just as we have instructed.

Tags

As of version 2.0 of MongoDB, each node within your replica set can be marked with additional descriptors called tags. Tags can be used to indicate a node’s location, membership in a designated set, or other characteristics. Tags enable your application to read from or write to specific nodes or a set of nodes in a replica set.

As an example, suppose we’re running a replica set of three nodes deployed across three separate data centers. Each of these data centers is in a separate geographic location. We want to ensure that our data will persist in the event of a disaster, so we mark each node with a tag indicating the region where it lives. Our replica set configuration might look similar to this:

foo:SECONDARY> rs.conf()
{
   "_id" : "foo",
   "version" : 103132,
   "members" : [
                  {
                     "_id" : 0,
                     "host" : "localhost:27017",
                     "priority" : 10,
                     "tags" : {
                                "datacenter" : "Los Angeles",
                                "region" : "US_West"
                     }
                  },
                  {
                     "_id" : 1,
                     "host" : "localhost:27018",
                     "tags" : {
                                "datacenter" : "San Jose",
                                "region" : "US_West"
                     }
                  },
                  {
                     "_id" : 2,
                     "host" : "localhost:27019",
                     "tags" : {
                                "datacenter" : "Richmond",
                                "region" : "US_East"
                     }
                  }
   ],
   "settings" : {
                  "getLastErrorModes" : {
                                          "DRSafe" : {
                                                       "region" : 2
                                          }
                  }
   }
}
foo:SECONDARY>

Notice the settings field in the replication configuration. We’ve defined a new getLastErrorModes object with the key DRSafe. When our client application uses this error mode in a write concern it is instructing the write operation to replicate to at least two regions before completion. Here’s an example in the Java driver:

// create a write concern with the specific getLastErrorMode
WriteConcern concern = new WriteConcern("DRSafe");

// an insert with the custom write concern
coll.insert(new BasicDBObject("name", "simple doc"), concern);

By allocating a write concern using the "DRSafe" error mode and passing it in on the insert, we have now ensured that our data has been backed up to two separate regions and will be available should a data center fail.

Using Tags with Read Preferences

Continuing with our sample application, we decide that we want to send our read requests to the nearest node to reduce request latency. The Java driver’s read preference API gives a couple of ways of doing this, the easiest is to simply use the nearest mode.

DBObject query = new BasicDBObject("name", "simple doc")
DBObject result =
    coll.findOne(query, null, ReadPreference.nearest());

By using the nearest mode the driver will automatically send the read to one of a set of nodes with the lowest ping time relative to the client application, (the receiving node could be either a primary or secondary). But suppose our client application can determine where its requests originate. We could have explicitly tagged the read preference to use the datacenter nearest to our location.

Look again at the replica set configuration from above. Each node has been tagged by data center. Let’s say that the current read request is coming from southern California. We can configure this read request to be served by the node living in our Los Angeles data center.

// initialize a properly tagged read preference
ReadPreference tagged_pref =
    ReadPreference.secondaryPreferred(new BasicDBObject("datacenter", "Los Angeles"));

// include the tagged read preference in this request}}
DBObject result = coll.findOne(}}
    new BasicDBObject("name", "simple doc"), null, tagged_pref);

Read preferences can also accept multiple tags. Returning to our example application, suppose we want to send our reads either the "Los Angeles" node, or failing to find a healthy member in Los Angeles a node in the "US_West" region:

// read from either LA or US_West
DBObject tagSetOne = new BasicDBObject("datacenter", "Los Angeles"):
DBObject tagSetTwo = new BasicDBObject("region", "US_West");

ReadPreference pref =
    ReadPreference.primaryPreferred(tagSetOne, tagSetTwo);

In this example, the driver looks first for a member tagged with datacenter "Los Angeles". If it cannot find an available member, the driver will it look for a member tagged with region of "US_West". You may use a tag set to define a set of required tags a member node must have to be used for the read. For example:

// read from either LA or US_West
DBObject tagSetOne = new BasicDBObject("datacenter", "Los Angeles");
tagSetOne.put("rack", "1");
DBObject tagSetTwo = new BasicDBObject("region", "US_West");

ReadPreference pref =
    ReadPreference.primaryPreferred(tagSetOne, tagSetTwo);

The difference being the driver is looking for a node that is tagged with both data center "Los Angeles" and on rack 1, or a node that is in region "US_West".

You can set the Read Preference at the operation, collection, DB, Mongo, MongoOptions, or MongoURI level, and the preference will be inherited similar to the way slaveOk and write concern are. Read preferences work with any server version that supports replica sets (1.6 and up). Tagged read preferences work with any version that supports tagging (2.0 and up). However, tagging will only work on a sharded cluster if you are connecting to a mongos running 2.2 or above.