@@ -78,9 +78,8 @@ namespace {
7878 }
7979
8080 void makeSelfPrimary (const OpTime& electionOpTime = OpTime(0 ,0 )) {
81- setSelfMemberState ( MemberState::RS_PRIMARY);
81+ getTopoCoord (). changeMemberState_forTest ( MemberState::RS_PRIMARY, electionOpTime );
8282 getTopoCoord ()._setCurrentPrimaryForTest (_selfIndex);
83- getTopoCoord ()._setElectionTime (electionOpTime);
8483 }
8584
8685 void setSelfMemberState (const MemberState& newState) {
@@ -2042,6 +2041,224 @@ namespace {
20422041 ASSERT_TRUE (response.obj ().isEmpty ());
20432042 }
20442043
2044+ class PrepareHeartbeatResponseTest : public TopoCoordTest {
2045+ public:
2046+
2047+ virtual void setUp () {
2048+ TopoCoordTest::setUp ();
2049+ updateConfig (BSON (" _id" << " rs0" <<
2050+ " version" << 1 <<
2051+ " members" << BSON_ARRAY (
2052+ BSON (" _id" << 10 << " host" << " hself" ) <<
2053+ BSON (" _id" << 20 << " host" << " h2" ) <<
2054+ BSON (" _id" << 30 << " host" << " h3" ))),
2055+ 0 );
2056+ setSelfMemberState (MemberState::RS_SECONDARY);
2057+ }
2058+
2059+ void prepareHeartbeatResponse (const ReplSetHeartbeatArgs& args,
2060+ OpTime lastOpApplied,
2061+ ReplSetHeartbeatResponse* response,
2062+ Status* result) {
2063+ getTopoCoord ().prepareHeartbeatResponse (cbData (),
2064+ now ()++,
2065+ args,
2066+ " rs0" ,
2067+ lastOpApplied,
2068+ response,
2069+ result);
2070+ }
2071+
2072+ };
2073+
2074+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseBadProtocolVersion) {
2075+ // set up args with bad protocol version
2076+ ReplSetHeartbeatArgs args;
2077+ args.setProtocolVersion (3 );
2078+ ReplSetHeartbeatResponse response;
2079+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2080+
2081+ // prepare response and check the results
2082+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2083+ ASSERT_EQUALS (ErrorCodes::BadValue, result);
2084+ ASSERT_EQUALS (" replset: incompatible replset protocol version: 3" , result.reason ());
2085+ ASSERT_EQUALS (" " , response.getHbMsg ());
2086+ }
2087+
2088+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseBadSetName) {
2089+ // set up args with incorrect replset name
2090+ ReplSetHeartbeatArgs args;
2091+ args.setProtocolVersion (1 );
2092+ args.setSetName (" rs1" );
2093+ ReplSetHeartbeatResponse response;
2094+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2095+
2096+ // prepare response and check the results, including log messages
2097+ startCapturingLogMessages ();
2098+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2099+ stopCapturingLogMessages ();
2100+ ASSERT_EQUALS (ErrorCodes::BadValue, result);
2101+ ASSERT_EQUALS (" repl set names do not match" , result.reason ());
2102+ ASSERT_EQUALS (1 ,
2103+ countLogLinesContaining (" replSet set names do not match, ours: rs0; remote "
2104+ " node's: rs1" ));
2105+ ASSERT_TRUE (response.isMismatched ());
2106+ ASSERT_EQUALS (" " , response.getHbMsg ());
2107+ }
2108+
2109+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseSenderIDMissing) {
2110+ // set up args without a senderID
2111+ ReplSetHeartbeatArgs args;
2112+ args.setProtocolVersion (1 );
2113+ args.setSetName (" rs0" );
2114+ args.setConfigVersion (1 );
2115+ ReplSetHeartbeatResponse response;
2116+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2117+
2118+ // prepare response and check the results
2119+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2120+ ASSERT_OK (result);
2121+ ASSERT_FALSE (response.isElectable ());
2122+ ASSERT_TRUE (response.isReplSet ());
2123+ ASSERT_EQUALS (MemberState::RS_SECONDARY, response.getState ().s );
2124+ ASSERT_EQUALS (OpTime (0 ,0 ), response.getOpTime ());
2125+ ASSERT_EQUALS (Seconds (0 ).total_milliseconds (), response.getTime ().total_milliseconds ());
2126+ ASSERT_EQUALS (" " , response.getHbMsg ());
2127+ ASSERT_EQUALS (" rs0" , response.getReplicaSetName ());
2128+ ASSERT_EQUALS (1 , response.getVersion ());
2129+ }
2130+
2131+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseConfigVersionLow) {
2132+ // set up args with a config version lower than ours
2133+ ReplSetHeartbeatArgs args;
2134+ args.setProtocolVersion (1 );
2135+ args.setConfigVersion (0 );
2136+ args.setSetName (" rs0" );
2137+ args.setSenderId (20 );
2138+ ReplSetHeartbeatResponse response;
2139+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2140+
2141+ // prepare response and check the results
2142+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2143+ ASSERT_OK (result);
2144+ ASSERT_TRUE (response.hasConfig ());
2145+ ASSERT_FALSE (response.isElectable ());
2146+ ASSERT_TRUE (response.isReplSet ());
2147+ ASSERT_EQUALS (MemberState::RS_SECONDARY, response.getState ().s );
2148+ ASSERT_EQUALS (OpTime (0 ,0 ), response.getOpTime ());
2149+ ASSERT_EQUALS (Seconds (0 ).total_milliseconds (), response.getTime ().total_milliseconds ());
2150+ ASSERT_EQUALS (" " , response.getHbMsg ());
2151+ ASSERT_EQUALS (" rs0" , response.getReplicaSetName ());
2152+ ASSERT_EQUALS (1 , response.getVersion ());
2153+ }
2154+
2155+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseSenderDown) {
2156+ // set up args with sender down from our perspective
2157+ ReplSetHeartbeatArgs args;
2158+ args.setProtocolVersion (1 );
2159+ args.setConfigVersion (1 );
2160+ args.setSetName (" rs0" );
2161+ args.setSenderId (20 );
2162+ ReplSetHeartbeatResponse response;
2163+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2164+
2165+ // prepare response and check the results
2166+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2167+ ASSERT_OK (result);
2168+ ASSERT_FALSE (response.isElectable ());
2169+ ASSERT_TRUE (response.isReplSet ());
2170+ ASSERT_EQUALS (MemberState::RS_SECONDARY, response.getState ().s );
2171+ ASSERT_EQUALS (OpTime (0 ,0 ), response.getOpTime ());
2172+ ASSERT_EQUALS (Seconds (0 ).total_milliseconds (), response.getTime ().total_milliseconds ());
2173+ ASSERT_EQUALS (" " , response.getHbMsg ());
2174+ ASSERT_EQUALS (" rs0" , response.getReplicaSetName ());
2175+ ASSERT_EQUALS (1 , response.getVersion ());
2176+ ASSERT_TRUE (response.isStateDisagreement ());
2177+ }
2178+
2179+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseSenderUp) {
2180+ // set up args and acknowledge sender
2181+ heartbeatFromMember (HostAndPort (" h2" ), " rs0" , MemberState::RS_SECONDARY, OpTime (0 ,0 ));
2182+ ReplSetHeartbeatArgs args;
2183+ args.setProtocolVersion (1 );
2184+ args.setConfigVersion (1 );
2185+ args.setSetName (" rs0" );
2186+ args.setSenderId (20 );
2187+ ReplSetHeartbeatResponse response;
2188+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2189+
2190+ // prepare response and check the results
2191+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2192+ ASSERT_OK (result);
2193+ // this change to true because we can now see a majority, unlike in the previous cases
2194+ ASSERT_TRUE (response.isElectable ());
2195+ ASSERT_TRUE (response.isReplSet ());
2196+ ASSERT_EQUALS (MemberState::RS_SECONDARY, response.getState ().s );
2197+ ASSERT_EQUALS (OpTime (0 ,0 ), response.getOpTime ());
2198+ ASSERT_EQUALS (Seconds (0 ).total_milliseconds (), response.getTime ().total_milliseconds ());
2199+ ASSERT_EQUALS (" " , response.getHbMsg ());
2200+ ASSERT_EQUALS (" rs0" , response.getReplicaSetName ());
2201+ ASSERT_EQUALS (1 , response.getVersion ());
2202+ }
2203+
2204+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseAsPrimary) {
2205+ makeSelfPrimary (OpTime (10 ,0 ));
2206+ heartbeatFromMember (HostAndPort (" h2" ), " rs0" , MemberState::RS_SECONDARY, OpTime (0 ,0 ));
2207+
2208+ ReplSetHeartbeatArgs args;
2209+ args.setProtocolVersion (1 );
2210+ args.setConfigVersion (1 );
2211+ args.setSetName (" rs0" );
2212+ args.setSenderId (20 );
2213+ ReplSetHeartbeatResponse response;
2214+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2215+
2216+ // prepare response and check the results
2217+ prepareHeartbeatResponse (args, OpTime (11 ,0 ), &response, &result);
2218+ ASSERT_OK (result);
2219+ // cannot be elected because we are already primary
2220+ ASSERT_FALSE (response.isElectable ());
2221+ ASSERT_TRUE (response.isReplSet ());
2222+ ASSERT_EQUALS (MemberState::RS_PRIMARY, response.getState ().s );
2223+ ASSERT_EQUALS (OpTime (11 ,0 ), response.getOpTime ());
2224+ ASSERT_EQUALS (OpTime (10 ,0 ), response.getElectionTime ());
2225+ ASSERT_EQUALS (Seconds (0 ).total_milliseconds (), response.getTime ().total_milliseconds ());
2226+ ASSERT_EQUALS (" " , response.getHbMsg ());
2227+ ASSERT_EQUALS (" rs0" , response.getReplicaSetName ());
2228+ ASSERT_EQUALS (1 , response.getVersion ());
2229+ }
2230+
2231+ TEST_F (PrepareHeartbeatResponseTest, PrepareHeartbeatResponseWithSyncSource) {
2232+ // get a sync source
2233+ heartbeatFromMember (HostAndPort (" h3" ), " rs0" , MemberState::RS_SECONDARY, OpTime (0 ,0 ));
2234+ heartbeatFromMember (HostAndPort (" h3" ), " rs0" , MemberState::RS_SECONDARY, OpTime (0 ,0 ));
2235+ heartbeatFromMember (HostAndPort (" h2" ), " rs0" , MemberState::RS_SECONDARY, OpTime (1 ,0 ));
2236+ heartbeatFromMember (HostAndPort (" h2" ), " rs0" , MemberState::RS_SECONDARY, OpTime (1 ,0 ));
2237+ getTopoCoord ().chooseNewSyncSource (now ()++, OpTime (0 ,0 ));
2238+
2239+ // set up args
2240+ ReplSetHeartbeatArgs args;
2241+ args.setProtocolVersion (1 );
2242+ args.setConfigVersion (1 );
2243+ args.setSetName (" rs0" );
2244+ args.setSenderId (20 );
2245+ ReplSetHeartbeatResponse response;
2246+ Status result (ErrorCodes::InternalError, " prepareHeartbeatResponse didn't set result" );
2247+
2248+ // prepare response and check the results
2249+ prepareHeartbeatResponse (args, OpTime (0 ,0 ), &response, &result);
2250+ ASSERT_OK (result);
2251+ ASSERT_TRUE (response.isElectable ());
2252+ ASSERT_TRUE (response.isReplSet ());
2253+ ASSERT_EQUALS (MemberState::RS_SECONDARY, response.getState ().s );
2254+ ASSERT_EQUALS (OpTime (0 ,0 ), response.getOpTime ());
2255+ ASSERT_EQUALS (Seconds (0 ).total_milliseconds (), response.getTime ().total_milliseconds ());
2256+ // changed to a syncing message because our sync source changed recently
2257+ ASSERT_EQUALS (" syncing to: h2:27017" , response.getHbMsg ());
2258+ ASSERT_EQUALS (" rs0" , response.getReplicaSetName ());
2259+ ASSERT_EQUALS (1 , response.getVersion ());
2260+ ASSERT_EQUALS (HostAndPort (" h2" ).toString (), response.getSyncingTo ());
2261+ }
20452262} // namespace
20462263} // namespace repl
20472264} // namespace mongo
0 commit comments