Skip to content

Commit ffc8a01

Browse files
committed
CDRIVER-751 don't recheck any host in same scan
Fix test_topology_scanner_oscillate case.
1 parent ddd99c2 commit ffc8a01

File tree

8 files changed

+236
-16
lines changed

8 files changed

+236
-16
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ set (SOURCES
101101
${SOURCE_DIR}/src/mongoc/mongoc-gridfs-file-list.c
102102
${SOURCE_DIR}/src/mongoc/mongoc-gridfs-file-page.c
103103
${SOURCE_DIR}/src/mongoc/mongoc-gridfs-file-list.c
104+
${SOURCE_DIR}/src/mongoc/mongoc-host-list.c
104105
${SOURCE_DIR}/src/mongoc/mongoc-index.c
105106
${SOURCE_DIR}/src/mongoc/mongoc-init.c
106107
${SOURCE_DIR}/src/mongoc/mongoc-list.c

src/mongoc/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ MONGOC_SOURCES_SHARED += \
120120
src/mongoc/mongoc-cursor-cursorid.c \
121121
src/mongoc/mongoc-cursor-transform.c \
122122
src/mongoc/mongoc-database.c \
123+
src/mongoc/mongoc-host-list.c \
123124
src/mongoc/mongoc-init.c \
124125
src/mongoc/mongoc-gridfs.c \
125126
src/mongoc/mongoc-gridfs-file.c \

src/mongoc/mongoc-host-list.c

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright 2015 MongoDB Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "mongoc-host-list.h"
18+
19+
20+
#ifdef _WIN32
21+
# define strcasecmp _stricmp
22+
#endif
23+
24+
/*
25+
*--------------------------------------------------------------------------
26+
*
27+
* mongoc_host_list_new --
28+
*
29+
* Empty new host list.
30+
*
31+
* Side effects:
32+
* None.
33+
*
34+
*--------------------------------------------------------------------------
35+
*/
36+
mongoc_host_list_t *
37+
mongoc_host_list_new (void)
38+
{
39+
return (mongoc_host_list_t *) bson_malloc0 (sizeof (mongoc_host_list_t));
40+
}
41+
42+
/*
43+
*--------------------------------------------------------------------------
44+
*
45+
* mongoc_host_list_equal --
46+
*
47+
* Check two hosts have the same domain (case-insensitive), port,
48+
* and address family.
49+
*
50+
* Side effects:
51+
* None.
52+
*
53+
*--------------------------------------------------------------------------
54+
*/
55+
bool
56+
mongoc_host_list_equal (const mongoc_host_list_t *host_a,
57+
const mongoc_host_list_t *host_b)
58+
{
59+
return (!strcasecmp (host_a->host_and_port, host_b->host_and_port)
60+
&& host_a->family == host_b->family);
61+
}
62+
63+
64+
/*
65+
*--------------------------------------------------------------------------
66+
*
67+
* mongoc_host_list_find --
68+
*
69+
* Search for an equal mongoc_host_list_t in a list of them.
70+
*
71+
* Returns:
72+
* Pointer to an entry in "list", or NULL.
73+
*
74+
* Side effects:
75+
* None.
76+
*
77+
*--------------------------------------------------------------------------
78+
*/
79+
const mongoc_host_list_t *
80+
mongoc_host_list_find (const mongoc_host_list_t *list,
81+
const mongoc_host_list_t *needle)
82+
{
83+
while (list) {
84+
if (mongoc_host_list_equal (list, needle)) {
85+
return list;
86+
}
87+
88+
list = list->next;
89+
}
90+
91+
return NULL;
92+
}
93+
94+
95+
/*
96+
*--------------------------------------------------------------------------
97+
*
98+
* mongoc_host_list_count --
99+
*
100+
* Return number of items in the host list.
101+
*
102+
* Returns:
103+
* 0 or greater.
104+
*
105+
* Side effects:
106+
* None.
107+
*
108+
*--------------------------------------------------------------------------
109+
*/
110+
size_t
111+
mongoc_host_list_count (const mongoc_host_list_t *list)
112+
{
113+
size_t count = 0;
114+
115+
while (list) {
116+
count++;
117+
list = list->next;
118+
}
119+
120+
return count;
121+
}
122+
123+
124+
/*
125+
*--------------------------------------------------------------------------
126+
*
127+
* host_list_copy --
128+
*
129+
* Make a copy of "host" with next set to "next".
130+
*
131+
* Returns:
132+
* New copy.
133+
*
134+
* Side effects:
135+
* None.
136+
*
137+
*--------------------------------------------------------------------------
138+
*/
139+
mongoc_host_list_t *
140+
mongoc_host_list_copy (const mongoc_host_list_t *host,
141+
mongoc_host_list_t *next)
142+
{
143+
mongoc_host_list_t *copy;
144+
145+
copy = mongoc_host_list_new ();
146+
memcpy ((void *) copy, (void *) host, sizeof (mongoc_host_list_t));
147+
copy->next = next;
148+
149+
return copy;
150+
}
151+
152+
153+
/*
154+
*--------------------------------------------------------------------------
155+
*
156+
* mongoc_host_list_destroy_all --
157+
*
158+
* Destroy whole linked list of hosts.
159+
*
160+
*--------------------------------------------------------------------------
161+
*/
162+
void
163+
mongoc_host_list_destroy_all (mongoc_host_list_t *host)
164+
{
165+
mongoc_host_list_t *tmp;
166+
167+
while (host) {
168+
tmp = host->next;
169+
bson_free (host);
170+
host = tmp;
171+
}
172+
}

src/mongoc/mongoc-host-list.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ struct _mongoc_host_list_t
5151
void *padding [4];
5252
};
5353

54+
mongoc_host_list_t *mongoc_host_list_new (void);
55+
56+
bool mongoc_host_list_equal (const mongoc_host_list_t *host_a,
57+
const mongoc_host_list_t *host_b);
58+
59+
const mongoc_host_list_t *mongoc_host_list_find (const mongoc_host_list_t *list,
60+
const mongoc_host_list_t *needle);
61+
62+
size_t mongoc_host_list_count (const mongoc_host_list_t *list);
63+
64+
mongoc_host_list_t *mongoc_host_list_copy (const mongoc_host_list_t *host,
65+
mongoc_host_list_t *next);
66+
67+
void mongoc_host_list_destroy_all (mongoc_host_list_t *host);
5468

5569
BSON_END_DECLS
5670

src/mongoc/mongoc-topology-scanner-private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef struct mongoc_topology_scanner
6969
mongoc_async_cmd_setup_t setup;
7070
mongoc_stream_initiator_t initiator;
7171
void *initiator_context;
72+
mongoc_host_list_t *seen;
7273

7374
#ifdef MONGOC_ENABLE_SSL
7475
mongoc_ssl_opt_t *ssl_opts;

src/mongoc/mongoc-topology-scanner.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ mongoc_topology_scanner_destroy (mongoc_topology_scanner_t *ts)
9292
mongoc_async_destroy (ts->async);
9393
bson_destroy (&ts->ismaster_cmd);
9494

95+
mongoc_host_list_destroy_all (ts->seen);
96+
9597
bson_free (ts);
9698
}
9799

@@ -100,7 +102,18 @@ _add_node (mongoc_topology_scanner_t *ts,
100102
const mongoc_host_list_t *host,
101103
uint32_t id)
102104
{
103-
mongoc_topology_scanner_node_t *node = (mongoc_topology_scanner_node_t *)bson_malloc0 (sizeof (*node));
105+
mongoc_topology_scanner_node_t *node;
106+
107+
if (mongoc_host_list_find (ts->seen, host)) {
108+
/* already seen this host -- avoid pathological case like two primaries
109+
* pointing at each other, each invalidating itself and trying to add the
110+
* other on each check within the same blocking scan, see
111+
* test_topology_scanner_oscillate
112+
*/
113+
return NULL;
114+
}
115+
116+
node = (mongoc_topology_scanner_node_t *) bson_malloc0 (sizeof (*node));
104117

105118
memcpy (&node->host, host, sizeof (*host));
106119

@@ -110,6 +123,8 @@ _add_node (mongoc_topology_scanner_t *ts,
110123

111124
DL_APPEND(ts->nodes, node);
112125

126+
ts->seen = mongoc_host_list_copy (host, ts->seen);
127+
113128
return node;
114129
}
115130

@@ -132,7 +147,7 @@ mongoc_topology_scanner_add_and_scan (mongoc_topology_scanner_t *ts,
132147

133148
node = _add_node (ts, host, id);
134149

135-
if (mongoc_topology_scanner_node_setup (node)) {
150+
if (node && mongoc_topology_scanner_node_setup (node)) {
136151
node->cmd = mongoc_async_cmd (
137152
ts->async, node->stream, ts->setup,
138153
node->host.host, "admin",
@@ -147,6 +162,7 @@ mongoc_topology_scanner_add_and_scan (mongoc_topology_scanner_t *ts,
147162
void
148163
mongoc_topology_scanner_node_destroy (mongoc_topology_scanner_node_t *node, bool failed)
149164
{
165+
/* delete from nodes but keep its host in "seen" so we don't rescan */
150166
DL_DELETE (node->ts->nodes, node);
151167

152168
if (node->dns_results) {
@@ -491,6 +507,8 @@ mongoc_topology_scanner_work (mongoc_topology_scanner_t *ts,
491507
r = mongoc_async_run (ts->async, timeout_msec);
492508

493509
if (! r) {
510+
mongoc_host_list_destroy_all (ts->seen);
511+
ts->seen = NULL;
494512
ts->in_progress = false;
495513
}
496514

tests/TestSuite.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ extern "C" {
101101
#define AWAIT(_condition) \
102102
do { \
103103
int64_t _start = bson_get_monotonic_time (); \
104-
while (! _condition) { \
104+
while (! (_condition)) { \
105105
if (bson_get_monotonic_time() - _start > 1000 * 1000) { \
106106
fprintf (stderr, \
107107
"%s:%d %s(): \"%s\" still false after 1 second\n", \

tests/test-mongoc-topology-scanner.c

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ test_topology_scanner_discovery ()
142142
mongoc_client_t *client;
143143
mongoc_topology_scanner_t *scanner;
144144
char *uri_str;
145-
mongoc_read_prefs_t *primary_pref;
145+
mongoc_read_prefs_t *secondary_pref;
146146
bson_error_t error;
147147
future_t *future;
148148
request_t *request;
@@ -174,32 +174,41 @@ test_topology_scanner_discovery ()
174174
mock_server_get_host_and_port (primary));
175175
client = mongoc_client_new (uri_str);
176176
scanner = client->topology->scanner;
177-
primary_pref = mongoc_read_prefs_new (MONGOC_READ_PRIMARY);
177+
secondary_pref = mongoc_read_prefs_new (MONGOC_READ_SECONDARY_PREFERRED);
178178

179179
future = future_topology_select (client->topology, MONGOC_SS_READ,
180-
primary_pref, 15, &error);
180+
secondary_pref, 15, &error);
181181

182182
/* a single scan discovers *and* checks the secondary */
183+
AWAIT (scanner->async->ncmds == 1);
184+
ASSERT_CMPINT (1, ==, (int) mongoc_host_list_count (scanner->seen));
183185
request = mock_server_receives_ismaster (primary);
184186
mock_server_replies_simple (request, primary_response);
185187
request_destroy (request);
186188

187189
/* let client process that response */
188190
_mongoc_usleep (250 * 1000);
191+
ASSERT_CMPINT (2, ==, (int) mongoc_host_list_count (scanner->seen));
189192

190193
/* a check of the secondary is scheduled in this scan */
191-
AWAIT (scanner->async->ncmds);
194+
AWAIT (scanner->async->ncmds == 1);
192195

193196
request = mock_server_receives_ismaster (secondary);
194197
mock_server_replies_simple (request, secondary_response);
195198
/* scan completes */
196-
AWAIT (!scanner->async->ncmds);
199+
AWAIT (scanner->async->ncmds == 0);
197200
ASSERT_OR_PRINT ((sd = future_get_mongoc_server_description_ptr (future)),
198201
error);
199202

203+
ASSERT_CMPSTR (sd->host.host_and_port,
204+
mock_server_get_host_and_port (secondary));
205+
206+
/* "seen" is reset */
207+
ASSERT_CMPINT (0, ==, (int) mongoc_host_list_count (scanner->seen));
208+
200209
mongoc_server_description_destroy (sd);
201210
request_destroy (request);
202-
mongoc_read_prefs_destroy (primary_pref);
211+
mongoc_read_prefs_destroy (secondary_pref);
203212
bson_free (secondary_response);
204213
bson_free (primary_response);
205214
bson_free (uri_str);
@@ -209,9 +218,7 @@ test_topology_scanner_discovery ()
209218
}
210219

211220

212-
/*
213-
* Check that scanner doesn't spinlock if two primaries point at each other.
214-
*/
221+
/* scanner shouldn't spin if two primaries point at each other */
215222
void
216223
test_topology_scanner_oscillate ()
217224
{
@@ -266,13 +273,21 @@ test_topology_scanner_oscillate ()
266273

267274
/* let client process that response */
268275
_mongoc_usleep (250 * 1000);
269-
AWAIT (scanner->async->ncmds);
276+
AWAIT (scanner->async->ncmds == 1);
270277

271278
request = mock_server_receives_ismaster (server1);
272279
mock_server_replies_simple (request, server1_response);
273-
AWAIT (!scanner->async->ncmds);
280+
281+
/* we don't schedule another check of server0 */
282+
_mongoc_usleep (250 * 1000);
283+
AWAIT (scanner->async->ncmds == 0);
274284

275285
assert (!future_get_mongoc_server_description_ptr (future));
286+
assert (scanner->async->ncmds == 0);
287+
288+
/* "seen" is reset */
289+
ASSERT_CMPINT (0, ==, (int) mongoc_host_list_count (scanner->seen));
290+
276291
request_destroy (request);
277292
mongoc_read_prefs_destroy (primary_pref);
278293
bson_free (server1_response);
@@ -293,8 +308,6 @@ test_topology_scanner_install (TestSuite *suite)
293308
#endif
294309
TestSuite_Add (suite, "/TOPOLOGY/scanner_discovery",
295310
test_topology_scanner_discovery);
296-
/*
297311
TestSuite_Add (suite, "/TOPOLOGY/scanner_oscillate",
298312
test_topology_scanner_oscillate);
299-
*/
300313
}

0 commit comments

Comments
 (0)