

coordinator.includeFile('disorder/tests/FailNodeTest.js');

MultinodeFailover = function(coordinator, testResults) {
	Failover.call(this, coordinator, testResults);
	this.testDescription='Test the FAILOVER command.  This test will try FAILOVER'
		+' with multiple nodes failing';
	this.compareQueryList.push(['select i_id,comments from disorder.do_item_review order by i_id','i_id']);
	this.nodeCount=5;
}
MultinodeFailover.prototype = new Failover();
MultinodeFailover.prototype.constructor = MultinodeFailover;

/**
 * Returns the number of nodes used in the test.
 */
MultinodeFailover.prototype.getNodeCount = function() {
	return this.nodeCount;
}

MultinodeFailover.prototype.runTest = function() {
    this.coordinator.log("MultinodeFailover.prototype.runTest - begin");
	this.testResults.newGroup("Multinode Fail Over Test");
	this.setupReplication();
	this.addCompletePaths();
	/**
	 * Start the slons.
	 */
	this.slonArray = [];
	for ( var idx = 1; idx <= this.getNodeCount(); idx++) {
		this.slonArray[idx - 1] = this.coordinator.createSlonLauncher('db' + idx);
		this.slonArray[idx - 1].run();
	}
	this.addCompletePaths();
	/**
	 * Add some tables to replication.
	 * 
	 */
	this.addTables();

	/**
	 * Subscribe the first node.
	 */
	this.subscribeSet(1,1, 1, [ 2, 3 ]);
	this.subscribeSet(1,1, 3, [ 4, 5 ]);
	this.slonikSync(1,1);
	this.createSecondSet(2);
	this.subscribeSet(2,2,2,[3,4,5]);
	this.slonikSync(2,2);	
	var load = this.generateLoad();
	java.lang.Thread.sleep(10*1000);
	this.slonikSync(1,1);
	this.populateReviewTable(2);
	/**
	 * make sure the _review data makes it to
	 * all slaves, then let some SYNC events get
	 * genereated.  Next we FAILOVER.
	 */
	this.slonikSync(2,2);
	java.lang.Thread.sleep(10*1000);
	this.failover(1,3,2,3);		
	load.stop();
	this.coordinator.join(load);
	/**
	 * rebuild the nodes.
	 */
	this.dropTwoNodes(1,2,3);
	this.slonikSync(1,3);
	this.compareDb('db3','db4');
	this.compareDb('db3','db5');
	this.reAddNode(1,3,3);		

	/**
	 * perform some updates on node3 to the review table
	 */
	this.updateReviewTable(3,'From node 3');
	this.moveSet(1,3,1);
	
	this.reAddNode(2,1,1);
	this.addCompletePaths();
	this.subscribeSet(2,3,3,[2]);
	this.moveSet(2,3,2);
	this.slonikSync(2,2);

	/**
	 * generate some load (node1) and
	 * reviews on node2. Let multiple txn snapshots be
	 * generated.
	 */
	load=this.generateLoad();
	for(var idx=0; idx < 20; idx++)
	{
		this.updateReviewTable(2,'From node 2.' + idx);
		java.lang.Thread.sleep(1000);
	}
	/**
	 * failover.  Node 1=>3, node2=>4
	 */
	this.failover(1,3,2,4);
	load.stop(); 
	this.coordinator.join(load);
	this.dropTwoNodes(1,2,3);
	this.slonikSync(2,4);
	this.compareDb('db3','db4');
		//exit(-1);
	//auto wait for should not require the
		//sync but we have a race condition.
		//		this.slonikSync(1,3);
		//	this.slonikSync(1,4);
	this.reAddNode(1,3,3);		
	this.reAddNode(2,3,3);
	this.addCompletePaths();
	this.subscribeSet(1,3,3,[1,2]);
	this.subscribeSet(2,4,4,[2,1]);
	this.slonikSync(1,1);	
	this.moveSet(1,3,1);
	this.moveSet(2,4,2);


	this.slonikSync(1,1);
	this.slonikSync(2,2);
	for ( var idx = 1; idx <= this.getNodeCount(); idx++) {
		this.slonArray[idx - 1].stop();
		this.coordinator.join(this.slonArray[idx - 1]);
	}		
	this.compareDb('db1','db3');
	this.compareDb('db2','db3');
	this.compareDb('db3','db4');
	this.compareDb('db3','db5');
	
	for ( var idx = 1; idx <= this.getNodeCount(); idx++) {
		this.slonArray[idx - 1] = this.coordinator.createSlonLauncher('db' + idx);
		this.slonArray[idx - 1].run();
	}
	
    /**
	 * Now configure the cluster as 
	 * 1 <---- 2 ----> 3 ----> 4
	 */
    this.moveSet(1,1,2);
	this.dropNode(5,1);
	this.nodeCount=4;
	this.resubscribe(2,2,1);
	this.resubscribe(2,2,3);
	this.resubscribe(2,3,4);
	//make sure the resubscribe propogates
	//before we start failing.
	this.slonikSync(2,2);

	
	this.currentOrigin='db2';
	load=this.generateLoad();
	java.lang.Thread.sleep(1000);
	/**
	 * failover.  Node 2=>1, node3=>1
	 */
	this.failover(2,1,3,1);
	load.stop(); 
	this.coordinator.join(load);
	this.slonikSync(1,1);
	java.lang.Thread.sleep(1000);
	for ( var idx = 1; idx <= this.getNodeCount(); idx++) {
		this.slonArray[idx - 1].stop();
		this.coordinator.join(this.slonArray[idx - 1]);
	}	
	this.compareDb('db1','db4');
}

MultinodeFailover.prototype.failover=function(originA,backupA,originB,backupB) 
{
	var slonikPreamble = this.getSlonikPreamble();
	var slonikScript = 'echo \'MultinodeFailover.prototype.failover\';\n';
	slonikScript += 'FAILOVER( node=(id='  + originA  + ',backup node=' + backupA +')'
	+ ', node=(id=' + originB + ',backup node=' + backupB + '));\n';
	var slonik=this.coordinator.createSlonik('failover',slonikPreamble,slonikScript);
	slonik.run();
	this.coordinator.join(slonik);
	this.testResults.assertCheck('failover passes',slonik.getReturnCode(),0);	

}

	MultinodeFailover.prototype.dropTwoNodes=function(node1,node2,event_node)
{
	var slonikPreamble = this.getSlonikPreamble();
	var slonikScript = 'echo \'MultinodeFailover.prototype.dropTwoNodes\';\n';
	slonikScript+= 'drop node(id=\'' + node1 + ',' + node2 + '\',event node = ' + event_node + ');\nuninstall node(id='+node1+');\nuninstall node(id='+node2+');\n'

	var slonik=this.coordinator.createSlonik('drop node',slonikPreamble,slonikScript);
	slonik.run();
	this.coordinator.join(slonik);
	this.testResults.assertCheck('drop 2 nodes passes',slonik.getReturnCode(),0);	

}
