/* Copyright 2014 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
/**
* This is an internal header.
* This should only be included by replica_set_monitor.cpp and replica_set_monitor_test.cpp.
* This should never be included by any header.
*/
#pragma once
#include
#include
#include
#include
#include
#include "mongo/base/disallow_copying.h"
#include "mongo/client/read_preference.h"
#include "mongo/client/replica_set_monitor.h"
#include "mongo/db/jsobj.h"
#include "mongo/platform/random.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/mutex.h"
#include "mongo/util/net/hostandport.h"
namespace mongo {
struct ReplicaSetMonitor::IsMasterReply {
IsMasterReply() : ok(false) {}
IsMasterReply(const HostAndPort& host, int64_t latencyMicros, const BSONObj& reply)
: ok(false), host(host), latencyMicros(latencyMicros) {
parse(reply);
}
/**
* Never throws. If parsing fails for any reason, sets ok to false.
*/
void parse(const BSONObj& obj);
bool ok; // if false, ignore all other fields
BSONObj raw; // Always owned. Other fields are allowed to be a view into this.
std::string setName;
bool isMaster;
bool secondary;
bool hidden;
int configVersion{};
OID electionId; // Set if this isMaster reply is from the primary
HostAndPort primary; // empty if not present
std::set normalHosts; // both "hosts" and "passives"
BSONObj tags;
int minWireVersion{};
int maxWireVersion{};
// remaining fields aren't in isMaster reply, but are known to caller.
HostAndPort host;
int64_t latencyMicros; // ignored if negative
Date_t lastWriteDate{};
repl::OpTime opTime{};
};
struct ReplicaSetMonitor::SetState {
MONGO_DISALLOW_COPYING(SetState);
public:
/**
* Holds the state of a single node in the replicaSet
*/
struct Node {
explicit Node(const HostAndPort& host);
void markFailed(const Status& status);
bool matches(const ReadPreference pref) const;
/**
* Checks if the given tag matches the tag attached to this node.
*
* Example:
*
* Tag of this node: { "dc": "nyc", "region": "na", "rack": "4" }
*
* match: {}
* match: { "dc": "nyc", "rack": 4 }
* match: { "region": "na", "dc": "nyc" }
* not match: { "dc": "nyc", "rack": 2 }
* not match: { "dc": "sf" }
*/
bool matches(const BSONObj&) const;
/**
* Returns true if all of the tags in the tag set match node's tags
*/
bool matches(const TagSet&) const;
/**
* Updates this Node based on information in reply. The reply must be from this host.
*/
void update(const IsMasterReply& reply);
HostAndPort host;
bool isUp{false};
bool isMaster{false};
int64_t latencyMicros{};
BSONObj tags; // owned
int minWireVersion{};
int maxWireVersion{};
Date_t lastWriteDate{}; // from isMasterReply
Date_t lastWriteDateUpdateTime{}; // set to the local system's time at the time of updating
// lastWriteDate
repl::OpTime opTime{}; // from isMasterReply
};
typedef std::vector Nodes;
/**
* seedNodes must not be empty
*/
SetState(StringData name, const std::set& seedNodes);
SetState(const MongoURI& uri);
bool isUsable() const;
/**
* Returns a host matching criteria or an empty host if no known host matches.
*
* Note: Uses only local data and does not go over the network.
*/
HostAndPort getMatchingHost(const ReadPreferenceSetting& criteria) const;
/**
* Returns the Node with the given host, or NULL if no Node has that host.
*/
Node* findNode(const HostAndPort& host);
/**
* Returns the Node with the given host, or creates one if no Node has that host.
* Maintains the sorted order of nodes.
*/
Node* findOrCreateNode(const HostAndPort& host);
void updateNodeIfInNodes(const IsMasterReply& reply);
/**
* Returns the connection string of the nodes that are known the be in the set because we've
* seen them in the isMaster reply of a PRIMARY.
*/
std::string getConfirmedServerAddress() const;
/**
* Returns the connection string of the nodes that are believed to be in the set because we've
* seen them in the isMaster reply of non-PRIMARY nodes in our seed list.
*/
std::string getUnconfirmedServerAddress() const;
/**
* Before unlocking, do DEV checkInvariants();
*/
void checkInvariants() const;
stdx::mutex mutex; // must hold this to access any other member or method (except name).
// If Refresher::getNextStep returns WAIT, you should wait on the condition_variable,
// releasing mutex. It will be notified when either getNextStep will return something other
// than WAIT, or a new host is available for consideration by getMatchingHost. Essentially,
// this will be hit whenever the _refreshUntilMatches loop has the potential to make
// progress.
// TODO consider splitting cv into two: one for when looking for a master, one for all other
// cases.
stdx::condition_variable cv;
const std::string name; // safe to read outside lock since it is const
int consecutiveFailedScans;
std::set seedNodes; // updated whenever a master reports set membership changes
OID maxElectionId; // largest election id observed by this ReplicaSetMonitor
int configVersion{0}; // version number of the replica set config.
HostAndPort lastSeenMaster; // empty if we have never seen a master. can be same as current
Nodes nodes; // maintained sorted and unique by host
ScanStatePtr currentScan; // NULL if no scan in progress
int64_t latencyThresholdMicros;
mutable PseudoRandom rand; // only used for host selection to balance load
mutable int roundRobin; // used when useDeterministicHostSelection is true
MongoURI setUri; // URI that may have constructed this
};
struct ReplicaSetMonitor::ScanState {
MONGO_DISALLOW_COPYING(ScanState);
public:
ScanState() : foundUpMaster(false), foundAnyUpNodes(false) {}
/**
* Adds all hosts in container that aren't in triedHosts to hostsToScan, then shuffles the
* queue.
*/
template
void enqueAllUntriedHosts(const Container& container, PseudoRandom& rand);
// Access to fields is guarded by associated SetState's mutex.
bool foundUpMaster;
bool foundAnyUpNodes;
std::deque hostsToScan; // Work queue.
std::set possibleNodes; // Nodes reported by non-primary hosts.
std::set waitingFor; // Hosts we have dispatched but haven't replied yet.
std::set triedHosts; // Hosts that have been returned from getNextStep.
// All responses go here until we find a master.
typedef std::vector UnconfirmedReplies;
UnconfirmedReplies unconfirmedReplies;
};
} // namespace mongo