text: /**
   * Tests read preference for explain command.
   *
   * Test is loosely based from read_pref_cmd.js.
   */
  
  load("jstests/replsets/rslib.js");
  
  var assertCorrectTargeting = function(explain, isMongos, secExpected) {
      assert.commandWorked(explain);
  
      var serverInfo;
      if (isMongos) {
          serverInfo = explain.queryPlanner.winningPlan.shards[0].serverInfo;
      }
      else {
          serverInfo = explain.serverInfo;
      }
  
      var explainDestConn = new Mongo(serverInfo.host + ':' + serverInfo.port);
      var isMaster = explainDestConn.getDB('admin').runCommand({ isMaster: 1 });
  
      if (secExpected) {
          assert(isMaster.secondary);
      }
      else {
          assert(isMaster.ismaster);
      }
  }
  
  var testAllModes = function(conn, isMongos) {
  
      // The primary is tagged with { tag: 'one' } and the secondary with
      // { tag: 'two' } so we can test the interaction of modes and tags. Test
      // a bunch of combinations.
      [
          // mode, tagSets, expectedHost
          ['primary', undefined, false],
          ['primary', [{}], false],
  
          ['primaryPreferred', undefined, false],
          ['primaryPreferred', [{tag: 'one'}], false],
          // Correctly uses primary and ignores the tag
          ['primaryPreferred', [{tag: 'two'}], false],
  
          ['secondary', undefined, true],
          ['secondary', [{tag: 'two'}], true],
          ['secondary', [{tag: 'doesntexist'}, {}], true],
          ['secondary', [{tag: 'doesntexist'}, {tag:'two'}], true],
  
          ['secondaryPreferred', undefined, true],
          ['secondaryPreferred', [{tag: 'one'}], false],
          ['secondaryPreferred', [{tag: 'two'}], true],
  
          // We don't have a way to alter ping times so we can't predict where an
          // untagged 'nearest' command should go, hence only test with tags.
          ['nearest', [{tag: 'one'}], false],
          ['nearest', [{tag: 'two'}], true]
  
      ].forEach(function(args) {
          var mode = args[0], tagSets = args[1], secExpected = args[2];
  
          var testDB = conn.getDB('TestDB');
          conn.setSlaveOk(false); // purely rely on readPref
          jsTest.log('Testing mode: ' + mode + ', tag sets: ' + tojson(tagSets));
  
          // .explain().find()
          var explainableQuery = testDB.user.explain().find();
          explainableQuery.readPref(mode, tagSets);
          var explain = explainableQuery.finish();
          assertCorrectTargeting(explain, isMongos, secExpected);
  
          // Set read pref on the connection.
          var oldReadPrefMode = testDB.getMongo().getReadPrefMode();
          var oldReadPrefTagSet = testDB.getMongo().getReadPrefTagSet();
          try {
              testDB.getMongo().setReadPref(mode, tagSets);
  
              // .explain().count();
              explain = testDB.user.explain().count();
              assertCorrectTargeting(explain, isMongos, secExpected);
  
              // .explain().distinct()
              explain = testDB.user.explain().distinct("_id");
              assertCorrectTargeting(explain, isMongos, secExpected);
  
              // .explain().group()
              explain = testDB.user.explain().group({
                  key: {_id: 1},
                  reduce: function(curr, result) {},
                  initial: {}
              });
              assertCorrectTargeting(explain, isMongos, secExpected);
          } finally {
              // Restore old read pref.
              testDB.getMongo().setReadPref(oldReadPrefMode, oldReadPrefTagSet);
          }
      });
  };
  
  var st = new ShardingTest({ shards: { rs0: { nodes: 2 }}});
  st.stopBalancer();
  
  ReplSetTest.awaitRSClientHosts(st.s, st.rs0.nodes);
  
  // Tag primary with { dc: 'ny', tag: 'one' }, secondary with { dc: 'ny', tag: 'two' }
  var primary = st.rs0.getPrimary();
  var secondary = st.rs0.getSecondary();
  var PRIMARY_TAG = { dc: 'ny', tag: 'one' };
  var SECONDARY_TAG = { dc: 'ny', tag: 'two' };
  
  var rsConfig = primary.getDB("local").system.replset.findOne();
  jsTest.log('got rsconf ' + tojson(rsConfig));
  rsConfig.members.forEach(function(member) {
      if (member.host == primary.host) {
          member.tags = PRIMARY_TAG;
      } else {
          member.tags = SECONDARY_TAG;
      }
  });
  
  rsConfig.version++;
  
  jsTest.log('new rsconf ' + tojson(rsConfig));
  
  try {
      primary.adminCommand({ replSetReconfig: rsConfig });
  }
  catch(e) {
      jsTest.log('replSetReconfig error: ' + e);
  }
  
  st.rs0.awaitSecondaryNodes();
  
  // Force mongos to reconnect after our reconfig and also create the test database
  assert.soon(function() {
      try {
          st.s.getDB('TestDB').runCommand({ create: 'TestColl' });
          return true;
      }
      catch (x) {
          // Intentionally caused an error that forces mongos's monitor to refresh.
          jsTest.log('Caught exception while doing dummy command: ' + tojson(x));
          return false;
      }
  });
  
  reconnect(primary);
  reconnect(secondary);
  
  rsConfig = primary.getDB("local").system.replset.findOne();
  jsTest.log('got rsconf ' + tojson(rsConfig));
  
  var replConn = new Mongo(st.rs0.getURL());
  
  // Make sure replica set connection is ready
  _awaitRSHostViaRSMonitor(primary.name, { ok: true, tags: PRIMARY_TAG }, st.rs0.name);
  _awaitRSHostViaRSMonitor(secondary.name, { ok: true, tags: SECONDARY_TAG }, st.rs0.name);
  
  testAllModes(replConn, false);
  
  jsTest.log('Starting test for mongos connection');
  
  testAllModes(st.s, true);
  
  st.stop();
  