unittest/scripts/manual/upgrade_metadata_crash_handling.js (155 lines of code) (raw):

// Tests handling of crashes and DB errors during a MD schema upgrade. // This will load a MD 1.0.1, try to upgrade it, fail after X DB queries and // try to restore. The final state should be either a reverted or completed // upgrade. // Run with mysqlshrec -f <thisfile>, from the build dir. // To customize whatthe script will do, go to the CUSTOMIZE section below function prepare() { println("\033[1;31mPREPARING", "\033[0m"); session.runSql("DROP SCHEMA IF EXISTS mysql_innodb_cluster_metadata"); session.runSql("DROP SCHEMA IF EXISTS mysql_innodb_cluster_metadata_bkp"); session.runSql("DROP SCHEMA IF EXISTS mysql_innodb_cluster_metadata_previous"); session.runSql("CREATE SCHEMA mysql_innodb_cluster_metadata"); testutil.importData(url, "metadata.sql", "mysql_innodb_cluster_metadata"); } function upgrade_and_fail(num_queries, test_crash) { var failure = "" if (test_crash) failure = "sql_test_abort"; else failure = "sql_test_error"; println("\033[1;31mUPGRADING", "\033[0m"); // perform upgrade and ensure it crashes after X queries r=testutil.callMysqlsh([url, "--js", "--verbose=3", "--dba-log-sql=2", "--debug=+d,"+failure, "--", "dba", "upgrade-metadata"], "", ["TEST_SQL_UNTIL_CRASH="+num_queries], mysqlshrec); println("\033[1;31mUPGRADE RC=",r, "\033[0m"); return r; } function recover() { println("\033[1;31mRECOVERING", "\033[0m"); r = testutil.callMysqlsh([url, "--js", "--verbose=2", "--dba-log-sql=2", "--", "dba", "upgrade-metadata"]); println("\033[1;31mRECOVERY RC=",r, "\033[0m"); if (r==1) { shell.prompt("Failed recovery scenario, time to debug..."); } } function recover_and_fail(num_queries, test_crash) { println("\033[1;31mRECOVERING", "\033[0m"); var failure = "" if (test_crash) failure = "sql_test_abort"; else failure = "sql_test_error"; r = testutil.callMysqlsh([url, "--js", "--verbose=2", "--dba-log-sql=2", "--debug=+d,"+failure, "--", "dba", "upgrade-metadata"], "", ["TEST_SQL_UNTIL_CRASH="+num_queries], mysqlshrec); println("\033[1;31mRECOVERY RC=",r, "\033[0m"); return r; } function check_state() { println("\033[1;31mCHECKING CONSISTENCY", "\033[0m"); // ensure that the MD schema is consistent try { c = dba.getCluster(); c.status(); return 0; } catch (error) { println("\033[1;31mERROR:", error, "\033[0m"); return -1; } } //=========== CUSTOMIZE =============== // This script could test both failures (exceptions) or crashes, this flag is used for that. var testing_crash = true; // ~180 SQL Statements are required for complete testing of the upgrade process // ~100 SQL Statements are required for complete testing of the rollback process // ~60 SQL Statements are required for complete testing of the rollforward process var testing_upgrade = true; var start_at = 101; var end_at = 200; // It is possible to test the restore process from 2 points // Rollback, data has been copied but upgrade state did not change to complete // Rollforward, upgrade process reached the DONE state but did not complete var test_rollback = false; // Holds the numbers of queries required to reach the DONE state, this constant // needs to be updated if the MD upgrade logic changes var DONE_STATE_QUERIES=172 // To explore the state at a specific number of queries on the upgrade process // turn on this flag. You will be prompted for the number of queries to be executed // before stopping so you can explore the state, when done enter a negative number var explore_state = false; // Port in which the sandbox will be created port = 3000; // Path to the mysqlsh binary to create schema 1.0.1 mysqlsh_old = "/usr/bin/mysqlsh"; // Path to the mysqlshrec executable mysqlshrec = "bin/mysqlshrec"; //=========== EXECUTION CONFIGURATION =============== if (explore_state) { println("MODE: Upgrade State EXplore"); println("TODO: Provide the number of queries to execute on the prompt, enter negative number to finish."); } else { if (testing_upgrade) { println("MODE: Testing Metadata Upgrade"); } else { if (test_rollback) { println("MODE: Testing Upgrade Recovery: Rollback"); } else { println("MODE: Testing Upgrade Recovery: Rollforward"); } } println(`Start At Query: ${start_at}`); println(`End At Query: ${end_at}`); } shell.prompt("Hit ENTER to continue or Ctrl+C to cancel...") //=========== EXECUTION STARTS =============== url = "root:root@localhost:"+port; println("Deploying test sandbox..."); var query_log = testutil.getSandboxLogPath(port).replace("error", "query"); testutil.deploySandbox(port, "root", {general_log:1}); shell.connect(url); // create a cluster with the old MD println("Creating test cluster..."); testutil.callMysqlsh([url, "--js", "--", "dba", "create-cluster", "mycluster"], "", [], mysqlsh_old); println("Backing up metadata schema..."); testutil.dumpData(url, "metadata.sql", ["mysql_innodb_cluster_metadata"]); if (explore_state) { var queries = 0; while(queries >= 0) { queries = parseInt(shell.prompt("Number of queries? ")) if (queries > 0) { prepare(); upgrade_and_fail(queries, true) } } } else { var test_description = "" if (testing_crash) test_description = "CRASH" else test_description = "FAILURE" if (testing_upgrade) { done = 0; for (i = start_at; i <= end_at && !done; i++) { println("\033[1;31m#### TEST UPGRADE", test_description, "AFTER", i, "\033[0m"); prepare(); if (upgrade_and_fail(i, testing_crash) == 0) { done = 1; } else { recover(); } if (check_state() != 0) { println("\033[1;31mFAILED AT", i, "\033[0m"); break; } else { println("\033[1;31mOK", "\033[0m"); } println("\033[1;31m#########################", "\033[0m"); } } else { done = 0; for (i = start_at; i <= end_at && !done; i++) { println("\033[1;31m#### TEST RESTORE", test_description, "AFTER", i, "\033[0m"); // DONE state is set on query #155 so to test full rollback we use 154 // to test rollforward we use 155 var queries = 0; if (test_rollback) { println("\033[1;31m#### ROLL BACK\033[0m"); queries = DONE_STATE_QUERIES-1; } else { println("\033[1;31m#### ROLL FORWARD\033[0m"); queries = DONE_STATE_QUERIES; } prepare(); // This will let the upgrade in the desired state upgrade_and_fail(queries, testing_crash); // This will cause the recovery to fail after i statements if (recover_and_fail(i, testing_crash) == 0 ) { done = 1; } else { // This should succeed recover(); } if (check_state() != 0) { println("\033[1;31mFAILED AT", i, "\033[0m"); break; } else { println("\033[1;31mOK", "\033[0m"); } println("\033[1;31m#########################", "\033[0m"); } } } testutil.destroySandbox(port);