/** * Tests the EXECUTE SCRIPT command. * */ coordinator.includeFile('disorder/tests/BasicTest.js'); ExecuteScript = function(coordinator, testResults) { BasicTest.call(this, coordinator, testResults); this.testDescription='This test uses the EXECUTE SCRIPT functions' + ' it calls execute script on ddl that works, ddl that fails' + ' nodes using the ONLY ON feature'; } ExecuteScript.prototype = new BasicTest(); ExecuteScript.prototype.constructor = ExecuteScript; ExecuteScript.prototype.runTest = function() { this.coordinator.log("ExecuteScript.prototype.runTest - begin"); this.testResults.newGroup("Execute Script"); this.setupReplication(); /** * Start the slons. */ this.coordinator.log("ExecuteScript.prototype.runTest - start slons"); var slonArray = []; for ( var idx = 1; idx <= this.getNodeCount(); idx++) { slonArray[idx - 1] = this.coordinator.createSlonLauncher('db' + idx); slonArray[idx - 1].run(); } this.coordinator.log("ExecuteScript.prototype.runTest - add tables"); /** * Add some tables to replication. * */ this.addTables(); this.coordinator.log("ExecuteScript.prototype.runTest - subscribe first set"); /** * Subscribe the first node. */ this.subscribeSet(1, 1,1, [ 2, 3 ]); this.subscribeSet(1, 1,3, [ 4, 5 ]); this.slonikSync(1,1); this.testAddDropColumn(1, 1, false); this.coordinator.log("ExecuteScript.prototype.runTest - subscribe first set"); /** * Now create a second replication set. * */ this.createSecondSet(2); /** * Replicate the set to nodes 1 and 3. */ this.subscribeSet(2,2, 2, [ 1 ]); this.subscribeSet(2,2, 1, [ 3 ]); /** * Now send it to node 4,5 */ this.subscribeSet(2, 2,3, [ 4, 5 ]); this.coordinator.log("ExecuteScript.prototype.runTest - move set to node 1"); /** * Move the set to node 1. We want to do this for the next test. */ this.moveSet(2, 2, 1); /** * Now try the setAddDrop execute scripts again. This time we tell slonik we * are doing the DDL on set 2 even though the table it changes is part of * set 1 */ /** * First use node 2 as the event node. * Since set 2 has set 1 as the origin (we just moved it above) * we EXPECT this to fail. */ this.testAddDropColumn(2, 2, true); /** * Now try it against node 1 */ this.testAddDropColumn(2, 1, false); /** * Move it back to node 2. */ this.moveSet(2, 1, 2); /** * DDL failure tests */ this.testDDLFailure(); /** * Now create a table and add it to set 1. * */ this.createAddTestTable(); /** * Now we generate READ only loads on all nodes. * We then start doing execute scripts. */ this.coordinator.log("ExecuteScript.prototype.runTest - starting execute script with read queries on all nodes"); var readLoad=[]; for(var idx=0; idx < this.getNodeCount(); idx++) { readLoad[idx] = this.startDataChecks(idx+1); }; for(var iteration=0; iteration < 100; iteration++) { if(iteration % 10 ==0) { this.coordinator.log("execute script with read load, iteration " + iteration); //Drop the replication set (3) created before. this.dropSet3(3); //Perform the DDL. this.createAddTestTable(); } } this.coordinator.log('stopping read queries'); for(var idx=0; idx < this.getNodeCount(); idx++) { readLoad[idx].stop(); this.coordinator.join(readLoad[idx]); } this.performInsert(1); for ( var idx = 1; idx <= this.getNodeCount(); idx++) { slonArray[idx - 1].stop(); this.coordinator.join(slonArray[idx - 1]); } this.coordinator.log("ExecuteScript.prototype.runTest - complete"); } ExecuteScript.prototype.validateDdl = function(dbname) { var connection = this.coordinator.createJdbcConnection(dbname); var statement = connection.createStatement(); var rs; try { rs = statement .executeQuery('SELECT MAX(testcolumn) FROM disorder.do_order'); rs.next(); return true; } catch (e) { return false; } finally { if (rs != undefined) { rs.close(); } statement.close(); connection.close(); } } ExecuteScript.prototype.testAddDropColumn = function(setid, eventNode, expectFailure) { this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - begin"); /** * start up some load. */ var load = this.generateLoad(); this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - add column to orders - expecting failure:" + expectFailure); /** * Now add a column to orders. We will do this via EXECUTE SCRIPT. */ var scriptFile = java.io.File.createTempFile('executeScript', '.sql'); scriptFile.deleteOnExit(); var fileWriter = new java.io.FileWriter(scriptFile); fileWriter .write('ALTER TABLE disorder.do_order ADD COLUMN testcolumn int4 default 7;'); fileWriter.close(); var slonikScript = 'echo \'ExecuteScript.prototype.testAddDropColumn\';\n'; slonikScript += 'EXECUTE SCRIPT( SET ID=' + setid + ', FILENAME=\'' + scriptFile.getAbsolutePath() + '\' , EVENT NODE=' + eventNode + ' );\n' + 'SYNC(id=' + eventNode + ');\n' + 'wait for event(origin=' + eventNode + ', confirmed=all, wait on=' + eventNode + ');\n'; var slonikPreamble = this.getSlonikPreamble(); var slonik = this.coordinator.createSlonik('add testcolumn', slonikPreamble, slonikScript); slonik.run(); this.coordinator.join(slonik); if (!expectFailure) { this.testResults.assertCheck('execute script for add column okay', slonik.getReturnCode(), 0); this.testResults.assertCheck('column was added', this .validateDdl('db1'), true); this.testResults.assertCheck('column was added', this .validateDdl('db2'), true); this.testResults.assertCheck('column was added', this .validateDdl('db4'), true); } else { this.testResults.assertCheck( 'execute script for add column failed as expected', slonik .getReturnCode() != 0, expectFailure); this.testResults.assertCheck('column was added', this .validateDdl('db1'), false); this.testResults.assertCheck('column was added', this .validateDdl('db2'), false); this.testResults.assertCheck('column was added', this .validateDdl('db4'), false); load.stop(); this.coordinator.join(load); return; } this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - terminate load"); load.stop(); this.coordinator.join(load); this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - synchronize"); this.slonikSync(1, 1); this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - compare databases"); this.compareDb('db1', 'db2'); this.compareDb('db1', 'db3'); this.compareDb('db1', 'db4'); this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - execute ONLY ON"); // Now execute ONLY ON. // We DROP the column, on the master first then moving out. load = this.generateLoad(); var scriptFile_drop = java.io.File.createTempFile('executeScript', '.sql'); scriptFile_drop.deleteOnExit(); fileWriter = new java.io.FileWriter(scriptFile_drop); fileWriter.write('ALTER TABLE disorder.do_order DROP COLUMN testcolumn;'); fileWriter.close(); for ( var idx = 1; idx <= 5; idx++) { slonikScript = 'EXECUTE SCRIPT( SET ID=' + setid + ', FILENAME=\'' + scriptFile_drop.getAbsolutePath() + '\' , EVENT NODE=' + idx + ', EXECUTE ONLY ON=' + idx + ');'; slonik = this.coordinator.createSlonik(' test drop column', slonikPreamble, slonikScript); slonik.run(); this.coordinator.join(slonik); this.testResults.assertCheck('execute script for drop column okay', slonik.getReturnCode(), 0); // Sync all nodes. this.slonikSync(1, 1); } load.stop(); this.coordinator.join(load); this.slonikSync(1, 1); this.compareDb('db1', 'db2'); this.compareDb('db1', 'db3'); this.compareDb('db1', 'db4'); this.coordinator.log("ExecuteScript.prototype.testAddDropColumn - complete"); } ExecuteScript.prototype.createAndReplicateTestTable=function() { this.coordinator.log("ExecuteScript.prototype.createAndReplicateTestTable - begin"); var scriptFile = java.io.File.createTempFile('executeScript', '.sql'); scriptFile.deleteOnExit(); fileWriter = new java.io.FileWriter(scriptFile); fileWriter .write('CREATE TABLE disorder.test_transient(id serial,data text, primary key(id));'); fileWriter.close(); var slonikScript = 'echo \'ExecuteScript.prototype.createAndReplicateTestTable\';\n'; slonikScript += 'EXECUTE SCRIPT( SET ID=1' + ', FILENAME=\'' + scriptFile.getAbsolutePath() + '\' , EVENT NODE=1 );\n' + 'SYNC(id=1);\n' + 'wait for event(origin=1, confirmed=all, wait on=1);\n' + 'create set(id=3, origin=1);\n' + 'set add table(origin=1,set id=3, id=' + this.tableIdCounter + ',fully qualified name=\'disorder.test_transient\');\n'; var tempTableId=this.tableIdCounter; this.tableIdCounter++; var slonikPreamble = this.getSlonikPreamble(); var slonik = this.coordinator.createSlonik('add temp table', slonikPreamble, slonikScript); slonik.run(); this.coordinator.join(slonik); this.testResults.assertCheck('CREATE TABLE worked', slonik.getReturnCode(), 0); // // Explicitly leave node 2 out of this. this.subscribeSet(3,1, 1, [ 3 ]); this.subscribeSet(3,1, 3, [ 4, 5 ]); this.coordinator.log("ExecuteScript.prototype.createAndReplicateTestTable - complete"); } ExecuteScript.prototype.createAddTestTable = function() { this.coordinator.log("ExecuteScript.prototype.createAddTestTable - begin"); this.coordinator.log('createAddTestTable'); this.createAndReplicateTestTable(); // Now we DROP the table. this.dropTestTable(1,3,true); // Did the table get dropped from node 2? // It should not have because we executed the DROP TABLE against set 3 and // node 2 // was not a subscriber of it. var connection = this.coordinator.createJdbcConnection('db2'); var statement = connection.createStatement(); var rs = statement .executeQuery('SELECT COUNT(*) FROM pg_tables WHERE tablename=\'test_transient\';'); rs.next(); this.testResults.assertCheck('test_transient did not get deleted from db2', rs.getInt(1), 1); if (rs.getInt(1) > 0) { statement.execute('DROP TABLE disorder.test_transient;'); } statement.close(); connection.close(); /** * Now move set 3 from node 1 to 3 */ //slonikScript='set drop table(id='+ tempTableId + ',origin=1);\n'; //slonik=this.coordinator.createSlonik('drop removed table',slonikPreamble,slonikScript); //slonik.run(); //this.coordinator.join(slonik); //this.testResults.assertCheck('drop removed table worked okay',slonik.getReturnCode(),0); this.moveSet(3, 1, 3); this.coordinator.log("ExecuteScript.prototype.createAddTestTable - complete"); } /** * Tests executing a DDL script that will fail. * * First 1 test the script such that it will fail on the EVENT NODE. * * This should fail at the slonik stage. * * Next we test a DDL script that is excepted on the event node but fails at a later * node. This should stop replication. (well that is what we expect it to do * what it 'should' do is open to debate). * */ ExecuteScript.prototype.testDDLFailure = function() { this.coordinator.log("ExecuteScript.prototype.testDDLFailure - begin"); var scriptFile = java.io.File.createTempFile('executeScript', '.sql'); scriptFile.deleteOnExit(); fileWriter = new java.io.FileWriter(scriptFile); fileWriter.write('CREATE TABLE disorder.test_transient(id serial,data text, primary key(id));'); fileWriter.close(); var connection = this.coordinator.createJdbcConnection('db2'); var statement = connection.createStatement(); statement.execute('CREATE TABLE disorder.test_transient(id int4, data text,primary key(id));'); this.coordinator.log("ExecuteScript.prototype.testDDLFailure - failure expected on node 2"); /** * First execute the script against node 2, it should fail. */ var slonikScript = 'echo \'ExecuteScript.prototype.testDDLFailure\';\n'; slonikScript += 'EXECUTE SCRIPT( SET ID=2' + ', FILENAME=\'' + scriptFile.getAbsolutePath() + '\' , EVENT NODE=2 );\n'; var slonikPreamble = this.getSlonikPreamble(); var slonik = this.coordinator.createSlonik('slonik, ddlFailure',slonikPreamble,slonikScript); slonik.run(); this.coordinator.join(slonik); this.testResults.assertCheck('ddl failed as expected',slonik.getReturnCode(),255); this.coordinator.log("ExecuteScript.prototype.testDDLFailure - failure expected on event node"); /** * Now try it against an event node that it should succeed. */ slonikScript = 'EXECUTE SCRIPT( SET ID=1' + ', FILENAME=\'' + scriptFile.getAbsolutePath() + '\' , EVENT NODE=1 );\n' slonik = this.coordinator.createSlonik('slonik, ddlFailure',slonikPreamble,slonikScript); slonik.run(); this.coordinator.join(slonik); this.testResults.assertCheck('ddl accepted on node 1',slonik.getReturnCode(),0); this.coordinator.log("ExecuteScript.prototype.testDDLFailure - paused replication to node 2"); /** * Replication to node 2 should now be 'paused', */ java.lang.Thread.sleep(10*1000); var lag = this.measureLag(1,2); this.coordinator.log('we expect lag, we measure it as ' + lag); this.testResults.assertCheck('node is lagged', lag >= 10,true); this.coordinator.log("ExecuteScript.prototype.testDDLFailure - lag analysis"); /** * Allow the DDL to work on node 2. */ statement.execute('DROP TABLE disorder.test_transient'); this.slonikSync(1, 1); var lag = this.measureLag(1,2); this.coordinator.log('we do not expect lag, we measure it as ' + lag); this.testResults.assertCheck('node is not lagged', lag < 10,true); this.slonikSync(1,1); this.dropTestTable(1,1,false); statement.close(); connection.close(); this.coordinator.log("ExecuteScript.prototype.testDDLFailure - complete"); } /** * Remove the test table 'disorder_transient' by performing a DROP table via * EXECUTE SCRIPT. * * if removeFromReplication is true then a 'set drop node' is also performed. * */ ExecuteScript.prototype.dropTestTable=function(node_id,set_id,removeFromReplication) { this.coordinator.log('ExecuteScript.prototype.dropTestTable ' + node_id + ',' + set_id); var slonikPreamble = this.getSlonikPreamble(); var scriptFile_drop = java.io.File.createTempFile('executeScript', '.sql'); var fileWriter = new java.io.FileWriter(scriptFile_drop); fileWriter.write('DROP TABLE disorder.test_transient;'); fileWriter.close(); var slonikScript ='echo \'ExecuteScript.prototype.dropTestTable\';\n'; if(removeFromReplication) { slonikScript+='set drop table(id=' + (this.tableIdCounter-1) + ',origin=' + node_id + ');\n'; } slonikScript+= 'EXECUTE SCRIPT( SET ID=' + set_id + ', FILENAME=\'' + scriptFile_drop.getAbsolutePath() + '\' , EVENT NODE=' + node_id +' );\n' + 'SYNC(id=' + node_id + ');\n' + 'wait for event(origin=' + node_id + ', confirmed=all, wait on=' + node_id+');\n'; var slonik = this.coordinator.createSlonik('DROP TABLE', slonikPreamble, slonikScript); slonik.run(); this.coordinator.join(slonik); this.testResults.assertCheck('slonik executed drop table okay', slonik .getReturnCode(), 0); this.coordinator.log('ExecuteScript.prototype.dropTestTable ' + node_id + ',' + set_id+ " complete"); } ExecuteScript.prototype.dropSet3 = function(set_origin) { this.coordinator.log('ExecuteScript.prototype.dropSet3 ' + set_origin + " - begin"); var slonikPreamble = this.getSlonikPreamble(); var slonikScript ='echo \'ExecuteScript.prototype.dropSet3\';\n' + 'drop set (id=3,origin=' + set_origin + ');' + 'wait for event(origin=' + set_origin + ', wait on=' + set_origin + ', confirmed=all);\n'; var slonik=this.coordinator.createSlonik('DROP SET',slonikPreamble,slonikScript); slonik.run(); this.coordinator.join(slonik); this.testResults.assertCheck('drop set 3 worked as expected',slonik.getReturnCode(),0); this.coordinator.log('ExecuteScript.prototype.dropSet3 ' + set_origin + " - complete"); } /** * Performs an INSERT on all nodes via EXECUTE script and verifies that * changes to the replica databases are allowed via execute script. * * */ ExecuteScript.prototype.performInsert=function(node_id) { this.coordinator.log("ExecuteScript.prototype.performInsert on node " + node_id + " - begin"); var slonikPreamble = this.getSlonikPreamble(); var scriptFile = java.io.File.createTempFile('executeScript', '.sql'); var fileWriter = new java.io.FileWriter(scriptFile); fileWriter.write('INSERT INTO disorder.do_customer(c_name) VALUES (disorder.digsyl(' + new java.util.Date().getTime() +',16));'); fileWriter.close(); var slonikScript = 'echo \'ExecuteScript.prototype.performInsert\';\n'; slonikScript += 'EXECUTE SCRIPT( SET ID=1' + ', FILENAME=\'' + scriptFile + '\' , EVENT NODE=' + node_id +' );\n' + 'SYNC(id=' + node_id + ');\n' + 'wait for event(origin=' + node_id + ', confirmed=all, wait on=' + node_id+');\n'; var slonik = this.coordinator.createSlonik('DROP TABLE', slonikPreamble, slonikScript); // Generate some WRITE load. // We want the EXECUTE script to go in sequence. this.coordinator.log("ExecuteScript.prototype.performInsert on node " + node_id + " - imposing load"); var disorderClientJs = this.coordinator.readFile('disorder/client/disorder.js'); disorderClientJs+= this.coordinator.readFile('disorder/client/run_fixed_load.js'); var load = this.coordinator.clientScript(disorderClientJs,'db' + node_id); load.run(); slonik.run(); this.coordinator.log("ExecuteScript.prototype.performInsert on node " + node_id + " - performing drop table"); this.coordinator.join(slonik); this.testResults.assertCheck('slonik executed drop table okay', slonik .getReturnCode(), 0); load.stop(); this.coordinator.join(load); this.coordinator.log("ExecuteScript.prototype.performInsert on node " + node_id + " - SYNC, compare DBs"); this.slonikSync(1,1); this.compareDb('db1','db2'); this.compareDb('db1','db3'); this.compareDb('db1','db4'); this.compareDb('db1','db5'); this.coordinator.log("ExecuteScript.prototype.performInsert on node " + node_id + " - complete"); }