void ShardRegistry::_addShard_inlock(const ShardType& shardType) { // This validation should ideally go inside the ShardType::validate call. However, doing // it there would prevent us from loading previously faulty shard hosts, which might have // been stored (i.e., the entire getAllShards call would fail). auto shardHostStatus = ConnectionString::parse(shardType.getHost()); if (!shardHostStatus.isOK()) { warning() << "Unable to parse shard host " << shardHostStatus.getStatus().toString(); } const ConnectionString& shardHost(shardHostStatus.getValue()); shared_ptr<Shard> shard = boost::make_shared<Shard>(shardType.getName(), shardHost, shardType.getMaxSize(), shardType.getDraining()); _lookup[shardType.getName()] = shard; _lookup[shardType.getHost()] = shard; if (shardHost.type() == ConnectionString::SET) { if (shardHost.getSetName().size()) { _rsLookup[shardHost.getSetName()] = shard; } vector<HostAndPort> servers = shardHost.getServers(); for (unsigned i = 0; i < servers.size(); i++) { _lookup[servers[i].toString()] = shard; } } }
void ShardRegistry::_addShard_inlock(const ShardType& shardType) { // This validation should ideally go inside the ShardType::validate call. However, doing // it there would prevent us from loading previously faulty shard hosts, which might have // been stored (i.e., the entire getAllShards call would fail). auto shardHostStatus = ConnectionString::parse(shardType.getHost()); if (!shardHostStatus.isOK()) { warning() << "Unable to parse shard host " << shardHostStatus.getStatus().toString(); } const ConnectionString& shardHost(shardHostStatus.getValue()); shared_ptr<Shard> shard; if (shardHost.type() == ConnectionString::SYNC) { // Sync cluster connections (legacy config server) do not go through the normal targeting // mechanism and must only be reachable through CatalogManagerLegacy or legacy-style queries // and inserts. Do not create targeter for these connections. This code should go away after // 3.2 is released. shard = std::make_shared<Shard>(shardType.getName(), shardHost, nullptr); } else { // Non-SYNC shards use targeter factory. shard = std::make_shared<Shard>( shardType.getName(), shardHost, _targeterFactory->create(shardHost)); } _updateLookupMapsForShard_inlock(std::move(shard), shardHost); }
void ShardRegistry::_addShard_inlock(const ShardType& shardType) { // This validation should ideally go inside the ShardType::validate call. However, doing // it there would prevent us from loading previously faulty shard hosts, which might have // been stored (i.e., the entire getAllShards call would fail). auto shardHostStatus = ConnectionString::parse(shardType.getHost()); if (!shardHostStatus.isOK()) { warning() << "Unable to parse shard host " << shardHostStatus.getStatus().toString(); } const ConnectionString& shardHost(shardHostStatus.getValue()); // Sync cluster connections (legacy config server) do not go through the normal targeting // mechanism and must only be reachable through CatalogManagerLegacy or legacy-style // queries and inserts. Do not create targeter for these connections. This code should go // away after 3.2 is released. if (shardHost.type() == ConnectionString::SYNC) { _lookup[shardType.getName()] = std::make_shared<Shard>(shardType.getName(), shardHost, nullptr); return; } // Non-SYNC shards shared_ptr<Shard> shard = std::make_shared<Shard>( shardType.getName(), shardHost, std::move(_targeterFactory->create(shardHost))); _lookup[shardType.getName()] = shard; // TODO: The only reason to have the shard host names in the lookup table is for the // setShardVersion call, which resolves the shard id from the shard address. This is // error-prone and will go away eventually when we switch all communications to go through // the remote command runner. _lookup[shardType.getHost()] = shard; for (const HostAndPort& hostAndPort : shardHost.getServers()) { _lookup[hostAndPort.toString()] = shard; // Maintain a mapping from host to shard it belongs to for the case where we need to // update the shard connection string on reconfigurations. if (shardHost.type() == ConnectionString::SET) { _rsLookup[hostAndPort.toString()] = shard; } } if (shardHost.type() == ConnectionString::SET) { _rsLookup[shardHost.getSetName()] = shard; } }
void ShardingTestFixture::expectSetShardVersion(const HostAndPort& expectedHost, const ShardType& expectedShard, const NamespaceString& expectedNs, const ChunkVersion& expectedChunkVersion) { onCommand([&](const RemoteCommandRequest& request) { ASSERT_EQ(expectedHost, request.target); ASSERT_EQUALS(rpc::makeEmptyMetadata(), request.metadata); SetShardVersionRequest ssv = assertGet(SetShardVersionRequest::parseFromBSON(request.cmdObj)); ASSERT(!ssv.isInit()); ASSERT(ssv.isAuthoritative()); ASSERT_EQ(grid.shardRegistry()->getConfigServerConnectionString().toString(), ssv.getConfigServer().toString()); ASSERT_EQ(expectedShard.getHost(), ssv.getShardConnectionString().toString()); ASSERT_EQ(expectedNs.toString(), ssv.getNS().ns()); ASSERT_EQ(expectedChunkVersion.toString(), ssv.getNSVersion().toString()); return BSON("ok" << true); }); }
Status checkClusterMongoVersions(const ConnectionString& configLoc, const string& minMongoVersion) { scoped_ptr<ScopedDbConnection> connPtr; // // Find mongos pings in config server // try { connPtr.reset(new ScopedDbConnection(configLoc, 30)); ScopedDbConnection& conn = *connPtr; scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(MongosType::ConfigNS, Query()))); while (cursor->more()) { BSONObj pingDoc = cursor->next(); MongosType ping; string errMsg; // NOTE: We don't care if the ping is invalid, legacy stuff will be if (!ping.parseBSON(pingDoc, &errMsg)) { warning() << "could not parse ping document: " << pingDoc << causedBy(errMsg) << endl; continue; } string mongoVersion = "2.0"; // Hack to determine older mongos versions from ping format if (ping.isWaitingSet()) mongoVersion = "2.2"; if (ping.isMongoVersionSet() && ping.getMongoVersion() != "") { mongoVersion = ping.getMongoVersion(); } Date_t lastPing = ping.getPing(); long long quietIntervalMillis = 0; Date_t currentJsTime = jsTime(); if (currentJsTime >= lastPing) { quietIntervalMillis = static_cast<long long>(currentJsTime - lastPing); } long long quietIntervalMins = quietIntervalMillis / (60 * 1000); // We assume that anything that hasn't pinged in 5 minutes is probably down if (quietIntervalMins >= 5) { log() << "stale mongos detected " << quietIntervalMins << " minutes ago," << " network location is " << pingDoc["_id"].String() << ", not checking version" << endl; } else { if (versionCmp(mongoVersion, minMongoVersion) < 0) { return Status(ErrorCodes::RemoteValidationError, stream() << "version " << mongoVersion << " of mongos at " << ping.getName() << " is not compatible with the config update, " << "you must wait 5 minutes " << "after shutting down a pre-" << minMongoVersion << " mongos"); } } } } catch (const DBException& e) { return e.toStatus("could not read mongos pings collection"); } // // Load shards from config server // vector<ConnectionString> shardLocs; try { ScopedDbConnection& conn = *connPtr; scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(ShardType::ConfigNS, Query()))); while (cursor->more()) { BSONObj shardDoc = cursor->next(); ShardType shard; string errMsg; if (!shard.parseBSON(shardDoc, &errMsg) || !shard.isValid(&errMsg)) { connPtr->done(); return Status(ErrorCodes::UnsupportedFormat, stream() << "invalid shard " << shardDoc << " read from the config server" << causedBy(errMsg)); } ConnectionString shardLoc = ConnectionString::parse(shard.getHost(), errMsg); if (shardLoc.type() == ConnectionString::INVALID) { connPtr->done(); return Status(ErrorCodes::UnsupportedFormat, stream() << "invalid shard host " << shard.getHost() << " read from the config server" << causedBy(errMsg)); } shardLocs.push_back(shardLoc); } } catch (const DBException& e) { return e.toStatus("could not read shards collection"); } connPtr->done(); // // We've now got all the shard info from the config server, start contacting the shards // and verifying their versions. // for (vector<ConnectionString>::iterator it = shardLocs.begin(); it != shardLocs.end(); ++it) { ConnectionString& shardLoc = *it; vector<HostAndPort> servers = shardLoc.getServers(); for (vector<HostAndPort>::iterator serverIt = servers.begin(); serverIt != servers.end(); ++serverIt) { // Note: This will *always* be a single-host connection ConnectionString serverLoc(*serverIt); log() << "checking that version of host " << serverLoc << " is compatible with " << minMongoVersion << endl; scoped_ptr<ScopedDbConnection> serverConnPtr; bool resultOk; BSONObj buildInfo; try { serverConnPtr.reset(new ScopedDbConnection(serverLoc, 30)); ScopedDbConnection& serverConn = *serverConnPtr; resultOk = serverConn->runCommand("admin", BSON("buildInfo" << 1), buildInfo); } catch (const DBException& e) { warning() << "could not run buildInfo command on " << serverLoc.toString() << causedBy(e) << ", you must manually verify this mongo server is " << "offline (for at least 5 minutes) or of a version >= 2.2" << endl; continue; } // TODO: Make running commands saner such that we can consolidate error handling if (!resultOk) { return Status(ErrorCodes::UnknownError, stream() << DBClientConnection::getLastErrorString(buildInfo) << causedBy(buildInfo.toString())); } serverConnPtr->done(); verify(buildInfo["version"].type() == String); string mongoVersion = buildInfo["version"].String(); if (versionCmp(mongoVersion, minMongoVersion) < 0) { return Status(ErrorCodes::RemoteValidationError, stream() << "version " << mongoVersion << " of mongo server at " << serverLoc.toString() << " is not compatible with the config update"); } } } return Status::OK(); }