using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using MongoDB.Bson; using MongoDB.Driver; using NUnit.Framework; namespace MongoDB.DriverUnitTests.Jira { [TestFixture()] public class CSharp724Tests { /* * D:\MongoDB\2.4.0\mongodb-win32-x86_64-2008plus-2.4.0\bin>mongod.exe --dbpath D:\MongoDB\2.4.0\mongodb-win32-x86_64-2008plus-2.4.0\bin\data\db\ */ private const string PathToMongoServer = @"D:\MongoDB\2.4.0\mongodb-win32-x86_64-2008plus-2.4.0\bin\"; private const string ReplicaSetName = @"CSharp724Tests"; private const string ReplicaSetArgs = @"--replSet " + ReplicaSetName; private const string AdditionalOptions = @" -v "; private const string MongoServerExeName = @"mongod.exe "; private const string firstDataDir = @"data1\db\ "; private const string secondDataDir = @"data2\db\ "; private const string thirdDataDir = @"data3\db\ "; private const int firstInstancePort = 27017; private const int secondInstancePort = 27018; private const int thirdInstancePort = 27019; private static List _mongoAddresses = new List { new MongoServerAddress("localhost", firstInstancePort), new MongoServerAddress("localhost", secondInstancePort), new MongoServerAddress("localhost", thirdInstancePort) }; internal static Dictionary InstancesByPort = new Dictionary(); private static Process firstMongoProcess; private static Process secondMongoProcess; private static Process thirdMongoProcess; private MongoServer server; [SetUp] public void Setup() { //Cleanup data CleanupMongoDb(PathToMongoServer + firstDataDir); CleanupMongoDb(PathToMongoServer + secondDataDir); CleanupMongoDb(PathToMongoServer + thirdDataDir); var firstInstanceCmd = ReplicaSetArgs + " --dbpath " + PathToMongoServer + firstDataDir + "--port " + firstInstancePort + AdditionalOptions; var secondInstanceCmd = ReplicaSetArgs + " --dbpath " + PathToMongoServer + secondDataDir + "--port " + secondInstancePort + AdditionalOptions; var thirdInstanceCmd = ReplicaSetArgs + " --dbpath " + PathToMongoServer + thirdDataDir + "--port " + thirdInstancePort + AdditionalOptions; MongoProcessLaucher.LaunchMongoProcess(out thirdMongoProcess,PathToMongoServer+MongoServerExeName, thirdInstanceCmd, thirdInstancePort, InstancesByPort); MongoProcessLaucher.LaunchMongoProcess(out firstMongoProcess,PathToMongoServer+MongoServerExeName, firstInstanceCmd, firstInstancePort, InstancesByPort); MongoProcessLaucher.LaunchMongoProcess(out secondMongoProcess, PathToMongoServer+MongoServerExeName, secondInstanceCmd, secondInstancePort, InstancesByPort); foreach (var address in _mongoAddresses) { var clientSetup = new MongoClient(new MongoClientSettings() { ConnectionMode = ConnectionMode.Direct, Server = new MongoServerAddress(address.Host, address.Port), WriteConcern = WriteConcern.Acknowledged, }); var setupSrvReplicaSet = clientSetup.GetServer(); SetupReplicaSet(setupSrvReplicaSet, _mongoAddresses); Thread.Sleep(TimeSpan.FromSeconds(5)); } foreach (var address in _mongoAddresses) { var clientSetup = new MongoClient(new MongoClientSettings() { ConnectionMode = ConnectionMode.Direct, Server = new MongoServerAddress(address.Host, address.Port), WriteConcern = WriteConcern.Acknowledged, }); var setupSrvReplicaSet = clientSetup.GetServer(); SetupReplicaSet(setupSrvReplicaSet, _mongoAddresses); Thread.Sleep(TimeSpan.FromSeconds(5)); } //wait for the 3 instances to decide who is primary. 30 seconds should be enough Thread.Sleep(TimeSpan.FromSeconds(10)); var client = new MongoClient(new MongoClientSettings() { ConnectionMode = ConnectionMode.ReplicaSet, Servers = new List{ new MongoServerAddress("localhost", firstInstancePort), new MongoServerAddress("localhost", secondInstancePort),new MongoServerAddress("localhost",thirdInstancePort) }, ReplicaSetName = ReplicaSetName, WriteConcern = WriteConcern.Acknowledged, ReadPreference = ReadPreference.Primary, }); server = client.GetServer(); } private void CleanupMongoDb(string pathToCleanup) { var info = new DirectoryInfo(pathToCleanup); if (info.Exists) { info.Delete(true); info.Create(); } else { info.Create(); } } private bool SetupReplicaSet(MongoServer srvConfigure, IEnumerable addresses) { int membersCount = WaitForLocalMongoToBeReady(srvConfigure); var mongoServerAddresses = addresses as MongoServerAddress[] ?? addresses.ToArray(); if (membersCount == 0) { UInt16 serverId = 0; var replReconfig = new CommandDocument { { "replSetInitiate", new BsonDocument { {"_id", ReplicaSetName}, { "members", new BsonArray(new [] { new BsonDocument { {"_id", serverId++}, {"host", string.Format("{0}:{1}", mongoServerAddresses.First().Host, mongoServerAddresses.First().Port)}, {"arbiterOnly", false} }}) // Never give priority 0 as the instance could not vote or initiate a vote } } } }; try { var result = srvConfigure.GetDatabase("admin").RunCommand(replReconfig); } catch(MongoCommandException ex) { return false; } } if (membersCount != mongoServerAddresses.Count()) { UInt16 serverId = 0; var replReconfig = new CommandDocument { { "replSetReconfig", new BsonDocument { {"_id", ReplicaSetName}, {"version", 2 }, { "members", new BsonArray( from server in mongoServerAddresses select new BsonDocument { {"_id", serverId++}, { "host", string.Format("{0}:{1}", server.Host, server.Port) }, {"arbiterOnly", serverId == 3},}) } } } }; try { var replReconfigResult = srvConfigure.GetDatabase("admin").RunCommand(replReconfig); } catch (MongoCommandException ex) { return false; } } return true; } private int WaitForLocalMongoToBeReady(MongoServer srvConfigure) { int retries = 0; while (true) { CommandResult res; try { res = srvConfigure.GetDatabase("admin").RunCommand("replSetGetStatus"); } catch (Exception ex) { if (retries == 3) throw; if (ex.Message.Contains("EMPTYCONFIG")) { return 0; } if (ex.Message.Contains("Received replSetInitiate - should come online shortly")) { Thread.Sleep(TimeSpan.FromSeconds(5)); continue; } retries++; // Maybe we got to wait a little before asking something to Mongo Thread.Sleep(TimeSpan.FromSeconds(10)); continue; } var members = res.Response["members"].AsBsonArray; return members.Count; } } [TearDown] public void TearDown() { try { firstMongoProcess.Kill(); } catch { } try { secondMongoProcess.Kill(); } catch { } try { thirdMongoProcess.Kill(); } catch { } } [Test, Timeout(120000)] public void TestWhenPrimaryGoesDownDriverDetectsTheSwitch() { server.Connect(TimeSpan.FromSeconds(10)); Assert.IsNotNull(server.Primary); Assert.AreEqual(3, server.Instances.Length); Assert.AreEqual(1,server.Secondaries.Length); Assert.AreEqual(1, server.Arbiters.Length); //detect which instance is primary var pimaryPort = server.Primary.Address.Port; var primaryProcess = InstancesByPort[pimaryPort]; //Simulate Mongo Primary Crash primaryProcess.Kill(); Thread.Sleep(TimeSpan.FromSeconds(5)); bool isConnected = false; while (!isConnected) { try { server.Connect(TimeSpan.FromSeconds(10)); isConnected = true; } catch { //ignore Exceptions Thread.Sleep(1000); } } Assert.AreNotEqual(pimaryPort,server.Primary.Address.Port); } } public static class MongoProcessLaucher { public static void LaunchMongoProcess(out Process process, string executable ,string cmdLine, int port, Dictionary instances) { process = Process.Start(executable, cmdLine); instances.Add(port,process); } } }