mcon_collection* mongo_find_candidate_servers(mongo_con_manager *manager, mongo_read_preference *rp, mongo_servers *servers) { int i; mcon_collection *all, *filtered; mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "finding candidate servers"); all = mongo_find_all_candidate_servers(manager, rp); if (servers->options.con_type == MONGO_CON_TYPE_REPLSET) { filtered = mongo_filter_candidates_by_replicaset_name(manager, all, servers); } else { filtered = mongo_filter_candidates_by_seed(manager, all, servers); } mcon_collection_free(all); all = filtered; filtered = mongo_filter_candidates_by_credentials(manager, all, servers); mcon_collection_free(all); all = filtered; if (rp->tagset_count != 0) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by tagsets"); /* If we have tagsets configured for the replicaset then we need to do * some more filtering */ for (i = 0; i < rp->tagset_count; i++) { char *tmp_ts = mongo_read_preference_squash_tagset(rp->tagsets[i]); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "checking tagset: %s", tmp_ts); filtered = mongo_filter_candidates_by_tagset(manager, all, rp->tagsets[i]); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "tagset %s matched %d candidates", tmp_ts, filtered->count); free(tmp_ts); if (filtered->count > 0) { mcon_collection_free(all); return filtered; } } mcon_collection_free(filtered); mcon_collection_free(all); return NULL; } else { return all; } }
mcon_collection *mongo_select_nearest_servers(mongo_con_manager *manager, mcon_collection *col, mongo_read_preference *rp) { mcon_collection *filtered; int i, nearest_ping; filtered = mcon_init_collection(sizeof(mongo_connection*)); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "selecting near servers"); switch (rp->type) { case MONGO_RP_PRIMARY: case MONGO_RP_PRIMARY_PREFERRED: case MONGO_RP_SECONDARY: case MONGO_RP_SECONDARY_PREFERRED: case MONGO_RP_NEAREST: /* The nearest ping time is in the first element */ nearest_ping = ((mongo_connection*)col->data[0])->ping_ms; mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "selecting near servers: nearest is %dms", nearest_ping); /* FIXME: Change to iterator later */ for (i = 0; i < col->count; i++) { if (((mongo_connection*)col->data[i])->ping_ms <= nearest_ping + MONGO_RP_CUTOFF) { mcon_collection_add(filtered, col->data[i]); } } break; default: return NULL; } /* Clean up the old collection that we no longer need */ mcon_collection_free(col); mcon_collection_iterate(manager, filtered, mongo_print_connection_iterate_wrapper); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "selecting near server: done"); return filtered; }
static mongo_connection *mongo_get_connection_multiple(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message) { mongo_connection *con = NULL; mongo_connection *tmp; mcon_collection *collection = NULL; mongo_read_preference tmp_rp; /* We only support NEAREST for MULTIPLE right now */ int i; int found_connected_server = 0; mcon_str *messages; int found_supported_wire_version = 1; mcon_str_ptr_init(messages); /* Create a connection to every of the servers in the seed list */ for (i = 0; i < servers->count; i++) { int ismaster_error = 0; char *con_error_message = NULL; tmp = mongo_get_connection_single(manager, servers->server[i], &servers->options, connection_flags, (char **) &con_error_message); if (tmp) { found_connected_server++; /* Run ismaster, if needed, to extract server flags */ ismaster_error = mongo_connection_ismaster(manager, tmp, &servers->options, NULL, NULL, NULL, &con_error_message, NULL); switch (ismaster_error) { case 1: /* Run just fine */ case 2: /* ismaster() skipped due to interval */ break; case 0: /* Some error */ case 3: /* Run just fine, but hostname didn't match what we expected */ case 4: /* Danger danger, reported wire version does not overlap what we support */ default: mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "server_flags: error while getting the server configuration %s:%d: %s", servers->server[i]->host, servers->server[i]->port, con_error_message); /* If it failed because of wire version, we have to bail out completely * later on, but we should continue to aggregate the errors in case more * servers are unsupported */ if (ismaster_error == 4) { mongo_manager_connection_deregister(manager, tmp); found_supported_wire_version = 0; } else { mongo_connection_destroy(manager, tmp, MONGO_CLOSE_BROKEN); } tmp = NULL; found_connected_server--; } } if (!tmp) { if (!(connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Couldn't connect to '%s:%d': %s", servers->server[i]->host, servers->server[i]->port, con_error_message); if (messages->l) { mcon_str_addl(messages, "; ", 2, 0); } mcon_str_add(messages, "Failed to connect to: ", 0); mcon_str_add(messages, servers->server[i]->host, 0); mcon_str_addl(messages, ":", 1, 0); mcon_str_add_int(messages, servers->server[i]->port); mcon_str_addl(messages, ": ", 2, 0); mcon_str_add(messages, con_error_message, 1); /* Also frees con_error_message */ } else { free(con_error_message); } } } if (!found_supported_wire_version) { *error_message = strdup("Found a server running unsupported wire version. Please upgrade the driver, or take the server out of rotation"); mcon_str_ptr_dtor(messages); return NULL; } /* If we don't have a connected server then there is no point in continueing */ if (!found_connected_server && (connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { mcon_str_ptr_dtor(messages); return NULL; } /* When selecting a *mongos* node, readPreferences make no sense as we * don't have a "primary" or "secondary" mongos. The mongos nodes aren't * tagged either. To pick a mongos we therefore simply pick the "nearest" * mongos node. */ tmp_rp.type = MONGO_RP_NEAREST; tmp_rp.tagsets = NULL; tmp_rp.tagset_count = 0; collection = mongo_find_candidate_servers(manager, &tmp_rp, servers); if (!collection || collection->count == 0) { if (messages->l) { *error_message = strdup(messages->d); } else { *error_message = strdup("No candidate servers found"); } goto bailout; } collection = mongo_sort_servers(manager, collection, &servers->read_pref); collection = mongo_select_nearest_servers(manager, collection, &servers->options, &servers->read_pref); if (!collection) { *error_message = strdup("No server near us"); goto bailout; } con = mongo_pick_server_from_set(manager, collection, &servers->read_pref); bailout: /* Cleaning up */ mcon_str_ptr_dtor(messages); if (collection) { mcon_collection_free(collection); } return con; }
static mongo_connection *mongo_get_read_write_connection_replicaset(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message) { mongo_connection *con = NULL; mongo_connection *tmp; mcon_collection *collection; char *con_error_message = NULL; int i; int found_connected_server = 0; /* Create a connection to all of the servers in the seed list */ for (i = 0; i < servers->count; i++) { tmp = mongo_get_connection_single(manager, servers->server[i], &servers->options, connection_flags, (char **) &con_error_message); if (tmp) { found_connected_server++; } else if (!(connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Couldn't connect to '%s:%d': %s", servers->server[i]->host, servers->server[i]->port, con_error_message); free(con_error_message); } } if (!found_connected_server && (connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { return NULL; } /* Discover more nodes. This also adds a connection to "servers" for each * new node */ if (!mongo_discover_topology(manager, servers)) { /* Total failure, we cannot proceed */ *error_message = strdup("Incompatible server detected. This driver release is not compatible with one of the connected servers"); return NULL; } /* Depending on whether we want a read or a write connection, run the correct algorithms */ if (connection_flags & MONGO_CON_FLAG_WRITE) { mongo_read_preference tmp_rp; tmp_rp.type = MONGO_RP_PRIMARY; tmp_rp.tagsets = NULL; tmp_rp.tagset_count = 0; collection = mongo_find_candidate_servers(manager, &tmp_rp, servers); mongo_read_preference_dtor(&tmp_rp); } else if (connection_flags & MONGO_CON_FLAG_DONT_FILTER) { /* We just want to know if we have something to talk to, irregardless of RP */ mongo_read_preference tmp_rp; tmp_rp.type = MONGO_RP_NEAREST; tmp_rp.tagsets = NULL; tmp_rp.tagset_count = 0; collection = mongo_find_candidate_servers(manager, &tmp_rp, servers); mongo_read_preference_dtor(&tmp_rp); } else { collection = mongo_find_candidate_servers(manager, &servers->read_pref, servers); } if (!collection) { *error_message = strdup("No candidate servers found"); return NULL; } if (collection->count == 0) { *error_message = strdup("No candidate servers found"); mcon_collection_free(collection); return NULL; } collection = mongo_sort_servers(manager, collection, &servers->read_pref); collection = mongo_select_nearest_servers(manager, collection, &servers->options, &servers->read_pref); con = mongo_pick_server_from_set(manager, collection, &servers->read_pref); /* Cleaning up */ mcon_collection_free(collection); return con; }
static mongo_connection *mongo_get_connection_multiple(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message) { mongo_connection *con = NULL; mongo_connection *tmp; mcon_collection *collection = NULL; char *con_error_message = NULL; mongo_read_preference tmp_rp; /* We only support NEAREST for MULTIPLE right now */ int i; int found_connected_server = 0; mcon_str *messages; mcon_str_ptr_init(messages); /* Create a connection to every of the servers in the seed list */ for (i = 0; i < servers->count; i++) { tmp = mongo_get_connection_single(manager, servers->server[i], &servers->options, connection_flags, (char **) &con_error_message); if (tmp) { found_connected_server = 1; } else if (!(connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Couldn't connect to '%s:%d': %s", servers->server[i]->host, servers->server[i]->port, con_error_message); if (messages->l) { mcon_str_addl(messages, "; ", 2, 0); } mcon_str_add(messages, "Failed to connect to: ", 0); mcon_str_add(messages, servers->server[i]->host, 0); mcon_str_addl(messages, ":", 1, 0); mcon_str_add_int(messages, servers->server[i]->port); mcon_str_addl(messages, ": ", 2, 0); mcon_str_add(messages, con_error_message, 1); /* Also frees con_error_message */ } } /* If we don't have a connected server then there is no point in continueing */ if (!found_connected_server && (connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { return NULL; } /* When selecting a *mongos* node, readPreferences make no sense as we * don't have a "primary" or "secondary" mongos. The mongos nodes aren't * tagged either. To pick a mongos we therefore simply pick the "nearest" * mongos node. */ tmp_rp.type = MONGO_RP_NEAREST; tmp_rp.tagsets = NULL; tmp_rp.tagset_count = 0; collection = mongo_find_candidate_servers(manager, &tmp_rp, servers); if (!collection || collection->count == 0) { if (messages->l) { *error_message = strdup(messages->d); } else { *error_message = strdup("No candidate servers found"); } goto bailout; } collection = mongo_sort_servers(manager, collection, &servers->read_pref); collection = mongo_select_nearest_servers(manager, collection, &servers->read_pref); if (!collection) { *error_message = strdup("No server near us"); goto bailout; } con = mongo_pick_server_from_set(manager, collection, &servers->read_pref); bailout: /* Cleaning up */ mcon_str_ptr_dtor(messages); if (collection) { mcon_collection_free(collection); } return con; }