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 #include "mongo/platform/basic.h"
32
33 #include "mongo/client/replica_set_monitor.h"
34 #include "mongo/client/replica_set_monitor_internal.h"
35 #include "mongo/unittest/unittest.h"
36
37 namespace {
38
39 using namespace mongo;
40 using std::set;
41
42 // Pull nested types to top-level scope
43 typedef ReplicaSetMonitor::SetState SetState;
44 typedef SetState::Node Node;
45 typedef SetState::Nodes Nodes;
46
isCompatible(const Node & node,ReadPreference pref,const TagSet & tagSet)47 bool isCompatible(const Node& node, ReadPreference pref, const TagSet& tagSet) {
48 set<HostAndPort> seeds;
49 seeds.insert(node.host);
50 SetState set("name", seeds);
51 set.nodes.push_back(node);
52
53 ReadPreferenceSetting criteria(pref, tagSet);
54 return !set.getMatchingHost(criteria).empty();
55 }
56
57 const BSONObj SampleIsMasterDoc = BSON("tags" << BSON("dc"
58 << "NYC"
59 << "p"
60 << "2"
61 << "region"
62 << "NA"));
63 const BSONObj SampleTags = SampleIsMasterDoc["tags"].Obj();
64 const BSONObj NoTags = BSONObj();
65 const BSONObj NoTagIsMasterDoc = BSON("isMaster" << true);
66
67
TEST(ReplSetMonitorNode,SimpleGoodMatch)68 TEST(ReplSetMonitorNode, SimpleGoodMatch) {
69 Node node(((HostAndPort())));
70 node.tags = BSON("dc"
71 << "sf");
72 ASSERT(node.matches(BSON("dc"
73 << "sf")));
74 }
75
TEST(ReplSetMonitorNode,SimpleBadMatch)76 TEST(ReplSetMonitorNode, SimpleBadMatch) {
77 Node node((HostAndPort()));
78 node.tags = BSON("dc"
79 << "nyc");
80 ASSERT(!node.matches(BSON("dc"
81 << "sf")));
82 }
83
TEST(ReplSetMonitorNode,ExactMatch)84 TEST(ReplSetMonitorNode, ExactMatch) {
85 Node node((HostAndPort()));
86 node.tags = SampleTags;
87 ASSERT(node.matches(SampleIsMasterDoc["tags"].Obj()));
88 }
89
TEST(ReplSetMonitorNode,EmptyTag)90 TEST(ReplSetMonitorNode, EmptyTag) {
91 Node node((HostAndPort()));
92 node.tags = SampleTags;
93 ASSERT(node.matches(BSONObj()));
94 }
95
TEST(ReplSetMonitorNode,MemberNoTagMatchesEmptyTag)96 TEST(ReplSetMonitorNode, MemberNoTagMatchesEmptyTag) {
97 Node node((HostAndPort()));
98 node.tags = NoTags;
99 ASSERT(node.matches(BSONObj()));
100 }
101
TEST(ReplSetMonitorNode,MemberNoTagDoesNotMatch)102 TEST(ReplSetMonitorNode, MemberNoTagDoesNotMatch) {
103 Node node((HostAndPort()));
104 node.tags = NoTags;
105 ASSERT(!node.matches(BSON("dc"
106 << "NYC")));
107 }
108
TEST(ReplSetMonitorNode,IncompleteMatch)109 TEST(ReplSetMonitorNode, IncompleteMatch) {
110 Node node((HostAndPort()));
111 node.tags = SampleTags;
112 ASSERT(!node.matches(BSON("dc"
113 << "NYC"
114 << "p"
115 << "2"
116 << "hello"
117 << "world")));
118 }
119
TEST(ReplSetMonitorNode,PartialMatch)120 TEST(ReplSetMonitorNode, PartialMatch) {
121 Node node((HostAndPort()));
122 node.tags = SampleTags;
123 ASSERT(node.matches(BSON("dc"
124 << "NYC"
125 << "p"
126 << "2")));
127 }
128
TEST(ReplSetMonitorNode,SingleTagCrit)129 TEST(ReplSetMonitorNode, SingleTagCrit) {
130 Node node((HostAndPort()));
131 node.tags = SampleTags;
132 ASSERT(node.matches(BSON("p"
133 << "2")));
134 }
135
TEST(ReplSetMonitorNode,BadSingleTagCrit)136 TEST(ReplSetMonitorNode, BadSingleTagCrit) {
137 Node node((HostAndPort()));
138 node.tags = SampleTags;
139 ASSERT(!node.matches(BSON("dc"
140 << "SF")));
141 }
142
TEST(ReplSetMonitorNode,NonExistingFieldTag)143 TEST(ReplSetMonitorNode, NonExistingFieldTag) {
144 Node node((HostAndPort()));
145 node.tags = SampleTags;
146 ASSERT(!node.matches(BSON("noSQL"
147 << "Mongo")));
148 }
149
TEST(ReplSetMonitorNode,UnorederedMatching)150 TEST(ReplSetMonitorNode, UnorederedMatching) {
151 Node node((HostAndPort()));
152 node.tags = SampleTags;
153 ASSERT(node.matches(BSON("p"
154 << "2"
155 << "dc"
156 << "NYC")));
157 }
158
TEST(ReplSetMonitorNode,SameValueDiffKey)159 TEST(ReplSetMonitorNode, SameValueDiffKey) {
160 Node node((HostAndPort()));
161 node.tags = SampleTags;
162 ASSERT(!node.matches(BSON("datacenter"
163 << "NYC")));
164 }
165
TEST(ReplSetMonitorNode,PriNodeCompatibleTag)166 TEST(ReplSetMonitorNode, PriNodeCompatibleTag) {
167 Node node(HostAndPort("dummy", 3));
168 node.tags = SampleTags;
169
170 node.isUp = true;
171 node.isMaster = true;
172
173 BSONArrayBuilder builder;
174 builder.append(BSON("dc"
175 << "NYC"));
176
177 TagSet tags(BSONArray(builder.done()));
178
179 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
180 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
181 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
182 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
183 ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
184 }
185
TEST(ReplSetMonitorNode,SecNodeCompatibleTag)186 TEST(ReplSetMonitorNode, SecNodeCompatibleTag) {
187 Node node(HostAndPort("dummy", 3));
188 node.tags = SampleTags;
189
190 node.isUp = true;
191 node.isMaster = false;
192
193 BSONArrayBuilder builder;
194 builder.append(BSON("dc"
195 << "NYC"));
196
197 TagSet tags(BSONArray(builder.done()));
198
199 ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
200 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
201 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
202 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
203 ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
204 }
205
TEST(ReplSetMonitorNode,PriNodeNotCompatibleTag)206 TEST(ReplSetMonitorNode, PriNodeNotCompatibleTag) {
207 Node node(HostAndPort("dummy", 3));
208 node.tags = SampleTags;
209
210 node.isUp = true;
211 node.isMaster = true;
212
213 BSONArrayBuilder builder;
214 builder.append(BSON("dc"
215 << "SF"));
216
217 TagSet tags(BSONArray(builder.done()));
218
219 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
220 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
221 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
222 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
223 ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
224 }
225
TEST(ReplSetMonitorNode,SecNodeNotCompatibleTag)226 TEST(ReplSetMonitorNode, SecNodeNotCompatibleTag) {
227 Node node(HostAndPort("dummy", 3));
228 node.tags = SampleTags;
229
230 node.isUp = true;
231 node.isMaster = false;
232
233 BSONArrayBuilder builder;
234 builder.append(BSON("dc"
235 << "SF"));
236
237 TagSet tags(BSONArray(builder.done()));
238
239 ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
240 ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
241 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
242 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
243 ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
244 }
245
TEST(ReplSetMonitorNode,PriNodeCompatiblMultiTag)246 TEST(ReplSetMonitorNode, PriNodeCompatiblMultiTag) {
247 Node node(HostAndPort("dummy", 3));
248 node.tags = SampleTags;
249
250 node.isUp = true;
251 node.isMaster = true;
252
253 BSONArrayBuilder builder;
254 builder.append(BSON("dc"
255 << "RP"));
256 builder.append(BSON("dc"
257 << "NYC"
258 << "p"
259 << "2"));
260
261 TagSet tags(BSONArray(builder.done()));
262
263 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
264 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
265 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
266 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
267 ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
268 }
269
TEST(ReplSetMonitorNode,SecNodeCompatibleMultiTag)270 TEST(ReplSetMonitorNode, SecNodeCompatibleMultiTag) {
271 Node node(HostAndPort("dummy", 3));
272 node.tags = SampleTags;
273
274 node.isUp = true;
275 node.isMaster = false;
276
277 BSONArrayBuilder builder;
278 builder.append(BSON("dc"
279 << "RP"));
280 builder.append(BSON("dc"
281 << "NYC"
282 << "p"
283 << "2"));
284
285 TagSet tags(BSONArray(builder.done()));
286
287 ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
288 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
289 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
290 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
291 ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
292 }
293
TEST(ReplSetMonitorNode,PriNodeNotCompatibleMultiTag)294 TEST(ReplSetMonitorNode, PriNodeNotCompatibleMultiTag) {
295 Node node(HostAndPort("dummy", 3));
296 node.tags = SampleTags;
297
298 node.isUp = true;
299 node.isMaster = true;
300
301 BSONArrayBuilder builder;
302 builder.append(BSON("dc"
303 << "sf"));
304 builder.append(BSON("dc"
305 << "NYC"
306 << "P"
307 << "4"));
308
309 TagSet tags(BSONArray(builder.done()));
310
311 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
312 ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
313 ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
314 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
315 ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
316 }
317
TEST(ReplSetMonitorNode,SecNodeNotCompatibleMultiTag)318 TEST(ReplSetMonitorNode, SecNodeNotCompatibleMultiTag) {
319 Node node(HostAndPort("dummy", 3));
320 node.tags = SampleTags;
321
322 node.isUp = true;
323 node.isMaster = false;
324
325 BSONArrayBuilder builder;
326 builder.append(BSON("dc"
327 << "sf"));
328 builder.append(BSON("dc"
329 << "NYC"
330 << "P"
331 << "4"));
332
333 TagSet tags(BSONArray(builder.done()));
334
335 ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
336 ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
337 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
338 ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
339 ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
340 }
341
342 } // namespace
343