diff options
Diffstat (limited to 'jstests/gle/gle_after_split_failure_during_migration.js')
-rw-r--r-- | jstests/gle/gle_after_split_failure_during_migration.js | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/jstests/gle/gle_after_split_failure_during_migration.js b/jstests/gle/gle_after_split_failure_during_migration.js new file mode 100644 index 00000000000..9d0a6a9ca2a --- /dev/null +++ b/jstests/gle/gle_after_split_failure_during_migration.js @@ -0,0 +1,136 @@ +/** + * SERVER-4987 This test tries to check the getLastError call will still use + * the same connection even if a split chunk triggered while doing inserts + * failed (cause by StaleConfigException). + * + * TODO: SERVER-5175 + * This test relies on the corresponding delays inside (1) WriteBackListener::run + * and (2) ShardStrategy::_insert and (3) receivedInsert from instance.cpp + * to make the bug easier to manifest. + * + * The purpose of (1) is to make the writebacks slower so the failed inserts won't + * be reapplied on time. + * + * The purpose of (2) is to make it easier for the moveChunk command from the other + * mongos to interleave in between the moment the insert has set its shard version and + * when in tries to autosplit (Note: it should be long enough to allow the moveChunk + * to actually complete before it tries to proceed to autosplit). + * + * The purpose of (3) is to make sure that the insert won't get applied to the + * shard right away so when a different connection is used to do the getLastError, + * the write will still not be applied. + */ +function testGleAfterSplitDuringMigration(){ + var st = new ShardingTest({ shards: 2, verbose: 2, mongos: 2, + other: { chunksize: 1 }}); + + // Stop the balancer to prevent it from contending with the distributed lock. + st.stopBalancer(); + + var DB_NAME = jsTest.name(); + var COLL_NAME = "coll"; + + var mongos = st.s0; + var confDB = mongos.getDB( "config" ); + var coll = mongos.getCollection( DB_NAME + "." + COLL_NAME ); + + var shardConn = st.d0; + var shardColl = shardConn.getCollection( coll.getFullName() ); + + var data = "x"; + var dataSize = 1024 * 256; // bytes, must be power of 2 + while( data.length < dataSize ) data += data; + + // Shard collection + st.shardColl( coll, { _id : 1 }, false ); + + var docID = 0; + + /** + * @return {Mongo} the connection object given the name of the shard. + */ + var getShardConn = function( shardName ) { + var shardLoc = confDB.shards.findOne({ _id: shardName }).host; + return new Mongo( shardLoc ); + }; + + /** + * Inserts documents using a direct shard connection to the max key chunk + * enough to make sure that it will trigger the auto split. + * + * variables from outer scope: docID, coll, confDB, data + */ + var primeForSplitting = function() { + var topChunk = confDB.chunks.find().sort({ max: -1 }).limit( 1 ).next(); + var shardLoc = getShardConn( topChunk.shard ); + var testColl = shardLoc.getCollection( coll.getFullName() ); + + var superSaturatedChunkSize = 1024 * 1024 * 10; // 10MB + var docsToSaturateChunkSize = superSaturatedChunkSize / dataSize; + + for ( var i = 0; i < docsToSaturateChunkSize; i++ ) { + testColl.insert({ _id: docID++, val: data }); + } + + assert.eq( null, testColl.getDB().getLastError() ); + }; + + /** + * Moves a random chunk to a new shard using a different mongos. + * + * @param tries {Number} number of retry attempts when the moveChunk command + * fails. + * + * variables from outer scope: coll, st + */ + var moveRandomChunk = function( tries ) { + var otherConfDB = st.s1.getDB( "config" ); + var chunksCursor = otherConfDB.chunks.find().sort({ max: 1 }); + var chunkCount = chunksCursor.count(); + + var randIdx = Math.floor( Math.random() * chunkCount ); + // Don't get the chunk with max/min key + randIdx = ( randIdx == chunkCount )? randIdx - 1 : randIdx; + randIdx = ( randIdx == 0 )? randIdx + 1 : randIdx; + + var chunk = chunksCursor.arrayAccess( randIdx ); + var chunkOwner = chunk.shard; + var newOwner = otherConfDB.shards.findOne({ _id: { $ne: chunkOwner }})._id; + + var result = otherConfDB.adminCommand({ moveChunk: coll.getFullName(), + find: { _id: chunk.min._id }, + to: newOwner }); + + jsTest.log( "moveChunk result: " + tojson( result )); + if( !result.ok && tries > 1 ) { + moveRandomChunk( tries - 1 ); + } + }; + + var chunks = 0; + do { + coll.insert({ _id: docID++, val: data }); + chunks = mongos.getDB( "config" ).chunks.find().count(); + } while ( chunks < 5 ); + + primeForSplitting(); + + jsTest.log( "Starting the insert that should trigger auto-split." ); + + // TODO: SERVER-5175 Trigger delays here + coll.insert({ _id: docID++, val: data }); + moveRandomChunk( 3 ); + + // getLastError should wait for all writes to this connection. + var errObj = coll.getDB().getLastErrorObj(); + jsTest.log( "Last Error Object: " + tojson( errObj )); + + assert.eq( docID, coll.find().itcount(), "Count does not match!" ); + + jsTest.log( "Finished counting." ); + + st.stop(); +} + +testGleAfterSplitDuringMigration(); + |