Array HHVM_METHOD(MongoDBDriverServer, getInfo) { MongoDBDriverServerData* data = Native::data<MongoDBDriverServerData>(this_); mongoc_server_description_t *sd; if ((sd = mongoc_client_get_server_description(data->m_client, data->m_server_id))) { const bson_t *is_master = mongoc_server_description_ismaster(sd); Variant v; hippo_bson_conversion_options_t options = HIPPO_TYPEMAP_DEBUG_INITIALIZER; /* Yeah, this is not pretty. But, C++ doesn't have finally, and I don't * want to bson_copy the is_master thing just because of that */ try { BsonToVariantConverter convertor(bson_get_data(is_master), is_master->len, options); convertor.convert(&v); } catch (...) { mongoc_server_description_destroy(sd); throw; } return v.toArray(); } throw MongoDriver::Utils::CreateAndConstruct(MongoDriver::s_MongoDriverExceptionRuntimeException_className, "Failed to get server description", HPHP::Variant((uint64_t) 0)); }
static void test_rtt_calculation_cb (bson_t *test) { mongoc_server_description_t *description; bson_iter_t iter; BSON_ASSERT (test); description = (mongoc_server_description_t *)bson_malloc0(sizeof *description); mongoc_server_description_init(description, "localhost:27017", 1); /* parse RTT into server description */ assert(bson_iter_init_find(&iter, test, "avg_rtt_ms")); description->round_trip_time_msec = bson_iter_int64(&iter); /* update server description with new rtt */ assert(bson_iter_init_find(&iter, test, "new_rtt_ms")); mongoc_server_description_update_rtt(description, bson_iter_int64(&iter)); /* ensure new RTT was calculated correctly */ assert(bson_iter_init_find(&iter, test, "new_avg_rtt")); assert(description->round_trip_time_msec == bson_iter_int64(&iter)); mongoc_server_description_destroy(description); }
static ha_node_t * get_replica (mongoc_client_t *client, uint32_t id) { mongoc_server_description_t *description; ha_node_t *iter; description = mongoc_topology_server_by_id(client->topology, id, NULL); BSON_ASSERT(description); for (iter = replica_set->nodes; iter; iter = iter->next) { if (iter->port == description->host.port) { mongoc_server_description_destroy(description); return iter; } } mongoc_server_description_destroy(description); BSON_ASSERT(false); return NULL; }
String HHVM_METHOD(MongoDBDriverServer, getHost) { MongoDBDriverServerData* data = Native::data<MongoDBDriverServerData>(this_); mongoc_server_description_t *sd; if ((sd = mongoc_client_get_server_description(data->m_client, data->m_server_id))) { String host(mongoc_server_description_host(sd)->host); mongoc_server_description_destroy(sd); return host; } throw MongoDriver::Utils::CreateAndConstruct(MongoDriver::s_MongoDriverExceptionRuntimeException_className, "Failed to get server description", HPHP::Variant((uint64_t) 0)); }
bool HHVM_METHOD(MongoDBDriverServer, isArbiter) { MongoDBDriverServerData* data = Native::data<MongoDBDriverServerData>(this_); mongoc_server_description_t *sd; if ((sd = mongoc_client_get_server_description(data->m_client, data->m_server_id))) { bool isType; isType = (strcmp(mongoc_server_description_type(sd), hippo_server_description_type_map[HIPPO_SERVER_RS_ARBITER].name) == 0); mongoc_server_description_destroy(sd); return isType; } throw MongoDriver::Utils::CreateAndConstruct(MongoDriver::s_MongoDriverExceptionRuntimeException_className, "Failed to get server description", HPHP::Variant((uint64_t) 0)); }
int64_t HHVM_METHOD(MongoDBDriverServer, getLatency) { MongoDBDriverServerData* data = Native::data<MongoDBDriverServerData>(this_); mongoc_server_description_t *sd; if ((sd = mongoc_client_get_server_description(data->m_client, data->m_server_id))) { int64_t round_trip; round_trip = mongoc_server_description_round_trip_time(sd); mongoc_server_description_destroy(sd); return round_trip; } throw MongoDriver::Utils::CreateAndConstruct(MongoDriver::s_MongoDriverExceptionRuntimeException_className, "Failed to get server description", HPHP::Variant((uint64_t) 0)); }
Array HHVM_METHOD(MongoDBDriverServer, __debugInfo) { MongoDBDriverServerData* data = Native::data<MongoDBDriverServerData>(this_); mongoc_server_description_t *sd; bson_error_t error; Array retval = Array::Create(); if ((sd = mongoc_topology_description_server_by_id(&data->m_client->topology->description, data->m_server_id, &error))) { mongodb_driver_add_server_debug(sd, &retval); mongoc_server_description_destroy(sd); return retval; } throw MongoDriver::Utils::CreateAndConstruct(MongoDriver::s_MongoDriverExceptionRuntimeException_className, "Failed to get server description", HPHP::Variant((uint64_t) 0)); }
Object HHVM_METHOD(MongoDBDriverManager, selectServer, const Object &readPreference) { MongoDBDriverManagerData* data = Native::data<MongoDBDriverManagerData>(this_); MongoDBDriverReadPreferenceData *rp_data = Native::data<MongoDBDriverReadPreferenceData>(readPreference.get()); bson_error_t error; mongoc_server_description_t *selected_server = NULL; Object tmp; selected_server = mongoc_client_select_server(data->m_client, false, rp_data->m_read_preference, &error); if (selected_server) { tmp = hippo_mongo_driver_server_create_from_id(data->m_client, selected_server->id); mongoc_server_description_destroy(selected_server); return tmp; } else { throw MongoDriver::Utils::throwExceptionFromBsonError(&error); } }
bool HHVM_METHOD(MongoDBDriverServer, isPassive) { MongoDBDriverServerData* data = Native::data<MongoDBDriverServerData>(this_); mongoc_server_description_t *sd; if ((sd = mongoc_client_get_server_description(data->m_client, data->m_server_id))) { bson_iter_t iter; const bson_t *is_master = mongoc_server_description_ismaster(sd); bool retval = !!(bson_iter_init_find_case(&iter, is_master, "passive") && bson_iter_as_bool(&iter)); mongoc_server_description_destroy(sd); return retval; } throw MongoDriver::Utils::CreateAndConstruct(MongoDriver::s_MongoDriverExceptionRuntimeException_className, "Failed to get server description", HPHP::Variant((uint64_t) 0)); }
static void _mongoc_topology_server_dtor (void *server_, void *ctx_) { mongoc_server_description_destroy ((mongoc_server_description_t *)server_); }
static void test1 (void) { mongoc_server_description_t *description; mongoc_collection_t *collection; mongoc_read_prefs_t *read_prefs; mongoc_cursor_t *cursor; mongoc_client_t *client; mongoc_client_pool_t *pool = NULL; const bson_t *doc; bson_error_t error; bool r; ha_node_t *replica; bson_t q; int i; bson_init(&q); if (use_pool) { pool = ha_replica_set_create_client_pool(replica_set); client = mongoc_client_pool_pop (pool); } else { client = ha_replica_set_create_client(replica_set); } collection = mongoc_client_get_collection(client, "test1", "test1"); MONGOC_DEBUG("Inserting test documents."); insert_test_docs(collection); MONGOC_INFO("Test documents inserted."); read_prefs = mongoc_read_prefs_new(MONGOC_READ_SECONDARY); MONGOC_DEBUG("Sending query to a SECONDARY."); cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 100, &q, NULL, read_prefs); BSON_ASSERT(cursor); BSON_ASSERT(!cursor->server_id); /* * Send OP_QUERY to server and get first document back. */ MONGOC_INFO("Sending OP_QUERY."); r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(r); BSON_ASSERT(cursor->server_id); BSON_ASSERT(cursor->sent); BSON_ASSERT(!cursor->done); BSON_ASSERT(cursor->rpc.reply.n_returned == 100); BSON_ASSERT(!cursor->end_of_event); /* * Make sure we queried a secondary. */ description = mongoc_topology_server_by_id(client->topology, cursor->server_id, &error); ASSERT_OR_PRINT (description, error); BSON_ASSERT (description->type != MONGOC_SERVER_RS_PRIMARY); mongoc_server_description_destroy(description); /* * Exhaust the items in our first OP_REPLY. */ MONGOC_DEBUG("Exhausting OP_REPLY."); for (i = 0; i < 98; i++) { r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(r); BSON_ASSERT(cursor->server_id); BSON_ASSERT(!cursor->done); BSON_ASSERT(!cursor->end_of_event); } /* * Finish off the last item in this OP_REPLY. */ MONGOC_INFO("Fetcing last doc from OP_REPLY."); r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(r); BSON_ASSERT(cursor->server_id); BSON_ASSERT(cursor->sent); BSON_ASSERT(!cursor->done); BSON_ASSERT(!cursor->end_of_event); /* * Determine which node we queried by using the server_id to * get the cluster information. */ BSON_ASSERT(cursor->server_id); replica = get_replica(client, cursor->server_id); /* * Kill the node we are communicating with. */ MONGOC_INFO("Killing replicaSet node to synthesize failure."); ha_node_kill(replica); /* * Try to fetch the next result set, expect failure. */ MONGOC_DEBUG("Checking for expected failure."); r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(!r); r = mongoc_cursor_error(cursor, &error); BSON_ASSERT(r); MONGOC_WARNING("%s", error.message); mongoc_cursor_destroy(cursor); mongoc_read_prefs_destroy(read_prefs); mongoc_collection_destroy(collection); if (use_pool) { mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); } else { mongoc_client_destroy(client); } bson_destroy(&q); ha_node_restart(replica); }
bool _mongoc_client_command_simple_with_hint (mongoc_client_t *client, const char *db_name, const bson_t *command, const mongoc_read_prefs_t *read_prefs, bool is_write_command, bson_t *reply, uint32_t hint, bson_error_t *error) { mongoc_cluster_t *cluster; mongoc_stream_t *stream; mongoc_server_description_t *sd; mongoc_ss_optype_t optype; bool reply_initialized = false; bool ret = false; BSON_ASSERT (client); BSON_ASSERT (db_name); BSON_ASSERT (command); cluster = &client->cluster; if (!hint) { optype = is_write_command ? MONGOC_SS_WRITE : MONGOC_SS_READ; sd = mongoc_cluster_select_by_optype (cluster, optype, read_prefs, error); if (!sd) { GOTO (done); } hint = sd->id; mongoc_server_description_destroy (sd); } stream = mongoc_cluster_fetch_stream (cluster, hint, true /* reconnect_ok */, error); if (!stream) { GOTO (done); } ret = mongoc_cluster_run_command_with_read_preference (cluster, stream, db_name, command, hint, read_prefs, is_write_command, reply, error); reply_initialized = true; done: if (!reply_initialized && reply) { bson_init (reply); } return ret; }
/*--------------------------------------------------------------------------- * * _make_cursor -- * * Construct and send the aggregate command and create the resulting * cursor. On error, stream->cursor remains NULL, otherwise it is * created and must be destroyed. * * Return: * False on error and sets stream->err. * *-------------------------------------------------------------------------- */ static bool _make_cursor (mongoc_change_stream_t *stream) { mongoc_client_session_t *cs = NULL; bson_t command_opts; bson_t command; /* { aggregate: "coll", pipeline: [], ... } */ bson_t reply; bson_t getmore_opts = BSON_INITIALIZER; bson_iter_t iter; mongoc_server_description_t *sd; uint32_t server_id; int32_t max_wire_version = -1; BSON_ASSERT (stream); BSON_ASSERT (!stream->cursor); _make_command (stream, &command); bson_copy_to (&(stream->opts.extra), &command_opts); sd = mongoc_client_select_server ( stream->client, false /* for_writes */, stream->read_prefs, &stream->err); if (!sd) { goto cleanup; } server_id = mongoc_server_description_id (sd); bson_append_int32 (&command_opts, "serverId", 8, server_id); bson_append_int32 (&getmore_opts, "serverId", 8, server_id); max_wire_version = sd->max_wire_version; mongoc_server_description_destroy (sd); if (bson_iter_init_find (&iter, &command_opts, "sessionId")) { if (!_mongoc_client_session_from_iter ( stream->client, &iter, &cs, &stream->err)) { goto cleanup; } } else if (stream->implicit_session) { /* If an implicit session was created before, and this cursor is now * being recreated after resuming, then use the same session as before. */ cs = stream->implicit_session; if (!mongoc_client_session_append (cs, &command_opts, &stream->err)) { goto cleanup; } } else { /* Create an implicit session. This session lsid must be the same for the * agg command and the subsequent getMores. Thus, this implicit session is * passed as if it were an explicit session to * collection_read_command_with_opts and cursor_new_from_reply, but it is * still implicit and its lifetime is owned by this change_stream_t. */ mongoc_session_opt_t *session_opts; session_opts = mongoc_session_opts_new (); mongoc_session_opts_set_causal_consistency (session_opts, false); /* returns NULL if sessions aren't supported. ignore errors. */ cs = mongoc_client_start_session (stream->client, session_opts, NULL); stream->implicit_session = cs; mongoc_session_opts_destroy (session_opts); if (cs && !mongoc_client_session_append (cs, &command_opts, &stream->err)) { goto cleanup; } } if (cs && !mongoc_client_session_append (cs, &getmore_opts, &stream->err)) { goto cleanup; } if (stream->read_concern && !bson_has_field (&command_opts, "readConcern")) { mongoc_read_concern_append (stream->read_concern, &command_opts); } /* even though serverId has already been set, still pass the read prefs. * they are necessary for OP_MSG if sending to a secondary. */ if (!mongoc_client_read_command_with_opts (stream->client, stream->db, &command, stream->read_prefs, &command_opts, &reply, &stream->err)) { bson_destroy (&stream->err_doc); bson_copy_to (&reply, &stream->err_doc); bson_destroy (&reply); goto cleanup; } bson_append_bool ( &getmore_opts, MONGOC_CURSOR_TAILABLE, MONGOC_CURSOR_TAILABLE_LEN, true); bson_append_bool (&getmore_opts, MONGOC_CURSOR_AWAIT_DATA, MONGOC_CURSOR_AWAIT_DATA_LEN, true); /* maxTimeMS is only appended to getMores if these are set in cursor opts. */ if (stream->max_await_time_ms > 0) { bson_append_int64 (&getmore_opts, MONGOC_CURSOR_MAX_AWAIT_TIME_MS, MONGOC_CURSOR_MAX_AWAIT_TIME_MS_LEN, stream->max_await_time_ms); } if (stream->batch_size > 0) { bson_append_int32 (&getmore_opts, MONGOC_CURSOR_BATCH_SIZE, MONGOC_CURSOR_BATCH_SIZE_LEN, stream->batch_size); } /* Change streams spec: "If neither startAtOperationTime nor resumeAfter are * specified, and the max wire version is >= 7, and the initial aggregate * command does not return a resumeToken (indicating no results), the * ChangeStream MUST save the operationTime from the initial aggregate * command when it returns." */ if (bson_empty (&stream->resume_token) && _mongoc_timestamp_empty (&stream->operation_time) && max_wire_version >= 7 && bson_iter_init_find (&iter, &reply, "operationTime")) { _mongoc_timestamp_set_from_bson (&stream->operation_time, &iter); } /* steals reply. */ stream->cursor = mongoc_cursor_new_from_command_reply_with_opts ( stream->client, &reply, &getmore_opts); cleanup: bson_destroy (&command); bson_destroy (&command_opts); bson_destroy (&getmore_opts); return stream->err.code == 0; }
/* Construct and send the aggregate command and create the resulting cursor. * Returns false on error, and sets stream->err. On error, stream->cursor * remains NULL, otherwise it is created and must be destroyed. */ static bool _make_cursor (mongoc_change_stream_t *stream) { mongoc_client_session_t *cs = NULL; bson_t command_opts; bson_t command; /* { aggregate: "coll", pipeline: [], ... } */ bson_t reply; bson_iter_t iter; mongoc_server_description_t *sd; uint32_t server_id; BSON_ASSERT (stream); BSON_ASSERT (!stream->cursor); _make_command (stream, &command); bson_copy_to (&stream->opts, &command_opts); sd = mongoc_client_select_server (stream->coll->client, false /* for_writes */, stream->coll->read_prefs, &stream->err); if (!sd) { goto cleanup; } server_id = mongoc_server_description_id (sd); bson_append_int32 (&command_opts, "serverId", 8, server_id); mongoc_server_description_destroy (sd); if (bson_iter_init_find (&iter, &command_opts, "sessionId")) { if (!_mongoc_client_session_from_iter ( stream->coll->client, &iter, &cs, &stream->err)) { goto cleanup; } } else if (stream->implicit_session) { /* If an implicit session was created before, and this cursor is now * being recreated after resuming, then use the same session as before. */ cs = stream->implicit_session; if (!mongoc_client_session_append (cs, &command_opts, &stream->err)) { goto cleanup; } } else { /* Create an implicit session. This session lsid must be the same for the * agg command and the subsequent getMores. Thus, this implicit session is * passed as if it were an explicit session to * collection_read_command_with_opts and cursor_new_from_reply, but it is * still implicit and its lifetime is owned by this change_stream_t. */ mongoc_session_opt_t *session_opts; session_opts = mongoc_session_opts_new (); mongoc_session_opts_set_causal_consistency (session_opts, false); /* returns NULL if sessions aren't supported. ignore errors. */ cs = mongoc_client_start_session (stream->coll->client, session_opts, NULL); stream->implicit_session = cs; mongoc_session_opts_destroy (session_opts); if (cs && !mongoc_client_session_append (cs, &command_opts, &stream->err)) { goto cleanup; } } /* use inherited read preference and read concern of the collection */ if (!mongoc_collection_read_command_with_opts ( stream->coll, &command, NULL, &command_opts, &reply, &stream->err)) { bson_destroy (&stream->err_doc); bson_copy_to (&reply, &stream->err_doc); bson_destroy (&reply); goto cleanup; } stream->cursor = mongoc_cursor_new_from_command_reply ( stream->coll->client, &reply, server_id); /* steals reply */ if (cs) { stream->cursor->client_session = cs; stream->cursor->explicit_session = true; } /* maxTimeMS is only appended to getMores if these are set in cursor opts */ bson_append_bool (&stream->cursor->opts, MONGOC_CURSOR_TAILABLE, MONGOC_CURSOR_TAILABLE_LEN, true); bson_append_bool (&stream->cursor->opts, MONGOC_CURSOR_AWAIT_DATA, MONGOC_CURSOR_AWAIT_DATA_LEN, true); if (stream->max_await_time_ms > 0) { BSON_ASSERT ( _mongoc_cursor_set_opt_int64 (stream->cursor, MONGOC_CURSOR_MAX_AWAIT_TIME_MS, stream->max_await_time_ms)); } if (stream->batch_size > 0) { mongoc_cursor_set_batch_size (stream->cursor, (uint32_t) stream->batch_size); } cleanup: bson_destroy (&command); bson_destroy (&command_opts); return stream->err.code == 0; }