Hello, Sergei!

In unversioned -> versioned scenario in the code below it first gets to Set time 4, creates some records (on slave) and then seconds on slave increased (X+1) while on master seconds are not yet increased (X). Then we get to Set time 3 and reset time on slave to X.0 therefore we get back in time and all stored records with timestamp X.n will be in future. 'n' came from ++system_time.sec_part in Set time 4.

Why did you decided to use such logic of getting seconds from master and microseconds from slave? Since microseconds sooner or later reset to 0 it's not better than just assigning some random number. What sending microseconds from master conditionally only is good for?

And for RBR since you don't send microseconds at all I see no good solution for this.

  inline void set_time(my_time_t t, ulong sec_part)
  {
    if (opt_secure_timestamp > (slave_thread ? SECTIME_REPL : SECTIME_SUPER))
    {
      set_time();                 // note that BINLOG itself requires SUPER
      DBUG_EXECUTE("time", print_start_time("Set time 1"););
    }
    else
    {
      if (sec_part <= TIME_MAX_SECOND_PART)
      {
        start_time= system_time.sec= t;
        start_time_sec_part= system_time.sec_part= sec_part;
        DBUG_EXECUTE("time", print_start_time("Set time 2"););
      }
      else if (t != system_time.sec)
      {
        DBUG_EXECUTE("time", print_system_time("System time"););;
        start_time= system_time.sec= t;
        start_time_sec_part= system_time.sec_part= 0;
        DBUG_EXECUTE("time", print_start_time("Set time 3"););
      }
      else
      {
        start_time= t;
        start_time_sec_part= ++system_time.sec_part;
        DBUG_EXECUTE("time", print_start_time("Set time 4"););
      }
      user_time.val= hrtime_from_time(start_time) + start_time_sec_part;
      PSI_CALL_set_thread_start_time(start_time);
      start_utime= utime_after_lock= microsecond_interval_timer();
    }
  }

--
All the best,

Aleksey Midenkov
@midenok