[JAVA-866] Not using read preference in all cases Created: 08/Jul/13  Updated: 31/Mar/15  Resolved: 12/Jul/13

Status: Closed
Project: Java Driver
Component/s: Connection Management
Affects Version/s: 2.11.2
Fix Version/s: 3.0.0

Type: Bug Priority: Major - P3
Reporter: Richard Wallace Assignee: Jeffrey Yemin
Resolution: Done Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

In some cases the connection pinned to the request thread is used instead of the connection that should be. Specifically, if you do db.requestStart(), do a query with secondary preferred, then do a query with primary, the second query is run against the secondary and an error occurs. Example code

import com.mongodb._
 
class Test {
  public static void main(args: String[]) throws Exception {
    MongoClient m = new MongoClient();
    DB db = m.getDB("test");
    db.setReadPreference(ReadPreference.secondaryPreferred());
    DBCollection c = db.getCollection("test");
    
    db.requestStart();
    try {
      c.findOne(); // works fine, pins request to secondary
 
      c.findOne(new BasicDBObject(), null, ReadPreference.primary()); // throws com.mongodb.MongoException: not talking to master and retries used up
 
    } finally {
      db.requestDone();
    }
  } 
} 



 Comments   
Comment by Jeffrey Yemin [ 31/Mar/15 ]

Closing all resolved 3.0.0 issues, as 3.0.0 has been tagged and released.

Comment by Jeffrey Yemin [ 12/Jul/13 ]

This can be re-opened if we decide to backport, but it's unlikely.

Comment by Jeffrey Yemin [ 09/Jul/13 ]

This is fixed in the 3.0.x branch. Have not decided yet to backport it to master.

Comment by Richard Wallace [ 09/Jul/13 ]

I'm trying to make sure that some of the reads are against primary and therefore do not get stale data because it is used in a subsequent update. Other parts of the request can safely read stale data and so can read from secondaries. As a workaround, we've limited the scope of the "request" to the area performing the update.

As a side note, it is might also be worth mentioning that using findAndModify as the second action causes the same result.

Comment by Jeffrey Yemin [ 09/Jul/13 ]

By using the default MongoClient constructor, you are making a direct connection to a secondary member of a replica set, so it makes sense that a request to read from the primary would fail, and the failure will happen even if you query with ReadPreference.primary() outside of a DB.requestStart/requestDone block. If you do this instead:

    MongoClient m = new MongoClient(Arrays.asList(new ServerAddress("localhost:27017"), new ServerAddress("localhost:27018"), new ServerAddress("localhost:27019")));

|

you are now making a replica set connection, and the driver will discover the replica set shape and route queries accordingly. However, even if use this form of the constructor, the query with ReadPreference.primary() following one with ReadPreference.secondaryPreferred(), where both are inside a DB.requestStart/requestDone block, will still fail. That's because the first query after the call to DB.requestStart pins the thread to a connection to the server satisfying the first read preference.

What are you trying to achieve by putting queries with different read preferences inside a DB.requestStart/requestDone block?

Comment by Richard Wallace [ 09/Jul/13 ]

Member of a replica set.

rs0:SECONDARY> rs.status()
{
	"set" : "rs0",
	"date" : ISODate("2013-07-09T02:14:01Z"),
	"myState" : 2,
	"syncingTo" : "localhost:27019",
	"members" : [
		{
			"_id" : 0,
			"name" : "localhost:27017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 33324,
			"optime" : {
				"t" : 1373323766,
				"i" : 108
			},
			"optimeDate" : ISODate("2013-07-08T22:49:26Z"),
			"self" : true
		},
		{
			"_id" : 1,
			"name" : "localhost:27018",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 33323,
			"optime" : {
				"t" : 1373323766,
				"i" : 108
			},
			"optimeDate" : ISODate("2013-07-08T22:49:26Z"),
			"lastHeartbeat" : ISODate("2013-07-09T02:13:59Z"),
			"pingMs" : 0
		},
		{
			"_id" : 2,
			"name" : "localhost:27019",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 33323,
			"optime" : {
				"t" : 1373323766,
				"i" : 108
			},
			"optimeDate" : ISODate("2013-07-08T22:49:26Z"),
			"lastHeartbeat" : ISODate("2013-07-09T02:13:59Z"),
			"pingMs" : 0
		}
	],
	"ok" : 1
}

Comment by Jeffrey Yemin [ 09/Jul/13 ]

What sort of server is running on localhost:27017? A standalone, a replica set member (and if so, what does rs.status() say in the shell), or a mongos?

Comment by Richard Wallace [ 08/Jul/13 ]

This can also be reproduced without setting a global read preference.

import com.mongodb._
 
class Test {
  public static void main(args: String[]) throws Exception {
    MongoClient m = new MongoClient();
    DB db = m.getDB("test");
    DBCollection c = db.getCollection("test");
     
    db.requestStart();
    try {
      c.findOne(new BasicDBObject(), null, ReadPreference.secondaryPreferred()); // works fine, pins request to secondary
 
      c.findOne(); // throws com.mongodb.MongoException: not talking to master and retries used up
 
    } finally {
      db.requestDone();
    }
  } 
} 

Generated at Thu Feb 08 08:53:15 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.