# ==== Purpose ====
#
# Test that it is possible to change the topology completely randomly,
# and the GTID system takes care of sending the correct transactions,
# as long as all servers generate non-conflicting transactions.
#
# ==== Implementation ====
#
# Configure X servers and Y clients on each server.  Iterate Z times.
#
# In each iteration, select a random server and select a random client
# on that server.  On this connection, perform each of the following
# with a fixed probability:
#
#  - A transaction (DML)
#  - A DDL statement
#  - FLUSH LOGS
#  - CHANGE MASTER to a random other server
#
# After all iterations are done, change to a circular topology and
# wait for the servers to synchronize.  Then check that all servers
# have the same data.
#
# Details:
#  - We create as many databases as we have servers.
#  - Each server only executes DML / DDL on one database.
#  - Each database has one table per client. Initially, the table
#    names are t_1_0, t_2_0, ..., t_Y_0, where Y is the number of
#    clients per server, and all tables are empty.
#  - The DDL consists in replacing the table by t_y_i, where i is the
#    iteration counter and y is the client used in this iteration.
#    t_y_i is empty. We do this in a funny way (see the code) just to
#    use a couple of different DDL statements.
#  - The DML consists in replacing the contents of t_y_*, by the value
#    i, where i is the iteration counter and y is the client used in
#    this iteration. We do this in a funny way (see the code) just to
#    use a couple of diffrent DML statements.
#
# ==== Custom usage ====
#
# You can alter the behavior of this test by setting the following
# variables (as environment variables or mtr variables):
#
#  $debug
#    If set, print all statements (default off)
#
#  $max_iterations
#    The maximal number of iterations to run. (default 10000)
#
#  $max_seconds
#    The maximal time to run iterations. (default 300)
#
#  $n_servers
#    The number of servers to run on (default 5)
#
#  $n_clients_per_server
#    The number of clients to use for each server (default 5)
#
#  $change_master_probability
#    The probability to execute CHANGE MASTER in each iteration (default 0.1)
#
#  $flush_logs_probability
#    The probability to execute FLUSH LOGS in each iteration (default 0.05)
#
#  $dml_probability
#    The probability to execute DML in each iteration (default 1)
#
#  $ddl_probability
#    The probability to execute DDL in each iteration (default 0.1)
#
#  $deterministic
#    By default, the random seed is taken to be a random number,
#    different for each run, and the test is deliberately executing
#    several tasks concurrently.  If $deterministic is set, the random
#    seed is taken to be the value of $deterministic, and the
#    concurrency is reduced (but not entirely eliminated).
#
# ==== References ====
#
# WL#3584: Global Transaction Identifiers
# - Created as part of this worklog
# BUG#18385953: RPL_GTID_STRESS_FAILOVER FAILS WITH "ERROR IN SYNC_WITH_MASTER.INC"
# - fixed bug in cleanup code

#Want to skip this test from daily Valgrind execution
--source include/no_valgrind_without_big.inc
--source include/big_test.inc
--source include/have_gtid.inc

# This test case has path issues when executing the 
# external command mysqldump.exe on windows. Since
# what it tests is not platform dependent we disable 
# it on Windows.
--source include/not_windows.inc

# This test case fails in MTS mode due to BUG#12995174
# once that bug is fixed, the following line should be
# removed so that the test is enabled in MTS
--source include/not_mts_slave_parallel_workers.inc

--echo ==== Configure ====

if ($max_seconds == '')
{
  --let $max_seconds= 300
}
if ($max_iterations == '')
{
  --let $max_iterations= 10000
}
if (!$n_servers)
{
  --let $n_servers= 5
}
if (!$n_clients_per_server)
{
  --let $n_clients_per_server= 5
}

if ($change_master_probability == '')
{
  --let $change_master_probability= 0.1
}
if ($flush_logs_probability == '')
{
  --let $flush_logs_probability= 0.05
}
if ($dml_probability == '')
{
  --let $dml_probability= 1
}
if ($ddl_probability == '')
{
  --let $ddl_probability= 0.1
}

if ($deterministic != '')
{
  --let $rand_seed= $deterministic
}

--echo ==== Initialize ====

--let $underscore= _
--let $zero= 0

if (!$debug)
{
  --disable_query_log
  --disable_result_log
}
if ($debug)
{
  --echo debug: n_servers=$n_servers n_clients_per_server=$n_clients_per_server max_iterations=$max_iterations max_seconds=$max_seconds
  --echo debug: change_master_probability=$change_master_probability flush_logs_probability=$flush_logs_probability dml_probability=$dml_probability ddl_probability=$ddl_probability
}

--let $circular_topology= 1
--let $server_list=
--let $server= $n_servers
while ($server > 0)
{
  --let $circular_topology= $server->$circular_topology
  --let $server_list= $server $server_list
  --dec $server
}

--let $rpl_extra_connections_per_server= $n_clients_per_server
--let $rpl_topology= $circular_topology
--let $use_gtids= 1
--source include/rpl_init.inc

--let $server= 1
while ($server <= $n_servers)
{
  --connection server_$server
  eval CREATE DATABASE db_$server;

  --let $client= 1
  while ($client <= $n_clients_per_server)
  {
    --let $connection= server_$server$underscore$client
    --connection $connection

    eval CREATE TABLE db_$server.t_$client$underscore$zero (a INT) ENGINE = InnoDB;
    eval SET @last_value = -1;

    --inc $client
  }
  --inc $server
}

--source include/rpl_sync.inc
--source include/rpl_reset.inc

--let $server= 1
while ($server <= $n_servers)
{
  --let $client= 1
  while ($client <= $n_clients_per_server)
  {
    --connection server_$server$underscore$client
    send SELECT 1;
    --inc $client
  }
  --inc $server
}


--echo ==== Test ====

--connection default
--let $iteration= 1
--let $start_time= `SELECT UNIX_TIMESTAMP()`
--let $done= 0
while (!$done)
{
  --connection default

  --let $rand_type= int
  --let $rand_min= 1
  --let $rand_max= $n_servers + 1
  --source include/rand.inc
  --let $server= $rand

  --let $rand_max= $n_clients_per_server + 1
  --source include/rand.inc
  --let $client= $rand

  --let $rand_type= decide
  --let $rand_probability= $change_master_probability
  --source include/rand.inc
  --let $do_change_master= $rand

  --let $rand_probability= $flush_logs_probability
  --source include/rand.inc
  --let $do_flush_logs= $rand

  --let $rand_probability= $dml_probability
  --source include/rand.inc
  --let $do_dml= $rand

  --let $rand_probability= $ddl_probability
  --source include/rand.inc
  --let $do_ddl= $rand

  if ($debug)
  {
    --echo debug: server=$server client=$client change_master=$do_change_master flush=$do_flush_logs dml=$do_dml ddl=$do_ddl
  }

  --let $connection= server_$server$underscore$client
  --connection $connection

  if ($do_change_master)
  {
    --reap

    # Computes a random number between 1 and $n_servers that is
    # different from $server.
    --let $rand_type= int
    --let $rand_min= 0
    --let $rand_max= $n_servers - 1
    --source include/rand.inc
    --let $new_master= `SELECT 1 + (($server + $rand) % $n_servers)`
    if ($debug)
    {
      --echo change master for $server to $new_master
    }

    --let $include_silent= 1
    --source include/stop_slave.inc

    --let $port= \$SERVER_MYPORT_$new_master
    --disable_warnings
    eval CHANGE MASTER TO MASTER_HOST = '127.0.0.1',
                          MASTER_PORT = $port,
                          MASTER_USER = 'root',
                          MASTER_CONNECT_RETRY = 1,
                          MASTER_AUTO_POSITION = 1;
    --enable_warnings

    --source include/start_slave.inc
    --let $include_silent= 0
    # give something for the next reap
    send SELECT 1;
  }

  if ($do_flush_logs)
  {
    --reap
    send FLUSH LOGS;
  }

  if ($do_dml)
  {
    --reap
    --let $t= `SHOW TABLES IN db_$server LIKE 't_$client$underscore%'`
    --let $t= db_$server.$t
    --let $last_value= `SELECT @last_value`
    --delimiter |
    send|
    eval
    BEGIN;
      INSERT INTO $t VALUES (-1);
      UPDATE $t SET a = $iteration WHERE a = $last_value;
      DELETE FROM $t WHERE a = -1;
    COMMIT;
    SET @last_value= $iteration;
    |
    --delimiter ;
  }

  if ($do_ddl)
  {
    --reap
    --let $t= `SHOW TABLES IN db_$server LIKE 't_$client$underscore%'`
    --let $t= db_$server.$t
    --delimiter |
    send|
    eval
    CREATE TABLE db_$server.t_$client (a INT) ENGINE = InnoDB;
    DROP TABLE $t;
    RENAME TABLE db_$server.t_$client TO db_$server.t_$client$underscore$iteration;
    SET @last_value = -1;
    |
    --delimiter ;
  }

  if ($deterministic != '')
  {
    --reap
    --sleep 2
    --send SELECT 1
  }

  --inc $iteration

  if ($iteration > $max_iterations)
  {
    --let $done= 1
  }
  --connection default
  --let $elapsed_time= `SELECT UNIX_TIMESTAMP() - $start_time`
  if ($elapsed_time > $max_seconds)
  {
    --let $done= 1
  }
}
if ($debug)
{
  --echo exited after iteration $iteration, $elapsed_time seconds (max_seconds=$max_seconds, start_time=$start_time)
}
# extra debug info for show_rpl_debug_info
--let $extra_debug_info= iterations=$iterations


--echo ==== Sync ====

--echo Reap all sent queries.
--let $server= 1
while ($server <= $n_servers)
{
  --let $client= 1
  while ($client <= $n_clients_per_server)
  {
    --connection server_$server$underscore$client
    --reap
    --inc $client
  }
  --inc $server
}

# Wait for replication to catch up
--connection default

--source include/rpl_stop_slaves.inc

--let $rpl_topology= $circular_topology
--let $rpl_extra_connections_per_server= 5
--let $rpl_unconditional_change_master= 1
--source include/rpl_change_topology.inc

--source include/rpl_start_slaves.inc

--source include/rpl_sync.inc


--echo ==== Check result ====

--echo Check that GTID_EXECUTED is equal on all servers.
--let $server= 1
while ($server <= $n_servers)
{
  --connection server_$server
  --let $gtid_executed= `SELECT @@GLOBAL.GTID_EXECUTED`
  if ($server > 1)
  {
    if ($last_gtid_executed != $gtid_executed)
    {
      --source include/show_rpl_debug_info.inc
      --echo ERROR: GTID_EXECUTED differs between server 1 and server $server
      --echo ERROR: GTID_EXECUTED on server 1: '$last_gtid_executed'
      --echo ERROR: GTID_EXECUTED on server $server: '$gtid_executed'
      --die GTID_EXECUTED differs between two servers
    }
  }
  --let $last_gtid_executed= $gtid_executed
  --inc $server
}

--echo Check that database state is equal on all servers.
--let $diff_servers= $server_list
--source include/diff_servers.inc


--echo ==== Clean up ====

# drop database
--connection default
--let $server= 1
while ($server <= $n_servers)
{
  eval DROP DATABASE db_$server;
  --inc $server
}
--source include/rpl_sync.inc

# restore AUTO_POSITION
--let $server= 1
while ($server <= $n_servers)
{
  --connection server_$server

  --let $include_silent=1
  --source include/stop_slave.inc
  CHANGE MASTER TO MASTER_AUTO_POSITION=0;
  --source include/start_slave.inc
  --let $include_silent=0

  --inc $server
}

--source include/rpl_end.inc
