################################################################################
# It verifys that transaction retry works well on MTS. It is designed to work
# on both database and logical clock MTS.
#
# Test Scenarios:
# 1. slave_transaction_retries = 0, coordinator and workers should stop without
#    retry when encountering a temporary error.
# 2. coordinator and workers should stop after retrying a transaction
#    'slave_transaction_retries' times.
# 3. the worker can continue to finish the transaction correctly if retry does't
#    encounter any error and succeeds.
#
# Test Logic:
# 1. Inserts some data into tables on master and sync the data to slave.
# 2. Locks all rows on slave through SELECT ... FOR UPDATE
# 3. Inserts some new data into tables on master.
#    Slave workers will encounter the temporary error 'Lock wait timeout
#    exceeded' when applying the events.
# 4. check some status to make sure it acts as expected.
#
# In the test cases, we setup two slave workers. So it initializes two databases.
# in each test case, it will use two transactions. They operate on different
# databases. For databases MTS, it works. For logic clock MTS, the caller should
# call below DEBUG instruction before this include file. So the two transactions
# can be paralleled on slave.
# SET DEBUG = "d,set_commit_parent_100";
################################################################################
--echo #
--echo # Initialization the test
--echo #

# Create two database to support mts of db parallel
CREATE DATABASE db1;
CREATE DATABASE db2;
CREATE TABLE db1.t1(c1 INT PRIMARY KEY, c2 VARCHAR(4096)) ENGINE=InnoDB;
CREATE TABLE db1.t2(c1 INT PRIMARY KEY, c2 VARCHAR(4096)) ENGINE=InnoDB;
CREATE TABLE db2.t1(c1 INT PRIMARY KEY, c2 VARCHAR(4096)) ENGINE=InnoDB;

INSERT INTO db1.t1 VALUES(1, NULL), (10, NULL), (100, NULL);
INSERT INTO db1.t2 VALUES(1, NULL), (10, NULL), (100, NULL);
INSERT INTO db2.t1 VALUES(1, NULL), (10, NULL), (100, NULL);

--source include/sync_slave_sql_with_master.inc
CALL mtr.add_suppression("Slave SQL for channel '':.*Lock wait timeout exceeded.*");
CALL mtr.add_suppression("Slave SQL for channel '': Worker . failed executing transaction.*");
CALL mtr.add_suppression("Slave SQL for channel '': .*Error_code: 1756");
CALL mtr.add_suppression("Slave SQL for channel '': worker thread retried transaction.*");

# Backup the original value of the variables
SET @saved_innodb_lock_wait_timeout = @@GLOBAL.innodb_lock_wait_timeout;
SET @saved_slave_parallel_workers = @@GLOBAL.slave_parallel_workers;
SET @saved_slave_parallel_type = @@GLOBAL.slave_parallel_type;
SET @saved_max_relay_log_size = @@GLOBAL.max_relay_log_size;
SET @saved_slave_transaction_retries = @@GLOBAL.slave_transaction_retries;

# Set a small wait timeout, so the test can run fast
SET GLOBAL innodb_lock_wait_timeout = 1;
SET GLOBAL slave_parallel_workers = 2;

# Set a small relay log size, so the events of a transaction will be divided
# into more than one relay logs.
SET GLOBAL max_relay_log_size = 4096;

--source include/stop_slave_sql.inc

eval SET GLOBAL slave_parallel_type = $mts_parallel_type;

--source include/start_slave_sql.inc

--echo #
--echo # Case 1: slave will stop if slave_transaction_retries is 0
--echo #
SET GLOBAL slave_transaction_retries = 0;

# Lock all rows of db1.t1
BEGIN;
SELECT c1 FROM db1.t1 FOR UPDATE;

--source include/rpl_connection_master.inc
INSERT INTO db1.t1 VALUES(2, NULL);
INSERT INTO db2.t1 VALUES(2, NULL);
--source include/save_master_pos.inc

--source include/rpl_connection_slave.inc
# Lock wait timeout
--let $slave_sql_errno= convert_error(ER_LOCK_WAIT_TIMEOUT)
--source include/wait_for_slave_sql_error.inc

--let $assert_text= Value 2 is not in db1.t1.
--let $assert_cond= count(*) = 0 FROM db1.t1 WHERE c1 = 2
--source include/assert.inc

# release the lock on db1.t1
ROLLBACK;

--echo #
--echo # Case 2: Slave will stop after retrying a transaction
--echo #         slave_transaction_retries times.
--echo #
SET GLOBAL slave_transaction_retries = 2;
--source include/start_slave_sql.inc
--source include/sync_slave_sql.inc

# Lock all rows of db1.t2
BEGIN;
SELECT c1 FROM db1.t2 FOR UPDATE;


--source include/rpl_connection_master.inc
BEGIN;
INSERT INTO db1.t1 VALUES(20, NULL);
INSERT INTO db1.t2 VALUES(20, NULL);
COMMIT;

INSERT INTO db2.t1 VALUES(20, NULL);
--source include/save_master_pos.inc

--source include/rpl_connection_slave.inc
# Lock wait timeout
--let $slave_sql_errno= convert_error(ER_LOCK_WAIT_TIMEOUT)
--source include/wait_for_slave_sql_error.inc

--let $assert_text= Value 20 is not in db1.t1.
--let $assert_cond= count(*) = 0 FROM db1.t1 WHERE c1 = 20
--source include/assert.inc

# release the lock on db1.t2
ROLLBACK;

--echo #
--echo # Case 3: Slave worker will not stop after retring a transaction
--echo #         sucessfully.
--echo #
--let $retried_trans= query_get_value(SHOW STATUS LIKE 'slave_retried_transactions', Value, 1)
SET GLOBAL slave_transaction_retries = 10;
--source include/start_slave_sql.inc
--source include/sync_slave_sql.inc
--source include/check_slave_no_error.inc

# Lock all rows of db1.t2
BEGIN;
SELECT c1 FROM db1.t2 FOR UPDATE;

--source include/rpl_connection_master.inc
--let $str= `SELECT repeat('a', 4096)`
BEGIN;
# It will make below INSERTs to be stored into different relay logs.
--echo # INSERT INTO db1.t1 VALUES(30, 4096'a')
--echo # INSERT INTO db1.t1 VALUES(31, 4096'a')
--disable_query_log
eval INSERT INTO db1.t1 VALUES(30, '$str');
eval INSERT INTO db1.t1 VALUES(31, '$str');
--enable_query_log

# To Verify that Uservar_log_event works well
SET @str= 'abc';
eval INSERT INTO db1.t2 VALUES(30, @str);
COMMIT;

INSERT INTO db2.t1 VALUES(30, NULL);
--source include/save_master_pos.inc

--source include/rpl_connection_slave.inc
--let $status_var_comparsion= >=
--let $status_type= GLOBAL
--let $status_var= slave_retried_transactions
--let $status_var_value= $retried_trans+2
--let $status_fail_query= SELECT GLOBAL.slave_retried_transactions
--source include/wait_for_status_var.inc

#
# It is for verifying the fix of Bug#19282301
# To verify the temporary error is not reported through SHOW SLAVE STATUS
--source include/check_slave_no_error.inc

# Release the lock of db1.t2
ROLLBACK;

--source include/sync_slave_sql.inc

# It is for verifying the fix of Bug#19282301
--source include/check_slave_no_error.inc

--let $rpl_diff_statement= SELECT * FROM db1.t1
--source include/rpl_diff.inc

--let $rpl_diff_statement= SELECT * FROM db1.t2
--source include/rpl_diff.inc

--let $rpl_diff_statement= SELECT * FROM db2.t1
--source include/rpl_diff.inc

--source include/rpl_connection_master.inc
INSERT INTO db1.t1 VALUES(40, NULL);
INSERT INTO db2.t1 VALUES(40, NULL);
--source include/sync_slave_sql_with_master.inc

--echo #
--echo # Case 4: Non-temporary error will stop slave workers without retrying
--echo #
--let $retried_trans= query_get_value(SHOW STATUS LIKE 'slave_retried_transactions', Value, 1)
INSERT INTO db1.t1 VALUES(50, NULL);

--source include/rpl_connection_master.inc
INSERT INTO db1.t1 VALUES(50, NULL);
--source include/save_master_pos.inc

--source include/rpl_connection_slave.inc
--let $slave_sql_errno= convert_error(ER_DUP_ENTRY)
--source include/wait_for_slave_sql_error.inc

--let $assert_text= slave_retried_transactions should not increase
--let $assert_status_name= slave_retried_transactions
--let $assert_status_value= $retried_trans
--source include/assert_status.inc

DELETE FROM db1.t1 WHERE c1 = 50;

--source include/start_slave_sql.inc
--source include/sync_slave_sql.inc

--echo #
--echo # Cleanup
--echo #
SET GLOBAL innodb_lock_wait_timeout = @saved_innodb_lock_wait_timeout;
SET GLOBAL slave_parallel_workers = @saved_slave_parallel_workers;
SET GLOBAL max_relay_log_size = @saved_max_relay_log_size;
SET GLOBAL slave_transaction_retries = @saved_slave_transaction_retries;

--source include/stop_slave_sql.inc
SET GLOBAL slave_parallel_type= @saved_slave_parallel_type;
--source include/start_slave_sql.inc

--source include/rpl_connection_master.inc
DROP DATABASE db1;
DROP DATABASE db2;
