diff options
Diffstat (limited to 'src/mongo/dbtests/repltests.cpp')
| -rw-r--r-- | src/mongo/dbtests/repltests.cpp | 413 |
1 files changed, 116 insertions, 297 deletions
diff --git a/src/mongo/dbtests/repltests.cpp b/src/mongo/dbtests/repltests.cpp index f64d5b95c7b..9f720c1d0ca 100644 --- a/src/mongo/dbtests/repltests.cpp +++ b/src/mongo/dbtests/repltests.cpp @@ -15,27 +15,38 @@ * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. */ #include "mongo/pch.h" -#include "mongo/db/repl.h" - +#include "mongo/bson/mutable/document.h" +#include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/db.h" -#include "mongo/db/index_update.h" #include "mongo/db/instance.h" #include "mongo/db/json.h" -#include "mongo/db/oplog.h" -#include "mongo/db/queryoptimizer.h" +#include "mongo/db/queryutil.h" +#include "mongo/db/repl/master_slave.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/replication_server_status.h" #include "mongo/db/repl/rs.h" +#include "mongo/db/ops/update.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/structure/collection_iterator.h" #include "mongo/dbtests/dbtests.h" -namespace mongo { - void createOplog(); - void oldRepl(); -} - namespace ReplTests { BSONObj f( const char *s ) { @@ -48,11 +59,16 @@ namespace ReplTests { public: Base() : _context( ns() ) { oldRepl(); - cmdLine._replSet = ""; - cmdLine.oplogSize = 5 * 1024 * 1024; + replSettings.replSet = ""; + replSettings.oplogSize = 5 * 1024 * 1024; replSettings.master = true; createOplog(); - ensureHaveIdIndex( ns(), false ); + + Collection* c = _context.db()->getCollection( ns() ); + if ( ! c ) { + c = _context.db()->createCollection( ns() ); + } + c->getIndexCatalog()->ensureHaveIdIndex(); } ~Base() { try { @@ -96,21 +112,39 @@ namespace ReplTests { return client()->findOne( cllNS(), BSONObj() ); } int count() const { - int count = 0; Lock::GlobalWrite lk; Client::Context ctx( ns() ); - boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns() ); - for(; c->ok(); c->advance(), ++count ) { -// cout << "obj: " << c->current().toString() << endl; + Database* db = ctx.db(); + Collection* coll = db->getCollection( ns() ); + if ( !coll ) { + coll = db->createCollection( ns() ); } + + int count = 0; + CollectionIterator* it = coll->getIterator( DiskLoc(), false, + CollectionScanParams::FORWARD ); + for ( ; !it->isEOF(); it->getNext() ) { + ++count; + } + delete it; return count; } static int opCount() { Lock::GlobalWrite lk; Client::Context ctx( cllNS() ); + Database* db = ctx.db(); + Collection* coll = db->getCollection( cllNS() ); + if ( !coll ) { + coll = db->createCollection( cllNS() ); + } + int count = 0; - for( boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() ) + CollectionIterator* it = coll->getIterator( DiskLoc(), false, + CollectionScanParams::FORWARD ); + for ( ; !it->isEOF(); it->getNext() ) { ++count; + } + delete it; return count; } static void applyAllOperations() { @@ -118,8 +152,16 @@ namespace ReplTests { vector< BSONObj > ops; { Client::Context ctx( cllNS() ); - for( boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() ) - ops.push_back( c->current() ); + Database* db = ctx.db(); + Collection* coll = db->getCollection( cllNS() ); + + CollectionIterator* it = coll->getIterator( DiskLoc(), false, + CollectionScanParams::FORWARD ); + while ( !it->isEOF() ) { + DiskLoc currLoc = it->getNext(); + ops.push_back( currLoc.obj() ); + } + delete it; } { Client::Context ctx( ns() ); @@ -129,7 +171,7 @@ namespace ReplTests { ReplSource a(b.obj()); for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i ) { if ( 0 ) { - log() << "op: " << *i << endl; + mongo::unittest::log() << "op: " << *i << endl; } a.applyOperation( *i ); } @@ -138,30 +180,62 @@ namespace ReplTests { static void printAll( const char *ns ) { Lock::GlobalWrite lk; Client::Context ctx( ns ); - boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns ); - vector< DiskLoc > toDelete; + Database* db = ctx.db(); + Collection* coll = db->getCollection( ns ); + if ( !coll ) { + coll = db->createCollection( ns ); + } + + CollectionIterator* it = coll->getIterator( DiskLoc(), false, + CollectionScanParams::FORWARD ); out() << "all for " << ns << endl; - for(; c->ok(); c->advance() ) { - out() << c->current().toString() << endl; + while ( !it->isEOF() ) { + DiskLoc currLoc = it->getNext(); + out() << currLoc.obj().toString() << endl; } + delete it; } // These deletes don't get logged. static void deleteAll( const char *ns ) { Lock::GlobalWrite lk; Client::Context ctx( ns ); - boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns ); + Database* db = ctx.db(); + Collection* coll = db->getCollection( ns ); + if ( !coll ) { + coll = db->createCollection( ns ); + } + vector< DiskLoc > toDelete; - for(; c->ok(); c->advance() ) { - toDelete.push_back( c->currLoc() ); + CollectionIterator* it = coll->getIterator( DiskLoc(), false, + CollectionScanParams::FORWARD ); + while ( !it->isEOF() ) { + toDelete.push_back( it->getNext() ); } + delete it; for( vector< DiskLoc >::iterator i = toDelete.begin(); i != toDelete.end(); ++i ) { - theDataFileMgr.deleteRecord( ns, i->rec(), *i, true ); + coll->deleteDocument( *i, true ); } } - static void insert( const BSONObj &o, bool god = false ) { + static void insert( const BSONObj &o ) { Lock::GlobalWrite lk; Client::Context ctx( ns() ); - theDataFileMgr.insert( ns(), o.objdata(), o.objsize(), false, god ); + Database* db = ctx.db(); + Collection* coll = db->getCollection( ns() ); + if ( !coll ) { + coll = db->createCollection( ns() ); + } + + if ( o.hasField( "_id" ) ) { + coll->insertDocument( o, true ); + return; + } + + class BSONObjBuilder b; + OID id; + id.init(); + b.appendOID( "_id", &id ); + b.appendElements( o ); + coll->insertDocument( b.obj(), true ); } static BSONObj wid( const char *json ) { class BSONObjBuilder b; @@ -656,8 +730,8 @@ namespace ReplTests { } void reset() const { deleteAll( ns() ); - insert( ot_, true ); - insert( o_, true ); + insert( ot_ ); + insert( o_ ); } protected: BSONObj o_, u_, ot_; @@ -1034,7 +1108,9 @@ namespace ReplTests { using ReplTests::Base::check; void check() const { ASSERT_EQUALS( 1, count() ); - check( BSON( "_id" << 0 << "a" << 50 << "b" << 3 ) , one( fromjson( "{'_id':0}" ) ) ); + ASSERT_EQUALS( + mutablebson::unordered( BSON( "_id" << 0 << "a" << 50 << "b" << 3 ) ), + mutablebson::unordered( one( fromjson( "{'_id':0}" ) ) ) ); } void reset() const { deleteAll( ns() ); @@ -1051,7 +1127,9 @@ namespace ReplTests { using ReplTests::Base::check; void check() const { ASSERT_EQUALS( 1, count() ); - check( BSON( "_id" << 0 << "a" << 50 << "b" << 3 ) , one( fromjson( "{'_id':0}" ) ) ); + ASSERT_EQUALS( + mutablebson::unordered( BSON( "_id" << 0 << "a" << 50 << "b" << 3 ) ), + mutablebson::unordered( one( fromjson( "{'_id':0}" ) ) ) ); } void reset() const { deleteAll( ns() ); @@ -1067,7 +1145,9 @@ namespace ReplTests { using ReplTests::Base::check; void check() const { ASSERT_EQUALS( 1, count() ); - check( BSON( "_id" << 0 << "b" << 3 << "z" << 1 ) , one( fromjson( "{'_id':0}" ) ) ); + ASSERT_EQUALS( + mutablebson::unordered( BSON( "_id" << 0 << "b" << 3 << "z" << 1 ) ), + mutablebson::unordered( one( fromjson( "{'_id':0}" ) ) ) ); } void reset() const { deleteAll( ns() ); @@ -1281,251 +1361,6 @@ namespace ReplTests { } }; - /** - * Check against oldest document in the oplog before scanning backward - * from the newest document. - */ - class FindingStartCursorStale : public Base { - public: - void run() { - for( int i = 0; i < 10; ++i ) { - client()->insert( ns(), BSON( "_id" << i ) ); - } - Lock::GlobalWrite lk; - Client::Context ctx( cllNS() ); - NamespaceDetails *nsd = nsdetails( cllNS() ); - BSONObjBuilder b; - b.appendTimestamp( "$gte" ); - BSONObj query = BSON( "ts" << b.obj() ); - FieldRangeSetPair frsp( cllNS(), query ); - BSONObj order = BSON( "$natural" << 1 ); - scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd, -1, frsp, &frsp, query, order ) ); - scoped_ptr<FindingStartCursor> fsc( FindingStartCursor::make( *qp ) ); - ASSERT( fsc->done() ); - ASSERT_EQUALS( 0, fsc->cursor()->current()[ "o" ].Obj()[ "_id" ].Int() ); - } - }; - - /** Check unsuccessful yield recovery with FindingStartCursor */ - class FindingStartCursorYield : public Base { - public: - void run() { - for( int i = 0; i < 10; ++i ) { - client()->insert( ns(), BSON( "_id" << i ) ); - } - Date_t ts = client()->query( "local.oplog.$main", Query().sort( BSON( "$natural" << 1 ) ), 1, 4 )->next()[ "ts" ].date(); - Client::Context ctx( cllNS() ); - NamespaceDetails *nsd = nsdetails( cllNS() ); - BSONObjBuilder b; - b.appendDate( "$gte", ts ); - BSONObj query = BSON( "ts" << b.obj() ); - FieldRangeSetPair frsp( cllNS(), query ); - BSONObj order = BSON( "$natural" << 1 ); - scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd, -1, frsp, &frsp, query, order ) ); - scoped_ptr<FindingStartCursor> fsc( FindingStartCursor::make( *qp ) ); - ASSERT( !fsc->done() ); - fsc->next(); - ASSERT( !fsc->done() ); - ASSERT( fsc->prepareToYield() ); - ClientCursor::invalidate( "local.oplog.$main" ); - ASSERT_THROWS( fsc->recoverFromYield(), MsgAssertionException ); - } - }; - - class FindingStartExtentTraversalBase : public Base { - public: - FindingStartExtentTraversalBase() { - client()->dropCollection( ns() ); - } - virtual ~FindingStartExtentTraversalBase() { - client()->dropCollection( ns() ); - } - void run() { - BSONObj info; - // Create a capped collection ... - ASSERT( client()->runCommand( "unittests", - BSON( "create" << "repltests.findingstart" << - "capped" << true << - // ... with the specified extent sizes. - "$nExtents" << extentSizes() << - "autoIndexId" << false ), info ) ); - // Populate documents. - for( int i = 0; i < numDocs(); ++i ) { - client()->insert( ns(), BSON( "ts" << i << "payload" << payload8k() ) ); - } - NamespaceDetails *nsd = nsdetails( ns() ); - BSONObj query = BSON( "ts" << BSON( "$gte" << 0 ) ); - FieldRangeSetPair frsp( ns(), query ); - BSONObj order = BSON( "$natural" << 1 ); - // Create a QueryPlan. - scoped_ptr<QueryPlan> qp( QueryPlan::make( nsd, -1, frsp, &frsp, query, order ) ); - // Create a FindingStartCursor. - scoped_ptr<FindingStartCursor> fsc( FindingStartCursor::make( *qp ) ); - // Check the result of FindingStartCursor::prevExtentFirstLoc() called on the specified - // target(). - ASSERT_EQUALS( expectedPrevExtentFirstLoc().toString(), - fsc->prevExtentFirstLoc( target() ).toString() ); - } - protected: - static const char* ns() { return "unittests.repltests.findingstart"; } - static const NamespaceDetails* nsd() { return nsdetails( ns() ); } - /** Document payload. */ - static string payload8k() { return string( 8*1024, 'a' ); } - /** An extent of this size is too small to contain one document containing payload8k(). */ - static int tooSmall() { return 1*1024; } - /** An extent of this size fits one document. */ - static int fitsOne() { return 10*1024; } - /** An extent of this size fits many documents. */ - static int fitsMany() { return 50*1024; } - virtual BSONArray extentSizes() const = 0; - virtual int numDocs() const = 0; - virtual DiskLoc target() const = 0; - virtual DiskLoc expectedPrevExtentFirstLoc() const = 0; - }; - - class FindingStartEmptyExtentNonLoopedBase : public FindingStartExtentTraversalBase { - virtual int numDocs() const { - // Insert two documents. Empty extents may exist betwen or preceding them depending on - // the test case. - return 2; - } - /** Target the prevExtentFirstLoc() request on the non looped collection's last record. */ - virtual DiskLoc target() const { return nsd()->lastRecord(); } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return nsd()->firstRecord(); } - }; - - class FindingStartEmptyExtentLoopedBase : public FindingStartExtentTraversalBase { - virtual int numDocs() const { - // Insert three documents. Empty extents may exist betwen or preceding them depending - // on the test case. Some documents may overwrite others (it is a capped collection). - return 3; - } - /** - * Target the prevExtentFirstLoc() request on the looped collection's first record. (The - * most recently saved record is the first in the collection due to extent allocation - * looping.) - */ - virtual DiskLoc target() const { return nsd()->firstRecord(); } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return nsd()->lastRecord(); } - }; - - /** - * FindingStartCursor properly skips an empty extent in 'FindExtent' mode of a non looped - * capped collection. - */ - class FindingStartEmptyExtentNonLooped : public FindingStartEmptyExtentNonLoopedBase { - virtual BSONArray extentSizes() const { - // Iteration 2 <--------< 1 <---------< 0 - return BSON_ARRAY( fitsOne() << tooSmall() << fitsOne() ); - } - }; - - /** Properly skips two empty extents of a non looped capped collection. */ - class FindingStartTwoEmptyExtentsNonLooped : public FindingStartEmptyExtentNonLoopedBase { - virtual BSONArray extentSizes() const { - // Iteration 3 <--------< 2 <---------< 1 <---------< 0 - return BSON_ARRAY( fitsOne() << tooSmall() << tooSmall() << fitsOne() ); - } - }; - - /** Properly stops iteration at the beginning of a non looped capped collection. */ - class FindingStartTwoEmptyEarlyExtentsNonLooped : public FindingStartEmptyExtentNonLoopedBase { - virtual BSONArray extentSizes() const { - // Iteration 2 <---------< 1 <----------< 0 - return BSON_ARRAY( tooSmall() << tooSmall() << fitsMany() ); - } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return DiskLoc(); } - }; - - /** The first record of the previous extent is returned (as opposed to some other record). */ - class FindingStartFirstRecordNonLooped : public FindingStartExtentTraversalBase { - virtual int numDocs() const { return 10; } - virtual BSONArray extentSizes() const { - // Iteration 1 <--------< 0 - return BSON_ARRAY( fitsMany() << fitsMany() ); - } - virtual DiskLoc target() const { return nsd()->lastRecord(); } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return nsd()->firstRecord(); } - }; - - /** Properly skips an empty extent of a looped capped collection. */ - class FindingStartEmptyExtentLooped : public FindingStartEmptyExtentLoopedBase { - virtual BSONArray extentSizes() const { - // Iteration ---< 0 2 <--------< 1 <---- - return BSON_ARRAY( fitsOne() << fitsOne() << tooSmall() ); - } - }; - - /** Properly skips two empty extents of a looped capped collection. */ - class FindingStartTwoEmptyExtentsLooped : public FindingStartEmptyExtentLoopedBase { - virtual BSONArray extentSizes() const { - // Iteration ---< 0 3 <--------< 2 <---------< 1 <--- - return BSON_ARRAY( fitsOne() << fitsOne() << tooSmall() << tooSmall() ); - } - }; - - /** Properly stops iteration at the loop point of a looped capped collection. */ - class FindingStartTwoEmptyEarlyExtentsLooped : public FindingStartEmptyExtentLoopedBase { - virtual BSONArray extentSizes() const { - // Iteration --< 0 3 <------< 2 <--------< 1 <----- - return BSON_ARRAY( fitsOne() << tooSmall() << tooSmall() ); - } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return DiskLoc(); } - }; - - /** The first record of the previous extent is returned (as opposed to some other record). */ - class FindingStartFirstRecordLooped : public FindingStartExtentTraversalBase { - virtual int numDocs() const { return 35; } - virtual BSONArray extentSizes() const { - // Iteration 1 <--------< 0 - return BSON_ARRAY( fitsMany() << fitsMany() ); - } - virtual DiskLoc target() const { return nsd()->lastRecord(); } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return nsd()->firstRecord(); } - }; - - /** Advance from the fresh side of the cap extent to its previous extent. */ - class CapExtentFreshSidePrevExtent : public FindingStartExtentTraversalBase { - virtual int numDocs() const { - // Looped allocation with capExtent the first extent. - return 10; - } - virtual BSONArray extentSizes() const { - // Iteration ---< 0 1 <---- - return BSON_ARRAY( fitsMany() << fitsOne() ); - } - virtual DiskLoc target() const { - DiskLoc lastRecord = nsd()->lastRecord(); - // Return last record in first extent (second to last record overall). - return lastRecord.rec()->getPrev( lastRecord ); - } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return nsd()->lastRecord(); } - }; - - /** - * Advance from the stale side of the cap extent to its previous extent. This is not optimal, - * but good enough. See comments in FindingStartCursor::extentFirstLoc(). - */ - class CapExtentStaleSidePrevExtent : public CapExtentFreshSidePrevExtent { - virtual DiskLoc target() const { return nsd()->firstRecord(); } - }; - - /** prevExtentFirstLoc() when there is only one extent (non looped) returns DiskLoc(). */ - class FindingStartSingleExtentNonLooped : public FindingStartExtentTraversalBase { - virtual int numDocs() const { return 2; } - virtual BSONArray extentSizes() const { return BSON_ARRAY( fitsMany() ); } - virtual DiskLoc target() const { return nsd()->lastRecord(); } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return DiskLoc(); } - }; - - /** prevExtentFirstLoc() when there is only one extent (looped) returns DiskLoc(). */ - class FindingStartSingleExtentLooped : public FindingStartExtentTraversalBase { - virtual int numDocs() const { return 10; } - virtual BSONArray extentSizes() const { return BSON_ARRAY( fitsMany() ); } - virtual DiskLoc target() const { return nsd()->lastRecord(); } - virtual DiskLoc expectedPrevExtentFirstLoc() const { return DiskLoc(); } - }; - /** Check ReplSetConfig::MemberCfg equality */ class ReplSetMemberCfgEquality : public Base { public: @@ -1626,7 +1461,6 @@ namespace ReplTests { add< Idempotence::EmptyPush >(); add< Idempotence::EmptyPushSparseIndex >(); add< Idempotence::PushAll >(); - add< Idempotence::PushWithDollarSigns >(); add< Idempotence::PushSlice >(); add< Idempotence::PushSliceInitiallyInexistent >(); add< Idempotence::PushSliceToZero >(); @@ -1646,26 +1480,11 @@ namespace ReplTests { add< Idempotence::SingletonNoRename >(); add< Idempotence::IndexedSingletonNoRename >(); add< Idempotence::AddToSetEmptyMissing >(); - add< Idempotence::AddToSetWithDollarSigns >(); add< Idempotence::ReplaySetPreexistingNoOpPull >(); add< Idempotence::ReplayArrayFieldNotAppended >(); add< DeleteOpIsIdBased >(); add< DatabaseIgnorerBasic >(); add< DatabaseIgnorerUpdate >(); - add< FindingStartCursorStale >(); - add< FindingStartCursorYield >(); - add< FindingStartEmptyExtentNonLooped >(); - add< FindingStartTwoEmptyExtentsNonLooped >(); - add< FindingStartTwoEmptyEarlyExtentsNonLooped >(); - add< FindingStartFirstRecordNonLooped >(); - add< FindingStartEmptyExtentLooped >(); - add< FindingStartTwoEmptyExtentsLooped >(); - add< FindingStartTwoEmptyEarlyExtentsLooped >(); - add< FindingStartFirstRecordLooped >(); - add< CapExtentFreshSidePrevExtent >(); - add< CapExtentStaleSidePrevExtent >(); - add< FindingStartSingleExtentNonLooped >(); - add< FindingStartSingleExtentLooped >(); add< ReplSetMemberCfgEquality >(); add< ShouldRetry >(); } |
