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