# The tests verify concurrent execution of replicated (MDEV-742)
# XA transactions in the parallel optimistic mode.

--source include/have_innodb.inc
--source include/have_perfschema.inc
--source include/master-slave.inc

# Tests' global declarations
--let $trx = _trx_

call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
call mtr.add_suppression("WSREP: handlerton rollback failed");
#call mtr.add_suppression("Can't find record in 't1'");
CREATE VIEW v_processlist  as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';

--connection master
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
--save_master_pos

# Prepare to restart slave into optimistic parallel mode
--connection slave
--sync_with_master
--source include/stop_slave.inc
SET @old_parallel_threads            = @@GLOBAL.slave_parallel_threads;
SET @@global.slave_parallel_threads  = 7;
SET @old_parallel_mode               = @@GLOBAL.slave_parallel_mode;
SET @@global.slave_parallel_mode     ='optimistic';
# Run the first part of the test with high batch size and see that
# old rows remain in the table.
SET @old_gtid_cleanup_batch_size     = @@GLOBAL.gtid_cleanup_batch_size;
SET @@global.gtid_cleanup_batch_size = 1000000;

CHANGE MASTER TO master_use_gtid=slave_pos;

# LOAD GENERATOR creates XA:s interleaved in binlog when they are from
# different connections. All the following block XA:s of the same connection
# update the same data which challenges slave optimistic scheduler's correctness.
# Slave must eventually apply such load, and correctly (checked).

--connection master
CREATE TABLE t0 (a int,             b INT) ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 0);


# I. Logging some sequence of XA:s by one connection.
#
# The slave applier's task is to successfully execute a series of
# Prepare and Complete parts of a sequence of XA:s

--let $trx_num = 300
--let $i       = $trx_num
--let $conn    = master
--disable_query_log
while($i > 0)
{
  # 'decision' to commit 0, or rollback 1
  --let  $decision = `SELECT $i % 2`
  --eval XA START '$conn$trx$i'
  --eval   UPDATE t1 SET b = 1 - 2 * $decision WHERE a = 1
  --eval XA END '$conn$trx$i'
  --let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
  if (!$one_phase)
  {
    --eval XA PREPARE '$conn$trx$i'
    --let $one_phase =
  }

  --let $term = COMMIT
  if ($decision)
  {
    --let $term = ROLLBACK
    --let $one_phase =
  }
  --eval XA $term '$conn$trx$i' $one_phase

  --dec $i
}
--enable_query_log
--source include/save_master_gtid.inc

--connection slave
--source include/start_slave.inc
--source include/sync_with_master_gtid.inc
--source include/stop_slave.inc


# II. Logging XS:s from multiple connections in random interweaving manner:
#
# in a loop ($i) per connection
#   arrange an inner ($k) loop where
#     start and prepare an XA;
#     decide whether to terminate it and then continue to loop innerly
#     OR disconnect to break the inner loop;
#   the disconnected one's XA is taken care by 'master' connection
#
# Effectively binlog must collect a well mixed XA- prepared and terminated
# groups for slave to handle.

--connection master
# Total # of connections
--let $conn_num=53

--let $i = $conn_num
--disable_query_log
while($i > 0)
{
  --connect (master_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
--dec $i
}
--enable_query_log

--let $i = $conn_num
while($i > 0)
{
  --let $conn_i      = conn$i
  # $i2 indexes the current connection's "own" row
  --let $i2 = `SELECT $i + 2`
--disable_query_log
  --connection master_conn$i
--enable_query_log
  --disable_query_log
  --let $i_conn_id   = `SELECT connection_id()`

  --let $decision  = 0
  # the row id of the last connection that committed its XA
  --let $c_max     = 1
  --let $k         = 0
  while ($decision < 3)
  {
    --inc $k
    --eval XA START '$conn_i$trx$k'
    # UPDATE depends on previously *committed* transactions
    --eval UPDATE t1 SET b = b + $k + 1 WHERE a = $c_max
    if (`SELECT $k % 2 = 1`)
    {
    --eval REPLACE INTO t1 VALUES ($i2, $k)
    }
    if (`SELECT $k % 2 = 0`)
    {
    --eval DELETE FROM t1 WHERE a = $i2
    }
    CREATE TEMPORARY TABLE tmp LIKE t0;
    --eval INSERT INTO tmp SET a=$i, b= $k
    INSERT INTO t0 SELECT * FROM tmp;
    DROP TEMPORARY TABLE tmp;
    --eval XA END '$conn_i$trx$k'

    --let $term = COMMIT
    --let $decision  = `SELECT (floor(rand()*10 % 10) + ($i+$k)) % 4`
    if ($decision == 1)
    {
    --let $term = ROLLBACK
    }
    if ($decision < 2)
    {
    --eval XA PREPARE '$conn_i$trx$k'
    --eval XA $term '$conn_i$trx$k'
    # Iteration counter is taken care *now*
    }
    if ($decision == 2)
    {
    --eval XA COMMIT '$conn_i$trx$k' ONE PHASE
    }
  }

  # $decision = 3
  --eval XA PREPARE '$conn_i$trx$k'
  # disconnect now
  --disconnect master_conn$i
  --connection master

  --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $i_conn_id
  --source include/wait_condition.inc

  --disable_query_log
  --let $decision = `SELECT ($i+$k) % 2`
  --let $term     = COMMIT
  if ($decision == 1)
  {
  --let $term = ROLLBACK
  }
  --eval XA $term '$conn_i$trx$k'
  --let $c_max = $i2

--dec $i
}
--enable_query_log
--source include/save_master_gtid.inc

--connection slave
--source include/start_slave.inc
--source include/sync_with_master_gtid.inc

#
# Overall consistency check
#
--let $diff_tables= master:t0, slave:t0
--source include/diff_tables.inc
--let $diff_tables= master:t1, slave:t1
--source include/diff_tables.inc


#
# Clean up.
#
--connection slave
--source include/stop_slave.inc
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
SET GLOBAL slave_parallel_threads=@old_parallel_threads;
--source include/start_slave.inc

--connection master
DROP VIEW v_processlist;
DROP TABLE t0, t1;
--source include/save_master_gtid.inc

--connection slave
--source include/sync_with_master_gtid.inc
# Check that old rows are deleted from mysql.gtid_slave_pos.
# Deletion is asynchronous, so use wait_condition.inc.
# Also, there is a small amount of non-determinism in the deletion of old
# rows, so it is not guaranteed that there can never be more than
# @@gtid_cleanup_batch_size rows in the table; so allow a bit of slack
# here.
let $wait_condition=
  SELECT COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
    FROM mysql.gtid_slave_pos;
--source include/wait_condition.inc
eval $wait_condition;
SET GLOBAL gtid_cleanup_batch_size= @old_gtid_cleanup_batch_size;

--connection master
--source include/rpl_end.inc
