const_iterator begin() const { const_iterator::iterator_type start = _ranges.begin(), end = _ranges.begin(); if(end != _ranges.end()) ++end; return const_iterator(_ranges.end(), start, end); }
RangeMap MetadataManager::_getCopyOfRangesToClean_inlock() { RangeMap ranges = SimpleBSONObjComparator::kInstance.makeBSONObjIndexedMap<BSONObj>(); for (auto it = _rangesToClean.begin(); it != _rangesToClean.end(); ++it) { ranges.insert(std::make_pair(it->first, it->second.getMax())); } return ranges; }
void RepositoryXL::dump(std::ostream& out) { Repository::dump(out); out << endl << "calling ranges:"; if (callingRanges_.empty()) { out << " none." << endl; } else { out << endl << endl; for (RangeMap::const_iterator i = callingRanges_.begin(); i != callingRanges_.end(); ++i) { out << i->second; } } out << endl << "Error messages:"; if (errorMessageMap_.empty()) { out << " none." << endl; } else { out << endl << endl; for (ErrorMessageMap::const_iterator i = errorMessageMap_.begin(); i != errorMessageMap_.end(); ++i) { out << std::left << std::setw(50) << i->first << i->second->errorMessage() << endl; } } out << endl << endl << "VBA error message: " << vbaError_ << endl; }
shared_ptr<CallingRange> RepositoryXL::getCallingRange() { string callerName = FunctionCall::instance().callerName(); if (callerName == "VBA") { // Called from VBA - check whether the corresponding calling range // object exists and create it if not. RangeMap::const_iterator i = callingRanges_.find(callerName); if (i == callingRanges_.end()) { shared_ptr<CallingRange> callingRange(new CallingRange); callingRanges_[callingRange->key()] = callingRange; return callingRange; } else { return i->second; } // Called from a worksheet formula } else if (callerName.empty()) { // Calling range not yet named - create a new CallingRange object shared_ptr<CallingRange> callingRange(new CallingRange); callingRanges_[callingRange->key()] = callingRange; return callingRange; } else { // Calling range already named - return associated CallingRange object RangeMap::const_iterator i = callingRanges_.find(callerName); OH_REQUIRE(i != callingRanges_.end(), "No calling range named " << callerName); return i->second; } }
bool rangeBasedShouldSuppress(int beginLine, clang::ASTContext &context, oclint::RuleBase *rule) { std::string filePath = getMainFilePath(context); auto commentRangesIt = rangeMapping.find(filePath); RangeSet commentRanges; if (commentRangesIt == rangeMapping.end()) { DeclAnnotationRangeCollector annotationCollector; commentRanges = annotationCollector.collect(context, rule); rangeMapping[filePath] = commentRanges; } else { commentRanges = commentRangesIt->second; } for (const auto& range : commentRanges) { if (beginLine >= range.first && beginLine <= range.second) { return true; } } return false; }
TEST(RangeMapTest, inner_ranges) { RangeMap rangemap; rangemap.addRange(5, 20, 1); rangemap.addRange(10, 15, 6); ASSERT_EQ(rangemap.numRanges(), 1u); CHECK_RANGE("A", rangemap, 5, 20, 1); rangemap.addRange(10, 15, 100); ASSERT_EQ(rangemap.numRanges(), 3u); CHECK_RANGE("B", rangemap, 5, 9, 1); CHECK_RANGE("C", rangemap, 10, 15, 100); CHECK_RANGE("D", rangemap, 16, 20, 12); rangemap.addRange(15, 15, 50); ASSERT_EQ(rangemap.numRanges(), 4u); CHECK_RANGE("E", rangemap, 5, 9, 1); CHECK_RANGE("F", rangemap, 10, 14, 100); CHECK_RANGE("G", rangemap, 15, 15, 50); CHECK_RANGE("H", rangemap, 16, 20, 12); rangemap.addRange(6, 19, 1); ASSERT_EQ(rangemap.numRanges(), 3u); CHECK_RANGE("I", rangemap, 5, 5, 1); CHECK_RANGE("J", rangemap, 6, 19, 1); CHECK_RANGE("K", rangemap, 20, 20, 16); }
RangeMap MetadataManager::_getCopyOfRangesToClean_inlock() { RangeMap ranges; for (auto it = _rangesToClean.begin(); it != _rangesToClean.end(); ++it) { ranges.insert(std::make_pair(it->first, it->second.getMax())); } return ranges; }
TEST(RangeMapTest, iterators) { RangeMap rangemap; rangemap.addRange(5, 10, 1); rangemap.addRange(20, 25, 60); ASSERT_EQ(rangemap.numRanges(), 2u); using Pair32 = pair<uint32_t,uint32_t>; vector<Pair32> pairs = { Pair32(5, 1), Pair32(6, 2), Pair32(7, 3), Pair32(8, 4), Pair32(9, 5), Pair32(10, 6), Pair32(20, 60), Pair32(21, 61), Pair32(22, 62), Pair32(23, 63), Pair32(24, 64), Pair32(25, 65) }; int count=0; for (auto p : rangemap) ASSERT_EQ(p, pairs[count++]); }
void RepositoryXL::collectGarbage(const bool &deletePermanent) { RangeMap::iterator i = callingRanges_.begin(); while (i != callingRanges_.end()) { shared_ptr<CallingRange> callingRange = i->second; if (callingRange->valid()) { ++i; } else { callingRange->clearResidentObjects(deletePermanent); if (callingRange->empty()) callingRanges_.erase(i++); else ++i; } } }
TEST(RangeMapTest, touching_ranges2) { RangeMap rangemap; rangemap.addRange(7, 8, 10); ASSERT_EQ(rangemap.numRanges(), 1u); CHECK_RANGE("A", rangemap, 7, 8, 10); rangemap.addRange(2, 3, 1); ASSERT_EQ(rangemap.numRanges(), 2u); CHECK_RANGE("B", rangemap, 2, 3, 1); CHECK_RANGE("C", rangemap, 7, 8, 10); rangemap.addRange(4, 4, 3); ASSERT_EQ(rangemap.numRanges(), 2u); CHECK_RANGE("D", rangemap, 2, 4, 1); CHECK_RANGE("E", rangemap, 7, 8, 10); }
void add(value_type start, value_type end) { if(end <= start) return; // dirty list represents ranges with alternate elements: start,end,start,end // to insert a new range: // find insert point of start. find element before, if it is a start element then it is the start of the new range // otherwise the new insertion is // find insert point of end. find element after, if it is an end element then it is the end of the new range // otherwise the new insertion is std::pair<RangeMap::iterator, bool> sloc = _ranges.insert(RangeRec(start, true)); if(sloc.first != _ranges.begin()) { RangeMap::iterator prev = sloc.first; prev--; if(prev->start) { sloc.first--; } } std::pair<RangeMap::iterator, bool> eloc = _ranges.insert(RangeRec(end, false)); RangeMap::iterator next = eloc.first; next++; if(next != _ranges.end() && !next->start) { eloc.first++; } // erase the interior of the range ++sloc.first; if(sloc.first != eloc.first && sloc.first != _ranges.end()) { //eloc.first; if(sloc.first != eloc.first) _ranges.erase(sloc.first, eloc.first); else _ranges.erase(sloc.first); } assert((_ranges.size() - 1) % 2 != 0); }
TEST(RangeMapTest, overlapping_ranges) { RangeMap rangemap; rangemap.addRange(5, 8, 10); rangemap.addRange(7, 15, 12); ASSERT_EQ(rangemap.numRanges(), 1u); CHECK_RANGE("A", rangemap, 5, 15, 10); rangemap.addRange(10, 20, 1); ASSERT_EQ(rangemap.numRanges(), 2u); CHECK_RANGE("B", rangemap, 5, 9, 10); CHECK_RANGE("C", rangemap, 10, 20, 1); rangemap.addRange(2, 7, 7); ASSERT_EQ(rangemap.numRanges(), 2u); CHECK_RANGE("D", rangemap, 2, 9, 7); CHECK_RANGE("E", rangemap, 10, 20, 1); rangemap.addRange(1, 12, 100); ASSERT_EQ(rangemap.numRanges(), 2u); CHECK_RANGE("F", rangemap, 1, 12, 100); CHECK_RANGE("G", rangemap, 13, 20, 4); rangemap.addRange(0, 30, 1); ASSERT_EQ(rangemap.numRanges(), 1u); CHECK_RANGE("H", rangemap, 0, 30, 1); }
iterator end() { return iterator(_ranges.end(), _ranges.end(), _ranges.end()); }
const_iterator end() const { return const_iterator(_ranges.end(), _ranges.end(), _ranges.end()); }
namespace ObjectHandler { // Below are three structures which must be declared as static variables rather than // class members because std::map cannot be exported across DLL boundaries. // The object map declared in the cpp file for the base Repository class. extern Repository::ObjectMap objectMap_; // A map to associate error messages with Excel range addresses. typedef std::map<string, shared_ptr<RangeReference> > ErrorMessageMap; ErrorMessageMap errorMessageMap_; // Excel cell ranges in which objects have been constructed, // keyed by a unique ID which is assigned to each range. typedef std::map<string, shared_ptr<CallingRange> > RangeMap; RangeMap callingRanges_; RepositoryXL &RepositoryXL::instance() { if (instance_) { RepositoryXL *ret = dynamic_cast<RepositoryXL*>(instance_); if (ret) return *ret; } OH_FAIL("Attempt to reference uninitialized RepositoryXL object"); } void RepositoryXL::clear() { objectMap_.clear(); errorMessageMap_.clear(); callingRanges_.clear(); } string RepositoryXL::storeObject( const string &objectIDRaw, const shared_ptr<Object> &object, bool overwrite, boost::shared_ptr<ValueObject> valueObject) { shared_ptr<CallingRange> callingRange = getCallingRange(); string objectID = callingRange->initializeID(objectIDRaw); if (objectIDRaw.empty() && valueObject) valueObject->setProperty("OBJECTID", objectID); shared_ptr<ObjectWrapperXL> objectWrapperXL; ObjectMap::const_iterator result = objectMap_.find(objectID); if (result == objectMap_.end()) { objectWrapperXL = shared_ptr<ObjectWrapperXL> ( new ObjectWrapperXL(objectID, object, callingRange)); objectMap_[objectID] = objectWrapperXL; callingRange->registerObject(objectID, objectWrapperXL); } else { objectWrapperXL = boost::static_pointer_cast<ObjectWrapperXL>(result->second); if (objectWrapperXL->callerKey() != callingRange->key()) { OH_REQUIRE(overwrite, "Cannot create object with ID '" << objectID << "' in cell " << callingRange->addressString() << " because an object with that ID already resides in cell " << objectWrapperXL->callerAddress()); objectWrapperXL->resetCaller(callingRange); callingRange->registerObject(objectID, objectWrapperXL); } objectWrapperXL->reset(object); } registerObserver(objectWrapperXL); return objectWrapperXL->idFull(); } void RepositoryXL::setError( const string &message, const shared_ptr<FunctionCall> &functionCall) { std::ostringstream logMessage, cellMessage; cellMessage << functionCall->functionName() << " - " << message; string refStr = functionCall->refStr(); string refStrUpper = boost::algorithm::to_upper_copy(refStr); ErrorMessageMap::const_iterator i = errorMessageMap_.find(refStrUpper); if (i == errorMessageMap_.end()) { shared_ptr<RangeReference> rangeReference(new RangeReference(refStrUpper)); rangeReference->setErrorMessage(cellMessage.str()); errorMessageMap_[refStrUpper] = rangeReference; } else { i->second->setErrorMessage(cellMessage.str()); } } void RepositoryXL::logError( const string &message, const shared_ptr<FunctionCall> &functionCall) { // This function is called during error handling and must not throw. try { if (functionCall) { functionCall->setError(); std::ostringstream fullMessage; if (functionCall->callerType() == CallerType::Cell) { setError(message, functionCall); fullMessage << functionCall->addressString() << " - "; } else if (functionCall->callerType() == CallerType::VBA || functionCall->callerType() == CallerType::Menu) { vbaError_ = message; fullMessage << "VBA - "; } fullMessage << functionCall->functionName() << " - " << message; logWriteMessage(fullMessage.str(), 2); } else { logWriteMessage(message, 2); } } catch(...) {} } string RepositoryXL::retrieveError(const XLOPER *xRangeRef) { OH_REQUIRE(xRangeRef->xltype == xltypeRef || xRangeRef->xltype == xltypeSRef, "Input parameter is not a range reference."); Xloper xRangeText; Excel(xlfReftext, &xRangeText, 1, xRangeRef); string refStr = ConvertOper(xRangeText()); string refStrUpper = boost::algorithm::to_upper_copy(refStr); ErrorMessageMap::const_iterator i = errorMessageMap_.find(refStrUpper); if (i != errorMessageMap_.end()) return i->second->errorMessage(); RangeReference selectionReference(refStrUpper); for (i = errorMessageMap_.begin(); i != errorMessageMap_.end(); ++i) { if (i->second->contains(selectionReference)) return i->second->errorMessage(); } return ""; } void RepositoryXL::clearError() { string refStr = FunctionCall::instance().refStr(); errorMessageMap_.erase(boost::algorithm::to_upper_copy(refStr)); } void RepositoryXL::collectGarbage(const bool &deletePermanent) { RangeMap::iterator i = callingRanges_.begin(); while (i != callingRanges_.end()) { shared_ptr<CallingRange> callingRange = i->second; if (callingRange->valid()) { ++i; } else { callingRange->clearResidentObjects(deletePermanent); if (callingRange->empty()) callingRanges_.erase(i++); else ++i; } } } shared_ptr<CallingRange> RepositoryXL::getCallingRange() { string callerName = FunctionCall::instance().callerName(); if (callerName == "VBA") { // Called from VBA - check whether the corresponding calling range // object exists and create it if not. RangeMap::const_iterator i = callingRanges_.find(callerName); if (i == callingRanges_.end()) { shared_ptr<CallingRange> callingRange(new CallingRange); callingRanges_[callingRange->key()] = callingRange; return callingRange; } else { return i->second; } // Called from a worksheet formula } else if (callerName.empty()) { // Calling range not yet named - create a new CallingRange object shared_ptr<CallingRange> callingRange(new CallingRange); callingRanges_[callingRange->key()] = callingRange; return callingRange; } else { // Calling range already named - return associated CallingRange object RangeMap::const_iterator i = callingRanges_.find(callerName); OH_REQUIRE(i != callingRanges_.end(), "No calling range named " << callerName); return i->second; } } void RepositoryXL::dump(std::ostream& out) { Repository::dump(out); out << endl << "calling ranges:"; if (callingRanges_.empty()) { out << " none." << endl; } else { out << endl << endl; for (RangeMap::const_iterator i = callingRanges_.begin(); i != callingRanges_.end(); ++i) { out << i->second; } } out << endl << "Error messages:"; if (errorMessageMap_.empty()) { out << " none." << endl; } else { out << endl << endl; for (ErrorMessageMap::const_iterator i = errorMessageMap_.begin(); i != errorMessageMap_.end(); ++i) { out << std::left << std::setw(50) << i->first << i->second->errorMessage() << endl; } } out << endl << endl << "VBA error message: " << vbaError_ << endl; } std::vector<string> RepositoryXL::callerAddress(const std::vector<string> &objectList) { std::vector<string> ret; for (std::vector<string>::const_iterator i = objectList.begin(); i != objectList.end(); ++i) { string idStrip = CallingRange::getStub(*i); const shared_ptr<ObjectWrapperXL>& objectWrapperXL = boost::static_pointer_cast<ObjectWrapperXL>( getObjectWrapper(idStrip)); ret.push_back(objectWrapperXL->callerAddress()); } return ret; } std::vector<string> RepositoryXL::callerKey(const std::vector<string> &objectList) { std::vector<string> ret; for (std::vector<string>::const_iterator i = objectList.begin(); i != objectList.end(); ++i) { string idStrip = CallingRange::getStub(*i); const shared_ptr<ObjectWrapperXL>& objectWrapperXL = boost::static_pointer_cast<ObjectWrapperXL>( getObjectWrapper(idStrip)); ret.push_back(objectWrapperXL->callerKey()); } return ret; } std::vector<bool> RepositoryXL::isOrphan(const std::vector<string> &objectList){ std::vector<bool> ret; for (std::vector<string>::const_iterator i = objectList.begin(); i != objectList.end(); ++i) { shared_ptr<ObjectWrapperXL> objectWrapperXL; ObjectMap::const_iterator result = objectMap_.find(CallingRange::getStub(*i)); if (result != objectMap_.end()) { objectWrapperXL = boost::static_pointer_cast<ObjectWrapperXL>(result->second); ret.push_back(!objectWrapperXL->getCallingRange()->valid()); } else { OH_FAIL( "Unable to retrieve object with ID " << *i); } } return ret; } std::vector<string> RepositoryXL::updateCounter(const std::vector<string> &objectList) { std::vector<string> ret; for (std::vector<string>::const_iterator i = objectList.begin(); i != objectList.end(); ++i) { shared_ptr<ObjectWrapperXL> objectWrapperXL; ObjectMap::const_iterator result = objectMap_.find(CallingRange::getStub(*i)); if (result != objectMap_.end()) { objectWrapperXL = boost::static_pointer_cast<ObjectWrapperXL>(result->second); ret.push_back(objectWrapperXL->getCallingRange()->getUpdateCount()); } else { OH_FAIL( "Unable to retrieve object with ID " << *i); } } return ret; } string RepositoryXL::formatID(const string &objectID){ return CallingRange::getStub(objectID); } }
TEST(RangeMapTest, disjoint_ranges) { RangeMap rangemap; rangemap.addRange(5, 8, 1); ASSERT_EQ(rangemap.numRanges(), 1u); EXPECT_EQ(rangemap.numValues(), 4u); CHECK_RANGE("A", rangemap, 5, 8, 1); CHECK_ZERO("B", rangemap, 0, 4); CHECK_ZERO("C", rangemap, 9, 20); rangemap.addRange(10, 15, 5); ASSERT_EQ(rangemap.numRanges(), 2u); EXPECT_EQ(rangemap.numValues(), 10u); CHECK_RANGE("D", rangemap, 5, 8, 1); CHECK_RANGE("E", rangemap, 10, 15, 5); CHECK_ZERO("F", rangemap, 0, 4); CHECK_ZERO("G", rangemap, 9, 9); CHECK_ZERO("H", rangemap, 16, 20); rangemap.addRange(0, 3, 50); ASSERT_EQ(rangemap.numRanges(), 3u); EXPECT_EQ(rangemap.numValues(), 14u); CHECK_RANGE("I", rangemap, 0, 3, 50); CHECK_RANGE("J", rangemap, 5, 8, 1); CHECK_RANGE("K", rangemap, 10, 15, 5); CHECK_ZERO("L", rangemap, 4, 4); CHECK_ZERO("M", rangemap, 9, 9); CHECK_ZERO("N", rangemap, 16, 20); rangemap.addRange(16, 20, 1); ASSERT_EQ(rangemap.numRanges(), 4u); EXPECT_EQ(rangemap.numValues(), 19u); CHECK_RANGE("O", rangemap, 5, 8, 1); CHECK_RANGE("P", rangemap, 10, 15, 5); CHECK_RANGE("Q", rangemap, 0, 3, 50); CHECK_RANGE("R", rangemap, 16, 20, 1); }
static void check_range (const RangeMap &rangemap, int min, int max, int minval) { for (int i=min; i <= max; i++) ASSERT_EQ((int)rangemap.valueAt(i), minval+(i-min)); }
void clear() { _ranges.clear(); }
void RepositoryXL::clear() { objectMap_.clear(); errorMessageMap_.clear(); callingRanges_.clear(); }
static void check_zero (const RangeMap &rangemap, int min, int max) { for (int i=min; i <= max; i++) ASSERT_EQ(rangemap.valueAt(i), 0u); }
bool empty() const { return _ranges.size() == 0; }
TEST(RangeMapTest, touching_ranges1) { RangeMap rangemap; rangemap.addRange(5, 8, 10); ASSERT_EQ(rangemap.numRanges(), 1u); EXPECT_EQ(rangemap.numValues(), 4u); CHECK_RANGE("A", rangemap, 5, 8, 10); rangemap.addRange(9, 15, 14); ASSERT_EQ(rangemap.numRanges(), 1u); EXPECT_EQ(rangemap.numValues(), 11u); CHECK_RANGE("B", rangemap, 5, 15, 10); rangemap.addRange(1, 4, 5); ASSERT_EQ(rangemap.numRanges(), 2u); EXPECT_EQ(rangemap.numValues(), 15u); CHECK_RANGE("C", rangemap, 1, 4, 5); CHECK_RANGE("D", rangemap, 5, 15, 10); rangemap.addRange(1, 4, 6); ASSERT_EQ(rangemap.numRanges(), 1u); EXPECT_EQ(rangemap.numValues(), 15u); CHECK_RANGE("E", rangemap, 1, 15, 6); }
void run() { int numShards = 10; int numInitialChunks = 5; int maxChunks = 100000; // Needed to not overflow the BSONArray's max bytes int keySize = 2; BSONArrayBuilder chunksB; BSONObj lastSplitPt; ShardChunkVersion version( 1, 0, OID() ); // // Generate numChunks with a given key size over numShards // All chunks have double key values, so we can split them a bunch // for( int i = -1; i < numInitialChunks; i++ ){ BSONObjBuilder splitPtB; for( int k = 0; k < keySize; k++ ){ string field = string( "k" ) + string( 1, (char)('0' + k) ); if( i < 0 ) splitPtB.appendMinKey( field ); else if( i < numInitialChunks - 1 ) splitPtB.append( field, (double)i ); else splitPtB.appendMaxKey( field ); } BSONObj splitPt = splitPtB.obj(); if( i >= 0 ){ BSONObjBuilder chunkB; chunkB.append( "min", lastSplitPt ); chunkB.append( "max", splitPt ); int shardNum = rand( numShards ); chunkB.append( "shard", "shard" + string( 1, (char)('A' + shardNum) ) ); rand( 2 ) ? version.incMajor() : version.incMinor(); version.addToBSON( chunkB, "lastmod" ); chunksB.append( chunkB.obj() ); } lastSplitPt = splitPt; } BSONArray chunks = chunksB.arr(); // log() << "Chunks generated : " << chunks << endl; DBClientMockCursor chunksCursor( chunks ); // Setup the empty ranges and versions first RangeMap ranges; ShardChunkVersion maxVersion = ShardChunkVersion( 0, 0, OID() ); VersionMap maxShardVersions; // Create a differ which will track our progress boost::shared_ptr< DefaultDiffAdapter > differ( _inverse ? new InverseDiffAdapter() : new DefaultDiffAdapter() ); differ->attach( "test", ranges, maxVersion, maxShardVersions ); // Validate initial load differ->calculateConfigDiff( chunksCursor ); validate( chunks, ranges, maxVersion, maxShardVersions ); // Generate a lot of diffs, and keep validating that updating from the diffs always // gives us the right ranges and versions int numDiffs = 135; // Makes about 100000 chunks overall int numChunks = numInitialChunks; for( int i = 0; i < numDiffs; i++ ){ // log() << "Generating new diff... " << i << endl; BSONArrayBuilder diffsB; BSONArrayBuilder newChunksB; BSONObjIterator chunksIt( chunks ); while( chunksIt.more() ){ BSONObj chunk = chunksIt.next().Obj(); int randChoice = rand( 10 ); if( randChoice < 2 && numChunks < maxChunks ){ // Simulate a split // log() << " ...starting a split with chunk " << chunk << endl; BSONObjBuilder leftB; BSONObjBuilder rightB; BSONObjBuilder midB; for( int k = 0; k < keySize; k++ ){ string field = string( "k" ) + string( 1, (char)('0' + k) ); BSONType maxType = chunk["max"].Obj()[field].type(); double max = maxType == NumberDouble ? chunk["max"].Obj()[field].Number() : 0.0; BSONType minType = chunk["min"].Obj()[field].type(); double min = minType == NumberDouble ? chunk["min"].Obj()[field].Number() : 0.0; if( minType == MinKey ){ midB.append( field, max - 1.0 ); } else if( maxType == MaxKey ){ midB.append( field, min + 1.0 ); } else { midB.append( field, ( max + min ) / 2.0 ); } } BSONObj midPt = midB.obj(); // Only happens if we can't split the min chunk if( midPt.isEmpty() ) continue; leftB.append( chunk["min"] ); leftB.append( "max", midPt ); rightB.append( "min", midPt ); rightB.append( chunk["max"] ); leftB.append( chunk["shard"] ); rightB.append( chunk["shard"] ); version.incMajor(); version._minor = 0; version.addToBSON( leftB, "lastmod" ); version.incMinor(); version.addToBSON( rightB, "lastmod" ); BSONObj left = leftB.obj(); BSONObj right = rightB.obj(); // log() << " ... split into " << left << " and " << right << endl; newChunksB.append( left ); newChunksB.append( right ); diffsB.append( right ); diffsB.append( left ); numChunks++; } else if( randChoice < 4 && chunksIt.more() ){ // Simulate a migrate // log() << " ...starting a migrate with chunk " << chunk << endl; BSONObj prevShardChunk; while( chunksIt.more() ){ prevShardChunk = chunksIt.next().Obj(); if( prevShardChunk["shard"].String() == chunk["shard"].String() ) break; // log() << "... appending chunk from diff shard: " << prevShardChunk << endl; newChunksB.append( prevShardChunk ); prevShardChunk = BSONObj(); } // We need to move between different shards, hence the weirdness in logic here if( ! prevShardChunk.isEmpty() ){ BSONObjBuilder newShardB; BSONObjBuilder prevShardB; newShardB.append( chunk["min"] ); newShardB.append( chunk["max"] ); prevShardB.append( prevShardChunk["min"] ); prevShardB.append( prevShardChunk["max"] ); int shardNum = rand( numShards ); newShardB.append( "shard", "shard" + string( 1, (char)('A' + shardNum) ) ); prevShardB.append( prevShardChunk["shard"] ); version.incMajor(); version._minor = 0; version.addToBSON( newShardB, "lastmod" ); version.incMinor(); version.addToBSON( prevShardB, "lastmod" ); BSONObj newShard = newShardB.obj(); BSONObj prevShard = prevShardB.obj(); // log() << " ... migrated to " << newShard << " and updated " << prevShard << endl; newChunksB.append( newShard ); newChunksB.append( prevShard ); diffsB.append( newShard ); diffsB.append( prevShard ); } else{ // log() << "... appending chunk, no more left: " << chunk << endl; newChunksB.append( chunk ); } } else{ // log() << "Appending chunk : " << chunk << endl; newChunksB.append( chunk ); } } BSONArray diffs = diffsB.arr(); chunks = newChunksB.arr(); // log() << "Diffs generated : " << diffs << endl; // log() << "All chunks : " << chunks << endl; // Rarely entirely clear out our data if( rand( 10 ) < 1 ){ diffs = chunks; ranges.clear(); maxVersion = ShardChunkVersion( 0, 0, OID() ); maxShardVersions.clear(); } // log() << "Total number of chunks : " << numChunks << " iteration " << i << endl; DBClientMockCursor diffCursor( diffs ); differ->calculateConfigDiff( diffCursor ); validate( chunks, ranges, maxVersion, maxShardVersions ); } }