#
# Row specific tests for master_last_event_time
#
--source include/have_binlog_format_row.inc
--source include/have_innodb.inc
--let $rpl_skip_start_slave=1
--let $rpl_topology=1->2->3
--source include/rpl_init.inc

--echo #
--echo # Initialize test data
--echo # Ensure that all slaves has master_last_event_time == NULL
--echo #

--connection server_1
SET STATEMENT sql_log_bin=0 FOR create table t1 (a int) engine=innodb;
--source include/save_master_pos.inc
--connection server_2
SET STATEMENT sql_log_bin=0 FOR create table t1 (a int) engine=innodb;
--connection server_3
SET STATEMENT sql_log_bin=0 FOR create table t1 (a int) engine=innodb;

--echo #
--echo # Test *_last_event_time is updated at proper place.
--echo # Master_last_event_time should be updated by the IO thread when reading
--echo # in a new transaction from the primary.
--echo # Slave_last_event_time should be updated by the SQL thread
--echo #   1) immediately upon seeing the first transaction if the replica is
--echo #      starting fresh, or
--echo #   2) after committing a transaction.
--echo #

--connection server_2
start slave io_thread;
--source include/sync_io_with_master.inc

# Ensure Master_last_event_time and Slave_last_event_time are not yet set
--let $master_time= `select Master_last_event_time from information_schema.slave_status`
--echo master_time: $master_time (should be empty)
--let $slave_time=`select Slave_last_event_time from information_schema.slave_status`
--echo slave_time: $slave_time (should be empty)

--echo # Sleep 2s to create a time gap between the header events (i.e.
--echo # Format_description and Gtid_list) and the transaction event to allow
--echo # proving that header events should not update
--echo # (Master|Slave)_last_event_time
--sleep 2

--connect (server_1_1,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
--let $t1_time_begin= `select truncate(@@timestamp,0)`
--replace_result $t1_time_begin TIMESTAMP
--eval set @@timestamp= $t1_time_begin
insert into t1 values (0);

--source include/save_master_pos.inc

--connection server_2
--source include/sync_io_with_master.inc

--let $mle_time= `select Master_last_event_time from information_schema.slave_status`
--let $mle_time_unix= `select truncate(unix_timestamp("$mle_time"),0)`
if (`SELECT ($mle_time_unix < $t1_time_begin)`)
{
  --echo # Expected timestamp (master binlog time): $t1_time_begin
  --echo # Reported Master_last_event_time: $mle_time_unix ($mle_time)
  --die Master_last_event_time did not correspond to time that the transaction was binlogged on primary
}

--let $slave_time= `select Slave_last_event_time from information_schema.slave_status`
if (`select strcmp("$slave_time", "") != 0`)
{
  --echo # Slave_last_event_time: $slave_time
  --die SQL thread was never started, Slave_last_event_time should be NULL
}

# Check that we also get the values from show all slaves status
--let $time_diff= query_get_value(SHOW ALL SLAVES STATUS, Master_Slave_time_diff, 1)
if (`select strcmp("$time_diff", "NULL") != 0`)
{
  --echo # Master_Slave_time_diff: $time_diff
  --die SQL thread was never started, Master_Slave_time_diff should be NULL
}

--echo # For the first event, at execution start, Slave_last_event_time should
--echo # be updated to be 1 second prior to the time that the first transaction
--echo # was binlogged on the primary. This is to represent that the slave is
--echo # otherwise up-to-date. Note the table is locked to prevent the
--echo # transaction from committing (and thereby progressing
--echo # Slave_last_event_time to represent commit-time).

--connect (server_2_2,127.0.0.1,root,,test,$SERVER_MYPORT_2,)
lock tables t1 write;

--connection server_2
start slave sql_thread;

--echo # Waiting for replica to start executing the transaction (yet get stuck on the table lock)
--let $wait_condition= SELECT count(*) FROM information_schema.processlist WHERE state LIKE 'Waiting for table metadata lock';
--source include/wait_condition.inc

--let $slave_time= query_get_value(SHOW ALL SLAVES STATUS, Slave_last_event_time, 1)
--let $slave_time_unix= `select truncate(unix_timestamp("$slave_time"),0)`
--let $expected_slave_time= `select ($t1_time_begin - 1)`
if ($slave_time_unix != $expected_slave_time)
{
  --echo # Master_last_event_time: $mle_time_unix ($mle_time)
  --echo # Slave_last_event_time: $slave_time_unix ($slave_time)
  --echo # Expected value: $expected_slave_time
  --die SQL thread has not yet committed its first transaction, Slave_last_event_time should be 1s before that transaction
}

# Master_Slave_time_diff isn't guaranteed to be 1 second, despite the
# hard-coded logic to subtract 1s from the first non-group event that comes in.
# This is because the Gtid and Xid events can be logged with different
# timestamps, and Slave_last_event_time is updated using the Gtid log event,
# and Master_last_event_time is updated using the Xid log event. So to ensure
# that Master_Slave_time_diff is updated correctly for the first transaction,
# it must also take into account the difference in timestamps of these events
--let $xid_gtid_time_diff=`SELECT $mle_time_unix - $t1_time_begin`
--let $time_diff= query_get_value(SHOW ALL SLAVES STATUS, Master_Slave_time_diff, 1)
if (`SELECT $time_diff != 1 + $xid_gtid_time_diff`)
{
  --echo # Master_Slave_time_diff: $time_diff
  --echo # Xid Gtid Timestamp Difference: $xid_gtid_time_diff
  --die SQL thread has not yet committed its first transaction, Master_Slave_time_diff should be updated to look up-to-date prior to this trx
}

--connection server_2_2
unlock tables;

--connection server_2
--source include/sync_with_master.inc
--source master_last_event_time.inc

--echo #
--echo # Test simple insert
--echo #

--connection server_1
insert into t1 values (1+sleep(3));
--source master_last_event_time.inc

--echo #
--echo # Test insert with forced time
--echo #

SET TIMESTAMP=unix_timestamp("2000-01-01");
insert into t1 values (2+sleep(3));
SET TIMESTAMP=DEFAULT;
--source master_last_event_time.inc

--echo #
--echo # Test multi-transaction
--echo #

begin;
insert into t1 values (3+sleep(3));
insert into t1 values (4+sleep(3));
commit;
--source master_last_event_time.inc

--echo # cleanup
--connection server_3
--source include/start_slave.inc
--connection server_1
drop table t1;
--source include/rpl_end.inc
--echo # End of master_last_event_time_row
