void testSort() { DBClientConnection c; string err; if ( !c.connect("localhost", err) ) { out() << "can't connect to server " << err << endl; return; } cout << "findOne returns:" << endl; cout << c.findOne("test.foo", QUERY( "x" << 3 ) ).toString() << endl; cout << c.findOne("test.foo", QUERY( "x" << 3 ).sort("name") ).toString() << endl; }
BSONObj DBClientReplicaSet::findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions) { shared_ptr<ReadPreferenceSetting> readPref( _extractReadPref( query.obj, queryOptions ) ); if ( _isSecondaryQuery( ns, query.obj, *readPref ) ) { LOG( 3 ) << "dbclient_rs findOne using secondary or tagged node selection in " << _getMonitor()->getName() << ", read pref is " << readPref->toBSON() << " (primary : " << ( _master.get() != NULL ? _master->getServerAddress() : "[not cached]" ) << ", lastTagged : " << ( _lastSlaveOkConn.get() != NULL ? _lastSlaveOkConn->getServerAddress() : "[not cached]" ) << ")" << endl; string lastNodeErrMsg; for (size_t retry = 0; retry < MAX_RETRY; retry++) { try { DBClientConnection* conn = selectNodeUsingTags(readPref); if (conn == NULL) { break; } return conn->findOne(ns,query,fieldsToReturn,queryOptions); } catch ( const DBException &dbExcep ) { StringBuilder errMsgBuilder; errMsgBuilder << "can't findone replica set node " << _lastSlaveOkHost.toString() << ": " << causedBy( dbExcep ); lastNodeErrMsg = errMsgBuilder.str(); LOG(1) << lastNodeErrMsg << endl; invalidateLastSlaveOkCache(); } } StringBuilder assertMsg; assertMsg << "Failed to call findOne, no good nodes in " << _getMonitor()->getName(); if ( !lastNodeErrMsg.empty() ) { assertMsg << ", last error: " << lastNodeErrMsg; } uasserted(16379, assertMsg.str()); } LOG( 3 ) << "dbclient_rs findOne to primary node in " << _getMonitor()->getName() << endl; return checkMaster()->findOne(ns,query,fieldsToReturn,queryOptions); }
/* **************************************************************************** * * mongoRegisterContext - */ HttpStatusCode mongoRegisterContext(RegisterContextRequest* requestP, RegisterContextResponse* responseP) { /* Take semaphore. The LM_S* family of macros combines semaphore release with return */ semTake(); LM_T(LmtMongo, ("Register Context Request")); DBClientConnection* connection = getMongoConnection(); /* Check if new registration */ if (requestP->registrationId.isEmpty()) { HttpStatusCode result = processRegisterContext(requestP, responseP, NULL); LM_SR(result); } /* It is not a new registration, so it should be an update */ try { OID id = OID(requestP->registrationId.get()); BSONObj reg = connection->findOne(getRegistrationsCollectionName(), BSON("_id" << id)); if (reg.isEmpty()) { responseP->errorCode.fill(SccContextElementNotFound, std::string("registration id: '") + requestP->registrationId.get() + "'"); responseP->registrationId = requestP->registrationId; ++noOfRegistrationUpdateErrors; LM_SR(SccOk); } HttpStatusCode result = processRegisterContext(requestP, responseP, &id); LM_SR(result); } catch( const AssertionException &e ) { /* 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_SR(SccOk); } catch( const DBException &e ) { responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getRegistrationsCollectionName() + " - findOne() _id: " + requestP->registrationId.get() + " - exception: " + e.what()); ++noOfRegistrationUpdateErrors; LM_SR(SccOk); } }
int em_mongodb::query(std::string dbcoll,mongo::Query cond,std::string& out) { int ret = MDB_FAIL_QUERY; DBClientConnection* pconn = getConn(); if(!pconn) return ret; BSONObj obj = pconn->findOne(dbcoll,cond); out = obj.toString(); boost::mutex::scoped_lock lock(m_iomux); m_connpool[pconn] = false; sem_post(&m_jobsem); ret = MDB_RET_SUCCESS; return ret; }
/* **************************************************************************** * * unsubscribe - */ TEST(mongoUnsubscribeContext, unsubscribe) { HttpStatusCode ms; UnsubscribeContextRequest req; UnsubscribeContextResponse res; /* Prepare mock */ NotifierMock* notifierMock = new NotifierMock(); EXPECT_CALL(*notifierMock, destroyOntimeIntervalThreads("51307b66f481db11bf860001")) .Times(1); EXPECT_CALL(*notifierMock, sendNotifyContextRequest(_,_,_)) .Times(0); EXPECT_CALL(*notifierMock, createIntervalThread(_,_)) .Times(0); setNotifier(notifierMock); /* Forge the request (from "inside" to "outside") */ req.subscriptionId.set("51307b66f481db11bf860001"); /* Prepare database */ prepareDatabase(); /* Invoke the function in mongoBackend library */ ms = mongoUnsubscribeContext(&req, &res); /* Check response is as expected */ EXPECT_EQ(SccOk, ms); EXPECT_EQ("51307b66f481db11bf860001", res.subscriptionId.get()); EXPECT_EQ(SccOk, res.statusCode.code); EXPECT_EQ("OK", res.statusCode.reasonPhrase); EXPECT_EQ(0, res.statusCode.details.size()); /* Check database (one document, but not the deleted one) */ DBClientConnection* connection = getMongoConnection(); ASSERT_EQ(1, connection->count(SUBSCRIBECONTEXT_COLL, BSONObj())); BSONObj sub = connection->findOne(SUBSCRIBECONTEXT_COLL, BSON("_id" << OID("51307b66f481db11bf860002"))); EXPECT_EQ("51307b66f481db11bf860002", sub.getField("_id").OID().str()); /* Release connection */ mongoDisconnect(); /* Release mock */ delete notifierMock; }
int main( int argc, const char **argv ) { const char *port = "27017"; if ( argc != 1 ) { if ( argc != 3 ) throw -12; port = argv[ 2 ]; } DBClientConnection conn; string errmsg; if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) { cout << "couldn't connect : " << errmsg << endl; throw -11; } const char * ns = "test.test1"; conn.dropCollection(ns); // clean up old data from any previous tests conn.remove( ns, BSONObj() ); assert( conn.findOne( ns , BSONObj() ).isEmpty() ); // test insert conn.insert( ns ,BSON( "name" << "eliot" << "num" << 1 ) ); assert( ! conn.findOne( ns , BSONObj() ).isEmpty() ); // test remove conn.remove( ns, BSONObj() ); assert( conn.findOne( ns , BSONObj() ).isEmpty() ); // insert, findOne testing conn.insert( ns , BSON( "name" << "eliot" << "num" << 1 ) ); { BSONObj res = conn.findOne( ns , BSONObj() ); assert( strstr( res.getStringField( "name" ) , "eliot" ) ); assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); assert( 1 == res.getIntField( "num" ) ); } // cursor conn.insert( ns ,BSON( "name" << "sara" << "num" << 2 ) ); { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } assert( count == 2 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 1 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } assert( count == 1 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 3 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } assert( count == 0 ); } // update { BSONObj res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); BSONObj after = BSONObjBuilder().appendElements( res ).append( "name2" , "h" ).obj(); conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); assert( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); conn.update( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); assert( strstr( res.getStringField( "name" ) , "eliot" ) ); assert( strstr( res.getStringField( "name2" ) , "h" ) ); assert( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); // upsert conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after , 1 ); assert( ! conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ).isEmpty() ); } { // ensure index assert( conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); assert( ! conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); } { // hint related tests assert( conn.findOne(ns, "{}")["name"].str() == "sara" ); assert( conn.findOne(ns, "{ name : 'eliot' }")["name"].str() == "eliot" ); assert( conn.getLastError() == "" ); // nonexistent index test bool asserted = false; try { conn.findOne(ns, Query("{name:\"eliot\"}").hint("{foo:1}")); } catch ( ... ){ asserted = true; } assert( asserted ); //existing index assert( conn.findOne(ns, Query("{name:'eliot'}").hint("{name:1}")).hasElement("name") ); // run validate assert( conn.validate( ns ) ); } { // timestamp test const char * tsns = "test.tstest1"; conn.dropCollection( tsns ); { mongo::BSONObjBuilder b; b.appendTimestamp( "ts" ); conn.insert( tsns , b.obj() ); } mongo::BSONObj out = conn.findOne( tsns , mongo::BSONObj() ); Date_t oldTime = out["ts"].timestampTime(); unsigned int oldInc = out["ts"].timestampInc(); { mongo::BSONObjBuilder b1; b1.append( out["_id"] ); mongo::BSONObjBuilder b2; b2.append( out["_id"] ); b2.appendTimestamp( "ts" ); conn.update( tsns , b1.obj() , b2.obj() ); } BSONObj found = conn.findOne( tsns , mongo::BSONObj() ); cout << "old: " << out << "\nnew: " << found << endl; assert( ( oldTime < found["ts"].timestampTime() ) || ( oldTime == found["ts"].timestampTime() && oldInc < found["ts"].timestampInc() ) ); } { // check that killcursors doesn't affect last error assert( conn.getLastError().empty() ); BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendNum( (int)-1 ); // invalid # of cursors triggers exception b.appendNum( (int)-1 ); // bogus cursor id Message m; m.setData( dbKillCursors, b.buf(), b.len() ); // say() is protected in DBClientConnection, so get superclass static_cast< DBConnector* >( &conn )->say( m ); assert( conn.getLastError().empty() ); } { list<string> l = conn.getDatabaseNames(); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ){ cout << "db name : " << *i << endl; } l = conn.getCollectionNames( "test" ); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ){ cout << "coll name : " << *i << endl; } } cout << "client test finished!" << endl; }
/* **************************************************************************** * * mongoUpdateContextAvailabilitySubscription - */ HttpStatusCode mongoUpdateContextAvailabilitySubscription(UpdateContextAvailabilitySubscriptionRequest* requestP, UpdateContextAvailabilitySubscriptionResponse* responseP, Format inFormat, const std::string& tenant) { LM_T(LmtMongo, ("Update Context Subscription")); reqSemTake(__FUNCTION__, "ngsi9 update subscription request"); DBClientConnection* connection = getMongoConnection(); /* Look for document */ BSONObj sub; try { OID id = OID(requestP->subscriptionId.get()); mongoSemTake(__FUNCTION__, "findOne from SubscribeContextAvailabilityCollection"); sub = connection->findOne(getSubscribeContextAvailabilityCollectionName(tenant).c_str(), BSON("_id" << id)); mongoSemGive(__FUNCTION__, "findOne from SubscribeContextAvailabilityCollection"); } catch( const AssertionException &e ) { /* 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) mongoSemGive(__FUNCTION__, "findOne from SubscribeContextAvailabilityCollection (mongo assertion exception)"); reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo assertion exception)"); responseP->errorCode.fill(SccContextElementNotFound); return SccOk; } catch( const DBException &e ) { mongoSemGive(__FUNCTION__, "findOne from SubscribeContextAvailabilityCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo db exception)"); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName(tenant).c_str() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + e.what()); return SccOk; } catch(...) { mongoSemGive(__FUNCTION__, "findOne from SubscribeContextAvailabilityCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo generic exception)"); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName(tenant).c_str() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + "generic"); return SccOk; } if (sub.isEmpty()) { responseP->errorCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi9 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 (mandatory) */ BSONArrayBuilder entities; for (unsigned int ix = 0; ix < requestP->entityIdVector.size(); ++ix) { EntityId* en = requestP->entityIdVector.get(ix); if (en->type == "") { entities.append(BSON(CASUB_ENTITY_ID << en->id << CASUB_ENTITY_ISPATTERN << en->isPattern)); } else { entities.append(BSON(CASUB_ENTITY_ID << en->id << CASUB_ENTITY_TYPE << en->type << CASUB_ENTITY_ISPATTERN << en->isPattern)); } } newSub.append(CASUB_ENTITIES, entities.arr()); /* Attributes (always taken into account) */ BSONArrayBuilder attrs; for (unsigned int ix = 0; ix < requestP->attributeList.size(); ++ix) { attrs.append(requestP->attributeList.get(ix)); } newSub.append(CASUB_ATTRS, attrs.arr()); /* Duration (optional) */ if (requestP->duration.isEmpty()) { newSub.append(CASUB_EXPIRATION, sub.getField(CASUB_EXPIRATION).numberLong()); } else { long long expiration = getCurrentTime() + requestP->duration.parse(); newSub.append(CASUB_EXPIRATION, expiration); LM_T(LmtMongo, ("New subscription expiration: %l", expiration)); } /* Reference is not updatable, so it is appended directly */ newSub.append(CASUB_REFERENCE, STR_FIELD(sub, CASUB_REFERENCE)); int count = sub.hasField(CASUB_COUNT) ? sub.getIntField(CASUB_COUNT) : 0; /* The hasField check is needed due to lastNotification/count could not be present in the original doc */ if (sub.hasField(CASUB_LASTNOTIFICATION)) { newSub.append(CASUB_LASTNOTIFICATION, sub.getIntField(CASUB_LASTNOTIFICATION)); } if (sub.hasField(CASUB_COUNT)) { newSub.append(CASUB_COUNT, count); } /* Adding format to use in notifications */ newSub.append(CASUB_FORMAT, std::string(formatToString(inFormat))); /* Update document in MongoDB */ BSONObj update = newSub.obj(); LM_T(LmtMongo, ("update() in '%s' collection _id '%s': %s}", getSubscribeContextAvailabilityCollectionName(tenant).c_str(), requestP->subscriptionId.get().c_str(), update.toString().c_str())); try { mongoSemTake(__FUNCTION__, "update in SubscribeContextAvailabilityCollection"); connection->update(getSubscribeContextAvailabilityCollectionName(tenant).c_str(), BSON("_id" << OID(requestP->subscriptionId.get())), update); mongoSemGive(__FUNCTION__, "update in SubscribeContextAvailabilityCollection"); } catch( const DBException &e ) { mongoSemGive(__FUNCTION__, "update in SubscribeContextAvailabilityCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo db exception)"); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName(tenant).c_str() + " - update() _id: " + requestP->subscriptionId.get().c_str() + " - update() doc: " + update.toString() + " - exception: " + e.what()); return SccOk; } catch(...) { mongoSemGive(__FUNCTION__, "update in SubscribeContextAvailabilityCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi9 update subscription request (mongo generic exception)"); responseP->errorCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName(tenant).c_str() + " - update() _id: " + requestP->subscriptionId.get().c_str() + " - update() doc: " + update.toString() + " - exception: " + "generic"); return SccOk; } /* Send notifications for matching context registrations */ processAvailabilitySubscription(requestP->entityIdVector, requestP->attributeList, requestP->subscriptionId.get(), STR_FIELD(sub, CASUB_REFERENCE), inFormat, tenant); /* Duration is an optional parameter, it is only added in the case they * was used for update */ if (!requestP->duration.isEmpty()) { responseP->duration = requestP->duration; } responseP->subscriptionId = requestP->subscriptionId; reqSemGive(__FUNCTION__, "ngsi9 update subscription request"); return SccOk; }
void syncFixUp(OperationContext* txn, FixUpInfo& fixUpInfo, OplogReader* oplogreader, ReplicationCoordinator* replCoord) { DBClientConnection* them = oplogreader->conn(); // fetch all first so we needn't handle interruption in a fancy way unsigned long long totalSize = 0; list< pair<DocID, BSONObj> > goodVersions; BSONObj newMinValid; // fetch all the goodVersions of each document from current primary DocID doc; unsigned long long numFetched = 0; try { for (set<DocID>::iterator it = fixUpInfo.toRefetch.begin(); it != fixUpInfo.toRefetch.end(); it++) { doc = *it; verify(!doc._id.eoo()); { // TODO : slow. lots of round trips. numFetched++; BSONObj good = them->findOne(doc.ns, doc._id.wrap(), NULL, QueryOption_SlaveOk).getOwned(); totalSize += good.objsize(); uassert(13410, "replSet too much data to roll back", totalSize < 300 * 1024 * 1024); // note good might be eoo, indicating we should delete it goodVersions.push_back(pair<DocID, BSONObj>(doc,good)); } } newMinValid = oplogreader->getLastOp(rsOplogName); if (newMinValid.isEmpty()) { error() << "rollback error newMinValid empty?"; return; } } catch (DBException& e) { LOG(1) << "rollback re-get objects: " << e.toString(); error() << "rollback couldn't re-get ns:" << doc.ns << " _id:" << doc._id << ' ' << numFetched << '/' << fixUpInfo.toRefetch.size(); throw e; } log() << "rollback 3.5"; if (fixUpInfo.rbid != getRBID(oplogreader->conn())) { // our source rolled back itself. so the data we received isn't necessarily consistent. warning() << "rollback rbid on source changed during rollback, cancelling this attempt"; return; } // update them log() << "rollback 4 n:" << goodVersions.size(); bool warn = false; invariant(!fixUpInfo.commonPointOurDiskloc.isNull()); invariant(txn->lockState()->isW()); // we have items we are writing that aren't from a point-in-time. thus best not to come // online until we get to that point in freshness. Timestamp minValid = newMinValid["ts"].timestamp(); log() << "minvalid=" << minValid.toStringLong(); setMinValid(txn, minValid); // any full collection resyncs required? if (!fixUpInfo.collectionsToResyncData.empty() || !fixUpInfo.collectionsToResyncMetadata.empty()) { for (const string& ns : fixUpInfo.collectionsToResyncData) { log() << "rollback 4.1.1 coll resync " << ns; fixUpInfo.collectionsToResyncMetadata.erase(ns); const NamespaceString nss(ns); Database* db = dbHolder().openDb(txn, nss.db().toString()); invariant(db); { WriteUnitOfWork wunit(txn); db->dropCollection(txn, ns); wunit.commit(); } { string errmsg; // This comes as a GlobalWrite lock, so there is no DB to be acquired after // resume, so we can skip the DB stability checks. Also // copyCollectionFromRemote will acquire its own database pointer, under the // appropriate locks, so just releasing and acquiring the lock is safe. invariant(txn->lockState()->isW()); Lock::TempRelease release(txn->lockState()); bool ok = copyCollectionFromRemote(txn, them->getServerAddress(), ns, errmsg); uassert(15909, str::stream() << "replSet rollback error resyncing collection " << ns << ' ' << errmsg, ok); } } for (const string& ns : fixUpInfo.collectionsToResyncMetadata) { log() << "rollback 4.1.2 coll metadata resync " << ns; const NamespaceString nss(ns); auto db = dbHolder().openDb(txn, nss.db().toString()); invariant(db); auto collection = db->getCollection(ns); invariant(collection); auto cce = collection->getCatalogEntry(); const std::list<BSONObj> info = them->getCollectionInfos(nss.db().toString(), BSON("name" << nss.coll())); if (info.empty()) { // Collection dropped by "them" so we should drop it too. log() << ns << " not found on remote host, dropping"; fixUpInfo.toDrop.insert(ns); continue; } invariant(info.size() == 1); CollectionOptions options; auto status = options.parse(info.front()); if (!status.isOK()) { throw RSFatalException(str::stream() << "Failed to parse options " << info.front() << ": " << status.toString()); } WriteUnitOfWork wuow(txn); if (options.flagsSet || cce->getCollectionOptions(txn).flagsSet) { cce->updateFlags(txn, options.flags); } status = collection->setValidator(txn, options.validator); if (!status.isOK()) { throw RSFatalException(str::stream() << "Failed to set validator: " << status.toString()); } wuow.commit(); } // we did more reading from primary, so check it again for a rollback (which would mess // us up), and make minValid newer. log() << "rollback 4.2"; string err; try { newMinValid = oplogreader->getLastOp(rsOplogName); if (newMinValid.isEmpty()) { err = "can't get minvalid from sync source"; } else { Timestamp minValid = newMinValid["ts"].timestamp(); log() << "minvalid=" << minValid.toStringLong(); setMinValid(txn, minValid); } } catch (DBException& e) { err = "can't get/set minvalid: "; err += e.what(); } if (fixUpInfo.rbid != getRBID(oplogreader->conn())) { // our source rolled back itself. so the data we received isn't necessarily // consistent. however, we've now done writes. thus we have a problem. err += "rbid at primary changed during resync/rollback"; } if (!err.empty()) { severe() << "rolling back : " << err << ". A full resync will be necessary."; // TODO: reset minvalid so that we are permanently in fatal state // TODO: don't be fatal, but rather, get all the data first. throw RSFatalException(); } log() << "rollback 4.3"; } map<string,shared_ptr<Helpers::RemoveSaver> > removeSavers; log() << "rollback 4.6"; // drop collections to drop before doing individual fixups - that might make things faster // below actually if there were subsequent inserts to rollback for (set<string>::iterator it = fixUpInfo.toDrop.begin(); it != fixUpInfo.toDrop.end(); it++) { log() << "rollback drop: " << *it; Database* db = dbHolder().get(txn, nsToDatabaseSubstring(*it)); if (db) { WriteUnitOfWork wunit(txn); shared_ptr<Helpers::RemoveSaver>& removeSaver = removeSavers[*it]; if (!removeSaver) removeSaver.reset(new Helpers::RemoveSaver("rollback", "", *it)); // perform a collection scan and write all documents in the collection to disk boost::scoped_ptr<PlanExecutor> exec( InternalPlanner::collectionScan(txn, *it, db->getCollection(*it))); BSONObj curObj; PlanExecutor::ExecState execState; while (PlanExecutor::ADVANCED == (execState = exec->getNext(&curObj, NULL))) { removeSaver->goingToDelete(curObj); } if (execState != PlanExecutor::IS_EOF) { if (execState == PlanExecutor::FAILURE && WorkingSetCommon::isValidStatusMemberObject(curObj)) { Status errorStatus = WorkingSetCommon::getMemberObjectStatus(curObj); severe() << "rolling back createCollection on " << *it << " failed with " << errorStatus << ". A full resync is necessary."; } else { severe() << "rolling back createCollection on " << *it << " failed. A full resync is necessary."; } throw RSFatalException(); } db->dropCollection(txn, *it); wunit.commit(); } } log() << "rollback 4.7"; OldClientContext ctx(txn, rsOplogName); Collection* oplogCollection = ctx.db()->getCollection(rsOplogName); uassert(13423, str::stream() << "replSet error in rollback can't find " << rsOplogName, oplogCollection); unsigned deletes = 0, updates = 0; time_t lastProgressUpdate = time(0); time_t progressUpdateGap = 10; for (list<pair<DocID, BSONObj> >::iterator it = goodVersions.begin(); it != goodVersions.end(); it++) { time_t now = time(0); if (now - lastProgressUpdate > progressUpdateGap) { log() << deletes << " delete and " << updates << " update operations processed out of " << goodVersions.size() << " total operations"; lastProgressUpdate = now; } const DocID& doc = it->first; BSONObj pattern = doc._id.wrap(); // { _id : ... } try { verify(doc.ns && *doc.ns); if (fixUpInfo.collectionsToResyncData.count(doc.ns)) { // we just synced this entire collection continue; } // keep an archive of items rolled back shared_ptr<Helpers::RemoveSaver>& removeSaver = removeSavers[doc.ns]; if (!removeSaver) removeSaver.reset(new Helpers::RemoveSaver("rollback", "", doc.ns)); // todo: lots of overhead in context, this can be faster OldClientContext ctx(txn, doc.ns); // Add the doc to our rollback file BSONObj obj; Collection* collection = ctx.db()->getCollection(doc.ns); // Do not log an error when undoing an insert on a no longer existent collection. // It is likely that the collection was dropped as part of rolling back a // createCollection command and regardless, the document no longer exists. if (collection) { bool found = Helpers::findOne(txn, collection, pattern, obj, false); if (found) { removeSaver->goingToDelete(obj); } else { error() << "rollback cannot find object: " << pattern << " in namespace " << doc.ns; } } if (it->second.isEmpty()) { // wasn't on the primary; delete. // TODO 1.6 : can't delete from a capped collection. need to handle that here. deletes++; if (collection) { if (collection->isCapped()) { // can't delete from a capped collection - so we truncate instead. if // this item must go, so must all successors!!! try { // TODO: IIRC cappedTruncateAfter does not handle completely empty. // this will crazy slow if no _id index. long long start = Listener::getElapsedTimeMillis(); RecordId loc = Helpers::findOne(txn, collection, pattern, false); if (Listener::getElapsedTimeMillis() - start > 200) warning() << "roll back slow no _id index for " << doc.ns << " perhaps?"; // would be faster but requires index: // RecordId loc = Helpers::findById(nsd, pattern); if (!loc.isNull()) { try { collection->temp_cappedTruncateAfter(txn, loc, true); } catch (DBException& e) { if (e.getCode() == 13415) { // hack: need to just make cappedTruncate do this... MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { WriteUnitOfWork wunit(txn); uassertStatusOK(collection->truncate(txn)); wunit.commit(); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END( txn, "truncate", collection->ns().ns()); } else { throw e; } } } } catch (DBException& e) { error() << "rolling back capped collection rec " << doc.ns << ' ' << e.toString(); } } else {
void ReplSetImpl::syncFixUp(HowToFixUp& h, OplogReader& r) { DBClientConnection *them = r.conn(); // fetch all first so we needn't handle interruption in a fancy way unsigned long long totSize = 0; list< pair<DocID,bo> > goodVersions; bo newMinValid; /* fetch all the goodVersions of each document from current primary */ DocID d; unsigned long long n = 0; try { for( set<DocID>::iterator i = h.toRefetch.begin(); i != h.toRefetch.end(); i++ ) { d = *i; assert( !d._id.eoo() ); { /* TODO : slow. lots of round trips. */ n++; bo good= them->findOne(d.ns, d._id.wrap()).getOwned(); totSize += good.objsize(); uassert( 13410, "replSet too much data to roll back", totSize < 300 * 1024 * 1024 ); // note good might be eoo, indicating we should delete it goodVersions.push_back(pair<DocID,bo>(d,good)); } } newMinValid = r.getLastOp(rsoplog); if( newMinValid.isEmpty() ) { sethbmsg("rollback error newMinValid empty?"); return; } } catch(DBException& e) { sethbmsg(str::stream() << "rollback re-get objects: " << e.toString(),0); log() << "rollback couldn't re-get ns:" << d.ns << " _id:" << d._id << ' ' << n << '/' << h.toRefetch.size() << rsLog; throw e; } MemoryMappedFile::flushAll(true); sethbmsg("rollback 3.5"); if( h.rbid != getRBID(r.conn()) ) { // our source rolled back itself. so the data we received isn't necessarily consistent. sethbmsg("rollback rbid on source changed during rollback, cancelling this attempt"); return; } // update them sethbmsg(str::stream() << "rollback 4 n:" << goodVersions.size()); bool warn = false; assert( !h.commonPointOurDiskloc.isNull() ); dbMutex.assertWriteLocked(); /* we have items we are writing that aren't from a point-in-time. thus best not to come online until we get to that point in freshness. */ setMinValid(newMinValid); /** any full collection resyncs required? */ if( !h.collectionsToResync.empty() ) { for( set<string>::iterator i = h.collectionsToResync.begin(); i != h.collectionsToResync.end(); i++ ) { string ns = *i; sethbmsg(str::stream() << "rollback 4.1 coll resync " << ns); Client::Context c(*i, dbpath, 0, /*doauth*/false); try { bob res; string errmsg; dropCollection(ns, errmsg, res); { dbtemprelease r; bool ok = copyCollectionFromRemote(them->getServerAddress(), ns, bo(), errmsg, false); if( !ok ) { log() << "replSet rollback error resyncing collection " << ns << ' ' << errmsg << rsLog; throw "rollback error resyncing rollection [1]"; } } } catch(...) { log() << "replset rollback error resyncing collection " << ns << rsLog; throw "rollback error resyncing rollection [2]"; } } /* we did more reading from primary, so check it again for a rollback (which would mess us up), and make minValid newer. */ sethbmsg("rollback 4.2"); { string err; try { newMinValid = r.getLastOp(rsoplog); if( newMinValid.isEmpty() ) { err = "can't get minvalid from primary"; } else { setMinValid(newMinValid); } } catch(...) { err = "can't get/set minvalid"; } if( h.rbid != getRBID(r.conn()) ) { // our source rolled back itself. so the data we received isn't necessarily consistent. // however, we've now done writes. thus we have a problem. err += "rbid at primary changed during resync/rollback"; } if( !err.empty() ) { log() << "replSet error rolling back : " << err << ". A full resync will be necessary." << rsLog; /* todo: reset minvalid so that we are permanently in fatal state */ /* todo: don't be fatal, but rather, get all the data first. */ sethbmsg("rollback error"); throw rsfatal(); } } sethbmsg("rollback 4.3"); } sethbmsg("rollback 4.6"); /** drop collections to drop before doing individual fixups - that might make things faster below actually if there were subsequent inserts to rollback */ for( set<string>::iterator i = h.toDrop.begin(); i != h.toDrop.end(); i++ ) { Client::Context c(*i, dbpath, 0, /*doauth*/false); try { bob res; string errmsg; log(1) << "replSet rollback drop: " << *i << rsLog; dropCollection(*i, errmsg, res); } catch(...) { log() << "replset rollback error dropping collection " << *i << rsLog; } } sethbmsg("rollback 4.7"); Client::Context c(rsoplog, dbpath, 0, /*doauth*/false); NamespaceDetails *oplogDetails = nsdetails(rsoplog); uassert(13423, str::stream() << "replSet error in rollback can't find " << rsoplog, oplogDetails); map<string,shared_ptr<RemoveSaver> > removeSavers; unsigned deletes = 0, updates = 0; for( list<pair<DocID,bo> >::iterator i = goodVersions.begin(); i != goodVersions.end(); i++ ) { const DocID& d = i->first; bo pattern = d._id.wrap(); // { _id : ... } try { assert( d.ns && *d.ns ); if( h.collectionsToResync.count(d.ns) ) { /* we just synced this entire collection */ continue; } /* keep an archive of items rolled back */ shared_ptr<RemoveSaver>& rs = removeSavers[d.ns]; if ( ! rs ) rs.reset( new RemoveSaver( "rollback" , "" , d.ns ) ); // todo: lots of overhead in context, this can be faster Client::Context c(d.ns, dbpath, 0, /*doauth*/false); if( i->second.isEmpty() ) { // wasn't on the primary; delete. /* TODO1.6 : can't delete from a capped collection. need to handle that here. */ deletes++; NamespaceDetails *nsd = nsdetails(d.ns); if( nsd ) { if( nsd->capped ) { /* can't delete from a capped collection - so we truncate instead. if this item must go, so must all successors!!! */ try { /** todo: IIRC cappedTrunateAfter does not handle completely empty. todo. */ // this will crazy slow if no _id index. long long start = Listener::getElapsedTimeMillis(); DiskLoc loc = Helpers::findOne(d.ns, pattern, false); if( Listener::getElapsedTimeMillis() - start > 200 ) log() << "replSet warning roll back slow no _id index for " << d.ns << " perhaps?" << rsLog; //would be faster but requires index: DiskLoc loc = Helpers::findById(nsd, pattern); if( !loc.isNull() ) { try { nsd->cappedTruncateAfter(d.ns, loc, true); } catch(DBException& e) { if( e.getCode() == 13415 ) { // hack: need to just make cappedTruncate do this... nsd->emptyCappedCollection(d.ns); } else { throw; } } } } catch(DBException& e) { log() << "replSet error rolling back capped collection rec " << d.ns << ' ' << e.toString() << rsLog; } } else { try { deletes++; deleteObjects(d.ns, pattern, /*justone*/true, /*logop*/false, /*god*/true, rs.get() ); } catch(...) { log() << "replSet error rollback delete failed ns:" << d.ns << rsLog; } } // did we just empty the collection? if so let's check if it even exists on the source. if( nsd->stats.nrecords == 0 ) { try { string sys = cc().database()->name + ".system.namespaces"; bo o = them->findOne(sys, QUERY("name"<<d.ns)); if( o.isEmpty() ) { // we should drop try { bob res; string errmsg; dropCollection(d.ns, errmsg, res); } catch(...) { log() << "replset error rolling back collection " << d.ns << rsLog; } } } catch(DBException& ) { /* this isn't *that* big a deal, but is bad. */ log() << "replSet warning rollback error querying for existence of " << d.ns << " at the primary, ignoring" << rsLog; } } } } else { // todo faster... OpDebug debug; updates++; _updateObjects(/*god*/true, d.ns, i->second, pattern, /*upsert=*/true, /*multi=*/false , /*logtheop=*/false , debug, rs.get() ); } } catch(DBException& e) { log() << "replSet exception in rollback ns:" << d.ns << ' ' << pattern.toString() << ' ' << e.toString() << " ndeletes:" << deletes << rsLog; warn = true; } } removeSavers.clear(); // this effectively closes all of them sethbmsg(str::stream() << "rollback 5 d:" << deletes << " u:" << updates); MemoryMappedFile::flushAll(true); sethbmsg("rollback 6"); // clean up oplog log(2) << "replSet rollback truncate oplog after " << h.commonPoint.toStringPretty() << rsLog; // todo: fatal error if this throws? oplogDetails->cappedTruncateAfter(rsoplog, h.commonPointOurDiskloc, false); /* reset cached lastoptimewritten and h value */ loadLastOpTimeWritten(); sethbmsg("rollback 7"); MemoryMappedFile::flushAll(true); // done if( warn ) sethbmsg("issues during syncRollback, see log"); else sethbmsg("rollback done"); }
void run(string router, string ns, long long start, long long range, int sleepTime) { DBClientConnection c; c.connect(router); c.setWriteConcern(W_NORMAL); struct timeval start_time, stop_time, delay; char timeStr[25]; bool flag; BSONObj b; srand(time(NULL)); long long user_id = -1; long long number = -1; int opSelector; string s; BSONObj insertObj; BSONObj query; BSONObj updateObj; BSONObj readObj; int numOps = 3; int i = 0; string operation = "none"; map<long long, int> insertedKeys; while( true ) { flag = false; curTimeString(timeStr); gettimeofday(&start_time, NULL); opSelector = i % numOps; i++; try { switch(opSelector) { case 0: //insert operation = "insert"; while(true) { user_id = start + (rand() % range); if( insertedKeys.find(user_id) == insertedKeys.end()) { //key not been inserted previously insertedKeys.insert(make_pair(user_id, 1)); cout<<operation<<": Info: inserting " << user_id << endl; break; } } //insert command goes here number = 2 * start + range - user_id; insertObj = BSON("user_id" << user_id << "number" << number << "name" << "name"); //cout<<"insert: "<<insertObj.toString()<<endl; c.insert(ns, insertObj); s = c.getLastError(ns, false, false, 1, 0); if (s.length() > 0) { flag = true; cout << "Error:" << s << endl; } break; case 1: //update operation = "update"; //update command goes here query = BSON("user_id" << user_id); updateObj = BSON("user_id" << user_id << "number" << number << "name" << "nameUpdated"); //cout<<"update: "<<updateObj.toString()<<endl; c.update(ns, Query(query), updateObj); s = c.getLastError(ns, false, false, 1, 0); if (s.length() > 0) { flag = true; cout << "Error:" << s << endl; } break; case 2: //read operation = "read"; readObj = BSON("user_id" << user_id); //cout<<"read: "<<readObj.toString()<<endl; b = c.findOne(ns, Query(readObj), 0, QueryOption_SlaveOk); if (b.isEmpty() <= 0) flag = true; s = c.getLastError(ns, false, false, 1, 0); if (s.length() > 0) { flag = true; cout << "Error:" << s << endl; } break; default: cout<<"Unrecognized opSelector ! " << opSelector << endl; cout<<"i : " << i << " numOps : " << numOps << endl; break; } } catch (DBException e){ flag = true; cout << "Error: " << e.toString() << endl; } if (!flag) { gettimeofday(&stop_time, NULL); if (opSelector == 2) cout << "Returned result:" << b.toString() << endl; delay = subtract(start_time, stop_time); cout<<operation<<": "; cout << timeStr << ": " << delay.tv_sec*1000 + delay.tv_usec/(double)1000 << endl; } else { cout<<operation<<": "; cout << timeStr << ": -100" << endl; } usleep(sleepTime); } }
int main( int argc, const char **argv ) { const char *port = "27017"; if ( argc != 1 ) { if ( argc != 3 ) { std::cout << "need to pass port as second param" << endl; return EXIT_FAILURE; } port = argv[ 2 ]; } DBClientConnection conn; string errmsg; if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) { cout << "couldn't connect : " << errmsg << endl; return EXIT_FAILURE; } const char * ns = "test.test1"; conn.dropCollection(ns); // clean up old data from any previous tests conn.remove( ns, BSONObj() ); verify( conn.findOne( ns , BSONObj() ).isEmpty() ); // test insert conn.insert( ns ,BSON( "name" << "eliot" << "num" << 1 ) ); verify( ! conn.findOne( ns , BSONObj() ).isEmpty() ); // test remove conn.remove( ns, BSONObj() ); verify( conn.findOne( ns , BSONObj() ).isEmpty() ); // insert, findOne testing conn.insert( ns , BSON( "name" << "eliot" << "num" << 1 ) ); { BSONObj res = conn.findOne( ns , BSONObj() ); verify( strstr( res.getStringField( "name" ) , "eliot" ) ); verify( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); verify( 1 == res.getIntField( "num" ) ); } // cursor conn.insert( ns ,BSON( "name" << "sara" << "num" << 2 ) ); { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } verify( count == 2 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 1 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } verify( count == 1 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 3 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } verify( count == 0 ); } // update { BSONObj res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); verify( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); BSONObj after = BSONObjBuilder().appendElements( res ).append( "name2" , "h" ).obj(); conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); verify( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); verify( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); conn.update( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); verify( strstr( res.getStringField( "name" ) , "eliot" ) ); verify( strstr( res.getStringField( "name2" ) , "h" ) ); verify( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); // upsert conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after , 1 ); verify( ! conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ).isEmpty() ); } { // ensure index verify( conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); verify( ! conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); } { // 5 second TTL index const char * ttlns = "test.ttltest1"; conn.dropCollection( ttlns ); { mongo::BSONObjBuilder b; b.appendTimeT("ttltime", time(0)); b.append("name", "foo"); conn.insert(ttlns, b.obj()); } conn.ensureIndex(ttlns, BSON("ttltime" << 1), false, false, "", true, false, -1, 5); verify(!conn.findOne(ttlns, BSONObjBuilder().append("name", "foo").obj()).isEmpty()); // Sleep 66 seconds, 60 seconds for the TTL loop, 5 seconds for the TTL and 1 to ensure sleepsecs(66); verify(conn.findOne(ttlns, BSONObjBuilder().append("name", "foo").obj()).isEmpty()); } { // hint related tests // tokumx doesn't reorder documents just because you updated one, what even is that verify( conn.findOne(ns, "{}")["name"].str() == "eliot" ); verify( conn.findOne(ns, "{ name : 'sara' }")["name"].str() == "sara" ); verify( conn.getLastError() == "" ); // nonexistent index test bool asserted = false; try { conn.findOne(ns, Query("{name:\"eliot\"}").hint("{foo:1}")); } catch ( ... ) { asserted = true; } verify( asserted ); //existing index verify( conn.findOne(ns, Query("{name:'eliot'}").hint("{name:1}")).hasElement("name") ); // run validate verify( conn.validate( ns ) ); } { // timestamp test const char * tsns = "test.tstest1"; conn.dropCollection( tsns ); { mongo::BSONObjBuilder b; b.appendTimestamp( "ts" ); conn.insert( tsns , b.obj() ); } mongo::BSONObj out = conn.findOne( tsns , mongo::BSONObj() ); Date_t oldTime = out["ts"].timestampTime(); unsigned int oldInc = out["ts"].timestampInc(); { mongo::BSONObjBuilder b1; b1.append( out["_id"] ); mongo::BSONObjBuilder b2; b2.append( out["_id"] ); b2.appendTimestamp( "ts" ); conn.update( tsns , b1.obj() , b2.obj() ); } BSONObj found = conn.findOne( tsns , mongo::BSONObj() ); cout << "old: " << out << "\nnew: " << found << endl; verify( ( oldTime < found["ts"].timestampTime() ) || ( oldTime == found["ts"].timestampTime() && oldInc < found["ts"].timestampInc() ) ); } { // check that killcursors doesn't affect last error verify( conn.getLastError().empty() ); BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendNum( (int)-1 ); // invalid # of cursors triggers exception b.appendNum( (int)-1 ); // bogus cursor id Message m; m.setData( dbKillCursors, b.buf(), b.len() ); // say() is protected in DBClientConnection, so get superclass static_cast< DBConnector* >( &conn )->say( m ); verify( conn.getLastError().empty() ); } { list<string> l = conn.getDatabaseNames(); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ) { cout << "db name : " << *i << endl; } l = conn.getCollectionNames( "test" ); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ) { cout << "coll name : " << *i << endl; } } { //Map Reduce (this mostly just tests that it compiles with all output types) const string ns = "test.mr"; conn.insert(ns, BSON("a" << 1)); conn.insert(ns, BSON("a" << 1)); const char* map = "function() { emit(this.a, 1); }"; const char* reduce = "function(key, values) { return Array.sum(values); }"; const string outcoll = ns + ".out"; BSONObj out; out = conn.mapreduce(ns, map, reduce, BSONObj()); // default to inline //MONGO_PRINT(out); out = conn.mapreduce(ns, map, reduce, BSONObj(), outcoll); //MONGO_PRINT(out); out = conn.mapreduce(ns, map, reduce, BSONObj(), outcoll.c_str()); //MONGO_PRINT(out); out = conn.mapreduce(ns, map, reduce, BSONObj(), BSON("reduce" << outcoll)); //MONGO_PRINT(out); } { // test timeouts DBClientConnection conn( true , 0 , 2 ); if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) { cout << "couldn't connect : " << errmsg << endl; throw -11; } conn.insert( "test.totest" , BSON( "x" << 1 ) ); BSONObj res; bool gotError = false; verify( conn.eval( "test" , "return db.totest.findOne().x" , res ) ); try { conn.eval( "test" , "sleep(5000); return db.totest.findOne().x" , res ); } catch ( std::exception& e ) { gotError = true; log() << e.what() << endl; } verify( gotError ); // sleep so the server isn't locked anymore sleepsecs( 4 ); verify( conn.eval( "test" , "return db.totest.findOne().x" , res ) ); } cout << "client test finished!" << endl; return EXIT_SUCCESS; }
/* **************************************************************************** * * mongoUnsubscribeContext - */ HttpStatusCode mongoUnsubscribeContext(UnsubscribeContextRequest* requestP, UnsubscribeContextResponse* responseP) { /* Take semaphore. The LM_S* family of macros combines semaphore release with return */ semTake(); LM_T(LmtMongo, ("Unsubscribe Context")); DBClientConnection* connection = getMongoConnection(); /* No matter if success or failure, the subscriptionId in the response is always the one * in the request */ responseP->subscriptionId = requestP->subscriptionId; /* Look for document */ BSONObj sub; try { OID id = OID(requestP->subscriptionId.get()); LM_T(LmtMongo, ("findOne() in '%s' collection _id '%s'}", getSubscribeContextCollectionName(), requestP->subscriptionId.get().c_str())); sub = connection->findOne(getSubscribeContextCollectionName(), BSON("_id" << id)); if (sub.isEmpty()) { responseP->statusCode.fill(SccContextElementNotFound, std::string("subscriptionId: '") + requestP->subscriptionId.get() + "'"); LM_SR(SccOk); } } catch( const AssertionException &e ) { /* 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_SR(SccOk); } catch( const DBException &e ) { responseP->statusCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + e.what()); LM_SR(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? try { LM_T(LmtMongo, ("remove() in '%s' collection _id '%s'}", getSubscribeContextCollectionName(), requestP->subscriptionId.get().c_str())); connection->remove(getSubscribeContextCollectionName(), BSON("_id" << OID(requestP->subscriptionId.get()))); } catch( const DBException &e ) { responseP->statusCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextCollectionName() + " - remove() _id: " + requestP->subscriptionId.get().c_str() + " - exception: " + e.what()); LM_SR(SccOk); } /* Destroy any previous ONTIMEINTERVAL thread */ getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get()); responseP->statusCode.fill(SccOk); LM_SR(SccOk); }
/* **************************************************************************** * * mongoUnsubscribeContextAvailability - */ HttpStatusCode mongoUnsubscribeContextAvailability(UnsubscribeContextAvailabilityRequest* requestP, UnsubscribeContextAvailabilityResponse* responseP) { reqSemTake(__FUNCTION__, "ngsi9 unsubscribe request"); LM_T(LmtMongo, ("Unsubscribe Context Availability")); DBClientConnection* connection = getMongoConnection(); /* No matter if success or failure, the subscriptionId in the response is always the one * in the request */ responseP->subscriptionId = requestP->subscriptionId; /* Look for document */ BSONObj sub; try { OID id = OID(requestP->subscriptionId.get()); LM_T(LmtMongo, ("findOne() in '%s' collection _id '%s'}", getSubscribeContextAvailabilityCollectionName(), requestP->subscriptionId.get().c_str())); mongoSemTake(__FUNCTION__, "findOne in SubscribeContextAvailabilityCollection"); sub = connection->findOne(getSubscribeContextAvailabilityCollectionName(), BSON("_id" << id)); mongoSemGive(__FUNCTION__, "findOne in SubscribeContextAvailabilityCollection"); if (sub.isEmpty()) { responseP->statusCode.fill(SccContextElementNotFound); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (no subscriptions)"); return SccOk; } } catch( const AssertionException &e ) { /* 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 // (odl issues #95) mongoSemGive(__FUNCTION__, "findOne in SubscribeContextAvailabilityCollection (mongo assertion exception)"); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo assertion exception)"); responseP->statusCode.fill(SccContextElementNotFound); return SccOk; } catch( const DBException &e ) { mongoSemGive(__FUNCTION__, "findOne in SubscribeContextAvailabilityCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo db exception)"); responseP->statusCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + e.what()); return SccOk; } catch(...) { mongoSemGive(__FUNCTION__, "findOne in SubscribeContextAvailabilityCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo generic exception)"); responseP->statusCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName() + " - findOne() _id: " + requestP->subscriptionId.get() + " - exception: " + "generic"); return SccOk; } /* Remove document in MongoDB */ // FIXME: I would prefer to do the find and remove in a single operation. Is the some similar // to findAndModify for this? try { LM_T(LmtMongo, ("remove() in '%s' collection _id '%s'}", getSubscribeContextAvailabilityCollectionName(), requestP->subscriptionId.get().c_str())); mongoSemTake(__FUNCTION__, "remove in SubscribeContextAvailabilityCollection"); connection->remove(getSubscribeContextAvailabilityCollectionName(), BSON("_id" << OID(requestP->subscriptionId.get()))); mongoSemGive(__FUNCTION__, "remove in SubscribeContextAvailabilityCollection"); } catch( const DBException &e ) { mongoSemGive(__FUNCTION__, "remove in SubscribeContextAvailabilityCollection (mongo db exception)"); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo db exception)"); responseP->statusCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName() + " - remove() _id: " + requestP->subscriptionId.get().c_str() + " - exception: " + e.what()); return SccOk; } catch(...) { mongoSemGive(__FUNCTION__, "remove in SubscribeContextAvailabilityCollection (mongo generic exception)"); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request (mongo generic exception)"); responseP->statusCode.fill(SccReceiverInternalError, std::string("collection: ") + getSubscribeContextAvailabilityCollectionName() + " - remove() _id: " + requestP->subscriptionId.get().c_str() + " - exception: " + "generic"); return SccOk; } responseP->statusCode.fill(SccOk); reqSemGive(__FUNCTION__, "ngsi9 unsubscribe request"); return SccOk; }