// SERVER-2068 (function() { var chunkSize = 25 var s = new ShardingTest({ name: "migrate_cursor1", shards: 2, mongos: 1, other: { chunkSize : chunkSize } }); s.adminCommand( { enablesharding : "test" } ); db = s.getDB( "test" ) s.ensurePrimaryShard('test', 'shard0001'); t = db.foo bigString = "" stringSize = 1024; while ( bigString.length < stringSize ) bigString += "asdasdas"; stringSize = bigString.length docsPerChunk = Math.ceil( ( chunkSize * 1024 * 1024 ) / ( stringSize - 12 ) ) numChunks = 5 numDocs = 20 * docsPerChunk print( "stringSize: " + stringSize + " docsPerChunk: " + docsPerChunk + " numDocs: " + numDocs ) var bulk = t.initializeUnorderedBulkOp(); for (var i = 0; i < numDocs; i++){ bulk.insert({ _id: i, s: bigString }); } assert.writeOK(bulk.execute()); s.adminCommand( { shardcollection : "test.foo" , key : { _id : 1 } } ); assert.lt( numChunks , s.config.chunks.find().count() , "initial 1" ); primary = s.getServer( "test" ).getDB( "test" ).foo; secondaryName = s.getOther( primary.name ) secondary = secondaryName.getDB( "test" ).foo; assert.eq( numDocs , primary.count() , "initial 2" ); assert.eq( 0 , secondary.count() , "initial 3" ); assert.eq( numDocs , t.count() , "initial 4" ) x = primary.find( { _id : { $lt : 500 } } ).batchSize(2) x.next(); // 1. Create an open cursor print("start moving chunks...") // 2. Move chunk from s0 to s1 without waiting for deletion. // Command returns, but the deletion on s0 will block due to the open cursor. s.adminCommand( { moveChunk : "test.foo" , find : { _id : 0 } , to : secondaryName.name } ) // 3. Start second moveChunk command from s0 to s1. // This moveChunk should not observe the above deletion as a 'mod', transfer it to s1 and cause deletion on s1. // This moveChunk will wait for deletion. join = startParallelShell( "db.x.insert( {x:1} ); db.adminCommand( { moveChunk : 'test.foo' , find : { _id : " + docsPerChunk * 3 + " } , to : '" + secondaryName.name + "', _waitForDelete: true } )" ) assert.soon( function(){ return db.x.count() > 0; } , "XXX" , 30000 , 1 ) // 4. Close the cursor to enable chunk deletion. print( "itcount: " + x.itcount() ) x = null; for ( i=0; i<5; i++ ) gc() print( "cursor should be gone" ) // 5. Waiting for the second moveChunk to finish its deletion. // Note the deletion for the first moveChunk may not be finished. join(); //assert.soon( function(){ return numDocs == t.count(); } , "at end 1" ) // 6. Check the total number of docs on both shards to make sure no doc is lost. // Use itcount() to ignore orphan docments. assert.eq( numDocs , t.find().itcount() , "at end 2" ) s.stop(); })();