context: /*-
   * See the file LICENSE for redistribution information.
   *
   * Copyright (c) 2009, 2014 Oracle and/or its affiliates.  All rights reserved.
   *
   */
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Net;
  using System.Net.NetworkInformation;
  using System.Text;
  using System.Threading;
  using System.Xml;
  using NUnit.Framework;
  using BerkeleyDB;
  
  namespace CsharpAPITest
  {
  	[TestFixture]
  	public class ReplicationTest : CSharpTestFixture
  	{
  		private EventWaitHandle clientStartSignal;
  		private EventWaitHandle masterCloseSignal;
  
  		private EventWaitHandle client1StartSignal;
  		private EventWaitHandle client2StartSignal;
  		private EventWaitHandle client3StartSignal;
  		private EventWaitHandle clientsElectionSignal;
  		private EventWaitHandle masterLeaveSignal;
  
  		private uint startUpDone;
  		private bool electionDone;
  
  		List<uint> ports = new List<uint>();
  
  		[TestFixtureSetUp]
  		public void SetUp()
  		{
  			testFixtureName = "ReplicationTest";
  			base.SetUpTestfixture();
  		}
  
  		[Test]
  		public void TestReplicationView()
  		{
  			testName = "TestReplicationView";
  			SetUpTest(true);
  
  			string masterHome = testHome + "\\Master";
  			Configuration.ClearDir(masterHome);
  
  			string clientHome1 = testHome + "\\Client1";
  			Configuration.ClearDir(clientHome1);
  
  			string clientHome2 = testHome + "\\Client2";
  			Configuration.ClearDir(clientHome2);
  
  			ports.Clear();
  			AvailablePorts portGen = new AvailablePorts();
  			uint mPort = portGen.Current;
  			portGen.MoveNext();
  			uint cPort1 = portGen.Current;
  			portGen.MoveNext();
  			uint cPort2 = portGen.Current;
  
  			/* Open environment with replication configuration. */
  			DatabaseEnvironmentConfig cfg =
  			    new DatabaseEnvironmentConfig();
  			cfg.Create = true;
  			cfg.RunRecovery = true;
  			cfg.UseLocking = true;
  			cfg.UseLogging = true;
  			cfg.UseMPool = true;
  			cfg.UseReplication = true;
  			cfg.FreeThreaded = true;
  			cfg.UseTxns = true;
  			cfg.EventNotify = new EventNotifyDelegate(stuffHappened);
  
  			cfg.RepSystemCfg = new ReplicationConfig();
  			cfg.RepSystemCfg.RepmgrSitesConfig.Add(new DbSiteConfig());
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Host = "127.0.0.1";
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Port = mPort;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].LocalSite = true;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].GroupCreator = true;
  			cfg.RepSystemCfg.Priority = 100;
  
  			/* Start up the master site. */
  			DatabaseEnvironment mEnv = DatabaseEnvironment.Open(
  			    masterHome, cfg);
  			mEnv.DeadlockResolution = DeadlockPolicy.DEFAULT;
  			mEnv.RepMgrStartMaster(2);
  
  			/* Open the environment of the client 1 site. */
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Port = cPort1;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].GroupCreator = false;
  			cfg.RepSystemCfg.Priority = 10;
  			cfg.RepSystemCfg.RepmgrSitesConfig.Add(new DbSiteConfig());
  			cfg.RepSystemCfg.RepmgrSitesConfig[1].Host = "127.0.0.1";
  			cfg.RepSystemCfg.RepmgrSitesConfig[1].Port = mPort;
  			cfg.RepSystemCfg.RepmgrSitesConfig[1].Helper = true;
  			/* Set the site as a partial view. */
  			cfg.RepSystemCfg.ReplicationView = repView;
  			DatabaseEnvironment cEnv1 = DatabaseEnvironment.Open(
  			    clientHome1, cfg);
  
  			/* Open the environment of the client 2 site. */
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Port = cPort2;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].GroupCreator = false;
  			cfg.RepSystemCfg.Priority = 10;
  			/* Set the site as a full view. */
  			cfg.RepSystemCfg.ReplicationView = null;
  			DatabaseEnvironment cEnv2 = DatabaseEnvironment.Open(
  			    clientHome2, cfg);
  
  			/*
  			 * Create two database files db1.db and db2.db
  			 * on the master.
  			 */
  			BTreeDatabaseConfig btreeDBConfig =
  			    new BTreeDatabaseConfig();
  			btreeDBConfig.Env = mEnv;
  			btreeDBConfig.Creation = CreatePolicy.ALWAYS;
  			btreeDBConfig.AutoCommit = true;
  			BTreeDatabase db1 =
  			    BTreeDatabase.Open("db1.db", btreeDBConfig);
  			BTreeDatabase db2 =
  			    BTreeDatabase.Open("db2.db", btreeDBConfig);
  			db1.Close();
  			db2.Close();
  
  			/* Start up the client sites. */
  			cEnv1.RepMgrStartClient(2, false);
  			cEnv2.RepMgrStartClient(2, false);
  
  			/* Wait for clients to start up */
  			int i = 0;
  			while (!cEnv1.ReplicationSystemStats().ClientStartupComplete)
  			{
  				if (i < 20)
  				{
  					Thread.Sleep(1000);
  					i++;
  				}
  				else
  					throw new TestException();
  			}
  			i = 0;
  			while (!cEnv2.ReplicationSystemStats().ClientStartupComplete)
  			{
  				if (i < 20)
  				{
  					Thread.Sleep(1000);
  					i++;
  				}
  				else
  					throw new TestException();
  			}
  
  			/*
  			 * Verify that the file db2.db is replicated to the
  			 * client 2 (full view), but not to the client 1
  			 * (partial view), and the file db1.db is
  			 * replicated to both sites.
  			 */
  			btreeDBConfig.Env = cEnv1;
  			btreeDBConfig.Creation = CreatePolicy.NEVER;
  			db1 = BTreeDatabase.Open("db1.db", btreeDBConfig);
  			try
  			{
  				db2 = BTreeDatabase.Open("db2.db", btreeDBConfig);
  				throw new TestException();
  			}
  			catch (DatabaseException e){
  				Assert.AreEqual(0, String.Compare(
  				    "No such file or directory", e.Message));
  			}
  			db1.Close();
  			btreeDBConfig.Env = cEnv2;
  			db1 = BTreeDatabase.Open("db1.db", btreeDBConfig);
  			db2 = BTreeDatabase.Open("db2.db", btreeDBConfig);
  			db1.Close();
  			db2.Close();
  
  			/* Get the replication manager statistic. */
  			RepMgrStats repMgrStats = mEnv.RepMgrSystemStats();
  			Assert.AreEqual(1, repMgrStats.ParticipantSites);
  			Assert.AreEqual(3, repMgrStats.TotalSites);
  			Assert.AreEqual(2, repMgrStats.ViewSites);
  
  			/*
  			 * Verify the master is not a view locally
  			 * or from remote site.
  			 */
  			ReplicationStats repstats =
  			    mEnv.ReplicationSystemStats();
  			Assert.AreEqual(false, repstats.View);
  			RepMgrSite[] rsite = cEnv1.RepMgrRemoteSites;
  			Assert.AreEqual(2, rsite.Length);
  			for (i = 0; i < rsite.Length; i++) {
  				if (rsite[i].Address.Port == mPort)
  					break;
  			}
  			Assert.Greater(rsite.Length, i);
  			Assert.AreEqual(false, rsite[i].isView);
  
  			/*
  			 * Verify the clients are views locally
  			 * and from remote site.
  			 */
  			rsite = mEnv.RepMgrRemoteSites;
  			Assert.AreEqual(2, rsite.Length);
  			Assert.AreEqual(true, rsite[0].isView);
  			Assert.AreEqual(true, rsite[1].isView);
  			repstats = cEnv1.ReplicationSystemStats();
  			Assert.AreEqual(true, repstats.View);
  			repstats = cEnv2.ReplicationSystemStats();
  			Assert.AreEqual(true, repstats.View);
  
  			cEnv2.Close();
  			cEnv1.Close();
  			mEnv.Close();
  		}
  
  		int repView(string name, ref int result, uint flags)
  		{
  			if (name == "db1.db")
  				result = 1;
  			else
  				result = 0;
  			return (0);
  		}
  
  		[Test]
  		public void TestRepMgrSite() 
  		{
  			testName = "TestRepMgrSite";
  			SetUpTest(true);
  
  			string masterHome = testHome + "\\Master";
  			Configuration.ClearDir(masterHome);
  
  			string clientHome = testHome + "\\Client";
  			Configuration.ClearDir(clientHome);
  
  			ports.Clear();
  			AvailablePorts portGen = new AvailablePorts();
  			uint mPort = portGen.Current;
  			portGen.MoveNext();
  			uint cPort = portGen.Current;
  
  			// Open environment with replication configuration.
  			DatabaseEnvironmentConfig cfg =
  			    new DatabaseEnvironmentConfig();
  			cfg.Create = true;
  			cfg.RunRecovery = true;
  			cfg.UseLocking = true;
  			cfg.UseLogging = true;
  			cfg.UseMPool = true;
  			cfg.UseReplication = true;
  			cfg.FreeThreaded = true;
  			cfg.UseTxns = true;
  			cfg.EventNotify = new EventNotifyDelegate(stuffHappened);
  
  			cfg.RepSystemCfg = new ReplicationConfig();
  			cfg.RepSystemCfg.RepmgrSitesConfig.Add(new DbSiteConfig());
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Host = "127.0.0.1";
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Port = mPort;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].LocalSite = true;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].GroupCreator = true;
  			cfg.RepSystemCfg.Priority = 100;
  
  			DatabaseEnvironment mEnv = DatabaseEnvironment.Open(
  			    masterHome, cfg);
  			mEnv.DeadlockResolution = DeadlockPolicy.DEFAULT;
  			mEnv.RepMgrStartMaster(2);
  
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Port = cPort;
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].GroupCreator = false;
  			cfg.RepSystemCfg.Priority = 10;
  			cfg.RepSystemCfg.RepmgrSitesConfig.Add(new DbSiteConfig());
  			cfg.RepSystemCfg.RepmgrSitesConfig[1].Host = "127.0.0.1";
  			cfg.RepSystemCfg.RepmgrSitesConfig[1].Port = mPort;
  			cfg.RepSystemCfg.RepmgrSitesConfig[1].Helper = true;
  			DatabaseEnvironment cEnv = DatabaseEnvironment.Open(
  			    clientHome, cfg);
  			cEnv.RepMgrStartClient(2, false);
  
  			/* Wait for client to start up */
  			int i = 0;
  			while (!cEnv.ReplicationSystemStats().ClientStartupComplete) {
  				if (i < 20) {
  					Thread.Sleep(1000);
  					i++;
  				} else
  					throw new TestException();
  			}
  
  			/*
  			 * Verify the client info could be observed by master's
  			 * remote site.
  			 */ 			 
  			Assert.AreEqual(1, mEnv.RepMgrRemoteSites.Length);
  			RepMgrSite rsite = mEnv.RepMgrRemoteSites[0];
  			Assert.AreEqual("127.0.0.1", rsite.Address.Host);
  			Assert.AreEqual(cPort, rsite.Address.Port);
  			Assert.AreEqual(true, rsite.isConnected);
  			Assert.AreEqual(false, rsite.isPeer);
  
  			DbSite site = mEnv.RepMgrSite("127.0.0.1", mPort);
  			Assert.AreEqual("127.0.0.1", site.Address.Host);
  			Assert.AreEqual(mPort, site.Address.Port);
  			Assert.LessOrEqual(0, site.EId);
  			Assert.AreEqual(true, site.GroupCreator);
  			Assert.AreEqual(true, site.LocalSite);
  			Assert.AreEqual(false, site.Helper);
  			Assert.AreEqual(false, site.Legacy);
  			Assert.AreEqual(false, site.Peer);
  			site.Close();
  
  			site = mEnv.RepMgrSite("127.0.0.1", cPort);
  			Assert.AreEqual("127.0.0.1", site.Address.Host);
  			Assert.AreEqual(cPort, site.Address.Port);
  			Assert.AreEqual(rsite.EId, site.EId);
  			Assert.AreEqual(false, site.GroupCreator);
  			Assert.AreEqual(false, site.LocalSite);
  			Assert.AreEqual(false, site.Helper);
  			Assert.AreEqual(false, site.Legacy);
  			Assert.AreEqual(false, site.Peer);
  			site.Remove();
  
  			cEnv.Close();
  			mEnv.Close();
  
  			/*
  			 * Update the repmgr site, and verify it is updated in
  			 * unmanaged memory.
  			 */ 			 
  			rsite.Address = new ReplicationHostAddress(
  			    "192.168.1.1", 1000);
  			rsite.EId = 1024;
  			rsite.isConnected = false;
  			rsite.isPeer = true;
  			Assert.AreEqual("192.168.1.1", rsite.Address.Host);
  			Assert.AreEqual(1000, rsite.Address.Port);
  			Assert.AreEqual(1024, rsite.EId);
  			Assert.AreEqual(false, rsite.isConnected);
  			Assert.AreEqual(true, rsite.isPeer);
  		}
  
  		[Test]
  		public void TestRepMgr()
  		{
  			testName = "TestRepMgr";
  			SetUpTest(true);
  
  			// Initialize ports for master and client.
  			ports.Clear();
  			AvailablePorts portGen = new AvailablePorts();
  			ports.Insert(0, portGen.Current);
  			portGen.MoveNext();
  			ports.Insert(1, portGen.Current);
  
  			clientStartSignal = new AutoResetEvent(false);
  			masterCloseSignal = new AutoResetEvent(false);
  
  			Thread thread1 = new Thread(new ThreadStart(Master));
  			Thread thread2 = new Thread(new ThreadStart(Client));
  
  			// Start master thread before client thread.
  			thread1.Start();
  			Thread.Sleep(1000);
  			thread2.Start();
  			thread2.Join();
  			thread1.Join();
  
  			clientStartSignal.Close();
  			masterCloseSignal.Close();
  		}
  
  		public void Master()
  		{
  			string home = testHome + "/Master";
  			string dbName = "rep.db";
  			uint metabyte = 1048576;
  			Configuration.ClearDir(home);
  
  			/*
  			 * Configure and open environment with replication 
  			 * application.
  			 */
  			DatabaseEnvironmentConfig cfg =
  			    new DatabaseEnvironmentConfig();
  			cfg.UseReplication = true;
  			cfg.MPoolSystemCfg = new MPoolConfig();
  			cfg.MPoolSystemCfg.CacheSize =
  			    new CacheInfo(0, 20485760, 1);
  			cfg.UseLocking = true;
  			cfg.UseTxns = true;
  			cfg.UseMPool = true;
  			cfg.Create = true;
  			cfg.UseLogging = true;
  			cfg.RunRecovery = true;
  			cfg.TxnNoSync = true;
  			cfg.FreeThreaded = true;
  			cfg.RepSystemCfg = new ReplicationConfig();
  			cfg.RepSystemCfg.RepmgrSitesConfig.Add(new DbSiteConfig());
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Host = "127.0.0.1";
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].Port = ports[0];
  			cfg.RepSystemCfg.RepmgrSitesConfig[0].LocalSite = true;
  			cfg.RepSystemCfg.Priority = 100;
  			cfg.RepSystemCfg.BulkTransfer = true;
  			cfg.RepSystemCfg.AckTimeout = 2000;
  			cfg.RepSystemCfg.BulkTransfer = true;
  			cfg.RepSystemCfg.CheckpointDelay = 1500;
  			cfg.RepSystemCfg.Clockskew(102, 100);
  			cfg.RepSystemCfg.ConnectionRetry = 10;
  			cfg.RepSystemCfg.DelayClientSync = false;
  			cfg.RepSystemCfg.ElectionRetry = 5;
  			cfg.RepSystemCfg.ElectionTimeout = 3000;
  			cfg.RepSystemCfg.FullElectionTimeout = 5000;
  			cfg.RepSystemCfg.HeartbeatMonitor = 100;
  			cfg.RepSystemCfg.HeartbeatSend = 10;
  			cfg.RepSystemCfg.LeaseTimeout = 1300;
  			cfg.RepSystemCfg.AutoInit = true;
  			cfg.RepSystemCfg.NoBlocking = false;
  			cfg.RepSystemCfg.RepMgrAckPolicy = 
  			    AckPolicy.ALL_PEERS;
  			cfg.RepSystemCfg.RetransmissionRequest(10, 100);
  			cfg.RepSystemCfg.Strict2Site = true;
  			cfg.RepSystemCfg.UseMasterLeases = false;
  			cfg.EventNotify = new EventNotifyDelegate(stuffHappened);
  			DatabaseEnvironment env = DatabaseEnvironment.Open(
  			    home, cfg);
  
  			// Get initial replication stats.
  			ReplicationStats repStats = env.ReplicationSystemStats();
  			env.Msgfile = home + "/master.log";
  			env.PrintReplicationSystemStats();
  			Assert.AreEqual(100, repStats.EnvPriority);
  			Assert.AreEqual(1, 
  			    repStats.CurrentElectionGenerationNumber);
  			Assert.AreEqual(0, repStats.CurrentGenerationNumber);
  			Assert.AreEqual(0, repStats.AppliedTransactions);
  			Assert.AreEqual(0, repStats.ElectionDataGeneration);
  
  			// Get repmgr incoming queue max setting.
  			Assert.AreEqual(0, env.RepmgrIncomingQueueMaxGBytes);
  			Assert.AreEqual(100 * metabyte, env.RepmgrIncomingQueueMaxBytes);
  
  			// Start a master site with replication manager.
  			env.RepMgrStartMaster(3);
  
  			// Change repmgr incoming queue setting and verify it.
  			env.RepmgrSetIncomingQueueMax(123, 321);
  			Assert.AreEqual(123, env.RepmgrIncomingQueueMaxGBytes);
  			Assert.AreEqual(321, env.RepmgrIncomingQueueMaxBytes);
  
  			// Open a btree database and write some data.
  			Transaction txn = env.BeginTransaction();
  			BTreeDatabaseConfig dbConfig = 
  			    new BTreeDatabaseConfig();
  			dbConfig.Creation = CreatePolicy.IF_NEEDED;
  			dbConfig.Env = env;
  			dbConfig.PageSize = 512;
  			BTreeDatabase db = BTreeDatabase.Open(dbName, 
  			    dbConfig, txn);
  			txn.Commit();
  			txn = env.BeginTransaction();
  			for (int i = 0; i < 5; i++)
  				db.Put(new DatabaseEntry(BitConverter.GetBytes(i)),
  
gt: 				    new DatabaseEntry(BitConverter.GetBytes(i)), txn);