示例#1
0
文件: balance.cpp 项目: erickt/mongo
    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();
    }    
/* ****************************************************************************
*
* mongoUpdateCsubNewNotification_fail -
*/
TEST(mongoOntimeintervalOperations, mongoUpdateCsubNewNotification_fail)
{
    HttpStatusCode ms;

    /* Prepare mocks */
    TimerMock* timerMock = new TimerMock();
    ON_CALL(*timerMock, getCurrentTime())
            .WillByDefault(Return(1360232700));
    setTimer(timerMock);

    /* Forge the parameters */
    std::string subId = "51307b66f481db11bf869999";
    std::string err;

    /* Prepare database */
    prepareDatabase();

    /* Do operation */
    ms = mongoUpdateCsubNewNotification(subId, &err);

    /* Check results */
    EXPECT_EQ(SccOk, ms);

    /* Check that database is as expected (untouched) */
    DBClientBase* connection = getMongoConnection();
    BSONObj sub1 = connection->findOne(SUBSCRIBECONTEXT_COLL, BSON("_id" << OID("51307b66f481db11bf860001")));
    BSONObj sub2 = connection->findOne(SUBSCRIBECONTEXT_COLL, BSON("_id" << OID("51307b66f481db11bf860002")));
    EXPECT_EQ(20000000, sub1.getIntField("lastNotification"));
    EXPECT_EQ(20, sub1.getIntField("count"));
    EXPECT_EQ(30000000, sub2.getIntField("lastNotification"));
    EXPECT_EQ(30, sub2.getIntField("count"));

    /* Release mocks */
    delete timerMock;
}
/* ****************************************************************************
*
* collectionFindOne -
*/
extern bool collectionFindOne
(
  const std::string&  col,
  const BSONObj&      q,
  BSONObj*            doc,
  std::string*        err
)
{
  TIME_STAT_MONGO_READ_WAIT_START();
  DBClientBase* connection = getMongoConnection();

  if (connection == NULL)
  {
    TIME_STAT_MONGO_READ_WAIT_STOP();

    LM_E(("Fatal Error (null DB connection)"));
    *err = "null DB connection";
    return false;
  }

  LM_T(LmtMongo, ("findOne() in '%s' collection: '%s'", col.c_str(), q.toString().c_str()));
  try
  {
    *doc = connection->findOne(col.c_str(), q);
    releaseMongoConnection(connection);
    TIME_STAT_MONGO_READ_WAIT_STOP();
    LM_I(("Database Operation Successful (findOne: %s)", q.toString().c_str()));
  }
  catch (const std::exception &e)
  {
    releaseMongoConnection(connection);
    TIME_STAT_MONGO_READ_WAIT_STOP();

    std::string msg = std::string("collection: ") + col.c_str() +
        " - findOne(): " + q.toString() +
        " - exception: " + e.what();
    *err = "Database Error (" + msg + ")";
    LM_E((err->c_str()));
    return false;
  }
  catch (...)
  {
    releaseMongoConnection(connection);
    TIME_STAT_MONGO_READ_WAIT_STOP();

    std::string msg = std::string("collection: ") + col.c_str() +
        " - findOne(): " + q.toString() +
        " - exception: generic";
    *err = "Database Error (" + msg + ")";
    LM_E((err->c_str()));
    return false;
  }

  return true;
}
/* ****************************************************************************
*
* mongoUpdateCsubNewNotification_dbfail -
*/
TEST(mongoOntimeintervalOperations, mongoUpdateCsubNewNotification_dbfail)
{
    HttpStatusCode ms;

    /* Prepare mock */
    const DBException e = DBException("boom!!", 33);
    DBClientConnectionMock* connectionMock = new DBClientConnectionMock();
    ON_CALL(*connectionMock, update("utest.csubs",_,_,_,_,_))
            .WillByDefault(Throw(e));

    TimerMock* timerMock = new TimerMock();
    ON_CALL(*timerMock, getCurrentTime())
            .WillByDefault(Return(1360232700));
    setTimer(timerMock);

    /* Forge the parameters */
    std::string subId = "51307b66f481db11bf860001";
    std::string err;

    /* Set MongoDB connection (prepare database first with the "actual" connection object)
     * Note that we preserve the "actual" connection for future database checking */
    prepareDatabase();
    DBClientBase* connectionDb = getMongoConnection();
    setMongoConnectionForUnitTest(connectionMock);

    /* Do operation */
    ms = mongoUpdateCsubNewNotification(subId, &err);

    /* Check results */
    EXPECT_EQ(SccReceiverInternalError, ms);
    EXPECT_EQ("Database Error (collection: utest.csubs "
              "- update(): <{ _id: ObjectId('51307b66f481db11bf860001') },{ $set: { lastNotification: 1360232700 }, $inc: { count: 1 } }> "
              "- exception: boom!!)", err);

    /* Check that database is as expected (untouched) */   

    // Sleeping a little to "give mongod time to process its input".
    usleep(1000);

    BSONObj sub1 = connectionDb->findOne(SUBSCRIBECONTEXT_COLL, BSON("_id" << OID("51307b66f481db11bf860001")));
    BSONObj sub2 = connectionDb->findOne(SUBSCRIBECONTEXT_COLL, BSON("_id" << OID("51307b66f481db11bf860002")));
    EXPECT_EQ(20000000, sub1.getIntField("lastNotification"));
    EXPECT_EQ(20, sub1.getIntField("count"));
    EXPECT_EQ(30000000, sub2.getIntField("lastNotification"));
    EXPECT_EQ(30, sub2.getIntField("count"));

    /* Restore real DB connection */
    setMongoConnectionForUnitTest(connectionDb);

    /* Release mocks */    
    delete timerMock;
    delete connectionMock;
}
/* ****************************************************************************
*
* mongoGetFwdRegId -
*/
std::string mongoGetFwdRegId(const std::string& regId, const std::string& tenant)
{
  DBClientBase*  connection  = NULL;
  std::string    retVal      = "";
  bool           reqSemTaken = false;

  reqSemTake(__FUNCTION__, "Mongo Get Forward RegId", SemReadOp, &reqSemTaken);

  LM_T(LmtMongo, ("Mongo Get Forward RegId"));

  try
  {
    LM_T(LmtMongo, ("findOne() in '%s' collection doc _id '%s'",
                    getRegistrationsCollectionName(tenant).c_str(),
                    regId.c_str()));
    BSONObj doc;

    connection = getMongoConnection();
    doc = connection->findOne(getRegistrationsCollectionName(tenant).c_str(), BSON("_id" << OID(regId)));
    releaseMongoConnection(connection);

    LM_T(LmtMongo, ("reg doc: '%s'", doc.toString().c_str()));
    retVal = STR_FIELD(doc, REG_FWS_REGID);
    LM_I(("Database Operation Successful (findOne _id: %s)", regId.c_str()));
  }
  catch (const DBException &e)
  {
    //
    // FIXME: probably we can do something apart from printing the error, but currently
    // we haven't a use case for that
    //
    releaseMongoConnection(connection);

    LM_E(("Database Error ('findOne tenant=%s, id=%s', '%s')", tenant.c_str(), regId.c_str(), e.what()));
  }
  catch (...)
  {
    //
    // FIXME: probably we can do something apart from printing the error, but currently
    // we haven't a use case for that
    //
    releaseMongoConnection(connection);

    LM_E(("Database Error ('findOne tenant=%s, id=%s', 'generic exception')", tenant.c_str(), regId.c_str()));
  }

  reqSemGive(__FUNCTION__, "Mongo Get Forward RegId", reqSemTaken);

  return retVal;
}
/* ****************************************************************************
*
* unsubscribe -
*/
TEST(mongoUnsubscribeContext, unsubscribe)
{
    HttpStatusCode             ms;
    UnsubscribeContextRequest  req;
    UnsubscribeContextResponse res;

    /* Prepare mock */
    NotifierMock* notifierMock = new NotifierMock();
    EXPECT_CALL(*notifierMock, destroyOntimeIntervalThreads("51307b66f481db11bf860001"))
            .Times(1);
    EXPECT_CALL(*notifierMock, sendNotifyContextRequest(_,_,_,_))
            .Times(0);
    EXPECT_CALL(*notifierMock, createIntervalThread(_,_,_))
            .Times(0);
    setNotifier(notifierMock);

    /* Forge the request (from "inside" to "outside") */
    req.subscriptionId.set("51307b66f481db11bf860001");

    /* Prepare database */
    prepareDatabase();

    /* Invoke the function in mongoBackend library */
    ms = mongoUnsubscribeContext(&req, &res);

    /* Check response is as expected */
    EXPECT_EQ(SccOk, ms);
    EXPECT_EQ("51307b66f481db11bf860001", res.subscriptionId.get());
    EXPECT_EQ(SccOk, res.statusCode.code);
    EXPECT_EQ("OK", res.statusCode.reasonPhrase);
    EXPECT_EQ(0, res.statusCode.details.size());

    /* Check database (one document, but not the deleted one) */
    DBClientBase* connection = getMongoConnection();
    ASSERT_EQ(1, connection->count(SUBSCRIBECONTEXT_COLL, BSONObj()));
    BSONObj sub = connection->findOne(SUBSCRIBECONTEXT_COLL, BSON("_id" << OID("51307b66f481db11bf860002")));
    EXPECT_EQ("51307b66f481db11bf860002", sub.getField("_id").OID().str());

    /* Release connection */
    mongoDisconnect();

    /* Release mock */
    delete notifierMock;
}
/* ****************************************************************************
*
* mongoUnsubscribeContext - 
*/
HttpStatusCode mongoUnsubscribeContext(UnsubscribeContextRequest* requestP, UnsubscribeContextResponse* responseP, const std::string& tenant)
{
    bool           reqSemTaken;
    BSONObj        sub;
    DBClientBase*  connection = NULL;

    reqSemTake(__FUNCTION__, "ngsi10 unsubscribe request", SemWriteOp, &reqSemTaken);

    LM_T(LmtMongo, ("Unsubscribe Context"));

    /* No matter if success or failure, the subscriptionId in the response is always the one
     * in the request */
    responseP->subscriptionId = requestP->subscriptionId;

    if (responseP->subscriptionId.get() == "")
    {
        responseP->statusCode.fill(SccContextElementNotFound);
        LM_W(("Bad Input (no subscriptionId)"));
        return SccOk;
    }

    LM_T(LmtMongo, ("findOne() in '%s' collection _id '%s'}", getSubscribeContextCollectionName(tenant).c_str(),
                    requestP->subscriptionId.get().c_str()));

    /* Look for document */
    connection = getMongoConnection();
    try
    {
        OID id = OID(requestP->subscriptionId.get());
        sub = connection->findOne(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << id));
        releaseMongoConnection(connection);
        LM_I(("Database Operation Successful (findOne _id: %s)", id.toString().c_str()));
    }
    catch (const AssertionException &e)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo assertion exception)", reqSemTaken);

        //
        // This happens when OID format is wrong
        // FIXME: this checking should be done at parsing stage, without progressing to
        // mongoBackend. By the moment we can live this here, but we should remove in the future
        // (old issue #95)
        //
        responseP->statusCode.fill(SccContextElementNotFound);
        LM_W(("Bad Input (invalid OID format)"));
        return SccOk;
    }
    catch (const DBException &e)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - findOne() _id: " + requestP->subscriptionId.get() +
                                   " - exception: " + e.what());
        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));

        return SccOk;
    }
    catch (...)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo generic exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - findOne() _id: " + requestP->subscriptionId.get() +
                                   " - exception: " + "generic");
        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));

        return SccOk;
    }

    if (sub.isEmpty())
    {
       reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (no subscriptions found)", reqSemTaken);

       responseP->statusCode.fill(SccContextElementNotFound, std::string("subscriptionId: /") + requestP->subscriptionId.get() + "/");
       return SccOk;
    }

    /* Remove document in MongoDB */
    // FIXME: I will prefer to do the find and remove in a single operation. Is the some similar
    // to findAndModify for this?
    LM_T(LmtMongo, ("remove() in '%s' collection _id '%s'}", getSubscribeContextCollectionName(tenant).c_str(),
                    requestP->subscriptionId.get().c_str()));

    connection = getMongoConnection();    
    try
    {
        connection->remove(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << OID(requestP->subscriptionId.get())));
        releaseMongoConnection(connection);
        
        LM_I(("Database Operation Successful (remove _id: %s)", requestP->subscriptionId.get().c_str()));
    }
    catch (const DBException &e)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - remove() _id: " + requestP->subscriptionId.get().c_str() +
                                   " - exception: " + e.what());
        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));
        return SccOk;
    }
    catch (...)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo generic exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - remove() _id: " + requestP->subscriptionId.get().c_str() +
                                   " - exception: " + "generic");

        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));
        return SccOk;
    }

    /* Destroy any previous ONTIMEINTERVAL thread */
    getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get());

    responseP->statusCode.fill(SccOk);
    reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request", reqSemTaken);

    //
    // Removing subscription from cache
    //
    subCache->remove(tenant, "", requestP->subscriptionId.get());

    return SccOk;
}
/* ****************************************************************************
*
* 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;
}
/* ****************************************************************************
*
* createEntity -
*/
TEST(mongoNotifyContextRequest, createEntity)
{
    HttpStatusCode         ms;
    NotifyContextRequest   req;
    NotifyContextResponse  res;

    /* Prepare database */
    prepareDatabase();

    /* Forge the request */
    ContextElementResponse cer;
    req.subscriptionId.set("51307b66f481db11bf860001");
    req.originator.set("localhost");
    cer.contextElement.entityId.fill("E10", "T10", "false");
    ContextAttribute ca("A1", "TA1", "new_val");
    cer.contextElement.contextAttributeVector.push_back(&ca);
    cer.statusCode.fill(SccOk);
    req.contextElementResponseVector.push_back(&cer);

    /* Prepare mock */
    TimerMock* timerMock = new TimerMock();
    ON_CALL(*timerMock, getCurrentTime())
            .WillByDefault(Return(1360232700));
    setTimer(timerMock);

    /* Invoke the function in mongoBackend library */
    ms = mongoNotifyContext(&req, &res);

    /* Check response is as expected */
    EXPECT_EQ(SccOk, ms);

    EXPECT_EQ(SccOk, res.responseCode.code);
    EXPECT_EQ("OK", res.responseCode.reasonPhrase);
    EXPECT_EQ(0, res.responseCode.details.size());

    /* Check that every involved collection at MongoDB is as expected */
    /* Note we are using EXPECT_STREQ() for some cases, as Mongo Driver returns const char*, not string
     * objects (see http://code.google.com/p/googletest/wiki/Primer#String_Comparison) */

    DBClientBase* connection = getMongoConnection();

    /* entities collection */
    BSONObj ent;
    std::vector<BSONElement> attrs;
    ASSERT_EQ(6, connection->count(ENTITIES_COLL, BSONObj()));

    ent = connection->findOne(ENTITIES_COLL, BSON("_id.id" << "E1" << "_id.type" << "T1"));
    EXPECT_STREQ("E1", C_STR_FIELD(ent.getObjectField("_id"), "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent.getObjectField("_id"), "type"));
    EXPECT_FALSE(ent.hasField("modDate"));
    attrs = ent.getField("attrs").Array();
    ASSERT_EQ(2, attrs.size());
    BSONObj a1 = getAttr(attrs, "A1", "TA1");
    BSONObj a2 = getAttr(attrs, "A2", "TA2");
    EXPECT_STREQ("A1", C_STR_FIELD(a1, "name"));
    EXPECT_STREQ("TA1",C_STR_FIELD(a1, "type"));
    EXPECT_STREQ("val1", C_STR_FIELD(a1, "value"));
    EXPECT_FALSE(a1.hasField("modDate"));
    EXPECT_STREQ("A2", C_STR_FIELD(a2, "name"));
    EXPECT_STREQ("TA2", C_STR_FIELD(a2, "type"));
    EXPECT_FALSE(a2.hasField("value"));
    EXPECT_FALSE(a2.hasField("modDate"));

    ent = connection->findOne(ENTITIES_COLL, BSON("_id.id" << "E2" << "_id.type" << "T2"));
    EXPECT_STREQ("E2", C_STR_FIELD(ent.getObjectField("_id"), "id"));
    EXPECT_STREQ("T2", C_STR_FIELD(ent.getObjectField("_id"), "type"));
    EXPECT_FALSE(ent.hasField("modDate"));
    attrs = ent.getField("attrs").Array();
    ASSERT_EQ(2, attrs.size());
    BSONObj a3 = getAttr(attrs, "A3", "TA3");
    BSONObj a4 = getAttr(attrs, "A4", "TA4");
    EXPECT_STREQ("A3", C_STR_FIELD(a3, "name"));
    EXPECT_STREQ("TA3", C_STR_FIELD(a3, "type"));
    EXPECT_STREQ("val3", C_STR_FIELD(a3, "value"));
    EXPECT_FALSE(a3.hasField("modDate"));
    EXPECT_STREQ("A4", C_STR_FIELD(a4, "name"));
    EXPECT_STREQ("TA4", C_STR_FIELD(a4, "type"));
    EXPECT_FALSE(a4.hasField("value"));
    EXPECT_FALSE(a4.hasField("modDate"));

    ent = connection->findOne(ENTITIES_COLL, BSON("_id.id" << "E3" << "_id.type" << "T3"));
    EXPECT_STREQ("E3", C_STR_FIELD(ent.getObjectField("_id"), "id"));
    EXPECT_STREQ("T3", C_STR_FIELD(ent.getObjectField("_id"), "type"));
    EXPECT_FALSE(ent.hasField("modDate"));
    attrs = ent.getField("attrs").Array();
    ASSERT_EQ(2, attrs.size());
    BSONObj a5 = getAttr(attrs, "A5", "TA5");
    BSONObj a6 = getAttr(attrs, "A6", "TA6");
    EXPECT_STREQ("A5", C_STR_FIELD(a5, "name"));
    EXPECT_STREQ("TA5", C_STR_FIELD(a5, "type"));
    EXPECT_STREQ("val5", C_STR_FIELD(a5, "value"));
    EXPECT_FALSE(a5.hasField("modDate"));
    EXPECT_STREQ("A6", C_STR_FIELD(a6, "name"));
    EXPECT_STREQ("TA6", C_STR_FIELD(a6, "type"));
    EXPECT_FALSE(a6.hasField("value"));
    EXPECT_FALSE(a6.hasField("modDate"));

    ent = connection->findOne(ENTITIES_COLL, BSON("_id.id" << "E1" << "_id.type" << "T1bis"));
    EXPECT_STREQ("E1", C_STR_FIELD(ent.getObjectField("_id"), "id"));
    EXPECT_STREQ("T1bis", C_STR_FIELD(ent.getObjectField("_id"), "type"));
    EXPECT_FALSE(ent.hasField("modDate"));
    attrs = ent.getField("attrs").Array();
    ASSERT_EQ(1, attrs.size());
    a1 = getAttr(attrs, "A1", "TA1");
    EXPECT_STREQ("A1", C_STR_FIELD(a1, "name"));
    EXPECT_STREQ("TA1",C_STR_FIELD(a1, "type"));
    EXPECT_STREQ("val1bis2", C_STR_FIELD(a1, "value"));
    EXPECT_FALSE(a1.hasField("modDate"));

    ent = connection->findOne(ENTITIES_COLL, BSON("_id.id" << "E10" << "_id.type" << "T10"));
    EXPECT_STREQ("E10", C_STR_FIELD(ent.getObjectField("_id"), "id"));
    EXPECT_STREQ("T10", C_STR_FIELD(ent.getObjectField("_id"), "type"));
    EXPECT_TRUE(ent.hasField("creDate"));
    EXPECT_TRUE(ent.hasField("modDate"));
    attrs = ent.getField("attrs").Array();
    ASSERT_EQ(1, attrs.size());
    a1 = getAttr(attrs, "A1", "TA1");
    EXPECT_STREQ("A1", C_STR_FIELD(a1, "name"));
    EXPECT_STREQ("TA1",C_STR_FIELD(a1, "type"));
    EXPECT_STREQ("new_val", C_STR_FIELD(a1, "value"));
    EXPECT_TRUE(a1.hasField("creDate"));
    EXPECT_TRUE(a1.hasField("modDate"));

    /* Note "_id.type: {$exists: false}" is a way for querying for entities without type */
    ent = connection->findOne(ENTITIES_COLL, BSON("_id.id" << "E1" << "_id.type" << BSON("$exists" << false)));
    EXPECT_STREQ("E1", C_STR_FIELD(ent.getObjectField("_id"), "id"));
    EXPECT_FALSE(ent.getObjectField("_id").hasField("type"));
    EXPECT_FALSE(ent.hasField("modDate"));
    attrs = ent.getField("attrs").Array();
    ASSERT_EQ(2, attrs.size());
    a1 = getAttr(attrs, "A1", "TA1");
    a2 = getAttr(attrs, "A2", "TA2");
    EXPECT_STREQ("A1", C_STR_FIELD(a1, "name"));
    EXPECT_STREQ("TA1",C_STR_FIELD(a1, "type"));
    EXPECT_STREQ("val1-nt", C_STR_FIELD(a1, "value"));
    EXPECT_FALSE(a1.hasField("modDate"));
    EXPECT_STREQ("A2", C_STR_FIELD(a2, "name"));
    EXPECT_STREQ("TA2", C_STR_FIELD(a2, "type"));
    EXPECT_FALSE(a2.hasField("value"));
    EXPECT_FALSE(a2.hasField("modDate"));

    /* Release connection */
    mongoDisconnect();

    /* Release mock */
    delete timerMock;

}
/* ****************************************************************************
*
* MongoDbFindOneFail -
*/
TEST(mongoRegisterContext_update, MongoDbFindOneFail)
{
    HttpStatusCode           ms;
    RegisterContextRequest   req;
    RegisterContextResponse  res;   

    utInit();

    /* Prepare mock */
    const DBException e = DBException("boom!!", 33);
    DBClientConnectionMock* connectionMock = new DBClientConnectionMock();
    ON_CALL(*connectionMock, findOne("utest.registrations",_,_,_))
            .WillByDefault(Throw(e));

    /* Forge the request (from "inside" to "outside") */
    EntityId en("E1", "T1");
    ContextRegistrationAttribute cra("A1", "TA1", "true");
    ContextRegistration cr;
    cr.entityIdVector.push_back(&en);
    cr.contextRegistrationAttributeVector.push_back(&cra);
    cr.providingApplication.set("http://newurl.com");
    req.contextRegistrationVector.push_back(&cr);
    req.registrationId.set("51307b66f481db11bf860001");
    req.duration.set("PT1M");

    /* Prepare database */
    prepareDatabase();

    /* Set MongoDB connection mock (preserving "actual" connection for later use) */
    DBClientBase* connectionDb = getMongoConnection();
    setMongoConnectionForUnitTest(connectionMock);

    /* Invoke the function in mongoBackend library */
    ms = mongoRegisterContext(&req, &res, uriParams);

    /* Check response is as expected */
    EXPECT_EQ(SccOk, ms);
    EXPECT_TRUE(res.duration.isEmpty());
    EXPECT_TRUE(res.registrationId.isEmpty());
    EXPECT_EQ(SccReceiverInternalError, res.errorCode.code);
    EXPECT_EQ("Internal Server Error", res.errorCode.reasonPhrase);
    EXPECT_EQ("Database Error (collection: utest.registrations "
              "- findOne(): { _id: ObjectId('51307b66f481db11bf860001'), servicePath: \"/\" } "
              "- exception: boom!!)", res.errorCode.details);

    /* Restore real DB connection */
    setMongoConnectionForUnitTest(connectionDb);

    /* Release mock */    
    delete connectionMock;

    /* Check that every involved collection at MongoDB is as expected */
    /* Note we are using EXPECT_STREQ() for some cases, as Mongo Driver returns const char*, not string
     * objects (see http://code.google.com/p/googletest/wiki/Primer#String_Comparison) */   

    /* registrations collection: */
    ASSERT_EQ(2, connectionDb->count(REGISTRATIONS_COLL, BSONObj()));

    BSONObj reg, contextRegistration, ent0, attr0, attr1, attr2;
    std::vector<BSONElement> contextRegistrationV, entities, attrs;

    /* reg #1 (untouched) */
    reg = connectionDb->findOne(REGISTRATIONS_COLL, BSON("_id" << OID("51307b66f481db11bf860001")));
    EXPECT_EQ("51307b66f481db11bf860001", reg.getField("_id").OID().toString());
    EXPECT_EQ(10000000, reg.getIntField("expiration"));

    contextRegistrationV = reg.getField("contextRegistration").Array();
    ASSERT_EQ(1, contextRegistrationV.size());
    contextRegistration = contextRegistrationV[0].embeddedObject();

    EXPECT_STREQ("http://cr1.com", C_STR_FIELD(contextRegistration, "providingApplication"));
    entities = contextRegistration.getField("entities").Array();
    ASSERT_EQ(1, entities.size());
    ent0 = entities[0].embeddedObject();
    EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));

    attrs = contextRegistration.getField("attrs").Array();
    ASSERT_EQ(3, attrs.size());
    attr0 = attrs[0].embeddedObject();
    attr1 = attrs[1].embeddedObject();
    attr2 = attrs[2].embeddedObject();
    EXPECT_STREQ("A1", C_STR_FIELD(attr0, "name"));
    EXPECT_STREQ("TA1", C_STR_FIELD(attr0, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr0, "isDomain"));
    EXPECT_STREQ("A2", C_STR_FIELD(attr1, "name"));
    EXPECT_STREQ("TA2", C_STR_FIELD(attr1, "type"));
    EXPECT_STREQ("false", C_STR_FIELD(attr1, "isDomain"));
    EXPECT_STREQ("A3", C_STR_FIELD(attr2, "name"));
    EXPECT_STREQ("TA3", C_STR_FIELD(attr2, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr2, "isDomain"));

    /* reg #2 (untouched) */
    reg = connectionDb->findOne(REGISTRATIONS_COLL, BSON("_id" << OID("51307b66f481db11bf860002")));
    EXPECT_EQ("51307b66f481db11bf860002", reg.getField("_id").OID().toString());
    EXPECT_EQ(20000000, reg.getIntField("expiration"));

    contextRegistrationV = reg.getField("contextRegistration").Array();
    ASSERT_EQ(2, contextRegistrationV.size());
    contextRegistration = contextRegistrationV[0].embeddedObject();

    EXPECT_STREQ("http://cr1.com", C_STR_FIELD(contextRegistration, "providingApplication"));
    entities = contextRegistration.getField("entities").Array();    
    ASSERT_EQ(1, entities.size());
    ent0 = entities[0].embeddedObject();
    EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));

    attrs = contextRegistration.getField("attrs").Array();    
    ASSERT_EQ(1, attrs.size());
    attr0 = attrs[0].embeddedObject();
    EXPECT_STREQ("A1", C_STR_FIELD(attr0, "name"));
    EXPECT_STREQ("TA1", C_STR_FIELD(attr0, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr0, "isDomain"));

    contextRegistration = contextRegistrationV[1].embeddedObject();

    EXPECT_STREQ("http://cr2.com", C_STR_FIELD(contextRegistration, "providingApplication"));
    entities = contextRegistration.getField("entities").Array();    
    ASSERT_EQ(1, entities.size());
    ent0 = entities[0].embeddedObject();
    EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));

    attrs = contextRegistration.getField("attrs").Array();    
    ASSERT_EQ(1, attrs.size());
    attr0 = attrs[0].embeddedObject();
    EXPECT_STREQ("A1", C_STR_FIELD(attr0, "name"));
    EXPECT_STREQ("TA1", C_STR_FIELD(attr0, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr0, "isDomain"));

    utExit();
}
/* ****************************************************************************
*
* updateWrongIdNoHex -
*
* FIXME P3: check that we cover this case in the proper place, e.g. check() in the pre-mongoBackend layers, before permanent removal
*/
TEST(DISABLED_mongoRegisterContext_update, updateWrongIdNoHex)
{
    HttpStatusCode           ms;
    RegisterContextRequest   req;
    RegisterContextResponse  res;

    utInit();

    /* Forge the request (from "inside" to "outside") */
    EntityId en("E1", "T1");
    ContextRegistrationAttribute cra("A1", "TA1", "true");
    ContextRegistration cr;
    cr.entityIdVector.push_back(&en);
    cr.contextRegistrationAttributeVector.push_back(&cra);
    cr.providingApplication.set("http://newurl.com");
    req.contextRegistrationVector.push_back(&cr);
    req.registrationId.set("51307b66f481db11bf8600XX");
    req.duration.set("PT1M");

    /* Prepare database */
    prepareDatabase();

    /* Invoke the function in mongoBackend library */
    ms = mongoRegisterContext(&req, &res, uriParams);

    /* Check that every involved collection at MongoDB is as expected */
    /* Note we are using EXPECT_STREQ() for some cases, as Mongo Driver returns const char*, not string
     * objects (see http://code.google.com/p/googletest/wiki/Primer#String_Comparison) */

    DBClientBase* connection = getMongoConnection();

    /* registrations collection: */
    ASSERT_EQ(2, connection->count(REGISTRATIONS_COLL, BSONObj()));

    BSONObj reg, contextRegistration, ent0, attr0, attr1, attr2;
    std::vector<BSONElement> contextRegistrationV, entities, attrs;

    /* reg #1 (untouched) */
    reg = connection->findOne(REGISTRATIONS_COLL, BSON("_id" << OID("51307b66f481db11bf860001")));
    EXPECT_EQ("51307b66f481db11bf860001", reg.getField("_id").OID().toString());
    EXPECT_EQ(10000000, reg.getIntField("expiration"));

    contextRegistrationV = reg.getField("contextRegistration").Array();
    ASSERT_EQ(1, contextRegistrationV.size());
    contextRegistration = contextRegistrationV[0].embeddedObject();

    EXPECT_STREQ("http://cr1.com", C_STR_FIELD(contextRegistration, "providingApplication"));
    entities = contextRegistration.getField("entities").Array();    
    ASSERT_EQ(1, entities.size());
    ent0 = entities[0].embeddedObject();
    EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));

    attrs = contextRegistration.getField("attrs").Array();
    ASSERT_EQ(3, attrs.size());
    attr0 = attrs[0].embeddedObject();
    attr1 = attrs[1].embeddedObject();
    attr2 = attrs[2].embeddedObject();
    EXPECT_STREQ("A1", C_STR_FIELD(attr0, "name"));
    EXPECT_STREQ("TA1", C_STR_FIELD(attr0, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr0, "isDomain"));
    EXPECT_STREQ("A2", C_STR_FIELD(attr1, "name"));
    EXPECT_STREQ("TA2", C_STR_FIELD(attr1, "type"));
    EXPECT_STREQ("false", C_STR_FIELD(attr1, "isDomain"));
    EXPECT_STREQ("A3", C_STR_FIELD(attr2, "name"));
    EXPECT_STREQ("TA3", C_STR_FIELD(attr2, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr2, "isDomain"));

    /* reg #2 (untouched) */
    reg = connection->findOne(REGISTRATIONS_COLL, BSON("_id" << OID("51307b66f481db11bf860002")));
    EXPECT_EQ("51307b66f481db11bf860002", reg.getField("_id").OID().toString());
    EXPECT_EQ(20000000, reg.getIntField("expiration"));

    contextRegistrationV = reg.getField("contextRegistration").Array();
    ASSERT_EQ(2, contextRegistrationV.size());
    contextRegistration = contextRegistrationV[0].embeddedObject();

    EXPECT_STREQ("http://cr1.com", C_STR_FIELD(contextRegistration, "providingApplication"));
    entities = contextRegistration.getField("entities").Array();    
    ASSERT_EQ(1, entities.size());
    ent0 = entities[0].embeddedObject();
    EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));

    attrs = contextRegistration.getField("attrs").Array();    
    ASSERT_EQ(1, attrs.size());
    attr0 = attrs[0].embeddedObject();
    EXPECT_STREQ("A1", C_STR_FIELD(attr0, "name"));
    EXPECT_STREQ("TA1", C_STR_FIELD(attr0, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr0, "isDomain"));

    contextRegistration = contextRegistrationV[1].embeddedObject();

    EXPECT_STREQ("http://cr2.com", C_STR_FIELD(contextRegistration, "providingApplication"));
    entities = contextRegistration.getField("entities").Array();    
    ASSERT_EQ(1, entities.size());
    ent0 = entities[0].embeddedObject();
    EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
    EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));

    attrs = contextRegistration.getField("attrs").Array();    
    ASSERT_EQ(1, attrs.size());
    attr0 = attrs[0].embeddedObject();
    EXPECT_STREQ("A1", C_STR_FIELD(attr0, "name"));
    EXPECT_STREQ("TA1", C_STR_FIELD(attr0, "type"));
    EXPECT_STREQ("true", C_STR_FIELD(attr0, "isDomain"));

    /* Check response is as expected */
    EXPECT_EQ(SccOk, ms);
    EXPECT_EQ(0, res.duration.get().size());
    EXPECT_EQ("51307b66f481db11bf8600XX", res.registrationId.get());
    EXPECT_EQ(SccContextElementNotFound, res.errorCode.code);
    EXPECT_EQ("Registration Not Found", res.errorCode.reasonPhrase);
    EXPECT_EQ(0, res.errorCode.details.size());

    utExit();
}
示例#12
0
    void ShardChunkManager::_init( const string& configServer , const string& ns , const string& shardName, ShardChunkManagerPtr oldManager ) {

        // have to get a connection to the config db
        // special case if I'm the configdb since I'm locked and if I connect to myself
        // its a deadlock
        scoped_ptr<ScopedDbConnection> scoped;
        scoped_ptr<DBDirectClient> direct;
        DBClientBase * conn;
        if ( configServer.empty() ) {
            direct.reset( new DBDirectClient() );
            conn = direct.get();
        }
        else {
            scoped.reset( ScopedDbConnection::getInternalScopedDbConnection( configServer, 30.0 ) );
            conn = scoped->get();
        }

        // get this collection's sharding key
        BSONObj collectionDoc = conn->findOne( "config.collections", BSON( "_id" << ns ) );

        if( collectionDoc.isEmpty() ){
            warning() << ns << " does not exist as a sharded collection" << endl;
            return;
        }

        if( collectionDoc["dropped"].Bool() ){
            warning() << ns << " was dropped.  Re-shard collection first." << endl;
            return;
        }

        _fillCollectionKey( collectionDoc );

        map<string,ShardChunkVersion> versionMap;
        versionMap[ shardName ] = _version;
        _collVersion = ShardChunkVersion( 0, OID() );

        // Check to see if we have an old ShardChunkManager to use
        if( oldManager && oldManager->_collVersion.isSet() ){

            versionMap[ shardName ] = oldManager->_version;
            _collVersion = oldManager->_collVersion;
            // TODO: This could be made more efficient if copying not required, but not as
            // frequently reloaded as in mongos.
            _chunksMap = oldManager->_chunksMap;

            LOG(2) << "loading new chunks for collection " << ns << " using old chunk manager w/ version " << _collVersion
                   << " and " << _chunksMap.size() << " chunks" << endl;
        }

        // Attach our config diff tracker to our range map and versions
        SCMConfigDiffTracker differ( shardName );
        differ.attach( ns, _chunksMap, _collVersion, versionMap );

        // Need to do the query ourselves, since we may use direct conns to the db
        Query query = differ.configDiffQuery();
        auto_ptr<DBClientCursor> cursor = conn->query( "config.chunks" , query );

        uassert( 16181, str::stream() << "could not initialize cursor to config server chunks collection for ns " << ns, cursor.get() );

        // Diff tracker should *always* find at least one chunk if collection exists
        int diffsApplied = differ.calculateConfigDiff( *cursor );
        if( diffsApplied > 0 ){

            LOG(2) << "loaded " << diffsApplied << " chunks into new chunk manager for " << ns
                   << " with version " << _collVersion << endl;

            // Save the new version of this shard
            _version = versionMap[ shardName ];
            _fillRanges();

        }
        else if( diffsApplied == 0 ){

            // No chunks were found for the ns
            warning() << "no chunks found when reloading " << ns << ", previous version was " << _collVersion << endl;

            _version = ShardChunkVersion( 0, OID() );
            _collVersion = ShardChunkVersion( 0, OID() );
            _chunksMap.clear();
        }
        else{

            // TODO: make this impossible by making sure we don't migrate / split on this shard during the
            // reload
            // No chunks were found for the ns
            warning() << "invalid chunks found when reloading " << ns << ", previous version was " << _collVersion
                      << ", this should be rare" << endl;

            // Handle the same way as a connectivity error, for now
            // TODO: handle inline
            uassert( 16229,
                     str::stream() << "could not initialize cursor to config server chunks collection for ns "
                                   << ns, cursor.get() );
        }

        if ( scoped.get() )
            scoped->done();

        if ( _chunksMap.empty() )
            log() << "no chunk for collection " << ns << " on shard " << shardName << endl;
    }
/* ****************************************************************************
*
* mongoRegisterContext - 
*/
HttpStatusCode mongoRegisterContext(RegisterContextRequest* requestP, RegisterContextResponse* responseP, const std::string& tenant)
{    
    reqSemTake(__FUNCTION__, "ngsi9 register request");

    LM_T(LmtMongo, ("Register Context Request"));

    DBClientBase* connection = getMongoConnection();

    /* Check if new registration */
    if (requestP->registrationId.isEmpty()) {
        HttpStatusCode result = processRegisterContext(requestP, responseP, NULL, tenant);
        reqSemGive(__FUNCTION__, "ngsi9 register request");
        return result;
    }

    /* It is not a new registration, so it should be an update */
    BSONObj reg;
    OID     id;
    try
    {
        id = OID(requestP->registrationId.get());

        mongoSemTake(__FUNCTION__, "findOne from RegistrationsCollection");
        reg = connection->findOne(getRegistrationsCollectionName(tenant).c_str(), BSON("_id" << id));
        mongoSemGive(__FUNCTION__, "findOne from RegistrationsCollection");
        LM_I(("Database Operation Successful (findOne _id: %s)", id.toString().c_str()));
    }
    catch (const AssertionException &e)
    {
        mongoSemGive(__FUNCTION__, "findOne from RegistrationsCollection (AssertionException)");
        reqSemGive(__FUNCTION__, "ngsi9 register request");

        /* This happens when OID format is wrong */
        // FIXME: this checking should be done at parsing stage, without progressing to
        // mongoBackend. By the moment we can live this here, but we should remove in the future
        responseP->errorCode.fill(SccContextElementNotFound);
        responseP->registrationId = requestP->registrationId;
        ++noOfRegistrationUpdateErrors;
        LM_W(("Bad Input (invalid OID format)"));
        return SccOk;
    }
    catch (const DBException &e)
    {
        mongoSemGive(__FUNCTION__, "findOne from RegistrationsCollection (DBException)");
        reqSemGive(__FUNCTION__, "ngsi9 register request");

        responseP->errorCode.fill(SccReceiverInternalError,
                                  std::string("collection: ") + getRegistrationsCollectionName(tenant).c_str() +
                                  " - findOne() _id: " + requestP->registrationId.get() +
                                  " - exception: " + e.what());
        ++noOfRegistrationUpdateErrors;
        LM_E(("Database Error (%s)", responseP->errorCode.details.c_str()));
        return SccOk;
    }
    catch (...)
    {
        mongoSemGive(__FUNCTION__, "findOne from RegistrationsCollection (Generic Exception)");
        reqSemGive(__FUNCTION__, "ngsi9 register request");

        responseP->errorCode.fill(SccReceiverInternalError,
                                  std::string("collection: ") + getRegistrationsCollectionName(tenant).c_str() +
                                  " - findOne() _id: " + requestP->registrationId.get() +
                                  " - exception: " + "generic");
        ++noOfRegistrationUpdateErrors;
        LM_E(("Database Error (%s)", responseP->errorCode.details.c_str()));
        return SccOk;
    }

    if (reg.isEmpty())
    {
       reqSemGive(__FUNCTION__, "ngsi9 register request (no registrations found)");
       responseP->errorCode.fill(SccContextElementNotFound, std::string("registration id: /") + requestP->registrationId.get() + "/");
       responseP->registrationId = requestP->registrationId;
       ++noOfRegistrationUpdateErrors;
       return SccOk;
    }

    HttpStatusCode result = processRegisterContext(requestP, responseP, &id, tenant);
    reqSemGive(__FUNCTION__, "ngsi9 register request");
    return result;
}
/* ****************************************************************************
*
* createSubscriptionNotCustomOK -
*/
TEST(mongoCreateSubscriptions, createSubscriptionNotCustomOK)
{
  OrionError  oe;

  utInit();

  /* Forge input subscription */
  Subscription sub;
  sub.description = "this is the sub";
  sub.expires     = 1360236300;
  sub.status      = "active";
  sub.throttling  = 5;
  sub.attrsFormat = NGSI_V2_NORMALIZED;

  EntID en1("E1", "", "T1");
  EntID en2("", "E.*", "T2");
  sub.subject.entities.push_back(en1);
  sub.subject.entities.push_back(en2);
  sub.subject.condition.attributes.push_back("A");
  sub.subject.condition.attributes.push_back("B");

  sub.subject.condition.expression.q        = "temperature<=20";
  sub.subject.condition.expression.coords   = "-40.4,-3.5;0,0";
  sub.subject.condition.expression.georel   = "coveredBy";
  sub.subject.condition.expression.geometry = "box";

  sub.notification.attributes.push_back("C");
  sub.notification.attributes.push_back("D");
  sub.notification.httpInfo.url      = "http://foo.bar";
  sub.notification.httpInfo.custom   = false;

  /* Invoke the function in mongoBackend library */
  std::string result = mongoCreateSubscription(sub, &oe, "", servicePathVector, "", "");

  /* Check response is as expected */  
  EXPECT_EQ(SccNone, oe.code);
  EXPECT_EQ("", oe.reasonPhrase);
  EXPECT_EQ("", oe.details);

  DBClientBase* connection = getMongoConnection();

  ASSERT_EQ(1, connection->count(SUBSCRIBECONTEXT_COLL, BSONObj()));
  BSONObj doc = connection->findOne(SUBSCRIBECONTEXT_COLL, BSONObj());

  EXPECT_EQ(result, doc.getField("_id").OID().toString());
  EXPECT_EQ(1360236300, doc.getIntField("expiration"));
  EXPECT_FALSE(doc.hasField("lastNotification"));
  EXPECT_EQ(5, doc.getIntField("throttling"));
  EXPECT_STREQ("http://foo.bar", C_STR_FIELD(doc, "reference"));
  EXPECT_STREQ("normalized", C_STR_FIELD(doc, "format"));

  EXPECT_STREQ("this is the sub", C_STR_FIELD(doc, "description"));
  EXPECT_STREQ("active", C_STR_FIELD(doc, "status"));
  EXPECT_FALSE(doc.getBoolField("custom"));

  BSONObj expression = doc.getField("expression").embeddedObject();
  EXPECT_STREQ("temperature<=20", C_STR_FIELD(expression, "q"));
  EXPECT_STREQ("-40.4,-3.5;0,0", C_STR_FIELD(expression, "coords"));
  EXPECT_STREQ("coveredBy", C_STR_FIELD(expression, "georel"));
  EXPECT_STREQ("box", C_STR_FIELD(expression, "geometry"));

  std::vector<BSONElement> entities = doc.getField("entities").Array();
  ASSERT_EQ(2, entities.size());
  BSONObj ent0 = entities[0].embeddedObject();
  EXPECT_STREQ("E1", C_STR_FIELD(ent0, "id"));
  EXPECT_STREQ("T1", C_STR_FIELD(ent0, "type"));
  EXPECT_STREQ("false", C_STR_FIELD(ent0, "isPattern"));
  BSONObj ent1 = entities[1].embeddedObject();
  EXPECT_STREQ("E.*", C_STR_FIELD(ent1, "id"));
  EXPECT_STREQ("T2", C_STR_FIELD(ent1, "type"));
  EXPECT_STREQ("true", C_STR_FIELD(ent1, "isPattern"));

  std::vector<BSONElement> attrs = doc.getField("attrs").Array();
  EXPECT_EQ(2, attrs.size());
  EXPECT_EQ("C", attrs[0].String());
  EXPECT_EQ("D", attrs[1].String());

  std::vector<BSONElement> conds = doc.getField("conditions").Array();
  ASSERT_EQ(1, conds.size());
  BSONObj cond0 = conds[0].embeddedObject();
  EXPECT_STREQ("ONCHANGE", C_STR_FIELD(cond0, "type"));
  std::vector<BSONElement> condValues = cond0.getField("value").Array();
  ASSERT_EQ(2, condValues.size());
  EXPECT_EQ("A", condValues[0].String());
  EXPECT_EQ("B", condValues[1].String());

  utExit();
}