/* ****************************************************************************
*
* 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;
}
Exemple #2
0
void _execute_update_stats(scoped_ptr<ScopedDbConnection> const& scoped_conn, stats_update_t const& params) {
    char ih_hex[41];
    to_hex((char const*)&params.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
}
Exemple #3
0
    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);
    }
Exemple #4
0
    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);
    }
Exemple #5
0
    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);
}
Exemple #7
0
    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)));
        }
    }
Exemple #8
0
    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),
                                                &notificationDone,
                                                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;
}