示例#1
0
void Definition::toString(char *buf, int max, int indent) const {
	int index=0;
	OID temp;
	int modi;
	char buf2[50];
	Event *evt2 = NEW Event(Event::GET_RANGE, m_def);
	evt2->param<0>(0);
	evt2->param<1>(m_size);
	evt2->send();
	OID boid = evt2->result();
	OID ifobj = Null;
	Buffer *def = Buffer::lookup(boid);
	delete evt2;
	if (def == 0) {
		buf[0] = 0;
		return;
	}
	
	for (int i=0; i<indent; i++) strcat(buf, "\t");
	
	while (index < m_size) {
		temp = def->get(index++);
		
		if (temp.isModifier()) {
			modi = temp.d();
		
			switch (modi) {
			case modifiers::ENDSUB:
				strcat(buf, ")");
				break;
			case modifiers::BEGINSUB:
				strcat(buf, "(");
				break;
			case modifiers::UNION:
				strcat(buf, "union ");
				break;
			case modifiers::COMPARE:
				strcat(buf, "== ");
				break;
			default: break;
			}
		} else {
			if (temp == This) {
				strcat(buf, ".");
			} else if (index == 1 && temp.get("type") == OID("if")) {
				ifobj = temp;
				strcat(buf, "if ");
				//buildif(buf, temp);
			} else {
				temp.toString(buf2, 50);
				strcat(buf, buf2);
				strcat(buf, " ");
			}
		}
	}
	
	if (ifobj != Null) buildif(buf, ifobj, indent);

	Buffer::free(boid);
}
示例#2
0
INT32 insertData ( sdbCollection *collection, CHAR **json_str )
{
   INT32 rc = SDB_OK ;
   OID oid ;
   INT32 len = 0 ;
   CHAR *temp = NULL ;
   BSONObj bs ;
   rc = fromjson ( *json_str, bs ) ;
   if ( rc )
   {
      return rc ;
   }

   rc = collection->insert ( bs, &oid ) ;
   if ( rc )
   {
      return rc ;
   }
   std::string str = oid.toString () ;
   const CHAR *p = str.c_str () ;
   INT32 eleSize = strlen(p) ;
   if ( eleSize > 0 )
   {
      CHAR *pTemp = (CHAR*)malloc(eleSize+1) ;
      if ( !pTemp )
         return SDB_OOM ;
      memcpy ( pTemp, p, eleSize ) ;
      pTemp[eleSize] = '\0' ;
      *json_str = pTemp ;
   }

   return rc ;
}
void
PublicKey::decode(CryptoPP::BufferedTransformation& in)
{
  // SubjectPublicKeyInfo ::= SEQUENCE {
  //     algorithm           AlgorithmIdentifier
  //     keybits             BIT STRING   }

  using namespace CryptoPP;
  try
    {
      std::string out;
      StringSink sink(out);

      ////////////////////////
      // part 1: copy as is //
      ////////////////////////
      BERSequenceDecoder decoder(in);
      {
        assert(decoder.IsDefiniteLength());

        DERSequenceEncoder encoder(sink);
        decoder.TransferTo(encoder, decoder.RemainingLength());
        encoder.MessageEnd();
      }
      decoder.MessageEnd();

      ////////////////////////
      // part 2: check if the key is RSA (since it is the only supported for now)
      ////////////////////////
      StringSource checkedSource(out, true);
      BERSequenceDecoder subjectPublicKeyInfo(checkedSource);
      {
        BERSequenceDecoder algorithmInfo(subjectPublicKeyInfo);
        {
          OID algorithm;
          algorithm.decode(algorithmInfo);

          if (algorithm == oid::RSA)
            m_type = KEY_TYPE_RSA;
          else if (algorithm == oid::ECDSA)
            m_type = KEY_TYPE_ECDSA;
          else
            BOOST_THROW_EXCEPTION(Error("Only RSA/ECDSA public keys are supported for now (" +
                                        algorithm.toString() + " requested)"));
        }
      }

      m_key.assign(out.begin(), out.end());
    }
  catch (CryptoPP::BERDecodeErr& err)
    {
      m_type = KEY_TYPE_NULL;
      BOOST_THROW_EXCEPTION(Error("PublicKey decoding error"));
    }

  m_digest.reset();
}
示例#4
0
DString cadence::operator+(DString m, const OID &o) {
	char buf[200];
	o.toString(buf,200);

	int s = m.size();
	int s2 = strlen(buf);

	for (int i=0; i<s2; i++) {
		m.m_obj[s+i].set(buf[i], true);
	}
	
	m.m_obj[Size].set(s+s2);

	return m;
}
示例#5
0
	string SavePaymentJson(int amount)
	{
		//BSONObj paymentBSON = mongo::fromjson(newPyamentJson);

		BSONObj paymentBSON = BSON(GENOID 
			<< "PayedToUserId" << 8888
			<< "PayedDate" << "2015-01-25 12:00:00"
			<< "PayedPeriodStartDate" << "2015-01-01 00:00:00"
			<< "PayedPeriodEndDate" << "2015-01-29 23:59:59" 
			<< "Amount" << amount);

		db.insert(PAYMENTS_COLLECTION_NAMESPASE, paymentBSON);

		BSONElement oi;
		paymentBSON.getObjectID(oi);
		OID oid = oi.__oid();
		return oid.toString(); 
	}
示例#6
0
void DString::toString(char *str, int max) {

	//The whole object may be an integer etc...
	if (m_obj[Size] == Null || !((OID)m_obj[0]).isChar()) {
		m_obj.toString(str,max);
		return;
	}

	int size = (OID)m_obj[Size];

	if (size >= max) size = max-1;
	int j = 0;
	OID v;

	Event *evt = new Event(Event::GET_RANGE, m_obj);
	evt->param<0>(0);
	evt->param<1>(size);
	evt->send();
	OID boid = evt->result();
	Buffer *str2 = Buffer::lookup(boid);
	delete evt;
	if (str2 == 0) {
		str[0] = 0;
		return;
	}

	for (int i=0; i<size; i++) {
		v = str2->get(i);
		if (v.isChar())
			str[j++] = str2->get(i);
		else {
			int k = 0;
			char buf[500];
			v.toString(buf,500);
			while (buf[k] != 0) {
				str[j++] = buf[k++];
			}
		}
	}

	Buffer::free(boid);

	str[j] = 0;
};
/* ****************************************************************************
*
* 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,
  const std::string&        fiwareCorrelator
)
{
  std::string err;

  /* 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 == "" ? DEFAULT_SERVICE_PATH_UPDATES : 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[ix];

    BSONArrayBuilder entities;
    for (unsigned int jx = 0; jx < cr->entityIdVector.size(); ++jx)
    {
      EntityId* en = cr->entityIdVector[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[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[ix]->contextRegistrationAttributeVector[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[ix]->providingApplication.get()));

    LM_T(LmtMongo, ("providingApplication registration: %s",
                    requestP->contextRegistrationVector[ix]->providingApplication.c_str()));

    std::string err;

    if (!addTriggeredSubscriptions(*cr, subsToNotify, err, tenant))
    {
      responseP->errorCode.fill(SccReceiverInternalError, err);
      return SccOk;
    }
  }
  reg.append(REG_CONTEXT_REGISTRATION, contextRegistration.arr());

  /* Note we are using upsert = "true". This means that if the document doesn't previously
   * exist in the collection, it is created. Thus, this way both uses of registerContext are OK
   * (either new registration or updating an existing one) */
  if (!collectionUpdate(getRegistrationsCollectionName(tenant), BSON("_id" << oid), reg.obj(), true, &err))
  {
    responseP->errorCode.fill(SccReceiverInternalError, err);
    releaseTriggeredSubscriptions(subsToNotify);
    return SccOk;
  }

  //
  // Send notifications for each one of the subscriptions accumulated by
  // previous addTriggeredSubscriptions() invocations
  //
  processSubscriptions(triggerEntitiesV, subsToNotify, err, tenant, fiwareCorrelator);

  // Fill the response element
  responseP->duration = requestP->duration;
  responseP->registrationId.set(oid.toString());
  responseP->errorCode.fill(SccOk);

  return SccOk;
}
示例#8
0
bool mergeChunks(OperationContext* txn,
                 const NamespaceString& nss,
                 const BSONObj& minKey,
                 const BSONObj& maxKey,
                 const OID& epoch,
                 string* errMsg) {
    // Get the distributed lock
    string whyMessage = stream() << "merging chunks in " << nss.ns() << " from " << minKey << " to "
                                 << maxKey;
    auto scopedDistLock = grid.catalogManager(txn)->distLock(
        txn, nss.ns(), whyMessage, DistLockManager::kSingleLockAttemptTimeout);

    if (!scopedDistLock.isOK()) {
        *errMsg = stream() << "could not acquire collection lock for " << nss.ns()
                           << " to merge chunks in [" << minKey << "," << maxKey << ")"
                           << causedBy(scopedDistLock.getStatus());

        warning() << *errMsg;
        return false;
    }

    ShardingState* shardingState = ShardingState::get(txn);

    //
    // We now have the collection lock, refresh metadata to latest version and sanity check
    //

    ChunkVersion shardVersion;
    Status status = shardingState->refreshMetadataNow(txn, nss.ns(), &shardVersion);

    if (!status.isOK()) {
        *errMsg = str::stream() << "could not merge chunks, failed to refresh metadata for "
                                << nss.ns() << causedBy(status.reason());

        warning() << *errMsg;
        return false;
    }

    if (epoch.isSet() && shardVersion.epoch() != epoch) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns() << " has changed"
                           << " since merge was sent"
                           << "(sent epoch : " << epoch.toString()
                           << ", current epoch : " << shardVersion.epoch().toString() << ")";

        warning() << *errMsg;
        return false;
    }

    shared_ptr<CollectionMetadata> metadata = shardingState->getCollectionMetadata(nss.ns());

    if (!metadata || metadata->getKeyPattern().isEmpty()) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                           << " is not sharded";

        warning() << *errMsg;
        return false;
    }

    dassert(metadata->getShardVersion().equals(shardVersion));

    if (!metadata->isValidKey(minKey) || !metadata->isValidKey(maxKey)) {
        *errMsg = stream() << "could not merge chunks, the range " << rangeToString(minKey, maxKey)
                           << " is not valid"
                           << " for collection " << nss.ns() << " with key pattern "
                           << metadata->getKeyPattern();

        warning() << *errMsg;
        return false;
    }

    //
    // Get merged chunk information
    //

    ChunkVersion mergeVersion = metadata->getCollVersion();
    mergeVersion.incMinor();

    std::vector<ChunkType> chunksToMerge;

    ChunkType itChunk;
    itChunk.setMin(minKey);
    itChunk.setMax(minKey);
    itChunk.setNS(nss.ns());
    itChunk.setShard(shardingState->getShardName());

    while (itChunk.getMax().woCompare(maxKey) < 0 &&
           metadata->getNextChunk(itChunk.getMax(), &itChunk)) {
        chunksToMerge.push_back(itChunk);
    }

    if (chunksToMerge.empty()) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                           << " range starting at " << minKey << " and ending at " << maxKey
                           << " does not belong to shard " << shardingState->getShardName();

        warning() << *errMsg;
        return false;
    }

    //
    // Validate the range starts and ends at chunks and has no holes, error if not valid
    //

    BSONObj firstDocMin = chunksToMerge.front().getMin();
    BSONObj firstDocMax = chunksToMerge.front().getMax();
    // minKey is inclusive
    bool minKeyInRange = rangeContains(firstDocMin, firstDocMax, minKey);

    if (!minKeyInRange) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                           << " range starting at " << minKey << " does not belong to shard "
                           << shardingState->getShardName();

        warning() << *errMsg;
        return false;
    }

    BSONObj lastDocMin = chunksToMerge.back().getMin();
    BSONObj lastDocMax = chunksToMerge.back().getMax();
    // maxKey is exclusive
    bool maxKeyInRange = lastDocMin.woCompare(maxKey) < 0 && lastDocMax.woCompare(maxKey) >= 0;

    if (!maxKeyInRange) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                           << " range ending at " << maxKey << " does not belong to shard "
                           << shardingState->getShardName();

        warning() << *errMsg;
        return false;
    }

    bool validRangeStartKey = firstDocMin.woCompare(minKey) == 0;
    bool validRangeEndKey = lastDocMax.woCompare(maxKey) == 0;

    if (!validRangeStartKey || !validRangeEndKey) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                           << " does not contain a chunk "
                           << (!validRangeStartKey ? "starting at " + minKey.toString() : "")
                           << (!validRangeStartKey && !validRangeEndKey ? " or " : "")
                           << (!validRangeEndKey ? "ending at " + maxKey.toString() : "");

        warning() << *errMsg;
        return false;
    }

    if (chunksToMerge.size() == 1) {
        *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                           << " already contains chunk for " << rangeToString(minKey, maxKey);

        warning() << *errMsg;
        return false;
    }

    // Look for hole in range
    for (size_t i = 1; i < chunksToMerge.size(); ++i) {
        if (chunksToMerge[i - 1].getMax().woCompare(chunksToMerge[i].getMin()) != 0) {
            *errMsg =
                stream() << "could not merge chunks, collection " << nss.ns()
                         << " has a hole in the range " << rangeToString(minKey, maxKey) << " at "
                         << rangeToString(chunksToMerge[i - 1].getMax(), chunksToMerge[i].getMin());

            warning() << *errMsg;
            return false;
        }
    }

    //
    // Run apply ops command
    //
    Status applyOpsStatus = runApplyOpsCmd(txn, chunksToMerge, shardVersion, mergeVersion);
    if (!applyOpsStatus.isOK()) {
        warning() << applyOpsStatus;
        return false;
    }

    //
    // Install merged chunk metadata
    //

    {
        ScopedTransaction transaction(txn, MODE_IX);
        Lock::DBLock writeLk(txn->lockState(), nss.db(), MODE_IX);
        Lock::CollectionLock collLock(txn->lockState(), nss.ns(), MODE_X);

        shardingState->mergeChunks(txn, nss.ns(), minKey, maxKey, mergeVersion);
    }

    //
    // Log change
    //

    BSONObj mergeLogEntry = buildMergeLogEntry(chunksToMerge, shardVersion, mergeVersion);

    grid.catalogManager(txn)->logChange(txn, "merge", nss.ns(), mergeLogEntry);

    return true;
}
示例#9
0
    Status MetadataLoader::initChunks(CatalogManager* catalogManager,
                                      const string& ns,
                                      const string& shard,
                                      const CollectionMetadata* oldMetadata,
                                      CollectionMetadata* metadata) const
    {
        map<string, ChunkVersion> versionMap;

        // Preserve the epoch
        versionMap[shard] = metadata->_shardVersion;
        OID epoch = metadata->getCollVersion().epoch();
        bool fullReload = true;

        // Check to see if we should use the old version or not.
        if ( oldMetadata ) {

            // If our epochs are compatible, it's useful to use the old metadata for diffs
            if ( oldMetadata->getCollVersion().hasEqualEpoch( epoch ) ) {

                fullReload = false;
                invariant( oldMetadata->isValid() );

                versionMap[shard] = oldMetadata->_shardVersion;
                metadata->_collVersion = oldMetadata->_collVersion;

                // TODO: This could be made more efficient if copying not required, but
                // not as frequently reloaded as in mongos.
                metadata->_chunksMap = oldMetadata->_chunksMap;

                LOG( 2 ) << "loading new chunks for collection " << ns
                         << " using old metadata w/ version " << oldMetadata->getShardVersion()
                         << " and " << metadata->_chunksMap.size() << " chunks" << endl;
            }
            else {
                warning() << "reloading collection metadata for " << ns << " with new epoch "
                          << epoch.toString() << ", the current epoch is "
                          << oldMetadata->getCollVersion().epoch().toString() << endl;
            }
        }


        // Exposes the new metadata's range map and version to the "differ," who
        // would ultimately be responsible of filling them up.
        SCMConfigDiffTracker differ( shard );
        differ.attach( ns, metadata->_chunksMap, metadata->_collVersion, versionMap );

        try {
            std::vector<ChunkType> chunks;
            Status status = catalogManager->getChunks(differ.configDiffQuery(), 0, &chunks);
            if (!status.isOK()) {
                if (status == ErrorCodes::HostUnreachable) {
                    // Make our metadata invalid
                    metadata->_collVersion = ChunkVersion( 0, 0, OID() );
                    metadata->_chunksMap.clear();
                }
                return status;
            }

            //
            // The diff tracker should always find at least one chunk (the highest chunk we saw
            // last time).  If not, something has changed on the config server (potentially between
            // when we read the collection data and when we read the chunks data).
            //
            int diffsApplied = differ.calculateConfigDiff(chunks);
            if ( diffsApplied > 0 ) {
                // Chunks found, return ok
                LOG(2) << "loaded " << diffsApplied << " chunks into new metadata for " << ns
                       << " with version " << metadata->_collVersion;

                metadata->_shardVersion = versionMap[shard];
                metadata->fillRanges();

                invariant( metadata->isValid() );
                return Status::OK();
            }
            else if ( diffsApplied == 0 ) {

                // No chunks found, the collection is dropping or we're confused
                // If this is a full reload, assume it is a drop for backwards compatibility
                // TODO: drop the config.collections entry *before* the chunks and eliminate this
                // ambiguity

                string errMsg =
                    str::stream() << "no chunks found when reloading " << ns
                                  << ", previous version was "
                                  << metadata->_collVersion.toString()
                                  << ( fullReload ? ", this is a drop" : "" );

                warning() << errMsg << endl;

                metadata->_collVersion = ChunkVersion( 0, 0, OID() );
                metadata->_chunksMap.clear();

                return fullReload ? Status( ErrorCodes::NamespaceNotFound, errMsg ) :
                                    Status( ErrorCodes::RemoteChangeDetected, errMsg );
            }
            else {
                // Invalid chunks found, our epoch may have changed because we dropped/recreated
                // the collection.
                string errMsg = str::stream() << "invalid chunks found when reloading " << ns
                                              << ", previous version was "
                                              << metadata->_collVersion.toString()
                                              << ", this should be rare";
                warning() << errMsg;

                metadata->_collVersion = ChunkVersion( 0, 0, OID() );
                metadata->_chunksMap.clear();

                return Status(ErrorCodes::RemoteChangeDetected, errMsg);
            }
        }
        catch (const DBException& e) {
            // We deliberately do not return connPtr to the pool, since it was involved with the
            // error here.
            return Status(ErrorCodes::HostUnreachable,
                          str::stream() << "problem querying chunks metadata" << causedBy(e));
        }
    }
示例#10
0
    bool mergeChunks( OperationContext* txn,
                      const NamespaceString& nss,
                      const BSONObj& minKey,
                      const BSONObj& maxKey,
                      const OID& epoch,
                      string* errMsg ) {

        //
        // Get sharding state up-to-date
        //

        ConnectionString configLoc = ConnectionString::parse( shardingState.getConfigServer(),
                                                              *errMsg );
        if ( !configLoc.isValid() ){
            warning() << *errMsg << endl;
            return false;
        }

        //
        // Get the distributed lock
        //

        ScopedDistributedLock collLock( configLoc, nss.ns() );
        collLock.setLockMessage( stream() << "merging chunks in " << nss.ns() << " from "
                                          << minKey << " to " << maxKey );

        Status acquisitionStatus = collLock.tryAcquire();
        if (!acquisitionStatus.isOK()) {
            *errMsg = stream() << "could not acquire collection lock for " << nss.ns()
                               << " to merge chunks in [" << minKey << "," << maxKey << ")"
                               << causedBy(acquisitionStatus);

            warning() << *errMsg << endl;
            return false;
        }

        //
        // We now have the collection lock, refresh metadata to latest version and sanity check
        //

        ChunkVersion shardVersion;
        Status status = shardingState.refreshMetadataNow(txn, nss.ns(), &shardVersion);

        if ( !status.isOK() ) {

            *errMsg = str::stream() << "could not merge chunks, failed to refresh metadata for "
                                    << nss.ns() << causedBy( status.reason() );

            warning() << *errMsg << endl;
            return false;
        }

        if ( epoch.isSet() && shardVersion.epoch() != epoch ) {

            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " has changed" << " since merge was sent" << "(sent epoch : "
                               << epoch.toString()
                               << ", current epoch : " << shardVersion.epoch().toString() << ")";

            warning() << *errMsg << endl;
            return false;
        }

        CollectionMetadataPtr metadata = shardingState.getCollectionMetadata( nss.ns() );

        if ( !metadata || metadata->getKeyPattern().isEmpty() ) {

            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " is not sharded";

            warning() << *errMsg << endl;
            return false;
        }

        dassert( metadata->getShardVersion().equals( shardVersion ) );

        if ( !metadata->isValidKey( minKey ) || !metadata->isValidKey( maxKey ) ) {

            *errMsg = stream() << "could not merge chunks, the range "
                               << rangeToString( minKey, maxKey ) << " is not valid"
                               << " for collection " << nss.ns() << " with key pattern "
                               << metadata->getKeyPattern();

            warning() << *errMsg << endl;
            return false;
        }

        //
        // Get merged chunk information
        //

        ChunkVersion mergeVersion = metadata->getCollVersion();
        mergeVersion.incMinor();

        OwnedPointerVector<ChunkType> chunksToMerge;

        ChunkType itChunk;
        itChunk.setMin( minKey );
        itChunk.setMax( minKey );
        itChunk.setNS( nss.ns() );
        itChunk.setShard( shardingState.getShardName() );

        while ( itChunk.getMax().woCompare( maxKey ) < 0 &&
                metadata->getNextChunk( itChunk.getMax(), &itChunk ) ) {
            auto_ptr<ChunkType> saved( new ChunkType );
            itChunk.cloneTo( saved.get() );
            chunksToMerge.mutableVector().push_back( saved.release() );
        }

        if ( chunksToMerge.empty() ) {

            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " range starting at " << minKey
                               << " and ending at " << maxKey
                               << " does not belong to shard " << shardingState.getShardName();

            warning() << *errMsg << endl;
            return false;
        }

        //
        // Validate the range starts and ends at chunks and has no holes, error if not valid
        //

        BSONObj firstDocMin = ( *chunksToMerge.begin() )->getMin();
        BSONObj firstDocMax = ( *chunksToMerge.begin() )->getMax();
        // minKey is inclusive
        bool minKeyInRange = rangeContains( firstDocMin, firstDocMax, minKey );

        if ( !minKeyInRange ) {

            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " range starting at " << minKey
                               << " does not belong to shard " << shardingState.getShardName();

            warning() << *errMsg << endl;
            return false;
        }

        BSONObj lastDocMin = ( *chunksToMerge.rbegin() )->getMin();
        BSONObj lastDocMax = ( *chunksToMerge.rbegin() )->getMax();
        // maxKey is exclusive
        bool maxKeyInRange = lastDocMin.woCompare( maxKey ) < 0 &&
                lastDocMax.woCompare( maxKey ) >= 0;

        if ( !maxKeyInRange ) {
            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " range ending at " << maxKey
                               << " does not belong to shard " << shardingState.getShardName();

            warning() << *errMsg << endl;
            return false;
        }

        bool validRangeStartKey = firstDocMin.woCompare( minKey ) == 0;
        bool validRangeEndKey = lastDocMax.woCompare( maxKey ) == 0;

        if ( !validRangeStartKey || !validRangeEndKey ) {

            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " does not contain a chunk "
                               << ( !validRangeStartKey ? "starting at " + minKey.toString() : "" )
                               << ( !validRangeStartKey && !validRangeEndKey ? " or " : "" )
                               << ( !validRangeEndKey ? "ending at " + maxKey.toString() : "" );

            warning() << *errMsg << endl;
            return false;
        }

        if ( chunksToMerge.size() == 1 ) {

            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " already contains chunk for " << rangeToString( minKey, maxKey );

            warning() << *errMsg << endl;
            return false;
        }

        bool holeInRange = false;

        // Look for hole in range
        ChunkType* prevChunk = *chunksToMerge.begin();
        ChunkType* nextChunk = NULL;
        for ( OwnedPointerVector<ChunkType>::const_iterator it = chunksToMerge.begin();
                it != chunksToMerge.end(); ++it ) {
            if ( it == chunksToMerge.begin() ) continue;

            nextChunk = *it;
            if ( prevChunk->getMax().woCompare( nextChunk->getMin() ) != 0 ) {
                holeInRange = true;
                break;
            }
            prevChunk = nextChunk;
        }

        if ( holeInRange ) {

            dassert( NULL != nextChunk );
            *errMsg = stream() << "could not merge chunks, collection " << nss.ns()
                               << " has a hole in the range " << rangeToString( minKey, maxKey )
                               << " at " << rangeToString( prevChunk->getMax(),
                                                           nextChunk->getMin() );

            warning() << *errMsg << endl;
            return false;
        }

        //
        // Run apply ops command
        //

        BSONObj applyOpsCmd = buildApplyOpsCmd( chunksToMerge,
                                                shardVersion,
                                                mergeVersion );

        bool ok;
        BSONObj result;
        try {
            ScopedDbConnection conn( configLoc, 30.0 );
            ok = conn->runCommand( "config", applyOpsCmd, result );
            if ( !ok ) *errMsg = result.toString();
            conn.done();
        }
        catch( const DBException& ex ) {
            ok = false;
            *errMsg = ex.toString();
        }

        if ( !ok ) {
            *errMsg = stream() << "could not merge chunks for " << nss.ns()
                               << ", writing to config failed" << causedBy( errMsg );

            warning() << *errMsg << endl;
            return false;
        }

        //
        // Install merged chunk metadata
        //

        {
            Lock::DBLock writeLk(txn->lockState(), nss.db(), newlm::MODE_X);
            shardingState.mergeChunks(txn, nss.ns(), minKey, maxKey, mergeVersion);
        }

        //
        // Log change
        //

        BSONObj mergeLogEntry = buildMergeLogEntry( chunksToMerge,
                                                    shardVersion,
                                                    mergeVersion );

        configServer.logChange( "merge", nss.ns(), mergeLogEntry );

        return true;
    }
示例#11
0
Status updateShardChunks(OperationContext* opCtx,
                         const NamespaceString& nss,
                         const std::vector<ChunkType>& chunks,
                         const OID& currEpoch) {
    invariant(!chunks.empty());

    NamespaceString chunkMetadataNss(ChunkType::ShardNSPrefix + nss.ns());

    try {
        DBDirectClient client(opCtx);

        /**
         * Here are examples of the operations that can happen on the config server to update
         * the config.chunks collection. 'chunks' only includes the chunks that result from the
         * operations, which can be read from the config server, not any that were removed, so
         * we must delete any chunks that overlap with the new 'chunks'.
         *
         * CollectionVersion = 10.3
         *
         * moveChunk
         * {_id: 3, max: 5, version: 10.1} --> {_id: 3, max: 5, version: 11.0}
         *
         * splitChunk
         * {_id: 3, max: 9, version 10.3} --> {_id: 3, max: 5, version 10.4}
         *                                    {_id: 5, max: 8, version 10.5}
         *                                    {_id: 8, max: 9, version 10.6}
         *
         * mergeChunk
         * {_id: 10, max: 14, version 4.3} --> {_id: 10, max: 22, version 10.4}
         * {_id: 14, max: 19, version 7.1}
         * {_id: 19, max: 22, version 2.0}
         *
         */
        for (auto& chunk : chunks) {
            // Check for a different epoch.
            if (!chunk.getVersion().hasEqualEpoch(currEpoch)) {
                return Status{ErrorCodes::ConflictingOperationInProgress,
                              str::stream() << "Invalid chunks found when reloading '"
                                            << nss.toString()
                                            << "'. Previous collection epoch was '"
                                            << currEpoch.toString()
                                            << "', but unexpectedly found a new epoch '"
                                            << chunk.getVersion().epoch().toString()
                                            << "'. Collection was dropped and recreated."};
            }

            // Delete any overlapping chunk ranges. Overlapping chunks will have a min value
            // ("_id") between (chunk.min, chunk.max].
            //
            // query: { "_id" : {"$gte": chunk.min, "$lt": chunk.max}}
            auto deleteDocs(stdx::make_unique<BatchedDeleteDocument>());
            deleteDocs->setQuery(BSON(ChunkType::minShardID << BSON(
                                          "$gte" << chunk.getMin() << "$lt" << chunk.getMax())));
            deleteDocs->setLimit(0);

            auto deleteRequest(stdx::make_unique<BatchedDeleteRequest>());
            deleteRequest->addToDeletes(deleteDocs.release());

            BatchedCommandRequest batchedDeleteRequest(deleteRequest.release());
            batchedDeleteRequest.setNS(chunkMetadataNss);
            const BSONObj deleteCmdObj = batchedDeleteRequest.toBSON();

            rpc::UniqueReply deleteCommandResponse =
                client.runCommandWithMetadata(chunkMetadataNss.db().toString(),
                                              deleteCmdObj.firstElementFieldName(),
                                              rpc::makeEmptyMetadata(),
                                              deleteCmdObj);
            auto deleteStatus =
                getStatusFromCommandResult(deleteCommandResponse->getCommandReply());

            if (!deleteStatus.isOK()) {
                return deleteStatus;
            }

            // Now the document can be expected to cleanly insert without overlap.
            auto insert(stdx::make_unique<BatchedInsertRequest>());
            insert->addToDocuments(chunk.toShardBSON());

            BatchedCommandRequest insertRequest(insert.release());
            insertRequest.setNS(chunkMetadataNss);
            const BSONObj insertCmdObj = insertRequest.toBSON();

            rpc::UniqueReply commandResponse =
                client.runCommandWithMetadata(chunkMetadataNss.db().toString(),
                                              insertCmdObj.firstElementFieldName(),
                                              rpc::makeEmptyMetadata(),
                                              insertCmdObj);
            auto insertStatus = getStatusFromCommandResult(commandResponse->getCommandReply());

            if (!insertStatus.isOK()) {
                return insertStatus;
            }
        }

        return Status::OK();
    } catch (const DBException& ex) {
        return ex.toStatus();
    }
}
/* ****************************************************************************
*
* 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;
}
示例#13
0
void VStore::Object::copy(const OID &nobj) {
	//OID nobj = OID::create();
	OID res, value;
	int flags = 0;
	Attribute *attrib;
	char *dbuf = new char [5000];
	char *dbuf2;
	DASM *dasm = (DASM*)(root.get("notations").get("dasm"));

	for (int i=0; i<m_keys.size(); i++) {
		if (m_keys[i] == This) continue;
		attrib = Attribute::get(m_oid, m_keys[i]);
		if (attrib == 0) {
			res = Null;
			value = Null;
		} else {
			flags = attrib->getFlags();
			res = attrib->getDefinition();
		}

		/*if (flags & OID::FLAG_OUT_OF_DATE) {
			attrib->setFlags(flags & (0xFF - OID::FLAG_OUT_OF_DATE));
			flags = attrib->getFlags();
			attrib->setValue(Definition(res).evaluate(m_oid, m_keys[i], false));
			value = attrib->getValue();
		}*/
		
		if (res != Null) {
			
			//Clone the definition
			//value = res;
			//res = s_alloc;
			//s_alloc++;
			//value.copy(res);
			//std::cout << "CLONING DEF\n";
			
			dbuf2 = dbuf;
			
			Definition d = res;
			nobj.toString(dbuf2, 100);
			dbuf2 += strlen(dbuf2);
			strcpy(dbuf2, " ");
			dbuf2 += strlen(dbuf2);
			m_keys[i].toString(dbuf2,100);
			dbuf2 += strlen(dbuf2);
			
			if (flags && 0x01) {
				//nobj.define(m_keys[i], res, true);
				strcpy(dbuf2, " is { ");
				dbuf2 += strlen(dbuf2);
			} else {
				value = attrib->getValue();
				//nobj.set(m_keys[i], value, true);
				//nobj.definefuture(m_keys[i], res, true);
				strcpy(dbuf2, " = ");
				dbuf2 += strlen(dbuf2);
				value.toString(dbuf2, 100);
				dbuf2 += strlen(dbuf2);
				strcpy(dbuf2, "\n");
				dbuf2 += strlen(dbuf2);
				m_keys[i].toString(dbuf2, 100);
				dbuf2 += strlen(dbuf2);
				strcpy(dbuf2, " := { ");
				dbuf2 += strlen(dbuf2);
			}
			
			d.toString(dbuf2, 4000);
			dbuf2 += strlen(dbuf2);
			strcpy(dbuf2, " }\n");
			
			//std::cout << "DBUF: " << dbuf << "\n";
			
			//Execute
			dasm->execute(dbuf);
		} else {
			if (flags && OID::FLAG_DEEP) {

				//#ifdef LINUX
				//SPINLOCK
				//pthread_mutex_lock(&oid_lock);
				//#endif
				res = s_alloc;
				s_alloc++;
				//#ifdef LINUX
				//SPINLOCK
				//pthread_mutex_unlock(&oid_lock);
				//#endif

				value = attrib->getValue();
				value.copy(res);
				res.set(This, nobj, true);
				nobj.set(m_keys[i], res, true);
			} else {
				value = attrib->getValue();
				if (value != Null) nobj.set(m_keys[i], value, true);
			}
			nobj.flags(m_keys[i],flags, true);
		}

		//delete evt;
	}
	
	delete [] dbuf;
}
示例#14
0
/* ****************************************************************************
*
* 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;
}
    /**
     * Upgrade v3 to v4 described here.
     *
     * This upgrade takes a config server without collection epochs (potentially) and adds
     * epochs to all mongo processes.
     *
     */
    bool doUpgradeV3ToV4(const ConnectionString& configLoc,
                         const VersionType& lastVersionInfo,
                         string* errMsg)
    {
        string dummy;
        if (!errMsg) errMsg = &dummy;

        verify(lastVersionInfo.getCurrentVersion() == UpgradeHistory_NoEpochVersion);

        if (lastVersionInfo.isUpgradeIdSet() && lastVersionInfo.getUpgradeId().isSet()) {

            //
            // Another upgrade failed, so cleanup may be necessary
            //

            BSONObj lastUpgradeState = lastVersionInfo.getUpgradeState();

            bool inCriticalSection;
            if (!FieldParser::extract(lastUpgradeState,
                                      inCriticalSectionField,
                                      &inCriticalSection,
                                      errMsg))
            {

                *errMsg = stream() << "problem reading previous upgrade state" << causedBy(errMsg);

                return false;
            }

            if (inCriticalSection) {

                // Manual intervention is needed here.  Somehow our upgrade didn't get applied
                // consistently across config servers.

                *errMsg = cannotCleanupMessage;

                return false;
            }

            if (!_cleanupUpgradeState(configLoc, lastVersionInfo.getUpgradeId(), errMsg)) {
                
                // If we can't cleanup the old upgrade state, the user might have done it for us,
                // not a fatal problem (we'll just end up with extra collections).
                
                warning() << "could not cleanup previous upgrade state" << causedBy(errMsg) << endl;
                *errMsg = "";
            }
        }

        //
        // Check the versions of other mongo processes in the cluster before upgrade.
        // We can't upgrade if there are active pre-v2.2 processes in the cluster
        //

        Status mongoVersionStatus = checkClusterMongoVersions(configLoc,
                                                              string(minMongoProcessVersion));

        if (!mongoVersionStatus.isOK()) {

            *errMsg = stream() << "cannot upgrade with pre-v" << minMongoProcessVersion
                               << " mongo processes active in the cluster"
                               << causedBy(mongoVersionStatus);

            return false;
        }

        VersionType newVersionInfo;
        lastVersionInfo.cloneTo(&newVersionInfo);

        // Set our upgrade id and state
        OID upgradeId = OID::gen();
        newVersionInfo.setUpgradeId(upgradeId);
        newVersionInfo.setUpgradeState(BSONObj());

        // Write our upgrade id and state
        {
            scoped_ptr<ScopedDbConnection> connPtr;

            try {
                connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
                ScopedDbConnection& conn = *connPtr;

                verify(newVersionInfo.isValid(NULL));

                conn->update(VersionType::ConfigNS,
                             BSON("_id" << 1 << VersionType::version_DEPRECATED(3)),
                             newVersionInfo.toBSON());
                _checkGLE(conn);
            }
            catch (const DBException& e) {

                *errMsg = stream() << "could not initialize version info for upgrade"
                                   << causedBy(e);

                return false;
            }

            connPtr->done();
        }

        //
        // First lock all collection namespaces that exist
        //

        OwnedPointerMap<string, CollectionType> ownedCollections;
        const map<string, CollectionType*>& collections = ownedCollections.map();

        Status findCollectionsStatus = findAllCollectionsV3(configLoc, &ownedCollections);

        if (!findCollectionsStatus.isOK()) {

            *errMsg = stream() << "could not read collections from config server"
                               << causedBy(findCollectionsStatus);

            return false;
        }

        //
        // Acquire locks for all sharded collections
        // Something that didn't involve getting thousands of locks would be better.
        //

        OwnedPointerVector<ScopedDistributedLock> collectionLocks;

        log() << "acquiring locks for " << collections.size() << " sharded collections..." << endl;
        
        // WARNING - this string is used programmatically when forcing locks, be careful when
        // changing!
        // TODO: Add programmatic "why" field to lock collection
        string lockMessage = str::stream() << "ensuring epochs for config upgrade"
                                           << " (" << upgradeId.toString() << ")";
        
        if (!_acquireAllCollectionLocks(configLoc,
                                        collections,
                                        lockMessage,
                                        20 * 60 * 1000,
                                        &collectionLocks,
                                        errMsg))
        {

            *errMsg = stream() << "could not acquire all namespace locks for upgrade" 
                               << " (" << upgradeId.toString() << ")"
                               << causedBy(errMsg);

            return false;
        }

        // We are now preventing all splits and migrates for all sharded collections

        // Get working and backup suffixes
        string workingSuffix = genWorkingSuffix(upgradeId);
        string backupSuffix = genBackupSuffix(upgradeId);

        log() << "copying collection and chunk metadata to working and backup collections..."
              << endl;

        // Get a backup and working copy of the config.collections and config.chunks collections

        Status copyStatus = copyFrozenCollection(configLoc,
                                                 CollectionType::ConfigNS,
                                                 CollectionType::ConfigNS + workingSuffix);

        if (!copyStatus.isOK()) {

            *errMsg = stream() << "could not copy " << CollectionType::ConfigNS << " to "
                               << (CollectionType::ConfigNS + workingSuffix)
                               << causedBy(copyStatus);

            return false;
        }

        copyStatus = copyFrozenCollection(configLoc,
                                          CollectionType::ConfigNS,
                                          CollectionType::ConfigNS + backupSuffix);

        if (!copyStatus.isOK()) {

            *errMsg = stream() << "could not copy " << CollectionType::ConfigNS << " to "
                               << (CollectionType::ConfigNS + backupSuffix) << causedBy(copyStatus);

            return false;
        }

        copyStatus = copyFrozenCollection(configLoc,
                                          ChunkType::ConfigNS,
                                          ChunkType::ConfigNS + workingSuffix);

        if (!copyStatus.isOK()) {

            *errMsg = stream() << "could not copy " << ChunkType::ConfigNS << " to "
                               << (ChunkType::ConfigNS + workingSuffix) << causedBy(copyStatus);

            return false;
        }

        copyStatus = copyFrozenCollection(configLoc,
                                          ChunkType::ConfigNS,
                                          ChunkType::ConfigNS + backupSuffix);

        if (!copyStatus.isOK()) {

            *errMsg = stream() << "could not copy " << ChunkType::ConfigNS << " to "
                               << (ChunkType::ConfigNS + backupSuffix) << causedBy(copyStatus);

            return false;
        }

        //
        // Go through sharded collections one-by-one and add epochs where missing
        //

        for (map<string, CollectionType*>::const_iterator it = collections.begin();
                it != collections.end(); ++it)
        {
            // Create a copy so that we can change the epoch later
            CollectionType collection;
            it->second->cloneTo(&collection);

            log() << "checking epochs for " << collection.getNS() << " collection..." << endl;

            OID epoch = collection.getEpoch();

            //
            // Go through chunks to find epoch if we haven't found it or to verify epoch is the same
            //

            OwnedPointerVector<ChunkType> ownedChunks;
            const vector<ChunkType*>& chunks = ownedChunks.vector();

            Status findChunksStatus = findAllChunks(configLoc, collection.getNS(), &ownedChunks);

            if (!findChunksStatus.isOK()) {

                *errMsg = stream() << "could not read chunks from config server"
                                   << causedBy(findChunksStatus);

                return false;
            }

            for (vector<ChunkType*>::const_iterator chunkIt = chunks.begin();
                    chunkIt != chunks.end(); ++chunkIt)
            {
                const ChunkType& chunk = *(*chunkIt);

                // If our chunk epoch is set and doesn't match
                if (epoch.isSet() && chunk.getVersion().epoch().isSet()
                    && chunk.getVersion().epoch() != epoch)
                {

                    *errMsg = stream() << "chunk epoch for " << chunk.toString() << " in "
                                       << collection.getNS() << " does not match found epoch "
                                       << epoch;

                    return false;
                }
                else if (!epoch.isSet() && chunk.getVersion().epoch().isSet()) {
                    epoch = chunk.getVersion().epoch();
                }
            }

            //
            // Write collection epoch if needed
            //

            if (!collection.getEpoch().isSet()) {

                OID newEpoch = OID::gen();

                log() << "writing new epoch " << newEpoch << " for " << collection.getNS()
                      << " collection..." << endl;

                scoped_ptr<ScopedDbConnection> connPtr;

                try {
                    connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
                    ScopedDbConnection& conn = *connPtr;

                    conn->update(CollectionType::ConfigNS + workingSuffix,
                                 BSON(CollectionType::ns(collection.getNS())),
                                 BSON("$set" << BSON(CollectionType::DEPRECATED_lastmodEpoch(newEpoch))));
                    _checkGLE(conn);
                }
                catch (const DBException& e) {

                    *errMsg = stream() << "could not write a new epoch for " << collection.getNS()
                                       << causedBy(e);

                    return false;
                }

                connPtr->done();
                collection.setEpoch(newEpoch);
            }

            epoch = collection.getEpoch();
            verify(epoch.isSet());

            //
            // Now write verified epoch to all chunks
            //

            log() << "writing epoch " << epoch << " for " << chunks.size() << " chunks in "
                  << collection.getNS() << " collection..." << endl;

            {
                scoped_ptr<ScopedDbConnection> connPtr;

                try {
                    connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
                    ScopedDbConnection& conn = *connPtr;

                    // Multi-update of all chunks
                    conn->update(ChunkType::ConfigNS + workingSuffix,
                                 BSON(ChunkType::ns(collection.getNS())),
                                 BSON("$set" << BSON(ChunkType::DEPRECATED_epoch(epoch))),
                                 false,
                                 true); // multi
                    _checkGLE(conn);
                }
                catch (const DBException& e) {

                    *errMsg = stream() << "could not write a new epoch " << epoch.toString()
                                       << " for chunks in " << collection.getNS() << causedBy(e);

                    return false;
                }

                connPtr->done();
            }
        }

        //
        // Paranoid verify the collection writes
        //

        {
            scoped_ptr<ScopedDbConnection> connPtr;

            try {
                connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
                ScopedDbConnection& conn = *connPtr;

                // Find collections with no epochs
                BSONObj emptyDoc =
                        conn->findOne(CollectionType::ConfigNS + workingSuffix,
                                      BSON("$unset" << BSON(CollectionType::DEPRECATED_lastmodEpoch() << 1)));

                if (!emptyDoc.isEmpty()) {

                    *errMsg = stream() << "collection " << emptyDoc
                                       << " is still missing epoch after config upgrade";

                    connPtr->done();
                    return false;
                }

                // Find collections with empty epochs
                emptyDoc = conn->findOne(CollectionType::ConfigNS + workingSuffix,
                                         BSON(CollectionType::DEPRECATED_lastmodEpoch(OID())));

                if (!emptyDoc.isEmpty()) {

                    *errMsg = stream() << "collection " << emptyDoc
                                       << " still has empty epoch after config upgrade";

                    connPtr->done();
                    return false;
                }

                // Find chunks with no epochs
                emptyDoc =
                        conn->findOne(ChunkType::ConfigNS + workingSuffix,
                                      BSON("$unset" << BSON(ChunkType::DEPRECATED_epoch() << 1)));

                if (!emptyDoc.isEmpty()) {

                    *errMsg = stream() << "chunk " << emptyDoc
                                       << " is still missing epoch after config upgrade";

                    connPtr->done();
                    return false;
                }

                // Find chunks with empty epochs
                emptyDoc = conn->findOne(ChunkType::ConfigNS + workingSuffix,
                                         BSON(ChunkType::DEPRECATED_epoch(OID())));

                if (!emptyDoc.isEmpty()) {

                    *errMsg = stream() << "chunk " << emptyDoc
                                       << " still has empty epoch after config upgrade";

                    connPtr->done();
                    return false;
                }
            }
            catch (const DBException& e) {

                *errMsg = stream() << "could not verify epoch writes" << causedBy(e);

                return false;
            }

            connPtr->done();
        }

        //
        // Double check that our collections haven't changed
        //

        Status idCheckStatus = checkIdsTheSame(configLoc,
                                               CollectionType::ConfigNS,
                                               CollectionType::ConfigNS + workingSuffix);

        if (!idCheckStatus.isOK()) {

            *errMsg = stream() << CollectionType::ConfigNS
                               << " was modified while working on upgrade"
                               << causedBy(idCheckStatus);

            return false;
        }

        idCheckStatus = checkIdsTheSame(configLoc,
                                        ChunkType::ConfigNS,
                                        ChunkType::ConfigNS + workingSuffix);

        if (!idCheckStatus.isOK()) {

            *errMsg = stream() << ChunkType::ConfigNS << " was modified while working on upgrade"
                               << causedBy(idCheckStatus);

            return false;
        }

        //
        // ENTER CRITICAL SECTION
        //

        newVersionInfo.setUpgradeState(BSON(inCriticalSectionField(true)));

        {
            scoped_ptr<ScopedDbConnection> connPtr;

            try {
                connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
                ScopedDbConnection& conn = *connPtr;

                verify(newVersionInfo.isValid(NULL));

                conn->update(VersionType::ConfigNS,
                             BSON("_id" << 1 << VersionType::version_DEPRECATED(3)),
                             newVersionInfo.toBSON());
                _checkGLE(conn);
            }
            catch (const DBException& e) {

                // No cleanup message here since we're not sure if we wrote or not, and
                // not dangerous either way except to prevent further updates (at which point
                // the message is printed)
                *errMsg = stream()
                        << "could not update version info to enter critical update section"
                        << causedBy(e);

                return false;
            }

            // AT THIS POINT ANY FAILURE REQUIRES MANUAL INTERVENTION!
            connPtr->done();
        }

        log() << "entered critical section for config upgrade" << endl;

        Status overwriteStatus = overwriteCollection(configLoc,
                                                     CollectionType::ConfigNS + workingSuffix,
                                                     CollectionType::ConfigNS);

        if (!overwriteStatus.isOK()) {

            error() << cleanupMessage << endl;
            *errMsg = stream() << "could not overwrite collection " << CollectionType::ConfigNS
                               << " with working collection "
                               << (CollectionType::ConfigNS + workingSuffix)
                               << causedBy(overwriteStatus);

            return false;
        }

        overwriteStatus = overwriteCollection(configLoc,
                                              ChunkType::ConfigNS + workingSuffix,
                                              ChunkType::ConfigNS);

        if (!overwriteStatus.isOK()) {

            error() << cleanupMessage << endl;
            *errMsg = stream() << "could not overwrite collection " << ChunkType::ConfigNS
                               << " with working collection "
                               << (ChunkType::ConfigNS + workingSuffix)
                               << causedBy(overwriteStatus);

            return false;
        }

        //
        // Finally update the version to latest and add clusterId to version
        //

        OID newClusterId = OID::gen();

        // Note: hardcoded versions, since this is a very particular upgrade
        // Note: DO NOT CLEAR the config version unless bumping the minCompatibleVersion,
        // we want to save the excludes that were set.

        newVersionInfo.setMinCompatibleVersion(UpgradeHistory_NoEpochVersion);
        newVersionInfo.setCurrentVersion(UpgradeHistory_MandatoryEpochVersion);
        newVersionInfo.setClusterId(newClusterId);

        // Leave critical section
        newVersionInfo.unsetUpgradeId();
        newVersionInfo.unsetUpgradeState();

        log() << "writing new version info and clusterId " << newClusterId << "..." << endl;

        {
            scoped_ptr<ScopedDbConnection> connPtr;

            try {
                connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
                ScopedDbConnection& conn = *connPtr;

                verify(newVersionInfo.isValid(NULL));

                conn->update(VersionType::ConfigNS,
                             BSON("_id" << 1 << VersionType::version_DEPRECATED(UpgradeHistory_NoEpochVersion)),
                             newVersionInfo.toBSON());
                _checkGLE(conn);
            }
            catch (const DBException& e) {

                error() << cleanupMessage << endl;
                *errMsg = stream() << "could not write new version info "
                                   << "and exit critical upgrade section" << causedBy(e);

                return false;
            }

            connPtr->done();
        }

        //
        // END CRITICAL SECTION
        //

        return true;
    }
    bool _cleanupUpgradeState(const ConnectionString& configLoc,
                              const OID& lastUpgradeId,
                              string* errMsg)
    {
        string dummy;
        if (!errMsg) errMsg = &dummy;

        scoped_ptr<ScopedDbConnection> connPtr;

        string workingSuffix = genWorkingSuffix(lastUpgradeId);

        try {
            connPtr.reset(ScopedDbConnection::getInternalScopedDbConnection(configLoc, 30));
            ScopedDbConnection& conn = *connPtr;

            // Drop old upgrade collections on config server

            bool resultOk;
            BSONObj dropResult;

            resultOk = conn->dropCollection(CollectionType::ConfigNS + workingSuffix, &dropResult);

            if (!resultOk) {

                *errMsg = stream() << "could not drop collection "
                                   << (CollectionType::ConfigNS + workingSuffix)
                                   << causedBy(dropResult.toString());

                return false;
            }

            resultOk = conn->dropCollection(ChunkType::ConfigNS + workingSuffix, &dropResult);

            if (!resultOk) {

                *errMsg = stream() << "could not drop collection "
                                   << (ChunkType::ConfigNS + workingSuffix)
                                   << causedBy(dropResult.toString());

                return false;
            }

            // Force old locks taken by previous upgrade process on config server
            // This is safe because no previous upgrade process can be active while we hold the
            // upgrade lock.

            log() << "forcing upgrade locks of previous failed upgrade with id "
                  << lastUpgradeId.toString() << endl;

            // Explicit builder needed b/c of regex
            BSONObjBuilder lockQueryB;
            lockQueryB.appendRegex(LocksType::why(), 
                                   pcrecpp::RE::QuoteMeta("(" + lastUpgradeId.toString() + ")"));

            conn->update(LocksType::ConfigNS,
                         lockQueryB.obj(),
                         BSON("$set" << BSON(LocksType::state(0))),
                         false, true); // multi
            _checkGLE(conn);

        }
        catch (const DBException& e) {

            *errMsg = stream() << "could not drop collections during cleanup of upgrade "
                               << lastUpgradeId << causedBy(e);

            return false;
        }

        connPtr->done();
        return true;
    }
示例#17
0
 string genWorkingSuffix(const OID& lastUpgradeId) {
     return "-upgrade-" + lastUpgradeId.toString();
 }
/* ****************************************************************************
*
* 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;
}
示例#19
0
 string genBackupSuffix(const OID& lastUpgradeId) {
     return "-backup-" + lastUpgradeId.toString();
 }
示例#20
0
/* ****************************************************************************
*
* 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;
}
/* ****************************************************************************
*
* mongoSubscribeContext - 
*/
HttpStatusCode mongoSubscribeContext
(
  SubscribeContextRequest*             requestP,
  SubscribeContextResponse*            responseP,
  const std::string&                   tenant,
  std::map<std::string, std::string>&  uriParam,
  const std::string&                   xauthToken,
  const std::vector<std::string>&      servicePathV
)
{
    const std::string  notifyFormatAsString  = uriParam[URI_PARAM_NOTIFY_FORMAT];
    Format             notifyFormat          = stringToFormat(notifyFormatAsString);
    std::string        servicePath           = (servicePathV.size() == 0)? "" : servicePathV[0];    
    bool               reqSemTaken           = false;

    LM_T(LmtMongo, ("Subscribe Context Request: notifications sent in '%s' format", notifyFormatAsString.c_str()));

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

    //
    // Calculate expiration (using the current time and the duration field in the request).
    // If expiration is not present, use a default value
    //
    long long expiration = -1;
    if (requestP->expires > 0)
    {
      expiration = requestP->expires;
    }
    else
    {
      if (requestP->duration.isEmpty())
      {
        requestP->duration.set(DEFAULT_DURATION);
      }

      expiration = getCurrentTime() + requestP->duration.parse();
    }
    LM_T(LmtMongo, ("Subscription expiration: %lu", expiration));

    /* Create the mongoDB subscription document */
    BSONObjBuilder  sub;
    OID             oid;

    oid.init();

    sub.append("_id", oid);
    sub.append(CSUB_EXPIRATION, expiration);
    sub.append(CSUB_REFERENCE,  requestP->reference.get());


    /* Throttling */
    long long throttling = 0;
    if (requestP->throttling.seconds != -1)
    {
      throttling = (long long) requestP->throttling.seconds;
      sub.append(CSUB_THROTTLING, throttling);
    }
    else if (!requestP->throttling.isEmpty())
    {
      throttling = (long long) requestP->throttling.parse();
      sub.append(CSUB_THROTTLING, throttling);
    }

    if (servicePath != "")
    {
      sub.append(CSUB_SERVICE_PATH, servicePath);
    }

    
    /* Build entities array */
    BSONArrayBuilder entities;
    for (unsigned int ix = 0; ix < requestP->entityIdVector.size(); ++ix)
    {
        EntityId* en = requestP->entityIdVector[ix];

        if (en->type == "")
        {
          entities.append(BSON(CSUB_ENTITY_ID << en->id <<
                               CSUB_ENTITY_ISPATTERN << en->isPattern));
        }
        else
        {
          entities.append(BSON(CSUB_ENTITY_ID << en->id <<
                               CSUB_ENTITY_TYPE << en->type <<
                               CSUB_ENTITY_ISPATTERN << en->isPattern));
        }
    }
    sub.append(CSUB_ENTITIES, entities.arr());

    /* Build attributes array */
    BSONArrayBuilder attrs;
    for (unsigned int ix = 0; ix < requestP->attributeList.size(); ++ix) {
        attrs.append(requestP->attributeList[ix]);
    }
    sub.append(CSUB_ATTRS, attrs.arr());

    /* Build conditions array (including side-effect notifications and threads creation) */
    bool notificationDone = false;
    BSONArray conds = processConditionVector(&requestP->notifyConditionVector,
                                             requestP->entityIdVector,
                                             requestP->attributeList, oid.toString(),
                                             requestP->reference.get(),
                                             &notificationDone,
                                             notifyFormat,
                                             tenant,
                                             xauthToken,
                                             servicePathV,
                                             requestP->expression.q);
    sub.append(CSUB_CONDITIONS, conds);

    /* Build expression */
    BSONObjBuilder expression;

    expression << CSUB_EXPR_Q << requestP->expression.q
               << CSUB_EXPR_GEOM << requestP->expression.geometry
               << CSUB_EXPR_COORDS << requestP->expression.coords
               << CSUB_EXPR_GEOREL << requestP->expression.georel;
    sub.append(CSUB_EXPR, expression.obj());

    /* Last notification */
    long long lastNotificationTime = 0;
    if (notificationDone)
    {
      lastNotificationTime = (long long) getCurrentTime();

      sub.append(CSUB_LASTNOTIFICATION, lastNotificationTime);
      sub.append(CSUB_COUNT, 1);
    }

    /* Adding format to use in notifications */
    sub.append(CSUB_FORMAT, notifyFormatAsString);

    /* Insert document in database */
    std::string err;
    if (!collectionInsert(getSubscribeContextCollectionName(tenant), sub.obj(), &err))
    {
      reqSemGive(__FUNCTION__, "ngsi10 subscribe request (mongo db exception)", reqSemTaken);
      responseP->subscribeError.errorCode.fill(SccReceiverInternalError, err);
      return SccOk;
    }

    //
    // 3. Create Subscription for the cache
    //
    std::string oidString = oid.toString();

    LM_T(LmtSubCache, ("inserting a new sub in cache (%s)", oidString.c_str()));

    cacheSemTake(__FUNCTION__, "Inserting subscription in cache");
    subCacheItemInsert(tenant.c_str(),
                       servicePath.c_str(),
                       requestP,
                       oidString.c_str(),
                       expiration,
                       throttling,
                       notifyFormat,
                       notificationDone,
                       lastNotificationTime,
                       requestP->expression.q,
                       requestP->expression.geometry,
                       requestP->expression.coords,
                       requestP->expression.georel);

    cacheSemGive(__FUNCTION__, "Inserting subscription in cache");

    reqSemGive(__FUNCTION__, "ngsi10 subscribe request", reqSemTaken);

    /* Fill int the response element */
    responseP->subscribeResponse.duration = requestP->duration;
    responseP->subscribeResponse.subscriptionId.set(oid.toString());
    responseP->subscribeResponse.throttling = requestP->throttling;

    return SccOk;
}