summaryrefslogtreecommitdiff
path: root/jstests/repl
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/repl')
-rw-r--r--jstests/repl/basic1.js59
-rw-r--r--jstests/repl/pair1.js99
-rw-r--r--jstests/repl/pair2.js71
-rw-r--r--jstests/repl/pair3.js235
-rw-r--r--jstests/repl/pair4.js159
-rw-r--r--jstests/repl/pair5.js95
-rw-r--r--jstests/repl/pair6.js115
-rw-r--r--jstests/repl/repl1.js55
-rw-r--r--jstests/repl/repl2.js45
-rw-r--r--jstests/repl/repl3.js47
-rw-r--r--jstests/repl/repl4.js30
-rw-r--r--jstests/repl/repl5.js32
-rw-r--r--jstests/repl/repl6.js73
-rw-r--r--jstests/repl/repl7.js45
-rw-r--r--jstests/repl/repl8.js30
-rw-r--r--jstests/repl/repl9.js48
-rw-r--r--jstests/repl/replacePeer1.js71
-rw-r--r--jstests/repl/replacePeer2.js72
18 files changed, 1381 insertions, 0 deletions
diff --git a/jstests/repl/basic1.js b/jstests/repl/basic1.js
new file mode 100644
index 0000000..9668a91
--- /dev/null
+++ b/jstests/repl/basic1.js
@@ -0,0 +1,59 @@
+
+// test repl basics
+// data on master/slave is the same
+
+var rt = new ReplTest( "basic1" );
+
+m = rt.start( true );
+s = rt.start( false );
+
+function hash( db ){
+ var s = "";
+ var a = db.getCollectionNames();
+ a = a.sort();
+ a.forEach(
+ function(cn){
+ var c = db.getCollection( cn );
+ s += cn + "\t" + c.find().count() + "\n";
+ c.find().sort( { _id : 1 } ).forEach(
+ function(o){
+ s += tojson( o , "" , true ) + "\n";
+ }
+ );
+ }
+ );
+ return s;
+}
+
+am = m.getDB( "foo" );
+as = s.getDB( "foo" );
+
+function check( note ){
+ var start = new Date();
+ var x,y;
+ while ( (new Date()).getTime() - start.getTime() < 30000 ){
+ x = hash( am );
+ y = hash( as );
+ if ( x == y )
+ return;
+ sleep( 200 );
+ }
+ assert.eq( x , y , note );
+}
+
+am.a.save( { x : 1 } );
+check( "A" );
+
+am.a.save( { x : 5 } );
+
+am.a.update( {} , { $inc : { x : 1 } } );
+check( "B" );
+
+am.a.update( {} , { $inc : { x : 1 } } , false , true );
+check( "C" );
+
+rt.stop();
+
+
+
+
diff --git a/jstests/repl/pair1.js b/jstests/repl/pair1.js
new file mode 100644
index 0000000..7004048
--- /dev/null
+++ b/jstests/repl/pair1.js
@@ -0,0 +1,99 @@
+// Basic pairing test
+
+var baseName = "jstests_pair1test";
+
+debug = function( p ) {
+// print( p );
+}
+
+ismaster = function( n ) {
+ var im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+// print( "ismaster: " + tojson( im ) );
+ assert( im, "command ismaster failed" );
+ return im.ismaster;
+}
+
+var writeOneIdx = 0;
+
+writeOne = function( n ) {
+ n.getDB( baseName ).z.save( { _id: new ObjectId(), i: ++writeOneIdx } );
+}
+
+getCount = function( n ) {
+ return n.getDB( baseName ).z.find( { i: writeOneIdx } ).toArray().length;
+}
+
+checkWrite = function( m, s ) {
+ writeOne( m );
+ assert.eq( 1, getCount( m ) );
+ check( s );
+}
+
+check = function( s ) {
+ s.setSlaveOk();
+ assert.soon( function() {
+ return 1 == getCount( s );
+ } );
+}
+
+// check that slave reads and writes are guarded
+checkSlaveGuard = function( s ) {
+ var t = s.getDB( baseName + "-temp" ).temp;
+ assert.throws( t.find().count, {}, "not master" );
+ assert.throws( t.find(), {}, "not master", "find did not assert" );
+
+ checkError = function() {
+ assert.eq( "not master", s.getDB( "admin" ).getLastError() );
+ s.getDB( "admin" ).resetError();
+ }
+ s.getDB( "admin" ).resetError();
+ t.save( {x:1} );
+ checkError();
+ t.update( {}, {x:2}, true );
+ checkError();
+ t.remove( {x:0} );
+ checkError();
+}
+
+doTest = function( signal ) {
+
+ ports = allocatePorts( 3 );
+
+ a = new MongodRunner( ports[ 0 ], "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( ports[ 1 ], "/data/db/" + baseName + "-left", "127.0.0.1:" + ports[ 2 ], "127.0.0.1:" + ports[ 0 ] );
+ r = new MongodRunner( ports[ 2 ], "/data/db/" + baseName + "-right", "127.0.0.1:" + ports[ 1 ], "127.0.0.1:" + ports[ 0 ] );
+
+ rp = new ReplPair( l, r, a );
+ rp.start();
+ rp.waitForSteadyState();
+
+ checkSlaveGuard( rp.slave() );
+
+ checkWrite( rp.master(), rp.slave() );
+
+ debug( "kill first" );
+ rp.killNode( rp.master(), signal );
+ rp.waitForSteadyState( [ 1, null ], rp.slave().host );
+ writeOne( rp.master() );
+
+ debug( "restart first" );
+ rp.start( true );
+ rp.waitForSteadyState();
+ check( rp.slave() );
+ checkWrite( rp.master(), rp.slave() );
+
+ debug( "kill second" );
+ rp.killNode( rp.master(), signal );
+ rp.waitForSteadyState( [ 1, null ], rp.slave().host );
+
+ debug( "restart second" );
+ rp.start( true );
+ rp.waitForSteadyState( [ 1, 0 ], rp.master().host );
+ checkWrite( rp.master(), rp.slave() );
+
+ ports.forEach( function( x ) { stopMongod( x ); } );
+
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/pair2.js b/jstests/repl/pair2.js
new file mode 100644
index 0000000..2491fb2
--- /dev/null
+++ b/jstests/repl/pair2.js
@@ -0,0 +1,71 @@
+// Pairing resync
+
+var baseName = "jstests_pair2test";
+
+ismaster = function( n ) {
+ im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+ assert( im );
+ return im.ismaster;
+}
+
+soonCount = function( m, count ) {
+ assert.soon( function() {
+// print( "counting" );
+//// print( "counted: " + l.getDB( baseName ).z.find().count() );
+ return m.getDB( baseName ).z.find().count() == count;
+ } );
+}
+
+doTest = function( signal ) {
+
+ ports = allocatePorts( 3 );
+
+ a = new MongodRunner( ports[ 0 ], "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( ports[ 1 ], "/data/db/" + baseName + "-left", "127.0.0.1:" + ports[ 2 ], "127.0.0.1:" + ports[ 0 ] );
+ r = new MongodRunner( ports[ 2 ], "/data/db/" + baseName + "-right", "127.0.0.1:" + ports[ 1 ], "127.0.0.1:" + ports[ 0 ] );
+
+ rp = new ReplPair( l, r, a );
+ rp.start();
+ rp.waitForSteadyState();
+
+ rp.slave().setSlaveOk();
+ mz = rp.master().getDB( baseName ).z;
+
+ mz.save( { _id: new ObjectId() } );
+ soonCount( rp.slave(), 1 );
+ assert.eq( 0, rp.slave().getDB( "admin" ).runCommand( { "resync" : 1 } ).ok );
+
+ sleep( 3000 ); // allow time to finish clone and save ReplSource
+ rp.killNode( rp.slave(), signal );
+ rp.waitForSteadyState( [ 1, null ], rp.master().host );
+
+ big = new Array( 2000 ).toString();
+ for( i = 0; i < 1000; ++i )
+ mz.save( { _id: new ObjectId(), i: i, b: big } );
+
+ rp.start( true );
+ rp.waitForSteadyState( [ 1, 0 ], rp.master().host );
+
+ sleep( 15000 );
+
+ rp.slave().setSlaveOk();
+ assert.soon( function() {
+ ret = rp.slave().getDB( "admin" ).runCommand( { "resync" : 1 } );
+// printjson( ret );
+ return 1 == ret.ok;
+ } );
+
+ sleep( 8000 );
+ soonCount( rp.slave(), 1001 );
+ sz = rp.slave().getDB( baseName ).z
+ assert.eq( 1, sz.find( { i: 0 } ).count() );
+ assert.eq( 1, sz.find( { i: 999 } ).count() );
+
+ assert.eq( 0, rp.slave().getDB( "admin" ).runCommand( { "resync" : 1 } ).ok );
+
+ ports.forEach( function( x ) { stopMongod( x ); } );
+
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/pair3.js b/jstests/repl/pair3.js
new file mode 100644
index 0000000..506e173
--- /dev/null
+++ b/jstests/repl/pair3.js
@@ -0,0 +1,235 @@
+// test arbitration
+
+var baseName = "jstests_pair3test";
+
+ismaster = function( n ) {
+ var im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+ print( "ismaster: " + tojson( im ) );
+ assert( im, "command ismaster failed" );
+ return im.ismaster;
+}
+
+// bring up node connections before arbiter connections so that arb can forward to node when expected
+connect = function() {
+ if ( lp == null ) {
+ lp = startMongoProgram( "mongobridge", "--port", lpPort, "--dest", "localhost:" + lPort );
+ }
+ if ( rp == null ) {
+ rp = startMongoProgram( "mongobridge", "--port", rpPort, "--dest", "localhost:" + rPort );
+ }
+ if ( al == null ) {
+ al = startMongoProgram( "mongobridge", "--port", alPort, "--dest", "localhost:" + aPort );
+ }
+ if ( ar == null ) {
+ ar = startMongoProgram( "mongobridge", "--port", arPort, "--dest", "localhost:" + aPort );
+ }
+}
+
+disconnectNode = function( mongo ) {
+ if ( lp ) {
+ stopMongoProgram( lpPort );
+ lp = null;
+ }
+ if ( rp ) {
+ stopMongoProgram( rpPort );
+ rp = null;
+ }
+ if ( mongo.host.match( new RegExp( "^127.0.0.1:" + lPort + "$" ) ) ) {
+ stopMongoProgram( alPort );
+ al = null;
+ } else if ( mongo.host.match( new RegExp( "^127.0.0.1:" + rPort + "$" ) ) ) {
+ stopMongoProgram( arPort );
+ ar = null;
+ } else {
+ assert( false, "don't know how to disconnect node: " + mongo );
+ }
+}
+
+doTest1 = function() {
+ al = ar = lp = rp = null;
+ ports = allocatePorts( 7 );
+ aPort = ports[ 0 ];
+ alPort = ports[ 1 ];
+ arPort = ports[ 2 ];
+ lPort = ports[ 3 ];
+ lpPort = ports[ 4 ];
+ rPort = ports[ 5 ];
+ rpPort = ports[ 6 ];
+
+ connect();
+
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + alPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + arPort );
+
+ pair = new ReplPair( l, r, a );
+
+ // normal startup
+ pair.start();
+ pair.waitForSteadyState();
+
+ // disconnect slave
+ disconnectNode( pair.slave() );
+ pair.waitForSteadyState( [ 1, -3 ], pair.master().host );
+
+ // disconnect master
+ disconnectNode( pair.master() );
+ pair.waitForSteadyState( [ -3, -3 ] );
+
+ // reconnect
+ connect();
+ pair.waitForSteadyState();
+
+ // disconnect master
+ disconnectNode( pair.master() );
+ pair.waitForSteadyState( [ 1, -3 ], pair.slave().host, true );
+
+ // disconnect new master
+ disconnectNode( pair.master() );
+ pair.waitForSteadyState( [ -3, -3 ] );
+
+ // reconnect
+ connect();
+ pair.waitForSteadyState();
+
+ // disconnect slave
+ disconnectNode( pair.slave() );
+ pair.waitForSteadyState( [ 1, -3 ], pair.master().host );
+
+ // reconnect slave
+ connect();
+ pair.waitForSteadyState( [ 1, 0 ], pair.master().host );
+
+ // disconnect master
+ disconnectNode( pair.master() );
+ pair.waitForSteadyState( [ 1, -3 ], pair.slave().host, true );
+
+ // reconnect old master
+ connect();
+ pair.waitForSteadyState( [ 1, 0 ], pair.master().host );
+
+ ports.forEach( function( x ) { stopMongoProgram( x ); } );
+}
+
+// this time don't start connected
+doTest2 = function() {
+ al = ar = lp = rp = null;
+ ports = allocatePorts( 7 );
+ aPort = ports[ 0 ];
+ alPort = ports[ 1 ];
+ arPort = ports[ 2 ];
+ lPort = ports[ 3 ];
+ lpPort = ports[ 4 ];
+ rPort = ports[ 5 ];
+ rpPort = ports[ 6 ];
+
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + alPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + arPort );
+
+ pair = new ReplPair( l, r, a );
+ pair.start();
+ pair.waitForSteadyState( [ -3, -3 ] );
+
+ startMongoProgram( "mongobridge", "--port", arPort, "--dest", "localhost:" + aPort );
+
+ // there hasn't been an initial sync, no no node will become master
+
+ for( i = 0; i < 10; ++i ) {
+ assert( pair.isMaster( pair.right() ) == -3 && pair.isMaster( pair.left() ) == -3 );
+ sleep( 500 );
+ }
+
+ stopMongoProgram( arPort );
+
+ startMongoProgram( "mongobridge", "--port", alPort, "--dest", "localhost:" + aPort );
+
+ for( i = 0; i < 10; ++i ) {
+ assert( pair.isMaster( pair.right() ) == -3 && pair.isMaster( pair.left() ) == -3 );
+ sleep( 500 );
+ }
+
+ stopMongoProgram( alPort );
+
+ // connect l and r without a
+
+ startMongoProgram( "mongobridge", "--port", lpPort, "--dest", "localhost:" + lPort );
+ startMongoProgram( "mongobridge", "--port", rpPort, "--dest", "localhost:" + rPort );
+
+ pair.waitForSteadyState( [ 1, 0 ] );
+
+ ports.forEach( function( x ) { stopMongoProgram( x ); } );
+}
+
+// recover from master - master setup
+doTest3 = function() {
+ al = ar = lp = rp = null;
+ ports = allocatePorts( 7 );
+ aPort = ports[ 0 ];
+ alPort = ports[ 1 ];
+ arPort = ports[ 2 ];
+ lPort = ports[ 3 ];
+ lpPort = ports[ 4 ];
+ rPort = ports[ 5 ];
+ rpPort = ports[ 6 ];
+
+ connect();
+
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + alPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + arPort );
+
+ pair = new ReplPair( l, r, a );
+ pair.start();
+ pair.waitForSteadyState();
+
+ // now can only talk to arbiter
+ stopMongoProgram( lpPort );
+ stopMongoProgram( rpPort );
+ pair.waitForSteadyState( [ 1, 1 ], null, true );
+
+ // recover
+ startMongoProgram( "mongobridge", "--port", lpPort, "--dest", "localhost:" + lPort );
+ startMongoProgram( "mongobridge", "--port", rpPort, "--dest", "localhost:" + rPort );
+ pair.waitForSteadyState( [ 1, 0 ], null, true );
+
+ ports.forEach( function( x ) { stopMongoProgram( x ); } );
+}
+
+// check that initial sync is persistent
+doTest4 = function( signal ) {
+ al = ar = lp = rp = null;
+ ports = allocatePorts( 7 );
+ aPort = ports[ 0 ];
+ alPort = ports[ 1 ];
+ arPort = ports[ 2 ];
+ lPort = ports[ 3 ];
+ lpPort = ports[ 4 ];
+ rPort = ports[ 5 ];
+ rpPort = ports[ 6 ];
+
+ connect();
+
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + alPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + arPort );
+
+ pair = new ReplPair( l, r, a );
+ pair.start();
+ pair.waitForSteadyState();
+
+ pair.killNode( pair.left(), signal );
+ pair.killNode( pair.right(), signal );
+ stopMongoProgram( rpPort );
+ stopMongoProgram( lpPort );
+
+ // now can only talk to arbiter
+ pair.start( true );
+ pair.waitForSteadyState( [ 1, 1 ], null, true );
+}
+
+doTest1();
+doTest2();
+doTest3();
+doTest4( 15 );
+doTest4( 9 );
diff --git a/jstests/repl/pair4.js b/jstests/repl/pair4.js
new file mode 100644
index 0000000..5a59c16
--- /dev/null
+++ b/jstests/repl/pair4.js
@@ -0,0 +1,159 @@
+// data consistency after master-master
+
+var baseName = "jstests_pair4test";
+
+debug = function( o ) {
+ printjson( o );
+}
+
+ismaster = function( n ) {
+ var im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+ print( "ismaster: " + tojson( im ) );
+ assert( im, "command ismaster failed" );
+ return im.ismaster;
+}
+
+connect = function() {
+ startMongoProgram( "mongobridge", "--port", lpPort, "--dest", "localhost:" + lPort );
+ startMongoProgram( "mongobridge", "--port", rpPort, "--dest", "localhost:" + rPort );
+}
+
+disconnect = function() {
+ stopMongoProgram( lpPort );
+ stopMongoProgram( rpPort );
+}
+
+write = function( m, n, id ) {
+ if ( id ) {
+ save = { _id:id, n:n };
+ } else {
+ save = { n:n };
+ }
+ m.getDB( baseName ).getCollection( baseName ).save( save );
+}
+
+check = function( m, n, id ) {
+ m.setSlaveOk();
+ if ( id ) {
+ find = { _id:id, n:n };
+ } else {
+ find = { n:n };
+ }
+ assert.soon( function() { return m.getDB( baseName ).getCollection( baseName ).find( find ).count() > 0; },
+ "failed waiting for " + m + " value of n to be " + n );
+}
+
+checkCount = function( m, c ) {
+ m.setSlaveOk();
+ assert.soon( function() {
+ actual = m.getDB( baseName ).getCollection( baseName ).find().count();
+ print( actual );
+ return c == actual; },
+ "count failed for " + m );
+}
+
+coll = function( m ) {
+ return m.getDB( baseName ).getCollection( baseName );
+}
+
+db2Coll = function( m ) {
+ return m.getDB( baseName + "_second" ).getCollection( baseName );
+}
+
+doTest = function( recover, newMaster, newSlave ) {
+ ports = allocatePorts( 5 );
+ aPort = ports[ 0 ];
+ lPort = ports[ 1 ];
+ lpPort = ports[ 2 ];
+ rPort = ports[ 3 ];
+ rpPort = ports[ 4 ];
+
+ // start normally
+ connect();
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + aPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + aPort );
+ pair = new ReplPair( l, r, a );
+ pair.start();
+ pair.waitForSteadyState();
+
+ firstMaster = pair.master();
+ firstSlave = pair.slave();
+
+ write( pair.master(), 0 );
+ write( pair.master(), 1 );
+ check( pair.slave(), 0 );
+ check( pair.slave(), 1 );
+
+ // now each can only talk to arbiter
+ disconnect();
+ pair.waitForSteadyState( [ 1, 1 ], null, true );
+
+ m = newMaster();
+ write( m, 10 );
+ write( m, 100, "a" );
+ coll( m ).update( {n:1}, {$set:{n:2}} );
+ db2Coll( m ).save( {n:500} );
+ db2Coll( m ).findOne();
+
+ s = newSlave();
+ write( s, 20 );
+ write( s, 200, "a" );
+ coll( s ).update( {n:1}, {n:1,m:3} );
+ db2Coll( s ).save( {_id:"a",n:600} );
+ db2Coll( s ).findOne();
+
+ // recover
+ recover();
+
+ nodes = [ pair.right(), pair.left() ];
+
+ nodes.forEach( function( x ) { checkCount( x, 5 ); } );
+ nodes.forEach( function( x ) { [ 0, 10, 20, 100 ].forEach( function( y ) { check( x, y ); } ); } );
+
+ checkM = function( c ) {
+ assert.soon( function() {
+ obj = coll( c ).findOne( {n:2} );
+ printjson( obj );
+ return obj.m == undefined;
+ }, "n:2 test for " + c + " failed" );
+ };
+ nodes.forEach( function( x ) { checkM( x ); } );
+
+ // check separate database
+ nodes.forEach( function( x ) { assert.soon( function() {
+ r = db2Coll( x ).findOne( {_id:"a"} );
+ debug( r );
+ if ( r == null ) {
+ return false;
+ }
+ return 600 == r.n;
+ } ) } );
+
+ ports.forEach( function( x ) { stopMongoProgram( x ); } );
+
+}
+
+debug( "basic test" );
+doTest( function() {
+ connect();
+ pair.waitForSteadyState( [ 1, 0 ], pair.right().host, true );
+ }, function() { return pair.right(); }, function() { return pair.left(); } );
+
+doRestartTest = function( signal ) {
+ doTest( function() {
+ if ( signal == 9 ) {
+ sleep( 3000 );
+ }
+ pair.killNode( firstMaster, signal );
+ connect();
+ pair.start( true );
+ pair.waitForSteadyState( [ 1, 0 ], firstSlave.host, true );
+ }, function() { return firstSlave; }, function() { return firstMaster; } );
+}
+
+debug( "sigterm restart test" );
+doRestartTest( 15 ) // SIGTERM
+
+debug( "sigkill restart test" );
+doRestartTest( 9 ) // SIGKILL
diff --git a/jstests/repl/pair5.js b/jstests/repl/pair5.js
new file mode 100644
index 0000000..ed8c72d
--- /dev/null
+++ b/jstests/repl/pair5.js
@@ -0,0 +1,95 @@
+// writes to new master while making master-master logs consistent
+
+var baseName = "jstests_pair5test";
+
+debug = function( p ) {
+ print( p );
+}
+
+ismaster = function( n ) {
+ var im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+ print( "ismaster: " + tojson( im ) );
+ assert( im, "command ismaster failed" );
+ return im.ismaster;
+}
+
+connect = function() {
+ startMongoProgram( "mongobridge", "--port", lpPort, "--dest", "localhost:" + lPort );
+ startMongoProgram( "mongobridge", "--port", rpPort, "--dest", "localhost:" + rPort );
+}
+
+disconnect = function() {
+ stopMongoProgram( lpPort );
+ stopMongoProgram( rpPort );
+}
+
+write = function( m, n, id ) {
+ if ( id ) {
+ save = { _id:id, n:n };
+ } else {
+ save = { n:n };
+ }
+ m.getDB( baseName ).getCollection( baseName ).save( save );
+}
+
+checkCount = function( m, c ) {
+ m.setSlaveOk();
+ assert.soon( function() {
+ actual = m.getDB( baseName ).getCollection( baseName ).find().count();
+ print( actual );
+ return c == actual; },
+ "count failed for " + m );
+}
+
+doTest = function( nSlave, opIdMem ) {
+ ports = allocatePorts( 5 );
+ aPort = ports[ 0 ];
+ lPort = ports[ 1 ];
+ lpPort = ports[ 2 ];
+ rPort = ports[ 3 ];
+ rpPort = ports[ 4 ];
+
+ // start normally
+ connect();
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + aPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + aPort );
+ pair = new ReplPair( l, r, a );
+ pair.start();
+ pair.waitForSteadyState();
+
+ // now each can only talk to arbiter
+ disconnect();
+ pair.waitForSteadyState( [ 1, 1 ], null, true );
+
+ // left will become slave
+ for( i = 0; i < nSlave; ++i ) {
+ write( pair.left(), i, i );
+ }
+ pair.left().getDB( baseName ).getCollection( baseName ).findOne();
+
+ for( i = 10000; i < 15000; ++i ) {
+ write( pair.right(), i, i );
+ }
+ pair.right().getDB( baseName ).getCollection( baseName ).findOne();
+
+ connect();
+ pair.waitForSteadyState( [ 1, 0 ], pair.right().host, true );
+
+ pair.master().getDB( baseName ).getCollection( baseName ).update( {_id:nSlave - 1}, {_id:nSlave - 1,n:-1}, true );
+ assert.eq( -1, pair.master().getDB( baseName ).getCollection( baseName ).findOne( {_id:nSlave - 1} ).n );
+ checkCount( pair.master(), 5000 + nSlave );
+ assert.eq( -1, pair.master().getDB( baseName ).getCollection( baseName ).findOne( {_id:nSlave - 1} ).n );
+ pair.slave().setSlaveOk();
+ assert.soon( function() {
+ n = pair.slave().getDB( baseName ).getCollection( baseName ).findOne( {_id:nSlave - 1} ).n;
+ print( n );
+ return -1 == n;
+ } );
+
+ ports.forEach( function( x ) { stopMongoProgram( x ); } );
+
+}
+
+doTest( 5000, 100000000 );
+doTest( 5000, 100 ); // force op id converstion to collection based storage
diff --git a/jstests/repl/pair6.js b/jstests/repl/pair6.js
new file mode 100644
index 0000000..b249fc0
--- /dev/null
+++ b/jstests/repl/pair6.js
@@ -0,0 +1,115 @@
+// pairing cases where oplogs run out of space
+
+var baseName = "jstests_pair6test";
+
+debug = function( p ) {
+ print( p );
+}
+
+ismaster = function( n ) {
+ var im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+ print( "ismaster: " + tojson( im ) );
+ assert( im, "command ismaster failed" );
+ return im.ismaster;
+}
+
+connect = function() {
+ startMongoProgram( "mongobridge", "--port", lpPort, "--dest", "localhost:" + lPort );
+ startMongoProgram( "mongobridge", "--port", rpPort, "--dest", "localhost:" + rPort );
+}
+
+disconnect = function() {
+ stopMongoProgram( lpPort );
+ stopMongoProgram( rpPort );
+}
+
+checkCount = function( m, c ) {
+ m.setSlaveOk();
+ assert.soon( function() {
+ actual = m.getDB( baseName ).getCollection( baseName ).find().count();
+ print( actual );
+ return c == actual; },
+ "expected count " + c + " for " + m );
+}
+
+resetSlave = function( s ) {
+ s.setSlaveOk();
+ assert.soon( function() {
+ ret = s.getDB( "admin" ).runCommand( { "resync" : 1 } );
+ // printjson( ret );
+ return 1 == ret.ok;
+ } );
+}
+
+big = new Array( 2000 ).toString();
+
+doTest = function() {
+ ports = allocatePorts( 5 );
+ aPort = ports[ 0 ];
+ lPort = ports[ 1 ];
+ lpPort = ports[ 2 ];
+ rPort = ports[ 3 ];
+ rpPort = ports[ 4 ];
+
+ // start normally
+ connect();
+ a = new MongodRunner( aPort, "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( lPort, "/data/db/" + baseName + "-left", "127.0.0.1:" + rpPort, "127.0.0.1:" + aPort );
+ r = new MongodRunner( rPort, "/data/db/" + baseName + "-right", "127.0.0.1:" + lpPort, "127.0.0.1:" + aPort );
+ pair = new ReplPair( l, r, a );
+ pair.start();
+ pair.waitForSteadyState();
+
+ disconnect();
+ pair.waitForSteadyState( [ 1, 1 ], null, true );
+
+ print( "test one" );
+
+ // fill new slave oplog
+ for( i = 0; i < 1000; ++i ) {
+ pair.left().getDB( baseName ).getCollection( baseName ).save( {b:big} );
+ }
+ pair.left().getDB( baseName ).getCollection( baseName ).findOne();
+
+ // write single to new master
+ pair.right().getDB( baseName ).getCollection( baseName ).save( {} );
+
+ connect();
+ pair.waitForSteadyState( [ 1, 0 ], pair.right().host, true );
+
+ resetSlave( pair.left() );
+
+ checkCount( pair.left(), 1 );
+ checkCount( pair.right(), 1 );
+
+ pair.right().getDB( baseName ).getCollection( baseName ).remove( {} );
+ checkCount( pair.left(), 0 );
+
+ disconnect();
+ pair.waitForSteadyState( [ 1, 1 ], null, true );
+
+ print( "test two" );
+
+ // fill new master oplog
+ for( i = 0; i < 1000; ++i ) {
+ pair.right().getDB( baseName ).getCollection( baseName ).save( {b:big} );
+ }
+
+ pair.left().getDB( baseName ).getCollection( baseName ).save( {_id:"abcde"} );
+
+ connect();
+ pair.waitForSteadyState( [ 1, 0 ], pair.right().host, true );
+
+ sleep( 15000 );
+
+ resetSlave( pair.left() );
+
+ checkCount( pair.left(), 1000 );
+ checkCount( pair.right(), 1000 );
+ assert.eq( 0, pair.left().getDB( baseName ).getCollection( baseName ).find( {_id:"abcde"} ).count() );
+
+ ports.forEach( function( x ) { stopMongoProgram( x ); } );
+
+}
+
+doTest(); \ No newline at end of file
diff --git a/jstests/repl/repl1.js b/jstests/repl/repl1.js
new file mode 100644
index 0000000..60f3942
--- /dev/null
+++ b/jstests/repl/repl1.js
@@ -0,0 +1,55 @@
+// Test basic replication functionality
+
+var baseName = "jstests_repl1test";
+
+soonCount = function( count ) {
+ assert.soon( function() {
+// print( "check count" );
+// print( "count: " + s.getDB( baseName ).z.find().count() );
+ return s.getDB( baseName ).a.find().count() == count;
+ } );
+}
+
+doTest = function( signal ) {
+
+ rt = new ReplTest( "repl1tests" );
+
+ m = rt.start( true );
+ s = rt.start( false );
+
+ am = m.getDB( baseName ).a
+
+ for( i = 0; i < 1000; ++i )
+ am.save( { _id: new ObjectId(), i: i } );
+
+ soonCount( 1000 );
+ as = s.getDB( baseName ).a
+ assert.eq( 1, as.find( { i: 0 } ).count() );
+ assert.eq( 1, as.find( { i: 999 } ).count() );
+
+ rt.stop( false, signal );
+
+ for( i = 1000; i < 1010; ++i )
+ am.save( { _id: new ObjectId(), i: i } );
+
+ s = rt.start( false, null, true );
+ soonCount( 1010 );
+ as = s.getDB( baseName ).a
+ assert.eq( 1, as.find( { i: 1009 } ).count() );
+
+ rt.stop( true, signal );
+
+ m = rt.start( true, null, true );
+ am = m.getDB( baseName ).a
+
+ for( i = 1010; i < 1020; ++i )
+ am.save( { _id: new ObjectId(), i: i } );
+
+ assert.soon( function() { return as.find().count() == 1020; } );
+ assert.eq( 1, as.find( { i: 1019 } ).count() );
+
+ rt.stop();
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/repl2.js b/jstests/repl/repl2.js
new file mode 100644
index 0000000..c9fe6b9
--- /dev/null
+++ b/jstests/repl/repl2.js
@@ -0,0 +1,45 @@
+// Test resync command
+
+soonCount = function( count ) {
+ assert.soon( function() {
+// print( "check count" );
+// print( "count: " + s.getDB( baseName ).z.find().count() );
+ return s.getDB("foo").a.find().count() == count;
+ } );
+}
+
+doTest = function( signal ) {
+
+ var rt = new ReplTest( "repl2tests" );
+
+ // implicit small oplog makes slave get out of sync
+ m = rt.start( true );
+ s = rt.start( false );
+
+ am = m.getDB("foo").a
+
+ am.save( { _id: new ObjectId() } );
+ soonCount( 1 );
+ assert.eq( 0, s.getDB( "admin" ).runCommand( { "resync" : 1 } ).ok );
+ rt.stop( false , signal );
+
+ big = new Array( 2000 ).toString();
+ for( i = 0; i < 1000; ++i )
+ am.save( { _id: new ObjectId(), i: i, b: big } );
+
+ s = rt.start( false , null , true );
+ assert.soon( function() { return 1 == s.getDB( "admin" ).runCommand( { "resync" : 1 } ).ok; } );
+
+ soonCount( 1001 );
+ as = s.getDB("foo").a
+ assert.eq( 1, as.find( { i: 0 } ).count() );
+ assert.eq( 1, as.find( { i: 999 } ).count() );
+
+ assert.eq( 0, s.getDB( "admin" ).runCommand( { "resync" : 1 } ).ok );
+
+ rt.stop();
+
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/repl3.js b/jstests/repl/repl3.js
new file mode 100644
index 0000000..d3c3848
--- /dev/null
+++ b/jstests/repl/repl3.js
@@ -0,0 +1,47 @@
+// Test autoresync
+
+var baseName = "jstests_repl3test";
+
+soonCount = function( count ) {
+ assert.soon( function() {
+// print( "check count" );
+// print( "count: " + s.getDB( baseName ).z.find().count() + ", expected: " + count );
+ return s.getDB( baseName ).a.find().count() == count;
+ } );
+}
+
+doTest = function( signal ) {
+
+ rt = new ReplTest( "repl3tests" );
+
+ m = rt.start( true );
+ s = rt.start( false );
+
+ am = m.getDB( baseName ).a
+
+ am.save( { _id: new ObjectId() } );
+ soonCount( 1 );
+ rt.stop( false, signal );
+
+ big = new Array( 2000 ).toString();
+ for( i = 0; i < 1000; ++i )
+ am.save( { _id: new ObjectId(), i: i, b: big } );
+
+ s = rt.start( false, { autoresync: null }, true );
+
+ // after SyncException, mongod waits 10 secs.
+ sleep( 15000 );
+
+ // Need the 2 additional seconds timeout, since commands don't work on an 'allDead' node.
+ soonCount( 1001 );
+ as = s.getDB( baseName ).a
+ assert.eq( 1, as.find( { i: 0 } ).count() );
+ assert.eq( 1, as.find( { i: 999 } ).count() );
+
+ assert.commandFailed( s.getDB( "admin" ).runCommand( { "resync" : 1 } ) );
+
+ rt.stop();
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/repl4.js b/jstests/repl/repl4.js
new file mode 100644
index 0000000..de7ca43
--- /dev/null
+++ b/jstests/repl/repl4.js
@@ -0,0 +1,30 @@
+// Test replication 'only' mode
+
+soonCount = function( db, coll, count ) {
+ assert.soon( function() {
+ return s.getDB( db )[ coll ].find().count() == count;
+ } );
+}
+
+doTest = function() {
+
+ rt = new ReplTest( "repl4tests" );
+
+ m = rt.start( true );
+ s = rt.start( false, { only: "c" } );
+
+ cm = m.getDB( "c" ).c
+ bm = m.getDB( "b" ).b
+
+ cm.save( { x:1 } );
+ bm.save( { x:2 } );
+
+ soonCount( "c", "c", 1 );
+ assert.eq( 1, s.getDB( "c" ).c.findOne().x );
+ sleep( 10000 );
+ printjson( s.getDBNames() );
+ assert.eq( -1, s.getDBNames().indexOf( "b" ) );
+ assert.eq( 0, s.getDB( "b" ).b.find().count() );
+}
+
+doTest();
diff --git a/jstests/repl/repl5.js b/jstests/repl/repl5.js
new file mode 100644
index 0000000..b9bcef9
--- /dev/null
+++ b/jstests/repl/repl5.js
@@ -0,0 +1,32 @@
+// Test auto reclone after failed initial clone
+
+soonCountAtLeast = function( db, coll, count ) {
+ assert.soon( function() {
+// print( "count: " + s.getDB( db )[ coll ].find().count() );
+ return s.getDB( db )[ coll ].find().count() >= count;
+ } );
+}
+
+doTest = function( signal ) {
+
+ rt = new ReplTest( "repl5tests" );
+
+ m = rt.start( true );
+
+ ma = m.getDB( "a" ).a;
+ for( i = 0; i < 10000; ++i )
+ ma.save( { i:i } );
+
+ s = rt.start( false );
+ soonCountAtLeast( "a", "a", 1 );
+ rt.stop( false, signal );
+
+ s = rt.start( false, null, true );
+ sleep( 1000 );
+ soonCountAtLeast( "a", "a", 10000 );
+
+ rt.stop();
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/repl6.js b/jstests/repl/repl6.js
new file mode 100644
index 0000000..f4fdc9b
--- /dev/null
+++ b/jstests/repl/repl6.js
@@ -0,0 +1,73 @@
+// Test one master replicating to two slaves
+
+var baseName = "jstests_repl6test";
+
+soonCount = function( m, count ) {
+ assert.soon( function() {
+ return m.getDB( baseName ).a.find().count() == count;
+ }, "expected count: " + count + " from : " + m );
+}
+
+doTest = function( signal ) {
+
+ ports = allocatePorts( 3 );
+
+ ms1 = new ReplTest( "repl6tests-1", [ ports[ 0 ], ports[ 1 ] ] );
+ ms2 = new ReplTest( "repl6tests-2", [ ports[ 0 ], ports[ 2 ] ] );
+
+ m = ms1.start( true );
+ s1 = ms1.start( false );
+ s2 = ms2.start( false );
+
+ am = m.getDB( baseName ).a
+
+ for( i = 0; i < 1000; ++i )
+ am.save( { _id: new ObjectId(), i: i } );
+
+ soonCount( s1, 1000 );
+ soonCount( s2, 1000 );
+
+ check = function( as ) {
+ assert.eq( 1, as.find( { i: 0 } ).count() );
+ assert.eq( 1, as.find( { i: 999 } ).count() );
+ }
+
+ as = s1.getDB( baseName ).a
+ check( as );
+ as = s2.getDB( baseName ).a
+ check( as );
+
+ ms1.stop( false, signal );
+ ms2.stop( false, signal );
+
+ for( i = 1000; i < 1010; ++i )
+ am.save( { _id: new ObjectId(), i: i } );
+
+ s1 = ms1.start( false, null, true );
+ soonCount( s1, 1010 );
+ as = s1.getDB( baseName ).a
+ assert.eq( 1, as.find( { i: 1009 } ).count() );
+
+ ms1.stop( true, signal );
+
+ m = ms1.start( true, null, true );
+ am = m.getDB( baseName ).a
+
+ for( i = 1010; i < 1020; ++i )
+ am.save( { _id: new ObjectId(), i: i } );
+
+ soonCount( s1, 1020 );
+ assert.eq( 1, as.find( { i: 1019 } ).count() );
+
+ s2 = ms2.start( false, null, true );
+ soonCount( s2, 1020 );
+ as = s2.getDB( baseName ).a
+ assert.eq( 1, as.find( { i: 1009 } ).count() );
+ assert.eq( 1, as.find( { i: 1019 } ).count() );
+
+ ms1.stop();
+ ms2.stop( false );
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/repl7.js b/jstests/repl/repl7.js
new file mode 100644
index 0000000..e3fdee9
--- /dev/null
+++ b/jstests/repl/repl7.js
@@ -0,0 +1,45 @@
+// Test persistence of list of dbs to add.
+
+doTest = function( signal ) {
+
+ rt = new ReplTest( "repl7tests" );
+
+ m = rt.start( true );
+
+ for( n = "a"; n != "aaaaa"; n += "a" ) {
+ m.getDB( n ).a.save( {x:1} );
+ }
+
+ s = rt.start( false );
+
+ assert.soon( function() {
+ return -1 != s.getDBNames().indexOf( "aa" );
+ }, "aa timeout", 60000, 1000 );
+
+ rt.stop( false, signal );
+
+ s = rt.start( false, null, signal );
+
+ assert.soon( function() {
+ for( n = "a"; n != "aaaaa"; n += "a" ) {
+ if ( -1 == s.getDBNames().indexOf( n ) )
+ return false;
+ }
+ return true;
+ }, "a-aaaa timeout", 60000, 1000 );
+
+ assert.soon( function() {
+ for( n = "a"; n != "aaaaa"; n += "a" ) {
+ if ( 1 != m.getDB( n ).a.find().count() ) {
+ return false;
+ }
+ }
+ return true; }, "a-aaaa count timeout" );
+
+ sleep( 300 );
+
+ rt.stop();
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/repl8.js b/jstests/repl/repl8.js
new file mode 100644
index 0000000..64e65cc
--- /dev/null
+++ b/jstests/repl/repl8.js
@@ -0,0 +1,30 @@
+// Test cloning of capped collections
+
+baseName = "jstests_repl_repl8";
+
+rt = new ReplTest( "repl8tests" );
+
+m = rt.start( true );
+
+m.getDB( baseName ).createCollection( "first", {capped:true,size:1000} );
+assert( m.getDB( baseName ).getCollection( "first" ).isCapped() );
+
+s = rt.start( false );
+
+assert.soon( function() { return s.getDB( baseName ).getCollection( "first" ).isCapped(); } );
+
+m.getDB( baseName ).createCollection( "second", {capped:true,size:1000} );
+assert.soon( function() { return s.getDB( baseName ).getCollection( "second" ).isCapped(); } );
+
+m.getDB( baseName ).getCollection( "third" ).save( { a: 1 } );
+assert.soon( function() { return s.getDB( baseName ).getCollection( "third" ).exists(); } );
+assert.commandWorked( m.getDB( "admin" ).runCommand( {renameCollection:"jstests_repl_repl8.third", to:"jstests_repl_repl8.third_rename"} ) );
+assert( m.getDB( baseName ).getCollection( "third_rename" ).exists() );
+assert( !m.getDB( baseName ).getCollection( "third" ).exists() );
+assert.soon( function() { return s.getDB( baseName ).getCollection( "third_rename" ).exists(); } );
+assert.soon( function() { return !s.getDB( baseName ).getCollection( "third" ).exists(); } );
+
+m.getDB( baseName ).getCollection( "fourth" ).save( {a:1} );
+assert.commandWorked( m.getDB( baseName ).getCollection( "fourth" ).convertToCapped( 1000 ) );
+assert( m.getDB( baseName ).getCollection( "fourth" ).isCapped() );
+assert.soon( function() { return s.getDB( baseName ).getCollection( "fourth" ).isCapped(); } );
diff --git a/jstests/repl/repl9.js b/jstests/repl/repl9.js
new file mode 100644
index 0000000..be06e08
--- /dev/null
+++ b/jstests/repl/repl9.js
@@ -0,0 +1,48 @@
+// Test replication of collection renaming
+
+baseName = "jstests_repl_repl9";
+
+rt = new ReplTest( "repl9tests" );
+
+m = rt.start( true );
+s = rt.start( false );
+
+admin = m.getDB( "admin" );
+
+debug = function( foo ) {} // print( foo ); }
+
+// rename within db
+
+m.getDB( baseName ).one.save( { a: 1 } );
+assert.soon( function() { v = s.getDB( baseName ).one.findOne(); return v && 1 == v.a } );
+
+assert.commandWorked( admin.runCommand( {renameCollection:"jstests_repl_repl9.one", to:"jstests_repl_repl9.two"} ) );
+assert.soon( function() {
+ if ( -1 == s.getDB( baseName ).getCollectionNames().indexOf( "two" ) ) {
+ debug( "no two coll" );
+ debug( tojson( s.getDB( baseName ).getCollectionNames() ) );
+ return false;
+ }
+ if ( !s.getDB( baseName ).two.findOne() ) {
+ debug( "no two object" );
+ return false;
+ }
+ return 1 == s.getDB( baseName ).two.findOne().a; });
+assert.eq( -1, s.getDB( baseName ).getCollectionNames().indexOf( "one" ) );
+
+// rename to new db
+
+first = baseName + "_first";
+second = baseName + "_second";
+
+m.getDB( first ).one.save( { a: 1 } );
+assert.soon( function() { return s.getDB( first ).one.findOne() && 1 == s.getDB( first ).one.findOne().a; } );
+
+assert.commandWorked( admin.runCommand( {renameCollection:"jstests_repl_repl9_first.one", to:"jstests_repl_repl9_second.two"} ) );
+assert.soon( function() {
+ return -1 != s.getDBNames().indexOf( second ) &&
+ -1 != s.getDB( second ).getCollectionNames().indexOf( "two" ) &&
+ s.getDB( second ).two.findOne() &&
+ 1 == s.getDB( second ).two.findOne().a; } );
+assert.eq( -1, s.getDB( first ).getCollectionNames().indexOf( "one" ) );
+
diff --git a/jstests/repl/replacePeer1.js b/jstests/repl/replacePeer1.js
new file mode 100644
index 0000000..45ee544
--- /dev/null
+++ b/jstests/repl/replacePeer1.js
@@ -0,0 +1,71 @@
+// test replace peer on master
+
+var baseName = "jstests_replacepeer1test";
+
+ismaster = function( n ) {
+ im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+// print( "ismaster: " + tojson( im ) );
+ assert( im );
+ return im.ismaster;
+}
+
+var writeOneIdx = 0;
+
+writeOne = function( n ) {
+ n.getDB( baseName ).z.save( { _id: new ObjectId(), i: ++writeOneIdx } );
+}
+
+getCount = function( n ) {
+ return n.getDB( baseName ).z.find( { i: writeOneIdx } ).toArray().length;
+}
+
+checkWrite = function( m, s ) {
+ writeOne( m );
+ assert.eq( 1, getCount( m ) );
+ s.setSlaveOk();
+ assert.soon( function() {
+ return 1 == getCount( s );
+ } );
+}
+
+doTest = function( signal ) {
+
+ ports = allocatePorts( 4 );
+
+ a = new MongodRunner( ports[ 0 ], "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( ports[ 1 ], "/data/db/" + baseName + "-left", "127.0.0.1:" + ports[ 3 ], "127.0.0.1:" + ports[ 0 ] );
+ r = new MongodRunner( ports[ 3 ], "/data/db/" + baseName + "-right", "127.0.0.1:" + ports[ 1 ], "127.0.0.1:" + ports[ 0 ] );
+
+ rp = new ReplPair( l, r, a );
+ rp.start();
+ rp.waitForSteadyState( [ 1, 0 ], rp.right().host );
+
+ checkWrite( rp.master(), rp.slave() );
+
+ rp.killNode( rp.slave(), signal );
+
+ writeOne( rp.master() );
+
+ assert.commandWorked( rp.master().getDB( "admin" ).runCommand( {replacepeer:1} ) );
+
+ rp.killNode( rp.master(), signal );
+ rp.killNode( rp.arbiter(), signal );
+
+ o = new MongodRunner( ports[ 2 ], "/data/db/" + baseName + "-left", "127.0.0.1:" + ports[ 3 ], "127.0.0.1:" + ports[ 0 ] );
+ r = new MongodRunner( ports[ 3 ], "/data/db/" + baseName + "-right", "127.0.0.1:" + ports[ 2 ], "127.0.0.1:" + ports[ 0 ] );
+
+ rp = new ReplPair( o, r, a );
+ resetDbpath( "/data/db/" + baseName + "-left" );
+ rp.start( true );
+ rp.waitForSteadyState( [ 1, 0 ], rp.right().host );
+
+ checkWrite( rp.master(), rp.slave() );
+ rp.slave().setSlaveOk();
+ assert.eq( 3, rp.slave().getDB( baseName ).z.find().toArray().length );
+
+ ports.forEach( function( x ) { stopMongod( x ); } );
+
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL
diff --git a/jstests/repl/replacePeer2.js b/jstests/repl/replacePeer2.js
new file mode 100644
index 0000000..09c8177
--- /dev/null
+++ b/jstests/repl/replacePeer2.js
@@ -0,0 +1,72 @@
+// test replace peer on slave
+
+var baseName = "jstests_replacepeer2test";
+
+ismaster = function( n ) {
+ im = n.getDB( "admin" ).runCommand( { "ismaster" : 1 } );
+// print( "ismaster: " + tojson( im ) );
+ assert( im );
+ return im.ismaster;
+}
+
+var writeOneIdx = 0;
+
+writeOne = function( n ) {
+ n.getDB( baseName ).z.save( { _id: new ObjectId(), i: ++writeOneIdx } );
+}
+
+getCount = function( n ) {
+ return n.getDB( baseName ).z.find( { i: writeOneIdx } ).toArray().length;
+}
+
+checkWrite = function( m, s ) {
+ writeOne( m );
+ assert.eq( 1, getCount( m ) );
+ s.setSlaveOk();
+ assert.soon( function() {
+ return 1 == getCount( s );
+ } );
+}
+
+doTest = function( signal ) {
+
+ ports = allocatePorts( 4 );
+
+ a = new MongodRunner( ports[ 0 ], "/data/db/" + baseName + "-arbiter" );
+ l = new MongodRunner( ports[ 1 ], "/data/db/" + baseName + "-left", "127.0.0.1:" + ports[ 3 ], "127.0.0.1:" + ports[ 0 ] );
+ r = new MongodRunner( ports[ 3 ], "/data/db/" + baseName + "-right", "127.0.0.1:" + ports[ 1 ], "127.0.0.1:" + ports[ 0 ] );
+
+ rp = new ReplPair( l, r, a );
+ rp.start();
+ rp.waitForSteadyState( [ 1, 0 ], rp.right().host );
+
+ checkWrite( rp.master(), rp.slave() );
+
+ // allow slave to finish initial sync
+ assert.soon( function() { return 1 == rp.slave().getDB( "admin" ).runCommand( {replacepeer:1} ).ok; } );
+
+ // Should not be saved to slave.
+ writeOne( rp.master() );
+ // Make sure there would be enough time to save to l if we hadn't called replacepeer.
+ sleep( 10000 );
+
+ ports.forEach( function( x ) { stopMongod( x, signal ); } );
+
+ l = new MongodRunner( ports[ 1 ], "/data/db/" + baseName + "-left", "127.0.0.1:" + ports[ 2 ], "127.0.0.1:" + ports[ 0 ] );
+ o = new MongodRunner( ports[ 2 ], "/data/db/" + baseName + "-right", "127.0.0.1:" + ports[ 1 ], "127.0.0.1:" + ports[ 0 ] );
+
+ rp = new ReplPair( l, o, a );
+ resetDbpath( "/data/db/" + baseName + "-right" );
+ rp.start( true );
+ rp.waitForSteadyState( [ 1, 0 ], rp.left().host );
+
+ checkWrite( rp.master(), rp.slave() );
+ rp.slave().setSlaveOk();
+ assert.eq( 2, rp.slave().getDB( baseName ).z.find().toArray().length );
+
+ ports.forEach( function( x ) { stopMongod( x ); } );
+
+}
+
+doTest( 15 ); // SIGTERM
+doTest( 9 ); // SIGKILL