1 /*
2 * Copyright 2013 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
18 #include "mongoc-config.h"
19 #include "mongoc-error.h"
20 #include "mongoc-read-prefs-private.h"
21 #include "mongoc-trace-private.h"
22
23
24 mongoc_read_prefs_t *
mongoc_read_prefs_new(mongoc_read_mode_t mode)25 mongoc_read_prefs_new (mongoc_read_mode_t mode)
26 {
27 mongoc_read_prefs_t *read_prefs;
28
29 read_prefs = (mongoc_read_prefs_t *) bson_malloc0 (sizeof *read_prefs);
30 read_prefs->mode = mode;
31 bson_init (&read_prefs->tags);
32 read_prefs->max_staleness_seconds = MONGOC_NO_MAX_STALENESS;
33
34 return read_prefs;
35 }
36
37
38 mongoc_read_mode_t
mongoc_read_prefs_get_mode(const mongoc_read_prefs_t * read_prefs)39 mongoc_read_prefs_get_mode (const mongoc_read_prefs_t *read_prefs)
40 {
41 return read_prefs ? read_prefs->mode : MONGOC_READ_PRIMARY;
42 }
43
44
45 void
mongoc_read_prefs_set_mode(mongoc_read_prefs_t * read_prefs,mongoc_read_mode_t mode)46 mongoc_read_prefs_set_mode (mongoc_read_prefs_t *read_prefs,
47 mongoc_read_mode_t mode)
48 {
49 BSON_ASSERT (read_prefs);
50 BSON_ASSERT (mode <= MONGOC_READ_NEAREST);
51
52 read_prefs->mode = mode;
53 }
54
55
56 const bson_t *
mongoc_read_prefs_get_tags(const mongoc_read_prefs_t * read_prefs)57 mongoc_read_prefs_get_tags (const mongoc_read_prefs_t *read_prefs)
58 {
59 BSON_ASSERT (read_prefs);
60 return &read_prefs->tags;
61 }
62
63
64 void
mongoc_read_prefs_set_tags(mongoc_read_prefs_t * read_prefs,const bson_t * tags)65 mongoc_read_prefs_set_tags (mongoc_read_prefs_t *read_prefs, const bson_t *tags)
66 {
67 BSON_ASSERT (read_prefs);
68
69 bson_destroy (&read_prefs->tags);
70
71 if (tags) {
72 bson_copy_to (tags, &read_prefs->tags);
73 } else {
74 bson_init (&read_prefs->tags);
75 }
76 }
77
78
79 void
mongoc_read_prefs_add_tag(mongoc_read_prefs_t * read_prefs,const bson_t * tag)80 mongoc_read_prefs_add_tag (mongoc_read_prefs_t *read_prefs, const bson_t *tag)
81 {
82 bson_t empty = BSON_INITIALIZER;
83 char str[16];
84 int key;
85
86 BSON_ASSERT (read_prefs);
87
88 key = bson_count_keys (&read_prefs->tags);
89 bson_snprintf (str, sizeof str, "%d", key);
90
91 if (tag) {
92 bson_append_document (&read_prefs->tags, str, -1, tag);
93 } else {
94 bson_append_document (&read_prefs->tags, str, -1, &empty);
95 }
96 }
97
98
99 int64_t
mongoc_read_prefs_get_max_staleness_seconds(const mongoc_read_prefs_t * read_prefs)100 mongoc_read_prefs_get_max_staleness_seconds (
101 const mongoc_read_prefs_t *read_prefs)
102 {
103 BSON_ASSERT (read_prefs);
104
105 return read_prefs->max_staleness_seconds;
106 }
107
108
109 void
mongoc_read_prefs_set_max_staleness_seconds(mongoc_read_prefs_t * read_prefs,int64_t max_staleness_seconds)110 mongoc_read_prefs_set_max_staleness_seconds (mongoc_read_prefs_t *read_prefs,
111 int64_t max_staleness_seconds)
112 {
113 BSON_ASSERT (read_prefs);
114
115 read_prefs->max_staleness_seconds = max_staleness_seconds;
116 }
117
118
119 bool
mongoc_read_prefs_is_valid(const mongoc_read_prefs_t * read_prefs)120 mongoc_read_prefs_is_valid (const mongoc_read_prefs_t *read_prefs)
121 {
122 BSON_ASSERT (read_prefs);
123
124 /*
125 * Tags or maxStalenessSeconds are not supported with PRIMARY mode.
126 */
127 if (read_prefs->mode == MONGOC_READ_PRIMARY) {
128 if (!bson_empty (&read_prefs->tags) ||
129 read_prefs->max_staleness_seconds != MONGOC_NO_MAX_STALENESS) {
130 return false;
131 }
132 }
133
134 if (read_prefs->max_staleness_seconds != MONGOC_NO_MAX_STALENESS &&
135 read_prefs->max_staleness_seconds <= 0) {
136 return false;
137 }
138
139 return true;
140 }
141
142
143 void
mongoc_read_prefs_destroy(mongoc_read_prefs_t * read_prefs)144 mongoc_read_prefs_destroy (mongoc_read_prefs_t *read_prefs)
145 {
146 if (read_prefs) {
147 bson_destroy (&read_prefs->tags);
148 bson_free (read_prefs);
149 }
150 }
151
152
153 mongoc_read_prefs_t *
mongoc_read_prefs_copy(const mongoc_read_prefs_t * read_prefs)154 mongoc_read_prefs_copy (const mongoc_read_prefs_t *read_prefs)
155 {
156 mongoc_read_prefs_t *ret = NULL;
157
158 if (read_prefs) {
159 ret = mongoc_read_prefs_new (read_prefs->mode);
160 bson_copy_to (&read_prefs->tags, &ret->tags);
161 ret->max_staleness_seconds = read_prefs->max_staleness_seconds;
162 }
163
164 return ret;
165 }
166
167
168 const char *
_mongoc_read_mode_as_str(mongoc_read_mode_t mode)169 _mongoc_read_mode_as_str (mongoc_read_mode_t mode)
170 {
171 switch (mode) {
172 case MONGOC_READ_PRIMARY:
173 return "primary";
174 case MONGOC_READ_PRIMARY_PREFERRED:
175 return "primaryPreferred";
176 case MONGOC_READ_SECONDARY:
177 return "secondary";
178 case MONGOC_READ_SECONDARY_PREFERRED:
179 return "secondaryPreferred";
180 case MONGOC_READ_NEAREST:
181 return "nearest";
182 default:
183 return "";
184 }
185 }
186
187
188 /* Update result with the read prefs, following Server Selection Spec.
189 * The driver must have discovered the server is a mongos.
190 */
191 static void
_apply_read_preferences_mongos(const mongoc_read_prefs_t * read_prefs,const bson_t * query_bson,mongoc_apply_read_prefs_result_t * result)192 _apply_read_preferences_mongos (
193 const mongoc_read_prefs_t *read_prefs,
194 const bson_t *query_bson,
195 mongoc_apply_read_prefs_result_t *result /* OUT */)
196 {
197 mongoc_read_mode_t mode;
198 const bson_t *tags = NULL;
199 bson_t child;
200 const char *mode_str;
201 int64_t max_staleness_seconds;
202
203 mode = mongoc_read_prefs_get_mode (read_prefs);
204 if (read_prefs) {
205 tags = mongoc_read_prefs_get_tags (read_prefs);
206 }
207
208 /* Server Selection Spec says:
209 *
210 * For mode 'primary', drivers MUST NOT set the slaveOK wire protocol flag
211 * and MUST NOT use $readPreference
212 *
213 * For mode 'secondary', drivers MUST set the slaveOK wire protocol flag and
214 * MUST also use $readPreference
215 *
216 * For mode 'primaryPreferred', drivers MUST set the slaveOK wire protocol
217 * flag and MUST also use $readPreference
218 *
219 * For mode 'secondaryPreferred', drivers MUST set the slaveOK wire protocol
220 * flag. If the read preference contains a non-empty tag_sets parameter,
221 * drivers MUST use $readPreference; otherwise, drivers MUST NOT use
222 * $readPreference
223 *
224 * For mode 'nearest', drivers MUST set the slaveOK wire protocol flag and
225 * MUST also use $readPreference
226 */
227 if (mode == MONGOC_READ_SECONDARY_PREFERRED && bson_empty0 (tags)) {
228 result->flags |= MONGOC_QUERY_SLAVE_OK;
229
230 } else if (mode != MONGOC_READ_PRIMARY) {
231 result->flags |= MONGOC_QUERY_SLAVE_OK;
232
233 /* Server Selection Spec: "When any $ modifier is used, including the
234 * $readPreference modifier, the query MUST be provided using the $query
235 * modifier".
236 *
237 * This applies to commands, too.
238 */
239 result->query_with_read_prefs = bson_new ();
240 result->query_owned = true;
241
242 if (bson_has_field (query_bson, "$query")) {
243 bson_concat (result->query_with_read_prefs, query_bson);
244 } else {
245 bson_append_document (
246 result->query_with_read_prefs, "$query", 6, query_bson);
247 }
248
249 bson_append_document_begin (
250 result->query_with_read_prefs, "$readPreference", 15, &child);
251 mode_str = _mongoc_read_mode_as_str (mode);
252 bson_append_utf8 (&child, "mode", 4, mode_str, -1);
253 if (!bson_empty0 (tags)) {
254 bson_append_array (&child, "tags", 4, tags);
255 }
256
257 max_staleness_seconds =
258 mongoc_read_prefs_get_max_staleness_seconds (read_prefs);
259
260 if (max_staleness_seconds != MONGOC_NO_MAX_STALENESS) {
261 bson_append_int64 (
262 &child, "maxStalenessSeconds", 19, max_staleness_seconds);
263 }
264
265 bson_append_document_end (result->query_with_read_prefs, &child);
266 }
267 }
268
269 /*
270 *--------------------------------------------------------------------------
271 *
272 * apply_read_preferences --
273 *
274 * Update @result based on @read prefs, following the Server Selection
275 * Spec.
276 *
277 * Side effects:
278 * Sets @result->query_with_read_prefs and @result->flags.
279 *
280 * Note:
281 * This function, the mongoc_apply_read_prefs_result_t struct, and all
282 * related functions are only used for find operations with OP_QUERY.
283 * Remove them once MongoDB 3.0 is EOL, all find operations will then
284 * use the "find" command.
285 *
286 *--------------------------------------------------------------------------
287 */
288
289 void
apply_read_preferences(const mongoc_read_prefs_t * read_prefs,const mongoc_server_stream_t * server_stream,const bson_t * query_bson,mongoc_query_flags_t initial_flags,mongoc_apply_read_prefs_result_t * result)290 apply_read_preferences (const mongoc_read_prefs_t *read_prefs,
291 const mongoc_server_stream_t *server_stream,
292 const bson_t *query_bson,
293 mongoc_query_flags_t initial_flags,
294 mongoc_apply_read_prefs_result_t *result /* OUT */)
295 {
296 mongoc_server_description_type_t server_type;
297
298 ENTRY;
299
300 BSON_ASSERT (server_stream);
301 BSON_ASSERT (query_bson);
302 BSON_ASSERT (result);
303
304 /* default values */
305 result->query_with_read_prefs = (bson_t *) query_bson;
306 result->query_owned = false;
307 result->flags = initial_flags;
308
309 server_type = server_stream->sd->type;
310
311 switch (server_stream->topology_type) {
312 case MONGOC_TOPOLOGY_SINGLE:
313 if (server_type == MONGOC_SERVER_MONGOS) {
314 _apply_read_preferences_mongos (read_prefs, query_bson, result);
315 } else {
316 /* Server Selection Spec: for topology type single and server types
317 * besides mongos, "clients MUST always set the slaveOK wire protocol
318 * flag on reads to ensure that any server type can handle the
319 * request."
320 */
321 result->flags |= MONGOC_QUERY_SLAVE_OK;
322 }
323
324 break;
325
326 case MONGOC_TOPOLOGY_RS_NO_PRIMARY:
327 case MONGOC_TOPOLOGY_RS_WITH_PRIMARY:
328 /* Server Selection Spec: for RS topology types, "For all read
329 * preferences modes except primary, clients MUST set the slaveOK wire
330 * protocol flag to ensure that any suitable server can handle the
331 * request. Clients MUST NOT set the slaveOK wire protocol flag if the
332 * read preference mode is primary.
333 */
334 if (read_prefs && read_prefs->mode != MONGOC_READ_PRIMARY) {
335 result->flags |= MONGOC_QUERY_SLAVE_OK;
336 }
337
338 break;
339
340 case MONGOC_TOPOLOGY_SHARDED:
341 _apply_read_preferences_mongos (read_prefs, query_bson, result);
342 break;
343
344 case MONGOC_TOPOLOGY_UNKNOWN:
345 case MONGOC_TOPOLOGY_DESCRIPTION_TYPES:
346 default:
347 /* must not call _apply_read_preferences with unknown topology type */
348 BSON_ASSERT (false);
349 }
350
351 EXIT;
352 }
353
354
355 void
apply_read_prefs_result_cleanup(mongoc_apply_read_prefs_result_t * result)356 apply_read_prefs_result_cleanup (mongoc_apply_read_prefs_result_t *result)
357 {
358 ENTRY;
359
360 BSON_ASSERT (result);
361
362 if (result->query_owned) {
363 bson_destroy (result->query_with_read_prefs);
364 }
365
366 EXIT;
367 }
368
369 bool
_mongoc_read_prefs_validate(const mongoc_read_prefs_t * read_prefs,bson_error_t * error)370 _mongoc_read_prefs_validate (const mongoc_read_prefs_t *read_prefs,
371 bson_error_t *error)
372 {
373 if (read_prefs && !mongoc_read_prefs_is_valid (read_prefs)) {
374 bson_set_error (error,
375 MONGOC_ERROR_COMMAND,
376 MONGOC_ERROR_COMMAND_INVALID_ARG,
377 "Invalid mongoc_read_prefs_t");
378 return false;
379 }
380 return true;
381 }
382