#ifndef RPL_TRX_TRACKING_INCLUDED
/* Copyright (c) 2018, 2021, Oracle and/or its affiliates.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License, version 2.0,
   as published by the Free Software Foundation.

   This program is also distributed with certain software (including
   but not limited to OpenSSL) that is licensed under separate terms,
   as designated in a particular file or component or in included license
   documentation.  The authors of MySQL hereby grant you an additional
   permission to link the program and your derivative works with the
   separately licensed software that they have included with MySQL.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License, version 2.0, for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */

#define RPL_TRX_TRACKING_INCLUDED

//#include <my_inttypes.h>
#include <map>

#include "my_dbug.h"
#include "sql_class.h"


/**
  Logical timestamp generator for logical timestamping binlog transactions.
  A transaction is associated with two sequence numbers see
  @c Transaction_ctx::last_committed and @c Transaction_ctx::sequence_number.
  The class provides necessary interfaces including that of
  generating a next consecutive value for the latter.
*/
class  Logical_clock
{
private:
  int64 state;
  /*
    Offset is subtracted from the actual "absolute time" value at
    logging a replication event. That is the event holds logical
    timestamps in the "relative" format. They are meaningful only in
    the context of the current binlog.
    The member is updated (incremented) per binary log rotation.
  */
  int64 offset;
public:
  Logical_clock();
  int64 step();
  int64 set_if_greater(int64 new_val);
  int64 get_timestamp();
  int64 get_offset() { return offset; }
  /*
    Updates the offset.
    This operation is invoked when binlog rotates and at that time
    there can't any concurrent step() callers so no need to guard
    the assignement.
  */
  void update_offset(int64 new_offset)
  {
    assert(offset <= new_offset);

    offset= new_offset;
  }
};

/**
  Generate logical timestamps for MTS using COMMIT_ORDER
  in the binlog-transaction-dependency-tracking option.
*/
class Commit_order_trx_dependency_tracker
{
public:
  /**
    Main function that gets the dependencies using the COMMIT_ORDER tracker.

    @param [in]     thd             THD of the caller.
    @param [in,out] sequence_number sequence_number initialized and returned.
    @param [in,out] commit_parent   commit_parent to be returned.
   */
  void get_dependency(THD *thd, int64 &sequence_number, int64 &commit_parent);

  void update_max_committed(int64 sequence_number);

  Logical_clock get_max_committed_transaction()
  {
    return m_max_committed_transaction;
  }

  int64 step();
  void rotate();

private:
  /* Committed transactions timestamp */
  Logical_clock m_max_committed_transaction;

  /* "Prepared" transactions timestamp */
  Logical_clock m_transaction_counter;
};

/**
  Generate logical timestamps for MTS using WRITESET
  in the binlog-transaction-dependency-tracking option.
*/
class Writeset_trx_dependency_tracker
{
public:
  Writeset_trx_dependency_tracker(uint64 max_history_size):
    m_opt_max_history_size(max_history_size),
    m_writeset_history_start(0)
  {}

  /**
    Main function that gets the dependencies using the WRITESET tracker.

    @param [in]     thd             THD of the caller.
    @param [in,out] sequence_number sequence_number initialized and returned.
    @param [in,out] commit_parent   commit_parent to be returned.
   */
  void get_dependency(THD *thd, int64 &sequence_number, int64 &commit_parent);

  void rotate(int64 start);

   /* option opt_binlog_transaction_dependency_history_size - atomic var */
  int64 m_opt_max_history_size;
  /* option opt_binlog_transaction_dependency_history_size */
  ulong m_opt_max_history_size_base_var;

  /**
    Returns the value for the max history size with an atomic read to the var
    @return the value of opt_max_history_size
  */
  ulong get_opt_max_history_size(){
    return static_cast<ulong>(my_atomic_load64(&m_opt_max_history_size));
  }

private:
  /*
    Monitor the last transaction with write-set to use as the minimal
    commit parent when logical clock source is WRITE_SET, i.e., the most recent
    transaction that is not in the history, or 0 when the history is empty.

    The m_writeset_history_start must to be set to 0 initially and whenever the
    binlog_transaction_dependency_tracking variable is changed or the history
    is cleared, so that it is updated to the first transaction for which the
    dependencies are checked.
  */
  int64 m_writeset_history_start;

  /*
    Track the last transaction sequence number that changed each row
    in the database, using row hashes from the writeset as the index.
  */
  typedef std::map<uint64,int64> Writeset_history;
  Writeset_history m_writeset_history;
};

/**
  Generate logical timestamps for MTS using WRITESET_SESSION
  in the binlog-transaction-dependency-tracking option.
*/
class Writeset_session_trx_dependency_tracker
{
public:
  /**
    Main function that gets the dependencies using the WRITESET_SESSION tracker.

    @param [in]     thd             THD of the caller.
    @param [in,out] sequence_number sequence_number initialized and returned.
    @param [in,out] commit_parent   commit_parent to be returned.
   */
  void get_dependency(THD *thd, int64 &sequence_number, int64 &commit_parent);
};

/**
  Modes for parallel transaction dependency tracking
*/
enum enum_binlog_transaction_dependency_tracking
{
  /**
    Tracks dependencies based on the commit order of transactions.
    The time intervals during which any transaction holds all its locks are
    tracked (the interval ends just before storage engine commit, when locks
    are released. For an autocommit transaction it begins just before storage
    engine prepare. For BEGIN..COMMIT transactions it begins at the end of the
    last statement before COMMIT). Two transactions are marked as
    non-conflicting if their respective intervals overlap. In other words,
    if trx1 appears before trx2 in the binlog, and trx2 had acquired all its
    locks before trx1 released its locks, then trx2 is marked such that the
    slave can schedule it in parallel with trx1.
  */
  DEPENDENCY_TRACKING_COMMIT_ORDER= 0,
  /**
    Tracks dependencies based on the set of rows updated. Any two transactions
    that change disjoint sets of rows, are said concurrent and non-contending.
  */
  DEPENDENCY_TRACKING_WRITESET= 1,
  /**
    Tracks dependencies based on the set of rows updated per session. Any two
    transactions that change disjoint sets of rows, on different sessions,
    are said concurrent and non-contending. Transactions from the same session
    are always said to be dependent, i.e., are never concurrent and
    non-contending.
  */
  DEPENDENCY_TRACKING_WRITESET_SESSION= 2
};

/**
  Dependency tracker is a container singleton that dispatches between the three
  methods associated with the binlog-transaction-dependency-tracking option.
  There is a singleton instance of each of these classes.
*/
class Transaction_dependency_tracker
{
public:
  Transaction_dependency_tracker():
    m_opt_tracking_mode(DEPENDENCY_TRACKING_COMMIT_ORDER), m_writeset(25000) {}

  void get_dependency(THD *thd, int64 &sequence_number, int64 &commit_parent);

  void tracking_mode_changed();

  void update_max_committed(THD *thd);
  int64 get_max_committed_timestamp();

  int64 step();
  void rotate();

public:
  /* option opt_binlog_transaction_dependency_tracking */
  int64 m_opt_tracking_mode;

  /* option opt_binlog_transaction_dependency_tracking associated with sysvar */
  long m_opt_tracking_mode_value;

  Writeset_trx_dependency_tracker *get_writeset()
  {
    return &m_writeset;
  }

private:
  Writeset_trx_dependency_tracker m_writeset;
  Commit_order_trx_dependency_tracker m_commit_order;
  Writeset_session_trx_dependency_tracker m_writeset_session;
};

#endif /* RPL_TRX_TRACKING_INCLUDED */
