/* **************************************************************************** * * collectionUpdate - */ bool collectionUpdate ( const std::string& col, const BSONObj& q, const BSONObj& doc, bool upsert, std::string* err ) { TIME_STAT_MONGO_WRITE_WAIT_START(); DBClientBase* connection = getMongoConnection(); if (connection == NULL) { TIME_STAT_MONGO_WRITE_WAIT_STOP(); LM_E(("Fatal Error (null DB connection)")); *err = "null DB connection"; return false; } LM_T(LmtMongo, ("update() in '%s' collection: query='%s' doc='%s', upsert=%s", col.c_str(), q.toString().c_str(), doc.toString().c_str(), FT(upsert))); try { connection->update(col.c_str(), q, doc, upsert); releaseMongoConnection(connection); TIME_STAT_MONGO_WRITE_WAIT_STOP(); LM_I(("Database Operation Successful (update: <%s, %s>)", q.toString().c_str(), doc.toString().c_str())); } catch (const std::exception& e) { releaseMongoConnection(connection); TIME_STAT_MONGO_WRITE_WAIT_STOP(); std::string msg = std::string("collection: ") + col.c_str() + " - update(): <" + q.toString() + "," + doc.toString() + ">" + " - exception: " + e.what(); *err = "Database Error (" + msg + ")"; LM_E((err->c_str())); return false; } catch (...) { releaseMongoConnection(connection); TIME_STAT_MONGO_WRITE_WAIT_STOP(); std::string msg = std::string("collection: ") + col.c_str() + " - update(): <" + q.toString() + "," + doc.toString() + ">" + " - exception: generic"; *err = "Database Error (" + msg + ")"; LM_E((err->c_str())); return false; } return true; }
void _execute_update_stats(scoped_ptr<ScopedDbConnection> const& scoped_conn, stats_update_t const& params) { char ih_hex[41]; to_hex((char const*)¶ms.info_hash[0], sha1_hash::size, ih_hex); DBClientBase* conn = scoped_conn->get(); if (conn->isFailed()) { log_util::error() << "mongodb connection failed trying to update stats (torrent: " << ih_hex << ", inc_completed: " << params.increment_completed << ")" << endl; } else { #ifdef _DEBUG log_util::debug() << "torrentdb::_execute_update_stats: Running mongodb update query (" << ih_hex << ")" << endl; #endif mongo::Date_t now = terasaur::date::get_now_mongo(); mongo::Query query = QUERY("info_hash" << ih_hex); BSONObj update_bson; if (params.increment_completed) { #ifdef _DEBUG log_util::debug() << "torrentdb::_execute_update_stats: increment completed true" << endl; #endif update_bson = BSON("$set" << BSON("seeds" << params.seeds << "peers" << params.peers << "updated" << now) << "$inc" << BSON( "completed" << 1)); } else { #ifdef _DEBUG log_util::debug() << "torrentdb::_execute_update_stats: increment completed false" << endl; #endif update_bson = BSON("$set" << BSON("seeds" << params.seeds << "peers" << params.peers << "updated" << now)); } #ifdef _DEBUG log_util::debug() << "torrentdb::_execute_update_stats: query: " << query << endl; log_util::debug() << "torrentdb::_execute_update_stats: update bson: " << update_bson << endl; log_util::debug() << "torrentdb::_execute_update_stats: calling mongodb update()" << endl; #endif conn->update(_param_map["torrentdb_ns"], query, update_bson); #ifdef _DEBUG log_util::debug() << "torrentdb::_execute_update_stats: calling mongodb getLastError()" << endl; #endif string err = conn->getLastError(); bool success = err.empty(); if (success == false) { log_util::error() << "torrentdb::_execute_update_stats: mongodb update returned error (" << err << ")" << endl; } #ifdef _DEBUG else { log_util::debug() << "torrentdb::_execute_update_stats: mongodb update successful" << endl; } #endif } #ifdef _DEBUG log_util::debug() << "torrentdb::_execute_update_stats: returning" << endl; #endif }
void Balancer::_ping( DBClientBase& conn ){ WriteConcern w = conn.getWriteConcern(); conn.setWriteConcern( W_NONE ); conn.update( ShardNS::mongos , BSON( "_id" << _myid ) , BSON( "$set" << BSON( "ping" << DATENOW << "up" << (int)(time(0)-_started) ) ) , true ); conn.setWriteConcern( w); }
void Balancer::_ping( DBClientBase& conn, bool waiting ) { WriteConcern w = conn.getWriteConcern(); conn.setWriteConcern( W_NONE ); conn.update( ConfigNS::mongos , BSON( MongosFields::name(_myid) ) , BSON( "$set" << BSON( MongosFields::ping(jsTime()) << MongosFields::up((int)(time(0)-_started)) << MongosFields::waiting(waiting) ) ) , true ); conn.setWriteConcern( w); }
bool Balancer::_shouldIBalance( DBClientBase& conn ){ BSONObj x = conn.findOne( ShardNS::settings , BSON( "_id" << "balancer" ) ); log(2) << "balancer: " << x << endl; if ( ! x.isEmpty() ){ if ( x["who"].String() == _myid ){ log(2) << "balancer: i'm the current balancer" << endl; return true; } BSONObj other = conn.findOne( ShardNS::mongos , x["who"].wrap( "_id" ) ); massert( 13125 , (string)"can't find mongos: " + x["who"].String() , ! other.isEmpty() ); int secsSincePing = (int)(( jsTime() - other["ping"].Date() ) / 1000 ); log(2) << "current balancer is: " << other << " ping delay(secs): " << secsSincePing << endl; if ( secsSincePing < ( 60 * 10 ) ){ return false; } log() << "balancer: going to take over" << endl; // we want to take over, so fall through to below } // Taking over means replacing 'who' with this balancer's address. Note that // to avoid any races, we use a compare-and-set strategy relying on the // incarnation of the previous balancer (the key 'x'). OID incarnation; incarnation.init(); BSONObjBuilder updateQuery; updateQuery.append( "_id" , "balancer" ); if ( x["x"].type() ) updateQuery.append( x["x"] ); else updateQuery.append( "x" , BSON( "$exists" << false ) ); conn.update( ShardNS::settings , updateQuery.obj() , BSON( "$set" << BSON( "who" << _myid << "x" << incarnation ) ) , true ); // If another balancer beats this one to the punch, the following query will see // the incarnation for that other guy. x = conn.findOne( ShardNS::settings , BSON( "_id" << "balancer" ) ); log() << "balancer: after update: " << x << endl; return _myid == x["who"].String() && incarnation == x["x"].OID(); }
/* **************************************************************************** * * mongoSetFwdRegId - */ void mongoSetFwdRegId(const std::string& regId, const std::string& fwdRegId, const std::string& tenant) { DBClientBase* connection = NULL; bool reqSemTaken = false; reqSemTake(__FUNCTION__, "Mongo Set Forward RegId", SemWriteOp, &reqSemTaken); LM_T(LmtMongo, ("Mongo Set Forward RegId")); try { BSONObj updateQuery = BSON("$set" << BSON(REG_FWS_REGID << fwdRegId)); LM_T(LmtMongo, ("update() in '%s' collection doc _id '%s': %s", getRegistrationsCollectionName(tenant).c_str(), regId.c_str(), updateQuery.toString().c_str())); connection = getMongoConnection(); connection->update(getRegistrationsCollectionName(tenant).c_str(), BSON("_id" << OID(regId)), updateQuery); releaseMongoConnection(connection); LM_I(("Database Operation Successful (update _id: %s, query: %s)", regId.c_str(), updateQuery.toString().c_str())); } catch (const DBException &e) { // // FIXME: probably we can do something apart of printing the error, but currently // we haven't a use case for that // releaseMongoConnection(connection); LM_E(("Database Error ('update tenant=%s, id=%s', '%s')", tenant.c_str(), regId.c_str(), e.what())); } catch (...) { // // FIXME: probably we can do something apart of printing the error, but currently // we haven't a use case for that // releaseMongoConnection(connection); LM_E(("Database Error ('update tenant=%s, id=%s', '%s')", tenant.c_str(), regId.c_str(), "generic exception")); } reqSemGive(__FUNCTION__, "Mongo Set Forward RegId", reqSemTaken); }
MongoDatabase(ConfigNode dbeconfig, doid_t min_id, doid_t max_id) : DatabaseBackend(dbeconfig, min_id, max_id), m_shutdown(false), m_monotonic_exhausted(false) { stringstream log_name; log_name << "Database-MongoDB" << "(Range: [" << min_id << ", " << max_id << "])"; m_log = new LogCategory("mongodb", log_name.str()); // Init connection. string error; m_connection_string = ConnectionString::parse(db_server.get_rval(m_config), error); if(!m_connection_string.isValid()) { m_log->fatal() << "Could not parse connection string: " << error << endl; exit(1); } // Init the collection/database names: m_db = m_connection_string.getDatabase(); // NOTE: If this line won't compile, install mongo-cxx-driver's 'legacy' branch. m_obj_collection = m_db + ".astron.objects"; m_global_collection = m_db + ".astron.globals"; // Init the globals collection/document: DBClientBase *client = new_connection(); BSONObj query = BSON("_id" << "GLOBALS"); BSONObj globals = BSON("$setOnInsert" << BSON( "doid" << BSON( "monotonic" << min_id << "free" << BSONArray()) )); client->update(m_global_collection, query, globals, true); delete client; // Spawn worker threads: for(int i = 0; i < num_workers.get_rval(m_config); ++i) { m_threads.push_back(new thread(bind(&MongoDatabase::run_thread, this))); } }
JSBool mongo_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ uassert( "mongo_find needs at elast 3 args" , argc >= 3 ); uassert( "2nd param to update has to be an object" , JSVAL_IS_OBJECT( argv[1] ) ); uassert( "3rd param to update has to be an object" , JSVAL_IS_OBJECT( argv[2] ) ); DBClientBase * conn = getConnection( cx, obj ); uassert( "no connection!" , conn ); Convertor c( cx ); string ns = c.toString( argv[0] ); bool upsert = argc > 3 && c.toBoolean( argv[3] ); try { conn->update( ns , c.toObject( argv[1] ) , c.toObject( argv[2] ) , upsert ); return JS_TRUE; } catch ( ... ){ JS_ReportError( cx , "error doing update" ); return JS_FALSE; } }
/* **************************************************************************** * * mongoUpdateContextSubscription - */ HttpStatusCode mongoUpdateContextSubscription(UpdateContextSubscriptionRequest* requestP, UpdateContextSubscriptionResponse* responseP, Format inFormat, const std::string& tenant) { reqSemTake(__FUNCTION__, "ngsi10 update subscription request"); LM_T(LmtMongo, ("Update Context Subscription")); DBClientBase* connection = getMongoConnection(); /* Look for document */ BSONObj sub; try { OID id = OID(requestP->subscriptionId.get()); mongoSemTake(__FUNCTION__, "findOne in SubscribeContextCollection"); sub = connection->findOne(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << id)); mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection"); LM_I(("Database Operation Successful (findOne _id: %s)", id.toString().c_str())); } catch (const AssertionException &e) { /* This happens when OID format is wrong */ // FIXME P4: this checking should be done at the parsing stage, without progressing to // mongoBackend. For the moment we can leave this here, but we should remove it in the future // (old issue #95) mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection (mongo assertion exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo assertion exception)"); responseP->subscribeError.errorCode.fill(SccContextElementNotFound); LM_W(("Bad Input (invalid OID format)")); return SccOk; } catch (const DBException &e) { mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo db exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + e.what()); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } catch (...) { mongoSemGive(__FUNCTION__, "findOne in SubscribeContextCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo generic exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + "generic"); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } if (sub.isEmpty()) { responseP->subscribeError.errorCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (no subscriptions found)"); return SccOk; } /* We start with an empty BSONObjBuilder and process requestP for all the fields that can * be updated. I don't like too much this strategy (I would have preferred to start with * a copy of the original document, then modify as neded, but this doesn't seem to be easy * using the API provide by the Mongo C++ driver) * * FIXME: a better implementation strategy could be doing an findAndModify() query to do the * update, so detecting if the document was not found, instead of using findOne() + update() * with $set operation. One operations to MongoDb. vs two operations. */ BSONObjBuilder newSub; /* Entities, attribute list and reference are not updatable, so they are appended directly */ newSub.appendArray(CSUB_ENTITIES, sub.getField(CSUB_ENTITIES).Obj()); newSub.appendArray(CSUB_ATTRS, sub.getField(CSUB_ATTRS).Obj()); newSub.append(CSUB_REFERENCE, STR_FIELD(sub, CSUB_REFERENCE)); /* Duration update */ if (requestP->duration.isEmpty()) { newSub.append(CSUB_EXPIRATION, sub.getField(CSUB_EXPIRATION).numberLong()); } else { long long expiration = getCurrentTime() + requestP->duration.parse(); newSub.append(CSUB_EXPIRATION, expiration); LM_T(LmtMongo, ("New subscription expiration: %l", expiration)); } /* Restriction update */ // FIXME: Restrictions not implemented yet /* Throttling update */ if (!requestP->throttling.isEmpty()) { /* Throttling equal to 0 removes throttling */ long long throttling = requestP->throttling.parse(); if (throttling != 0) { newSub.append(CSUB_THROTTLING, throttling); } } else { /* The hasField check is needed due to Throttling could not be present in the original doc */ if (sub.hasField(CSUB_THROTTLING)) { newSub.append(CSUB_THROTTLING, sub.getField(CSUB_THROTTLING).numberLong()); } } /* Notify conditions */ bool notificationDone = false; if (requestP->notifyConditionVector.size() == 0) { newSub.appendArray(CSUB_CONDITIONS, sub.getField(CSUB_CONDITIONS).embeddedObject()); } else { /* Destroy any previous ONTIMEINTERVAL thread */ getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get()); /* Build conditions array (including side-effect notifications and threads creation) * In order to do so, we have to create and EntityIdVector and AttributeList from sub * document, given the processConditionVector() signature */ EntityIdVector enV = subToEntityIdVector(sub); AttributeList attrL = subToAttributeList(sub); BSONArray conds = processConditionVector(&requestP->notifyConditionVector, enV, attrL, requestP->subscriptionId.get(), C_STR_FIELD(sub, CSUB_REFERENCE), ¬ificationDone, inFormat, tenant); newSub.appendArray(CSUB_CONDITIONS, conds); /* Remove EntityIdVector and AttributeList dynamic memory */ enV.release(); attrL.release(); } int count = sub.hasField(CSUB_COUNT) ? sub.getIntField(CSUB_COUNT) : 0; /* Last notification */ if (notificationDone) { newSub.append(CSUB_LASTNOTIFICATION, getCurrentTime()); newSub.append(CSUB_COUNT, count + 1); } else { /* The hasField check is needed due to lastNotification/count could not be present in the original doc */ if (sub.hasField(CSUB_LASTNOTIFICATION)) { newSub.append(CSUB_LASTNOTIFICATION, sub.getIntField(CSUB_LASTNOTIFICATION)); } if (sub.hasField(CSUB_COUNT)) { newSub.append(CSUB_COUNT, count); } } /* Adding format to use in notifications */ newSub.append(CSUB_FORMAT, std::string(formatToString(inFormat))); /* Update document in MongoDB */ BSONObj update = newSub.obj(); try { LM_T(LmtMongo, ("update() in '%s' collection _id '%s': %s}", getSubscribeContextCollectionName(tenant).c_str(), requestP->subscriptionId.get().c_str(), update.toString().c_str())); mongoSemTake(__FUNCTION__, "update in SubscribeContextCollection"); connection->update(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << OID(requestP->subscriptionId.get())), update); mongoSemGive(__FUNCTION__, "update in SubscribeContextCollection"); LM_I(("Database Operation Successful (update _id: %s, %s)", requestP->subscriptionId.get().c_str(), update.toString().c_str())); } catch (const DBException &e) { mongoSemGive(__FUNCTION__, "update in SubscribeContextCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo db exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - update() _id: " + requestP->subscriptionId.get().c_str() + " - update() doc: " + update.toString() + " - exception: " + e.what()); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } catch (...) { mongoSemGive(__FUNCTION__, "update in SubscribeContextCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi10 update subscription request (mongo generic exception)"); responseP->subscribeError.errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() + " - update() _id: " + requestP->subscriptionId.get().c_str() + " - update() doc: " + update.toString() + " - exception: " + "generic"); LM_E(("Database Error (%s)", responseP->subscribeError.errorCode.details.c_str())); return SccOk; } /* Duration and throttling are optional parameters, they are only added in the case they * was used for update */ if (!requestP->duration.isEmpty()) { responseP->subscribeResponse.duration = requestP->duration; } if (!requestP->throttling.isEmpty()) { responseP->subscribeResponse.throttling = requestP->throttling; } responseP->subscribeResponse.subscriptionId = requestP->subscriptionId; reqSemGive(__FUNCTION__, "ngsi10 update subscription request"); return SccOk; }
/* **************************************************************************** * * processRegisterContext - * * This function has a slightly different behaviour depending on whether the id * parameter is null (new registration case) or not null (update case), in * particular: * * - In the new registration case, the _id is generated and insert() is used to * put the document in the DB. * - In the update case, the _id is set according to the argument 'id' and update() is * used to put the document in the DB. * */ HttpStatusCode processRegisterContext ( RegisterContextRequest* requestP, RegisterContextResponse* responseP, OID* id, const std::string& tenant, const std::string& servicePath, const std::string& format ) { DBClientBase* connection = NULL; /* If expiration is not present, then use a default one */ if (requestP->duration.isEmpty()) { requestP->duration.set(DEFAULT_DURATION); } /* Calculate expiration (using the current time and the duration field in the request) */ long long expiration = getCurrentTime() + requestP->duration.parse(); LM_T(LmtMongo, ("Registration expiration: %lu", expiration)); /* Create the mongoDB registration document */ BSONObjBuilder reg; OID oid; if (id == NULL) { oid.init(); } else { oid = *id; } reg.append("_id", oid); reg.append(REG_EXPIRATION, expiration); reg.append(REG_SERVICE_PATH, servicePath); reg.append(REG_FORMAT, format); /* We accumulate the subscriptions in a map. The key of the map is the string representing * subscription id */ std::map<string, TriggeredSubscription*> subsToNotify; /* This vector is used to define which entities to include in notifications */ EntityIdVector triggerEntitiesV; BSONArrayBuilder contextRegistration; for (unsigned int ix = 0; ix < requestP->contextRegistrationVector.size(); ++ix) { ContextRegistration* cr = requestP->contextRegistrationVector.get(ix); BSONArrayBuilder entities; for (unsigned int jx = 0; jx < cr->entityIdVector.size(); ++jx) { EntityId* en = cr->entityIdVector.get(jx); triggerEntitiesV.push_back(en); if (en->type == "") { entities.append(BSON(REG_ENTITY_ID << en->id)); LM_T(LmtMongo, ("Entity registration: {id: %s}", en->id.c_str())); } else { entities.append(BSON(REG_ENTITY_ID << en->id << REG_ENTITY_TYPE << en->type)); LM_T(LmtMongo, ("Entity registration: {id: %s, type: %s}", en->id.c_str(), en->type.c_str())); } } BSONArrayBuilder attrs; for (unsigned int jx = 0; jx < cr->contextRegistrationAttributeVector.size(); ++jx) { ContextRegistrationAttribute* cra = cr->contextRegistrationAttributeVector.get(jx); attrs.append(BSON(REG_ATTRS_NAME << cra->name << REG_ATTRS_TYPE << cra->type << "isDomain" << cra->isDomain)); LM_T(LmtMongo, ("Attribute registration: {name: %s, type: %s, isDomain: %s}", cra->name.c_str(), cra->type.c_str(), cra->isDomain.c_str())); for (unsigned int kx = 0; kx < requestP->contextRegistrationVector.get(ix)->contextRegistrationAttributeVector.get(jx)->metadataVector.size(); ++kx) { // FIXME: metadata not supported at the moment } } contextRegistration.append(BSON( REG_ENTITIES << entities.arr() << REG_ATTRS << attrs.arr() << REG_PROVIDING_APPLICATION << requestP->contextRegistrationVector.get(ix)->providingApplication.get()) ); LM_T(LmtMongo, ("providingApplication registration: %s", requestP->contextRegistrationVector.get(ix)->providingApplication.c_str())); std::string err; if (!processAssociations(cr->registrationMetadataVector, &err, tenant)) { responseP->errorCode.fill(SccReceiverInternalError); return SccOk; } if (!addTriggeredSubscriptions(*cr, subsToNotify, err, tenant)) { responseP->errorCode.fill(SccReceiverInternalError, err); return SccOk; } } reg.append(REG_CONTEXT_REGISTRATION, contextRegistration.arr()); BSONObj regDoc = reg.obj(); LM_T(LmtMongo, ("upsert update() in '%s' collection: '%s'", getRegistrationsCollectionName(tenant).c_str(), regDoc.toString().c_str())); try { connection = getMongoConnection(); /* Note the fourth parameter is set to "true". This means "upsert", so if the document doesn't previously * exist in the collection, it is created. Thus, this way is ok with both uses of * registerContext (either new registration or updating an existing one) */ connection->update(getRegistrationsCollectionName(tenant).c_str(), BSON("_id" << oid), regDoc, true); releaseMongoConnection(connection); LM_I(("Database Operation Successful (_id: %s)", oid.toString().c_str())); } catch (const DBException& e) { releaseMongoConnection(connection); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getRegistrationsCollectionName(tenant).c_str() + " - upsert update(): " + regDoc.toString() + " - exception: " + e.what()); LM_E(("Database Error (%s)", responseP->errorCode.reasonPhrase.c_str())); releaseTriggeredSubscriptions(subsToNotify); return SccOk; } catch (...) { releaseMongoConnection(connection); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getRegistrationsCollectionName(tenant).c_str() + " - upsert update(): " + regDoc.toString() + " - exception: " + "generic"); LM_E(("Database Error (%s)", responseP->errorCode.reasonPhrase.c_str())); releaseTriggeredSubscriptions(subsToNotify); return SccOk; } /* Send notifications for each one of the subscriptions accumulated by * previous addTriggeredSubscriptions() invocations */ std::string err; processSubscriptions(triggerEntitiesV, subsToNotify, err, tenant); /* Fill the response element */ responseP->duration = requestP->duration; responseP->registrationId.set(oid.toString()); responseP->errorCode.fill(SccOk); return SccOk; }