1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 /**
32  * This is an internal header.
33  * This should only be included by replica_set_monitor.cpp and replica_set_monitor_test.cpp.
34  * This should never be included by any header.
35  */
36 
37 #pragma once
38 
39 #include <cstdint>
40 #include <deque>
41 #include <set>
42 #include <string>
43 #include <vector>
44 
45 #include "mongo/base/disallow_copying.h"
46 #include "mongo/client/read_preference.h"
47 #include "mongo/client/replica_set_monitor.h"
48 #include "mongo/db/jsobj.h"
49 #include "mongo/platform/random.h"
50 #include "mongo/stdx/condition_variable.h"
51 #include "mongo/stdx/mutex.h"
52 #include "mongo/util/net/hostandport.h"
53 
54 namespace mongo {
55 
56 struct ReplicaSetMonitor::IsMasterReply {
IsMasterReplyIsMasterReply57     IsMasterReply() : ok(false) {}
IsMasterReplyIsMasterReply58     IsMasterReply(const HostAndPort& host, int64_t latencyMicros, const BSONObj& reply)
59         : ok(false), host(host), latencyMicros(latencyMicros) {
60         parse(reply);
61     }
62 
63     /**
64      * Never throws. If parsing fails for any reason, sets ok to false.
65      */
66     void parse(const BSONObj& obj);
67 
68     bool ok;      // if false, ignore all other fields
69     BSONObj raw;  // Always owned. Other fields are allowed to be a view into this.
70     std::string setName;
71     bool isMaster;
72     bool secondary;
73     bool hidden;
74     int configVersion{};
75     OID electionId;                     // Set if this isMaster reply is from the primary
76     HostAndPort primary;                // empty if not present
77     std::set<HostAndPort> normalHosts;  // both "hosts" and "passives"
78     BSONObj tags;
79     int minWireVersion{};
80     int maxWireVersion{};
81 
82     // remaining fields aren't in isMaster reply, but are known to caller.
83     HostAndPort host;
84     int64_t latencyMicros;  // ignored if negative
85     Date_t lastWriteDate{};
86     repl::OpTime opTime{};
87 };
88 
89 struct ReplicaSetMonitor::SetState {
90     MONGO_DISALLOW_COPYING(SetState);
91 
92 public:
93     /**
94      * Holds the state of a single node in the replicaSet
95      */
96     struct Node {
97         explicit Node(const HostAndPort& host);
98 
99         void markFailed(const Status& status);
100 
101         bool matches(const ReadPreference pref) const;
102 
103         /**
104          * Checks if the given tag matches the tag attached to this node.
105          *
106          * Example:
107          *
108          * Tag of this node: { "dc": "nyc", "region": "na", "rack": "4" }
109          *
110          * match: {}
111          * match: { "dc": "nyc", "rack": 4 }
112          * match: { "region": "na", "dc": "nyc" }
113          * not match: { "dc": "nyc", "rack": 2 }
114          * not match: { "dc": "sf" }
115          */
116         bool matches(const BSONObj&) const;
117 
118         /**
119          *  Returns true if all of the tags in the tag set match node's tags
120          */
121         bool matches(const TagSet&) const;
122 
123         /**
124          * Updates this Node based on information in reply. The reply must be from this host.
125          */
126         void update(const IsMasterReply& reply);
127 
128         HostAndPort host;
129         bool isUp{false};
130         bool isMaster{false};
131         int64_t latencyMicros{};
132         BSONObj tags;  // owned
133         int minWireVersion{};
134         int maxWireVersion{};
135         Date_t lastWriteDate{};            // from isMasterReply
136         Date_t lastWriteDateUpdateTime{};  // set to the local system's time at the time of updating
137                                            // lastWriteDate
138         repl::OpTime opTime{};             // from isMasterReply
139     };
140 
141     typedef std::vector<Node> Nodes;
142 
143     /**
144      * seedNodes must not be empty
145      */
146     SetState(StringData name, const std::set<HostAndPort>& seedNodes);
147 
148     SetState(const MongoURI& uri);
149 
150     bool isUsable() const;
151 
152     /**
153      * Returns a host matching criteria or an empty host if no known host matches.
154      *
155      * Note: Uses only local data and does not go over the network.
156      */
157     HostAndPort getMatchingHost(const ReadPreferenceSetting& criteria) const;
158 
159     /**
160      * Returns the Node with the given host, or NULL if no Node has that host.
161      */
162     Node* findNode(const HostAndPort& host);
163 
164     /**
165      * Returns the Node with the given host, or creates one if no Node has that host.
166      * Maintains the sorted order of nodes.
167      */
168     Node* findOrCreateNode(const HostAndPort& host);
169 
170     void updateNodeIfInNodes(const IsMasterReply& reply);
171 
172     /**
173      * Returns the connection string of the nodes that are known the be in the set because we've
174      * seen them in the isMaster reply of a PRIMARY.
175      */
176     std::string getConfirmedServerAddress() const;
177 
178     /**
179      * Returns the connection string of the nodes that are believed to be in the set because we've
180      * seen them in the isMaster reply of non-PRIMARY nodes in our seed list.
181      */
182     std::string getUnconfirmedServerAddress() const;
183 
184     /**
185      * Before unlocking, do DEV checkInvariants();
186      */
187     void checkInvariants() const;
188 
189     stdx::mutex mutex;  // must hold this to access any other member or method (except name).
190 
191     // If Refresher::getNextStep returns WAIT, you should wait on the condition_variable,
192     // releasing mutex. It will be notified when either getNextStep will return something other
193     // than WAIT, or a new host is available for consideration by getMatchingHost. Essentially,
194     // this will be hit whenever the _refreshUntilMatches loop has the potential to make
195     // progress.
196     // TODO consider splitting cv into two: one for when looking for a master, one for all other
197     // cases.
198     stdx::condition_variable cv;
199 
200     const std::string name;  // safe to read outside lock since it is const
201     int consecutiveFailedScans;
202     std::set<HostAndPort> seedNodes;  // updated whenever a master reports set membership changes
203     OID maxElectionId;                // largest election id observed by this ReplicaSetMonitor
204     int configVersion{0};             // version number of the replica set config.
205     HostAndPort lastSeenMaster;  // empty if we have never seen a master. can be same as current
206     Nodes nodes;                 // maintained sorted and unique by host
207     ScanStatePtr currentScan;    // NULL if no scan in progress
208     int64_t latencyThresholdMicros;
209     mutable PseudoRandom rand;  // only used for host selection to balance load
210     mutable int roundRobin;     // used when useDeterministicHostSelection is true
211     MongoURI setUri;            // URI that may have constructed this
212 };
213 
214 struct ReplicaSetMonitor::ScanState {
215     MONGO_DISALLOW_COPYING(ScanState);
216 
217 public:
ScanStateScanState218     ScanState() : foundUpMaster(false), foundAnyUpNodes(false) {}
219 
220     /**
221      * Adds all hosts in container that aren't in triedHosts to hostsToScan, then shuffles the
222      * queue.
223      */
224     template <typename Container>
225     void enqueAllUntriedHosts(const Container& container, PseudoRandom& rand);
226 
227     // Access to fields is guarded by associated SetState's mutex.
228     bool foundUpMaster;
229     bool foundAnyUpNodes;
230     std::deque<HostAndPort> hostsToScan;  // Work queue.
231     std::set<HostAndPort> possibleNodes;  // Nodes reported by non-primary hosts.
232     std::set<HostAndPort> waitingFor;     // Hosts we have dispatched but haven't replied yet.
233     std::set<HostAndPort> triedHosts;     // Hosts that have been returned from getNextStep.
234 
235     // All responses go here until we find a master.
236     typedef std::vector<IsMasterReply> UnconfirmedReplies;
237     UnconfirmedReplies unconfirmedReplies;
238 };
239 
240 }  // namespace mongo
241