1114 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1114 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) 2022 OceanBase
 | 
						|
 * OceanBase CE is licensed under Mulan PubL v2.
 | 
						|
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 | 
						|
 * You may obtain a copy of Mulan PubL v2 at:
 | 
						|
 *          http://license.coscl.org.cn/MulanPubL-2.0
 | 
						|
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
						|
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
						|
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
						|
 * See the Mulan PubL v2 for more details.
 | 
						|
 */
 | 
						|
#define USING_LOG_PREFIX STORAGE
 | 
						|
#include "share/ob_rs_mgr.h"
 | 
						|
#include "share/ob_rpc_struct.h"
 | 
						|
#include "ob_storage_ha_utils.h"
 | 
						|
#include "logservice/ob_log_service.h"
 | 
						|
#include "storage/tx/ob_multi_data_source.h"
 | 
						|
#include "storage/tx_storage/ob_ls_service.h"
 | 
						|
#include "storage/tablet/ob_tablet.h"
 | 
						|
#include "share/transfer/ob_transfer_task_operator.h"
 | 
						|
#include "storage/high_availability/ob_finish_transfer.h"
 | 
						|
#include "storage/high_availability/ob_transfer_service.h"
 | 
						|
#include "storage/high_availability/ob_transfer_lock_utils.h"
 | 
						|
#include "storage/tablet/ob_tablet.h"
 | 
						|
#include "observer/ob_server_event_history_table_operator.h"
 | 
						|
 | 
						|
using namespace oceanbase::common;
 | 
						|
using namespace oceanbase::share;
 | 
						|
using namespace oceanbase::observer;
 | 
						|
using namespace oceanbase::transaction;
 | 
						|
 | 
						|
namespace oceanbase {
 | 
						|
namespace storage {
 | 
						|
 | 
						|
//errsim def
 | 
						|
ERRSIM_POINT_DEF(EN_DOING_UNLOCK_TRANSFER_MEMBER_LIST_FAILED);
 | 
						|
ERRSIM_POINT_DEF(EN_DOING_LOCK_TRANSFER_TASK_FAILED);
 | 
						|
ERRSIM_POINT_DEF(EN_DOING_LOCK_MEMBER_LIST_FAILED);
 | 
						|
ERRSIM_POINT_DEF(EN_FINISH_TRANSFER_IN_FAILED);
 | 
						|
ERRSIM_POINT_DEF(EN_DOING_WAIT_ALL_DEST_TABLET_NORAML);
 | 
						|
ERRSIM_POINT_DEF(EN_FINISH_TRANSFER_OUT_FAILED);
 | 
						|
ERRSIM_POINT_DEF(EN_DOING_UPDATE_TRANSFER_TASK_FAILED);
 | 
						|
ERRSIM_POINT_DEF(EN_DOING_COMMIT_TRANS_FAILED);
 | 
						|
 | 
						|
ObTxFinishTransfer::ObTxFinishTransfer()
 | 
						|
    : is_inited_(false),
 | 
						|
      task_id_(),
 | 
						|
      tenant_id_(OB_INVALID_ID),
 | 
						|
      src_ls_id_(),
 | 
						|
      dest_ls_id_(),
 | 
						|
      mutex_(),
 | 
						|
      cond_(),
 | 
						|
      sql_proxy_(NULL)
 | 
						|
{}
 | 
						|
 | 
						|
ObTxFinishTransfer::~ObTxFinishTransfer()
 | 
						|
{}
 | 
						|
 | 
						|
int ObTxFinishTransfer::init(const ObTransferTaskID &task_id, const uint64_t tenant_id, const share::ObLSID &src_ls_id,
 | 
						|
    const share::ObLSID &dest_ls_id, common::ObMySQLProxy &sql_proxy)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (IS_INIT) {
 | 
						|
    ret = OB_INIT_TWICE;
 | 
						|
    LOG_WARN("finish transfer do not init", K(ret));
 | 
						|
  } else if (!task_id.is_valid() || OB_INVALID_ID == tenant_id || !src_ls_id.is_valid() || !dest_ls_id.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(task_id), K(tenant_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else if (OB_FAIL(cond_.init(ObWaitEventIds::STORAGE_HA_FINISH_TRANSFER))) {
 | 
						|
    LOG_WARN("failed to init condition", K(ret));
 | 
						|
  } else {
 | 
						|
    task_id_ = task_id;
 | 
						|
    tenant_id_ = tenant_id;
 | 
						|
    src_ls_id_ = src_ls_id;
 | 
						|
    dest_ls_id_ = dest_ls_id;
 | 
						|
    sql_proxy_ = &sql_proxy;
 | 
						|
    is_inited_ = true;
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::process(int64_t &round)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (IS_NOT_INIT) {
 | 
						|
    ret = OB_NOT_INIT;
 | 
						|
    LOG_WARN("tx finish transfer do not init", K(ret));
 | 
						|
  } else if (OB_FAIL(do_tx_transfer_doing_(task_id_, tenant_id_, src_ls_id_, dest_ls_id_, round))) {
 | 
						|
    LOG_WARN("failed to do tx transfer doing", K(ret), K_(task_id), K_(tenant_id), K_(src_ls_id), K_(dest_ls_id), K(round));
 | 
						|
  } else {
 | 
						|
    LOG_INFO("process tx finish transfer", K_(task_id), K_(tenant_id), K_(src_ls_id), K_(dest_ls_id));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::do_tx_transfer_doing_(const ObTransferTaskID &task_id, const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &src_ls_id, const share::ObLSID &dest_ls_id, int64_t &round)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  int tmp_ret = OB_SUCCESS;
 | 
						|
  ObMySQLTransaction trans;
 | 
						|
  ObArray<ObTransferTabletInfo> tablet_list;
 | 
						|
  ObMemberList member_list;
 | 
						|
  int64_t quorum = 0;
 | 
						|
  observer::ObInnerSQLConnection *conn = NULL;
 | 
						|
  SCN start_scn;
 | 
						|
  SCN finish_scn;
 | 
						|
  bool is_majority_passed = false;
 | 
						|
  share::ObRsMgr *rs_mgr = GCTX.rs_mgr_;
 | 
						|
  obrpc::ObSrvRpcProxy *svr_rpc_proxy = GCTX.srv_rpc_proxy_;
 | 
						|
  int64_t result = OB_SUCCESS;
 | 
						|
  common::ObArray<common::ObAddr> member_addr_list;
 | 
						|
  bool majority_backfilled = false;
 | 
						|
  ObLSLocation ls_location;
 | 
						|
  bool is_leader = false;
 | 
						|
  bool is_ready = false;
 | 
						|
  ObTransferService *transfer_service = NULL;
 | 
						|
  const int64_t CONFIG_CHANGE_TIMEOUT = 10 * 1000 * 1000L;
 | 
						|
  ObLSHandle ls_handle;
 | 
						|
  ObDisplayTabletList table_lock_tablet_list;
 | 
						|
  transaction::tablelock::ObTableLockOwnerID lock_owner_id;
 | 
						|
  ObTimeoutCtx timeout_ctx;
 | 
						|
  const int64_t tmp_round = round;
 | 
						|
  if (!task_id.is_valid() || OB_INVALID_ID == tenant_id || !src_ls_id.is_valid() || !dest_ls_id.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(task_id), K(tenant_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else if (OB_ISNULL(transfer_service = MTL_WITH_CHECK_TENANT(ObTransferService *, tenant_id))) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("transfer service is NULL", K(ret), K(tenant_id));
 | 
						|
  } else if (OB_FAIL(check_self_ls_leader_(dest_ls_id, is_leader))) {
 | 
						|
    LOG_WARN("failed to check self ls leader", K(ret), K(dest_ls_id));
 | 
						|
  } else if (!is_leader) {
 | 
						|
    ret = OB_IS_CHANGING_LEADER;
 | 
						|
    LOG_WARN("self is not leader of dest ls", K(ret), K(dest_ls_id));
 | 
						|
  } else if (OB_FAIL(get_ls_member_list_(tenant_id, dest_ls_id, member_list))) {
 | 
						|
    LOG_WARN("failed to get ls member list", K(ret), K(tenant_id), K(dest_ls_id));
 | 
						|
  }  // TODO(yangyi.yyy): get member list and check self is leader together
 | 
						|
  // 1. Release the locking relationship of the member list of dest_ls and src_ls
 | 
						|
  // (the interface for binding and releasing the locking relationship needs to have the ability to re-entrant)
 | 
						|
  else if (OB_FAIL(unlock_src_and_dest_ls_member_list_(tenant_id, src_ls_id, dest_ls_id, member_list))) {
 | 
						|
    LOG_WARN("failed to unlock src and dest ls member list", K(ret), K(tenant_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
  }
 | 
						|
  // get tablet info list from inner table
 | 
						|
  else if (OB_FAIL(get_transfer_tablet_info_from_inner_table_(
 | 
						|
               task_id, tenant_id, tablet_list, start_scn, table_lock_tablet_list, lock_owner_id))) {
 | 
						|
    LOG_WARN("failed to get transfer tablet list", K(ret), K(task_id), K(tenant_id));
 | 
						|
  } else if (OB_FAIL(get_transfer_quorum_(member_list, quorum))) {
 | 
						|
    LOG_WARN("failed to get transfer quorum", K(ret), K(member_list));
 | 
						|
  }
 | 
						|
  // precheck if majority ls logical table replaced
 | 
						|
  else if (OB_FAIL(check_ls_logical_table_replaced(
 | 
						|
               tenant_id, dest_ls_id, member_list, tablet_list, quorum, is_ready))) {
 | 
						|
    LOG_WARN("wait all ls replica logical table replaced failed",
 | 
						|
        K(ret),
 | 
						|
        K(tenant_id),
 | 
						|
        K(dest_ls_id),
 | 
						|
        K(member_list),
 | 
						|
        K(quorum));
 | 
						|
  } else if (!is_ready) {
 | 
						|
    LOG_INFO("transfer in tablet not ready", K(ret), K(tenant_id), K(dest_ls_id));
 | 
						|
    transfer_service->wakeup();
 | 
						|
  } else if (OB_FAIL(get_ls_handle_(tenant_id, dest_ls_id_, ls_handle))) {
 | 
						|
    LOG_WARN("failed to get ls handle", K(ret));
 | 
						|
  } else {
 | 
						|
    const ObTransferLockStatus lock_status(ObTransferLockStatus::DOING);
 | 
						|
#ifdef ERRSIM
 | 
						|
    SERVER_EVENT_SYNC_ADD("TRANSFER", "BEFORE_TRANSFER_DOING_START_TRANS");
 | 
						|
#endif
 | 
						|
    DEBUG_SYNC(SWITCH_LEADER_BEFORE_TRANSFER_DOING_START_TRANS);
 | 
						|
    ObTimeoutCtx timeout_ctx;
 | 
						|
    // 2. The leader of dest_ls starts a transaction TRANS_TRANSFER_FINISH
 | 
						|
    if (FAILEDx(start_trans_(tenant_id, trans, timeout_ctx))) {
 | 
						|
      LOG_WARN("failed to start trans", K(ret), K(tenant_id));
 | 
						|
    } else {
 | 
						|
#ifdef ERRSIM
 | 
						|
    SERVER_EVENT_SYNC_ADD("TRANSFER", "AFTER_TRANSFER_DOING_START_TRANS");
 | 
						|
#endif
 | 
						|
      DEBUG_SYNC(SWITCH_LEADER_AFTER_TRANSFER_DOING_START_TRANS);
 | 
						|
      if (FAILEDx(select_transfer_task_for_update_(task_id, trans))) {
 | 
						|
        LOG_WARN("failed to select for update", K(ret), K(task_id));
 | 
						|
      }
 | 
						|
      // 3. The dest_ls leader checks whether the transfer tablet corresponding to the src_ls copy corresponding to the
 | 
						|
      // majority replica has been backfilled. Here, try to ensure that it is all completed, and then start the second
 | 
						|
      // step. Only if the threshold is exceeded will there be a majority.
 | 
						|
      //    a) This step needs to first lock the member list of dest_ls
 | 
						|
      //    b) The dest_ls leader checks whether the majority has completed backfilling
 | 
						|
      //    c) Unlock the member list of dest_ls This step and the migration still need to be mutually exclusive,
 | 
						|
      //       because there may be dest_leader checking that the majority meets
 | 
						|
      //       the conditions. After unlocking the member list, a copy is migrated in. This copy replaces one of the
 | 
						|
      //       majority, resulting in a majority If the dispatch does not meet the conditions, the following chapters on
 | 
						|
      //       transfer and migration will discuss
 | 
						|
      else if (OB_FAIL(lock_ls_member_list_(tenant_id, dest_ls_id, member_list, lock_status))) {
 | 
						|
        LOG_WARN("failed to lock ls member list", K(ret), K(tenant_id), K(dest_ls_id), K(member_list));
 | 
						|
      } else if (OB_FAIL(check_ls_logical_table_replaced(
 | 
						|
                    tenant_id, dest_ls_id, member_list, tablet_list, quorum, is_ready))) {
 | 
						|
        LOG_WARN("wait all ls replica logical table replaced failed",
 | 
						|
            K(ret),
 | 
						|
            K(tenant_id),
 | 
						|
            K(dest_ls_id),
 | 
						|
            K(member_list),
 | 
						|
            K(quorum));
 | 
						|
      } else if (!is_ready) {
 | 
						|
        LOG_INFO("transfer in tablet not ready", K(ret), K(tenant_id), K(dest_ls_id));
 | 
						|
        transfer_service->wakeup();
 | 
						|
      } else {
 | 
						|
        // 4. The leader node of dest_ls registers the multi-source transaction
 | 
						|
        // ObInnerSQLConnection->register_multi_source_data, and the type is TX_FINISH_TRANSFER_IN (two-way barrier)
 | 
						|
        //    The content of the log is src_ls_id, dest_ls_id, tablet_list. This step requires forcibly flushing the redo
 | 
						|
        //    log. The purpose of flushing redo here is to obtain finish_scn and check whether the dest_ls log playback is
 | 
						|
        //    new enough for src_ls.
 | 
						|
        //      a) When the leader of dest_ls receives the resgister_succ, it checks that its replay scn is greater than
 | 
						|
        //      start_scn, and has completed the replacement of the logical table.
 | 
						|
        //      b) When the leader and follower of dest_ls receive the on_redo stage,
 | 
						|
        //         they record the scn returned by on_redo as finish_scn. Check that your replay scn is greater than
 | 
						|
        //         start_scn, and the replacement of the logical table has been completed. If it has been completed,
 | 
						|
        //         change the ObTabletStatus of transfer_tablets to NORMAL. If it is not completed, the playback of
 | 
						|
        //         ob_redo needs to be stuck.
 | 
						|
        if (OB_ISNULL(conn = dynamic_cast<observer::ObInnerSQLConnection *>(trans.get_connection()))) {
 | 
						|
          ret = OB_ERR_UNEXPECTED;
 | 
						|
          LOG_WARN("conn_ is NULL", K(ret));
 | 
						|
        } else if (OB_FAIL(do_tx_finish_transfer_in_(
 | 
						|
                      task_id, tenant_id, src_ls_id, dest_ls_id, start_scn, tablet_list, conn))) {
 | 
						|
          LOG_WARN("failed to do tx finish transfer in",
 | 
						|
              K(ret),
 | 
						|
              K(task_id),
 | 
						|
              K(tenant_id),
 | 
						|
              K(src_ls_id),
 | 
						|
              K(dest_ls_id),
 | 
						|
              K(tablet_list));
 | 
						|
        }
 | 
						|
  #ifdef ERRSIM
 | 
						|
        SERVER_EVENT_SYNC_ADD("TRANSFER", "BETWEEN_REGISTER_FINISH_TRANSFER_IN_AND_OUT");
 | 
						|
  #endif
 | 
						|
        DEBUG_SYNC(SWITCH_LEADER_BETWEEN_FINISH_TRANSFER_IN_AND_OUT);
 | 
						|
        // 5. The leader of dest ls checks that majority ls replia satisfies the finish_scn of the playback
 | 
						|
        // (the step of non-multi-source transaction, only the leader of dest ls does).
 | 
						|
        // Abort is required if no majority replays to finish_scn beyond the threshold.
 | 
						|
        // Here, the leader of dest_ls checks the destination of the majority to be more secure, and if the
 | 
						|
        // FINISH_TRANSFER step fails, it will not affect the reading and writing of dest_ls, but the main switch will
 | 
						|
        // have an impact, so here we will also guarantee all within a certain threshold. Copy, after a certain threshold
 | 
						|
        // is exceeded, the majority is guaranteed; here, the transfer seq at the log stream level also needs to be
 | 
						|
        // incremented (in TX_START_TRASNFER_IN)
 | 
						|
        if (FAILEDx(wait_transfer_tablet_status_normal_(tenant_id, dest_ls_id, tablet_list, start_scn, timeout_ctx, finish_scn))) {
 | 
						|
          LOG_WARN("failed to wait tablet status normal", K(ret), K(tenant_id), K(dest_ls_id), K(tablet_list));
 | 
						|
        } else if (OB_FAIL(member_list.get_addr_array(member_addr_list))) {
 | 
						|
          LOG_WARN("failed to get addr array", K(ret), K(member_list));
 | 
						|
        } else if (OB_FAIL(wait_all_ls_replica_replay_scn_(task_id, tenant_id, dest_ls_id,
 | 
						|
            member_addr_list, finish_scn, quorum, timeout_ctx, is_majority_passed))) {
 | 
						|
          LOG_WARN("failed to check ls replica replay scn",
 | 
						|
              K(ret),
 | 
						|
              K(tenant_id),
 | 
						|
              K(dest_ls_id),
 | 
						|
              K(member_addr_list),
 | 
						|
              K(finish_scn));
 | 
						|
        } else if (!is_majority_passed) {
 | 
						|
          ret = OB_TIMEOUT;
 | 
						|
          LOG_WARN("majority replay scn not passed", K(ret));
 | 
						|
        }
 | 
						|
        // 6. The leader of dest_ls registers a multi-source transaction,
 | 
						|
        // and the type is TX_FINISH_TRANSFER_OUT. The contents of the log are src_ls_id, dest_ls_id, finish_scn, and
 | 
						|
        // tablet_id_list.
 | 
						|
        //    a) When the leader and follower of src_ls receive on_redo, change the ObTabletStatus of transfer_tablets to
 | 
						|
        //    TRANFER_OUT_DELETED (the status may share DELETED)
 | 
						|
        if (FAILEDx(
 | 
						|
                do_tx_finish_transfer_out_(task_id, tenant_id, src_ls_id, dest_ls_id, finish_scn, tablet_list, conn))) {
 | 
						|
          LOG_WARN("failed to do tx finish transfer out",
 | 
						|
              K(ret),
 | 
						|
              K(task_id),
 | 
						|
              K(tenant_id),
 | 
						|
              K(src_ls_id),
 | 
						|
              K(dest_ls_id),
 | 
						|
              K(finish_scn));
 | 
						|
        }
 | 
						|
        // 7. Update __all_transfer_task according to the result of transaction execution.
 | 
						|
        // If successful, push __all_transfer_task to FINISH state,
 | 
						|
        // otherwise keep the status of __all_transfer_task as DOING
 | 
						|
        else if (OB_FAIL(update_transfer_task_result_(task_id, tenant_id, finish_scn, OB_SUCCESS, trans))) {
 | 
						|
          LOG_WARN("failed to update transfer status", K(ret), K(task_id), K(tenant_id), K(finish_scn));
 | 
						|
        }
 | 
						|
        // 8. unlock table lock on src ls for tablet (must be successful)
 | 
						|
        else if (OB_FAIL(ObTransferLockUtil::unlock_tablet_on_src_ls_for_table_lock(
 | 
						|
                      trans, tenant_id, src_ls_id, lock_owner_id, table_lock_tablet_list))) {
 | 
						|
          LOG_WARN("failed to unlock tablet on src ls for table lock", KR(ret),
 | 
						|
              K(tenant_id), K(src_ls_id), K(lock_owner_id), K(table_lock_tablet_list));
 | 
						|
        }
 | 
						|
        // 9. unlock member list
 | 
						|
        else if (OB_FAIL(
 | 
						|
                    unlock_ls_member_list_(tenant_id, dest_ls_id, member_list, lock_status, CONFIG_CHANGE_TIMEOUT))) {
 | 
						|
          LOG_WARN("failed to unlock ls member list", K(ret), K(tenant_id), K(dest_ls_id), K(member_list));
 | 
						|
        }
 | 
						|
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_DOING_COMMIT_TRANS_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_DOING_COMMIT_TRANS_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
        DEBUG_SYNC(BEFORE_DOING_TRANSFER_COMMIT);
 | 
						|
        // 10. LOG_COMMIT_FINISH
 | 
						|
        bool is_commit = OB_SUCCESS == ret;
 | 
						|
        if (OB_TMP_FAIL(commit_trans_(is_commit, trans))) {
 | 
						|
          if (OB_SUCCESS == ret) {
 | 
						|
            ret = tmp_ret;
 | 
						|
          }
 | 
						|
        } else if (is_commit) {
 | 
						|
          round = 0;
 | 
						|
        }
 | 
						|
        // 11. After the dest_ls leader succeeds,
 | 
						|
        // it will report the corresponding results to RS.
 | 
						|
        // This step does not guarantee success.
 | 
						|
        if (OB_TMP_FAIL(report_result_(task_id, result, svr_rpc_proxy))) {
 | 
						|
          LOG_WARN("failed to report rpc result", K(ret), K(task_id), KP(rs_mgr), KP(svr_rpc_proxy));
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (OB_TMP_FAIL(record_server_event_(ret, is_ready, tmp_round, start_scn))) {
 | 
						|
    LOG_WARN("failed to record server event", K(tmp_ret), K(ret), K(is_ready));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::unlock_src_and_dest_ls_member_list_(const uint64_t tenant_id, const share::ObLSID &src_ls_id,
 | 
						|
    const share::ObLSID &dest_ls_id, common::ObMemberList &member_list)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  bool is_same = false;
 | 
						|
  const int64_t CONFIG_CHANGE_TIMEOUT = 10 * 1000 * 1000L;
 | 
						|
  const int64_t lock_timeout = CONFIG_CHANGE_TIMEOUT;
 | 
						|
  bool same_member_list = true;
 | 
						|
  const ObTransferLockStatus status(ObTransferLockStatus::START);
 | 
						|
  if (!src_ls_id.is_valid() || !dest_ls_id.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else if (OB_FAIL(unlock_ls_member_list_(tenant_id, src_ls_id, member_list, status, lock_timeout))) {
 | 
						|
    LOG_WARN("failed to unlock ls member list", K(ret), K(tenant_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else if (OB_FAIL(unlock_ls_member_list_(tenant_id, dest_ls_id, member_list, status, lock_timeout))) {
 | 
						|
    LOG_WARN("failed to unlock ls member list", K(ret), K(tenant_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else {
 | 
						|
    LOG_INFO(
 | 
						|
        "[TRANSFER] unlock src and dest ls member list", K(tenant_id), K(src_ls_id), K(dest_ls_id), K(member_list));
 | 
						|
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_DOING_UNLOCK_TRANSFER_MEMBER_LIST_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_DOING_UNLOCK_TRANSFER_MEMBER_LIST_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  }
 | 
						|
  UNUSEDx(member_list);
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::get_transfer_tablet_info_from_inner_table_(
 | 
						|
    const ObTransferTaskID &task_id,
 | 
						|
    const uint64_t tenant_id,
 | 
						|
    common::ObArray<ObTransferTabletInfo> &tablet_list,
 | 
						|
    SCN &start_scn,
 | 
						|
    ObDisplayTabletList &table_lock_tablet_list,
 | 
						|
    transaction::tablelock::ObTableLockOwnerID &lock_owner_id)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  tablet_list.reset();
 | 
						|
  table_lock_tablet_list.reset();
 | 
						|
  lock_owner_id.reset();
 | 
						|
  const bool for_update = false;
 | 
						|
  ObTransferTask transfer_task;
 | 
						|
  if (!task_id.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(task_id));
 | 
						|
  } else if (OB_FAIL(ObTransferTaskOperator::get(*sql_proxy_, tenant_id, task_id, for_update, transfer_task, share::OBCG_STORAGE))) {
 | 
						|
    LOG_WARN("failed to get transfer task", K(ret), K(tenant_id), K(task_id));
 | 
						|
  } else if (OB_FAIL(tablet_list.assign(transfer_task.get_tablet_list()))) {
 | 
						|
    LOG_WARN("failed to assign tablet_list", KR(ret), K(transfer_task));
 | 
						|
  } else if (OB_FAIL(table_lock_tablet_list.assign(transfer_task.get_table_lock_tablet_list()))) {
 | 
						|
    LOG_WARN("failed to assign table_lock_tablet_list", KR(ret), K(transfer_task));
 | 
						|
  } else {
 | 
						|
    start_scn = transfer_task.get_start_scn();
 | 
						|
    lock_owner_id = transfer_task.get_table_lock_owner_id();
 | 
						|
    LOG_INFO("get transfer info from inner table", K(task_id), K(tenant_id),
 | 
						|
        K(transfer_task), K(tablet_list), K(table_lock_tablet_list), K(lock_owner_id));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::wait_transfer_tablet_status_normal_(
 | 
						|
    const uint64_t tenant_id, const share::ObLSID &ls_id,
 | 
						|
    const common::ObArray<share::ObTransferTabletInfo> &tablet_list,
 | 
						|
    const share::SCN &start_scn,
 | 
						|
    ObTimeoutCtx &timeout_ctx,
 | 
						|
    share::SCN &finish_scn)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  int64_t begin_us = ObTimeUtility::current_time();
 | 
						|
  const int64_t CHECK_TABLET_STATUS_INTERVAL = 100_ms;
 | 
						|
  if (OB_INVALID_ID == tenant_id || !ls_id.is_valid() || tablet_list.empty() || !start_scn.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("wait transfer tablet status normal get invalid argument", K(ret), K(tenant_id), K(ls_id), K(tablet_list), K(start_scn));
 | 
						|
  } else {
 | 
						|
    while (OB_SUCC(ret)) {
 | 
						|
      bool is_ready = false;
 | 
						|
      if (timeout_ctx.is_timeouted()) {
 | 
						|
        ret = OB_TIMEOUT;
 | 
						|
        LOG_WARN("already timeout", K(ret), K(tenant_id), K(ls_id));
 | 
						|
      } else if (OB_FAIL(check_transfer_tablet_status_normal_(tenant_id, ls_id, tablet_list, start_scn, is_ready, finish_scn))) {
 | 
						|
        LOG_WARN("failed to check transfer tablet status normal", K(ret), K(tenant_id), K(tablet_list));
 | 
						|
      } else if (is_ready) {
 | 
						|
        LOG_INFO("tablet is ready", K(tenant_id), K(ls_id), K(tablet_list));
 | 
						|
        break;
 | 
						|
      } else {
 | 
						|
        ob_usleep(CHECK_TABLET_STATUS_INTERVAL);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::check_transfer_tablet_status_normal_(
 | 
						|
    const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &ls_id,
 | 
						|
    const common::ObArray<share::ObTransferTabletInfo> &tablet_list,
 | 
						|
    const share::SCN &start_scn,
 | 
						|
    bool &is_ready,
 | 
						|
    share::SCN &finish_scn)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  is_ready = true;
 | 
						|
  storage::ObLS *ls = NULL;
 | 
						|
  storage::ObLSHandle ls_handle;
 | 
						|
  finish_scn.reset();
 | 
						|
 | 
						|
  if (OB_FAIL(get_ls_handle_(tenant_id, ls_id, ls_handle))) {
 | 
						|
    LOG_WARN("failed to get ls handle", K(ret), K(tenant_id), K(ls_id));
 | 
						|
  } else if (OB_ISNULL(ls = ls_handle.get_ls())) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("log stream not exist", K(ret), K(ls_id));
 | 
						|
  } else {
 | 
						|
    for (int64_t i = 0; OB_SUCC(ret) && i < tablet_list.count(); ++i) {
 | 
						|
      ObTabletHandle tablet_handle;
 | 
						|
      ObTablet *tablet = NULL;
 | 
						|
      ObTabletCreateDeleteMdsUserData user_data;
 | 
						|
      const ObTransferTabletInfo &tablet_info = tablet_list.at(i);
 | 
						|
      const common::ObTabletID &tablet_id = tablet_info.tablet_id_;
 | 
						|
      if (OB_FAIL(ls->get_tablet(tablet_id, tablet_handle, 0, ObMDSGetTabletMode::READ_WITHOUT_CHECK))) {
 | 
						|
        LOG_WARN("failed to get tablet", K(ret), K(tablet_id));
 | 
						|
      } else if (OB_FAIL(ObTXTransferUtils::get_tablet_status(false/*get_commit*/, tablet_handle, user_data))) {
 | 
						|
        LOG_WARN("failed to get tablet status", K(ret), K(tablet_id), K(tablet_handle));
 | 
						|
      } else if (ObTabletStatus::NORMAL != user_data.tablet_status_) {
 | 
						|
        if (ObTabletStatus::TRANSFER_IN != user_data.tablet_status_) {
 | 
						|
          ret = OB_ERR_UNEXPECTED;
 | 
						|
          LOG_WARN("tablet status is not expected", K(ret), K(tablet_info), K(user_data));
 | 
						|
        } else {
 | 
						|
          is_ready = false;
 | 
						|
          LOG_INFO("tablet is not ready, need retry", K(tablet_info));
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      } else if (user_data.transfer_scn_ < start_scn) {
 | 
						|
        ret = OB_ERR_UNEXPECTED;
 | 
						|
        LOG_WARN("finish scn should not smaller than start scn", K(ret), K(tablet_id), K(start_scn), K(user_data));
 | 
						|
      } else if (user_data.transfer_scn_ > start_scn) {
 | 
						|
        if (0 == i) {
 | 
						|
          finish_scn = user_data.transfer_scn_;
 | 
						|
        } else if (finish_scn != user_data.transfer_scn_) {
 | 
						|
          ret = OB_ERR_UNEXPECTED;
 | 
						|
          LOG_WARN("tablet finish scn is not same, unexpected", K(ret), K(tablet_id),
 | 
						|
              K(start_scn), K(finish_scn), K(user_data));
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        is_ready = false;
 | 
						|
        LOG_INFO("tablet is not ready, need retry", K(tablet_info));
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::check_ls_logical_table_replaced(const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &dest_ls_id, const common::ObMemberList &member_list,
 | 
						|
    const common::ObArray<ObTransferTabletInfo> &tablet_list, const int64_t quorum, bool &all_backfilled)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  int tmp_ret = OB_SUCCESS;
 | 
						|
  bool is_leader = false;
 | 
						|
  const int64_t cluster_id = GCONF.cluster_id;
 | 
						|
  ObLSLocation ls_location;
 | 
						|
  ObArray<ObAddr> addr_array;
 | 
						|
  if (OB_FAIL(check_self_ls_leader_(dest_ls_id, is_leader))) {
 | 
						|
    LOG_WARN("failed to check self ls leader", K(ret), K(dest_ls_id));
 | 
						|
  } else if (!is_leader) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("self is not leader", K(ret), K(dest_ls_id));
 | 
						|
  } else if (OB_FAIL(member_list.get_addr_array(addr_array))) {
 | 
						|
    LOG_WARN("failed to get addr array", K(ret), K(member_list));
 | 
						|
  } else if (OB_FAIL(inner_check_ls_logical_table_replaced_(
 | 
						|
                 tenant_id, dest_ls_id, addr_array, tablet_list, quorum, all_backfilled))) {
 | 
						|
    LOG_WARN("failed to inner check majority backfilled", K(ret), K(tenant_id), K(dest_ls_id), K(addr_array));
 | 
						|
  } else {
 | 
						|
    LOG_INFO("check ls logical table replace", K(tenant_id), K(dest_ls_id), K(addr_array), K(tablet_list), K(quorum), K(all_backfilled));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::inner_check_ls_logical_table_replaced_(const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &dest_ls_id, const common::ObArray<common::ObAddr> &member_addr_list,
 | 
						|
    const common::ObArray<ObTransferTabletInfo> &tablet_list, const int64_t quorum, bool &all_backfilled)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  all_backfilled = true;
 | 
						|
  storage::ObCheckTransferTabletBackfillProxy batch_proxy(
 | 
						|
      *(GCTX.storage_rpc_proxy_), &obrpc::ObStorageRpcProxy::check_transfer_tablet_backfill_completed);
 | 
						|
  FOREACH_X(location, member_addr_list, OB_SUCC(ret))
 | 
						|
  {
 | 
						|
    if (OB_ISNULL(location)) {
 | 
						|
      ret = OB_ERR_UNEXPECTED;
 | 
						|
      LOG_WARN("location should not be null", K(ret));
 | 
						|
    } else {
 | 
						|
      const common::ObAddr &server = *location;
 | 
						|
      const int64_t timeout = GCONF.rpc_timeout;
 | 
						|
      const int64_t cluster_id = GCONF.cluster_id;
 | 
						|
      const uint64_t group_id = share::OBCG_STORAGE;
 | 
						|
      ObCheckTransferTabletBackfillArg arg;
 | 
						|
      arg.tenant_id_ = tenant_id;
 | 
						|
      arg.ls_id_ = dest_ls_id;
 | 
						|
      if (OB_FAIL(arg.tablet_list_.assign(tablet_list))) {
 | 
						|
        LOG_WARN("failed to assign tablet array", K(ret), K(tablet_list));
 | 
						|
      } else if (OB_FAIL(batch_proxy.call(server,
 | 
						|
                                          timeout,
 | 
						|
                                          cluster_id,
 | 
						|
                                          tenant_id,
 | 
						|
                                          group_id,
 | 
						|
                                          arg))) {
 | 
						|
        LOG_WARN("failed to send check transfer tablet backfill request", K(ret), K(server), K(tenant_id));
 | 
						|
      } else {
 | 
						|
        LOG_INFO("check_transfer_tablet_backfill_completed", K(arg), K(server));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  ObArray<int> return_code_array;
 | 
						|
  int tmp_ret = OB_SUCCESS;
 | 
						|
  if (OB_TMP_FAIL(batch_proxy.wait_all(return_code_array))) {
 | 
						|
    LOG_WARN("fail to wait all batch result", KR(ret), KR(tmp_ret));
 | 
						|
    ret = OB_SUCC(ret) ? tmp_ret : ret;
 | 
						|
  }
 | 
						|
  if (OB_FAIL(ret)) {
 | 
						|
  } else if (return_code_array.count() != member_addr_list.count()
 | 
						|
      || return_code_array.count() != batch_proxy.get_results().count()) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("cnt not match", K(ret),
 | 
						|
             "return_cnt", return_code_array.count(),
 | 
						|
             "result_cnt", batch_proxy.get_results().count(),
 | 
						|
             "server_cnt", member_addr_list.count());
 | 
						|
  } else {
 | 
						|
    ARRAY_FOREACH_X(batch_proxy.get_results(), idx, cnt, OB_SUCC(ret)) {
 | 
						|
      const ObCheckTransferTabletBackfillRes *response = batch_proxy.get_results().at(idx);
 | 
						|
      const int res_ret = return_code_array.at(idx);
 | 
						|
      if (OB_SUCCESS != res_ret) {
 | 
						|
        ret = res_ret;
 | 
						|
        LOG_WARN("rpc execute failed", KR(ret), K(idx));
 | 
						|
      } else if (OB_ISNULL(response)) {
 | 
						|
        ret = OB_ERR_UNEXPECTED;
 | 
						|
        LOG_WARN("response is null", K(ret));
 | 
						|
      } else if (!response->backfill_finished_) {
 | 
						|
        all_backfilled = false;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::do_tx_finish_transfer_in_(const ObTransferTaskID &task_id, const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &src_ls_id, const share::ObLSID &dest_ls_id, const SCN &start_scn,
 | 
						|
    const common::ObArray<ObTransferTabletInfo> &tablet_list, observer::ObInnerSQLConnection *conn)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  bool force_flush_redo = true;
 | 
						|
  bool is_leader = false;
 | 
						|
  char *buf = NULL;
 | 
						|
  int64_t buf_len = 0;
 | 
						|
  const transaction::ObTxDataSourceType type = transaction::ObTxDataSourceType::FINISH_TRANSFER_IN;
 | 
						|
  ObTXFinishTransferInInfo finish_transfer_in_info;
 | 
						|
  ObArenaAllocator allocator;
 | 
						|
  ObRegisterMdsFlag flag;
 | 
						|
  flag.need_flush_redo_instantly_ = true;
 | 
						|
  if (OB_ISNULL(conn)) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("conn should not be null", K(ret));
 | 
						|
  } else if (OB_FAIL(build_tx_finish_transfer_in_info_(
 | 
						|
                 src_ls_id, dest_ls_id, start_scn, tablet_list, finish_transfer_in_info))) {
 | 
						|
    LOG_WARN("failed to build tx finish transfer in info",
 | 
						|
        K(ret),
 | 
						|
        K(src_ls_id),
 | 
						|
        K(dest_ls_id),
 | 
						|
        K(start_scn),
 | 
						|
        K(tablet_list));
 | 
						|
  } else if (OB_FAIL(construct_multi_data_source_buf_(finish_transfer_in_info, allocator, buf, buf_len))) {
 | 
						|
    LOG_WARN("failed to construct multi data source buf", K(ret), K(finish_transfer_in_info));
 | 
						|
  } else if (OB_FAIL(conn->register_multi_data_source(tenant_id, dest_ls_id, type, buf, buf_len, flag))) {
 | 
						|
    LOG_WARN("failed to register multi data source", K(ret), K(tenant_id), K(dest_ls_id), K(type));
 | 
						|
  } else {
 | 
						|
#ifdef ERRSIM
 | 
						|
    ObTransferEventRecorder::record_transfer_task_event(task_id, "TX_FINISH_TRANSFER_IN", src_ls_id, dest_ls_id);
 | 
						|
#endif
 | 
						|
    LOG_INFO("register multi data source for finish transfer in", K(task_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_FINISH_TRANSFER_IN_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_FINISH_TRANSFER_IN_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::do_tx_finish_transfer_out_(const ObTransferTaskID &task_id, const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &src_ls_id, const share::ObLSID &dest_ls_id, const share::SCN &finish_scn,
 | 
						|
    const common::ObArray<ObTransferTabletInfo> &tablet_list, observer::ObInnerSQLConnection *conn)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  bool force_flush_redo = true;
 | 
						|
  bool is_leader = false;
 | 
						|
  char *buf = NULL;
 | 
						|
  int64_t buf_len = 0;
 | 
						|
  const transaction::ObTxDataSourceType type = transaction::ObTxDataSourceType::FINISH_TRANSFER_OUT;
 | 
						|
  ObTXFinishTransferOutInfo finish_transfer_out_info;
 | 
						|
  ObArenaAllocator allocator;
 | 
						|
  ObRegisterMdsFlag flag;
 | 
						|
  flag.need_flush_redo_instantly_ = false;
 | 
						|
  flag.mds_base_scn_ = finish_scn;
 | 
						|
 | 
						|
  if (OB_ISNULL(conn)) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("conn should not be null", K(ret));
 | 
						|
  } else if (OB_FAIL(build_tx_finish_transfer_out_info_(
 | 
						|
                 src_ls_id, dest_ls_id, finish_scn, tablet_list, finish_transfer_out_info))) {
 | 
						|
    LOG_WARN("failed to build tx finish transfer out info",
 | 
						|
        K(ret),
 | 
						|
        K(src_ls_id),
 | 
						|
        K(dest_ls_id),
 | 
						|
        K(finish_scn),
 | 
						|
        K(tablet_list));
 | 
						|
  } else if (OB_FAIL(construct_multi_data_source_buf_(finish_transfer_out_info, allocator, buf, buf_len))) {
 | 
						|
    LOG_WARN("failed to construct multi data source buf", K(ret), K(finish_transfer_out_info));
 | 
						|
  } else if (OB_FAIL(conn->register_multi_data_source(tenant_id, src_ls_id, type, buf, buf_len, flag))) {
 | 
						|
    LOG_WARN("failed to register multi data source", K(ret), K(tenant_id));
 | 
						|
  } else {
 | 
						|
#ifdef ERRSIM
 | 
						|
    ObTransferEventRecorder::record_transfer_task_event(task_id, "TX_FINISH_TRANSFER_OUT", src_ls_id, dest_ls_id);
 | 
						|
#endif
 | 
						|
    LOG_INFO("[TRANSFER] register multi data source for finish transfer out", K(task_id), K(src_ls_id), K(dest_ls_id));
 | 
						|
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_FINISH_TRANSFER_OUT_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_FINISH_TRANSFER_OUT_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    DEBUG_SYNC(AFTER_FINISH_TRANSFER_OUT);
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::wait_all_ls_replica_replay_scn_(const ObTransferTaskID &task_id, const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &ls_id, const common::ObArray<common::ObAddr> &member_addr_list, const share::SCN &finish_scn,
 | 
						|
    const int64_t quorum, ObTimeoutCtx &timeout_ctx, bool &check_passed)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  common::ObArray<common::ObAddr> finished_addr_list;
 | 
						|
 | 
						|
  while (OB_SUCC(ret)) {
 | 
						|
    check_passed = false;
 | 
						|
    if (timeout_ctx.is_timeouted()) {
 | 
						|
      ret = OB_TIMEOUT;
 | 
						|
      LOG_WARN("some ls replay not finished", K(ret), K(tenant_id), K(ls_id));
 | 
						|
    } else if (OB_FAIL(check_all_ls_replica_replay_scn_(
 | 
						|
            task_id, tenant_id, ls_id, member_addr_list, finish_scn, timeout_ctx, finished_addr_list))) {
 | 
						|
      LOG_WARN("failed to check all ls replica replay scn",
 | 
						|
          K(ret),
 | 
						|
          K(tenant_id),
 | 
						|
          K(member_addr_list),
 | 
						|
          K(ls_id),
 | 
						|
          K(quorum));
 | 
						|
    } else if (finished_addr_list.count() == member_addr_list.count()) {
 | 
						|
      check_passed = true;
 | 
						|
      LOG_INFO("all ls has passed ls replica replay scn", K(tenant_id), K(ls_id));
 | 
						|
      break;
 | 
						|
    } else {
 | 
						|
      ob_usleep(DEFAULT_WAIT_INTERVAL_US);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_DOING_WAIT_ALL_DEST_TABLET_NORAML ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_DOING_WAIT_ALL_DEST_TABLET_NORAML", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  DEBUG_SYNC(AFTER_DOING_TRANSFER_WAIT_REPLAY_SCN);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::check_all_ls_replica_replay_scn_(const ObTransferTaskID &task_id, const uint64_t tenant_id,
 | 
						|
    const share::ObLSID &ls_id, const common::ObIArray<common::ObAddr> &total_addr_list, const share::SCN &finish_scn,
 | 
						|
    ObTimeoutCtx &timeout_ctx, common::ObIArray<common::ObAddr> &finished_addr_list)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  int tmp_ret = OB_SUCCESS;
 | 
						|
  int64_t cur_quorum = 0;
 | 
						|
  common::ObArray<ObAddr> member_addr_list;
 | 
						|
  const int32_t group_id = share::OBCG_STORAGE;
 | 
						|
  if (OB_FAIL(ObTransferUtils::get_need_check_member(total_addr_list, finished_addr_list, member_addr_list))) {
 | 
						|
    LOG_WARN("failed to get need check member", K(ret), K(task_id), K(tenant_id), K(ls_id));
 | 
						|
  } else if (OB_FAIL(ObTransferUtils::check_ls_replay_scn(
 | 
						|
      tenant_id, ls_id, finish_scn, group_id, member_addr_list, timeout_ctx, finished_addr_list))) {
 | 
						|
    LOG_WARN("failed to check ls replay scn", K(ret), K(total_addr_list), K(finish_scn));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::get_ls_handle_(
 | 
						|
    const uint64_t tenant_id, const share::ObLSID &ls_id, storage::ObLSHandle &ls_handle)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  ls_handle.reset();
 | 
						|
  ObLSService *ls_service = NULL;
 | 
						|
  if (OB_INVALID_ID == tenant_id || !ls_id.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(tenant_id), K(ls_id));
 | 
						|
  } else if (OB_ISNULL(ls_service = MTL_WITH_CHECK_TENANT(ObLSService *, tenant_id))) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("log stream service is NULL", K(ret), K(tenant_id));
 | 
						|
  } else if (OB_FAIL(ls_service->get_ls(ls_id, ls_handle, ObLSGetMod::STORAGE_MOD))) {
 | 
						|
    LOG_WARN("failed to get log stream", K(ret), K(tenant_id), K(ls_id));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::get_ls_member_list_(
 | 
						|
    const uint64_t tenant_id, const share::ObLSID &ls_id, common::ObMemberList &member_list)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  storage::ObLS *ls = NULL;
 | 
						|
  storage::ObLSHandle ls_handle;
 | 
						|
  int64_t quorum = 0;
 | 
						|
  bool is_leader = false;
 | 
						|
  if (OB_FAIL(get_ls_handle_(tenant_id, ls_id, ls_handle))) {
 | 
						|
    LOG_WARN("failed to get ls", K(ret), K(tenant_id), K(ls_id));
 | 
						|
  } else if (OB_ISNULL(ls = ls_handle.get_ls())) {
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
    LOG_WARN("log stream not exist", K(ret), K(ls_id));
 | 
						|
  } else if (OB_FAIL(check_self_ls_leader_(ls_id, is_leader))) {
 | 
						|
    LOG_WARN("failed to check self ls leader", K(ret), K(ls_id));
 | 
						|
  } else if (!is_leader) {
 | 
						|
    ret = OB_IS_CHANGING_LEADER;
 | 
						|
    LOG_WARN("self is not leader", K(ret), K(ls_id));
 | 
						|
  } else if (OB_FAIL(ls->get_paxos_member_list(member_list, quorum))) {
 | 
						|
    LOG_WARN("failed to get paxos member list", K(ret));
 | 
						|
  } else if (OB_FAIL(check_self_ls_leader_(ls_id, is_leader))) {
 | 
						|
    LOG_WARN("failed to check self ls leader", K(ret), K(ls_id));
 | 
						|
  } else if (!is_leader) {
 | 
						|
    ret = OB_IS_CHANGING_LEADER;
 | 
						|
    LOG_WARN("self is not leader", K(ret), K(ls_id));
 | 
						|
  } else {
 | 
						|
    LOG_INFO("get ls member list", K(tenant_id), K(ls_id), K(member_list));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
// TODO(yangyi.yyy): impl later
 | 
						|
// extract common function later
 | 
						|
int ObTxFinishTransfer::check_same_member_list_(
 | 
						|
    const uint64_t tenant_id, const share::ObLSID &src_ls_id, const share::ObLSID &dest_ls_id, bool &same_member_list)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  UNUSEDx(tenant_id, src_ls_id, dest_ls_id, same_member_list);
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::unlock_ls_member_list_(const uint64_t tenant_id, const share::ObLSID &ls_id,
 | 
						|
    const common::ObMemberList &member_list, const ObTransferLockStatus &status, const int64_t lock_timeout)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (OB_FAIL(ObMemberListLockUtils::unlock_ls_member_list(
 | 
						|
      tenant_id, ls_id, task_id_.id(), member_list, status, share::OBCG_STORAGE, *sql_proxy_))) {
 | 
						|
    LOG_WARN("failed to unlock ls member list", K(ret), K(tenant_id), K(ls_id), K_(task_id), K(status));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::lock_ls_member_list_(const uint64_t tenant_id, const share::ObLSID &ls_id,
 | 
						|
    const common::ObMemberList &member_list, const ObTransferLockStatus &status)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (OB_FAIL(ObMemberListLockUtils::lock_ls_member_list(
 | 
						|
      tenant_id, ls_id, task_id_.id(), member_list, status, share::OBCG_STORAGE, *sql_proxy_))) {
 | 
						|
    LOG_WARN("failed to unlock ls member list", K(ret), K(ls_id), K(member_list));
 | 
						|
  } else {
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_DOING_LOCK_MEMBER_LIST_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_DOING_LOCK_MEMBER_LIST_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    DEBUG_SYNC(AFTER_DOING_TRANSFER_LOCK_MEMBER_LIST);
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::build_tx_finish_transfer_in_info_(const share::ObLSID &src_ls_id,
 | 
						|
    const share::ObLSID &dest_ls_id, const share::SCN &start_scn,
 | 
						|
    const common::ObArray<ObTransferTabletInfo> &tablet_list, ObTXFinishTransferInInfo &transfer_in_info)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  transfer_in_info.reset();
 | 
						|
  if (!src_ls_id.is_valid() || !dest_ls_id.is_valid() || !start_scn.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else {
 | 
						|
    transfer_in_info.src_ls_id_ = src_ls_id;
 | 
						|
    transfer_in_info.dest_ls_id_ = dest_ls_id;
 | 
						|
    transfer_in_info.start_scn_ = start_scn;
 | 
						|
    if (OB_FAIL(transfer_in_info.tablet_list_.assign(tablet_list))) {
 | 
						|
      LOG_WARN("failed to assign tablet list", K(ret), K(tablet_list));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::build_tx_finish_transfer_out_info_(const share::ObLSID &src_ls_id,
 | 
						|
    const share::ObLSID &dest_ls_id, const share::SCN &finish_scn,
 | 
						|
    const common::ObArray<ObTransferTabletInfo> &tablet_list, ObTXFinishTransferOutInfo &transfer_out_info)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  transfer_out_info.reset();
 | 
						|
  if (!src_ls_id.is_valid() || !dest_ls_id.is_valid() || !finish_scn.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(src_ls_id), K(dest_ls_id));
 | 
						|
  } else {
 | 
						|
    transfer_out_info.src_ls_id_ = src_ls_id;
 | 
						|
    transfer_out_info.dest_ls_id_ = dest_ls_id;
 | 
						|
    transfer_out_info.finish_scn_ = finish_scn;
 | 
						|
    if (OB_FAIL(transfer_out_info.tablet_list_.assign(tablet_list))) {
 | 
						|
      LOG_WARN("failed to assign tablet list", K(ret), K(tablet_list));
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
template <class TransferInfo>
 | 
						|
int ObTxFinishTransfer::construct_multi_data_source_buf_(
 | 
						|
    const TransferInfo &transfer_info, common::ObIAllocator &allocator, char *&buf, int64_t &buf_len)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  buf = NULL;
 | 
						|
  int64_t pos = 0;
 | 
						|
  buf_len = transfer_info.get_serialize_size();
 | 
						|
  if (!transfer_info.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(transfer_info));
 | 
						|
  } else if (OB_ISNULL(buf = static_cast<char *>(allocator.alloc(buf_len)))) {
 | 
						|
    ret = OB_ALLOCATE_MEMORY_FAILED;
 | 
						|
    LOG_WARN("failed to alloc memory", K(ret), K(buf_len));
 | 
						|
  } else if (OB_FAIL(transfer_info.serialize(buf, buf_len, pos))) {
 | 
						|
    LOG_WARN("failed to serialize", K(ret), K(transfer_info));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::update_transfer_task_result_(const ObTransferTaskID &task_id, const uint64_t tenant_id,
 | 
						|
    const share::SCN &finish_scn, const int64_t result, ObMySQLTransaction &trans)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (!task_id.is_valid() || OB_INVALID_ID == tenant_id) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(task_id), K(tenant_id));
 | 
						|
  } else {
 | 
						|
    ObTransferTask transfer_task;
 | 
						|
    const bool for_update = true;
 | 
						|
    ObTransferStatus next_status;
 | 
						|
    next_status = OB_SUCCESS == result ? ObTransferStatus::COMPLETED : ObTransferStatus::DOING;
 | 
						|
    if (OB_FAIL(ObTransferTaskOperator::get(trans, tenant_id, task_id, for_update, transfer_task, share::OBCG_STORAGE))) {
 | 
						|
      LOG_WARN("failed to get transfer task", K(ret), K(task_id), K(tenant_id));
 | 
						|
    } else if (transfer_task.get_start_scn() >= finish_scn) {
 | 
						|
      ret = OB_ERR_UNEXPECTED;
 | 
						|
      LOG_WARN("finish scn not expected", K(ret), K(transfer_task), K(finish_scn));
 | 
						|
    } else if (OB_FAIL(ObTransferTaskOperator::update_finish_scn(
 | 
						|
                   trans, tenant_id, task_id, transfer_task.get_status(), finish_scn, share::OBCG_STORAGE))) {
 | 
						|
      LOG_WARN("failed to update finish scn", K(ret), K(tenant_id), K(task_id), K(finish_scn));
 | 
						|
    } else if (OB_FAIL(ObTransferTaskOperator::finish_task(
 | 
						|
                   trans, tenant_id, task_id, transfer_task.get_status(), next_status, result, ObTransferTaskComment::EMPTY_COMMENT, share::OBCG_STORAGE))) {
 | 
						|
      LOG_WARN("failed to finish task", K(ret), K(tenant_id), K(task_id));
 | 
						|
    }
 | 
						|
#ifdef ERRSIM
 | 
						|
    ObTransferEventRecorder::record_advance_transfer_status_event(
 | 
						|
        tenant_id, task_id, src_ls_id_, dest_ls_id_, next_status, result);
 | 
						|
#endif
 | 
						|
    LOG_INFO("update transfer task result", K(ret), K(task_id), K(tenant_id), K(result), K(finish_scn));
 | 
						|
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_DOING_UPDATE_TRANSFER_TASK_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_DOING_UPDATE_TRANSFER_TASK_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::report_result_(
 | 
						|
    const ObTransferTaskID &task_id, const int64_t result, obrpc::ObSrvRpcProxy *rpc_proxy)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  ObAddr leader_addr;
 | 
						|
  int64_t retry_count = 0;
 | 
						|
  const int64_t MAX_RETRY_TIMES = 3;
 | 
						|
  const int64_t REPORT_RETRY_INTERVAL_MS = 100 * 1000;  // 100ms
 | 
						|
  ObFinishTransferTaskArg finish_task_arg;
 | 
						|
  const uint64_t tenant_id = MTL_ID();
 | 
						|
  const share::ObLSID &sys_ls_id = share::SYS_LS;
 | 
						|
 | 
						|
  if (OB_FAIL(finish_task_arg.init(tenant_id, task_id))) {
 | 
						|
    LOG_WARN("failed to init finish task arg", K(ret), K(tenant_id), K(task_id));
 | 
						|
  } else {
 | 
						|
    while (retry_count++ < MAX_RETRY_TIMES) {
 | 
						|
      if (OB_FAIL(ObStorageHAUtils::get_ls_leader(tenant_id, sys_ls_id, leader_addr))) {
 | 
						|
        LOG_WARN("failed to get ls leader", K(ret), K(tenant_id));
 | 
						|
      } else if (OB_FAIL(rpc_proxy->to(leader_addr).by(tenant_id).finish_transfer_task(finish_task_arg))) {
 | 
						|
        LOG_WARN("failed to report finish transfer task", K(ret), K(leader_addr), K(tenant_id), K(finish_task_arg));
 | 
						|
      }
 | 
						|
      if (OB_SUCC(ret)) {
 | 
						|
        break;
 | 
						|
      } else {
 | 
						|
        ob_usleep(REPORT_RETRY_INTERVAL_MS);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::check_self_ls_leader_(const share::ObLSID &ls_id, bool &is_leader)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  const uint64_t tenant_id = MTL_ID();
 | 
						|
  is_leader = false;
 | 
						|
  if (OB_FAIL(ObStorageHAUtils::check_ls_is_leader(tenant_id, ls_id, is_leader))) {
 | 
						|
    LOG_WARN("failed to check ls leader", K(ret), K(tenant_id), K(ls_id));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::start_trans_(
 | 
						|
    const uint64_t tenant_id,
 | 
						|
    ObMySQLTransaction &trans,
 | 
						|
    ObTimeoutCtx &timeout_ctx)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  int64_t finish_trans_timeout = 10_s;
 | 
						|
  omt::ObTenantConfigGuard tenant_config(TENANT_CONF(tenant_id));
 | 
						|
  if (tenant_config.is_valid()) {
 | 
						|
    finish_trans_timeout = tenant_config->_transfer_finish_trans_timeout;
 | 
						|
  }
 | 
						|
  const int64_t stmt_timeout = finish_trans_timeout;
 | 
						|
 | 
						|
  if (OB_FAIL(timeout_ctx.set_trx_timeout_us(stmt_timeout))) {
 | 
						|
    LOG_WARN("fail to set trx timeout", K(ret), K(stmt_timeout));
 | 
						|
  } else if (OB_FAIL(timeout_ctx.set_timeout(stmt_timeout))) {
 | 
						|
    LOG_WARN("set timeout context failed", K(ret));
 | 
						|
  } else if (OB_FAIL(trans.start(sql_proxy_, tenant_id, false/*with_snapshot*/, share::OBCG_STORAGE))) {
 | 
						|
    LOG_WARN("failed to start trans", K(ret), K(tenant_id));
 | 
						|
  } else {
 | 
						|
    LOG_INFO("start trans", K(tenant_id));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::commit_trans_(const bool is_commit, ObMySQLTransaction &trans)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (OB_FAIL(trans.end(is_commit))) {
 | 
						|
    LOG_WARN("end transaction failed", K(ret));
 | 
						|
  } else {
 | 
						|
    LOG_INFO("commit trans", K(is_commit));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::get_transfer_quorum_(const ObMemberList &member_list, int64_t &quorum)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  if (!member_list.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid args", K(ret), K(member_list));
 | 
						|
  } else {
 | 
						|
    quorum = member_list.get_member_number();
 | 
						|
    LOG_INFO("get transfer quorum", K(member_list), K(quorum));
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::select_transfer_task_for_update_(const ObTransferTaskID &task_id, ObMySQLTransaction &trans)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  const uint64_t tenant_id = MTL_ID();
 | 
						|
  const bool for_update = true;
 | 
						|
  ObTransferTask task;
 | 
						|
  if (!task_id.is_valid()) {
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
    LOG_WARN("get invalid arg", K(ret), K(task_id));
 | 
						|
  } else if (OB_FAIL(ObTransferTaskOperator::get(trans, tenant_id, task_id, for_update, task, share::OBCG_STORAGE))) {
 | 
						|
    LOG_WARN("failed to get transfer task", K(ret), K(tenant_id), K(task_id));
 | 
						|
  } else if (!task.get_status().is_doing_status()) {
 | 
						|
    ret = OB_STATE_NOT_MATCH;
 | 
						|
    LOG_WARN("transfer task status is not doing", K(ret), K(task));
 | 
						|
  } else {
 | 
						|
    LOG_INFO("select for update", K(task_id), K(task));
 | 
						|
#ifdef ERRSIM
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ret = EN_DOING_LOCK_TRANSFER_TASK_FAILED ? : OB_SUCCESS;
 | 
						|
      if (OB_FAIL(ret)) {
 | 
						|
        STORAGE_LOG(ERROR, "fake EN_DOING_LOCK_TRANSFER_TASK_FAILED", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::record_server_event_(
 | 
						|
    const int32_t result,
 | 
						|
    const bool is_ready,
 | 
						|
    const int64_t round,
 | 
						|
    const share::SCN &start_scn) const
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  ObSqlString extra_info_str;
 | 
						|
  const share::ObTransferStatus doing_status(ObTransferStatus::DOING);
 | 
						|
  const share::ObTransferStatus finish_status(ObTransferStatus::COMPLETED);
 | 
						|
  const int64_t start_scn_ts = start_scn.is_valid() ? start_scn.convert_to_ts() : 0;
 | 
						|
  const int64_t elapsed_us_from_start_scn = start_scn.is_valid() ? ObTimeUtility::current_time() - start_scn_ts : 0;
 | 
						|
  if (OB_SUCCESS == result) {
 | 
						|
    if (is_ready) {
 | 
						|
      if (OB_FAIL(extra_info_str.append_fmt("msg:\"transfer doing success\";"))) {
 | 
						|
        LOG_WARN("fail to printf wait info", K(ret));
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      if (OB_FAIL(extra_info_str.append_fmt("msg:\"wait for ls logical table replaced\";"))) {
 | 
						|
        LOG_WARN("fail to printf wait info", K(ret));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (OB_SUCC(ret)) {
 | 
						|
    if (OB_FAIL(extra_info_str.append_fmt("round:%ld;", round))) {
 | 
						|
      LOG_WARN("fail to printf retry time", K(ret));
 | 
						|
    } else if (elapsed_us_from_start_scn > 0 && OB_FAIL(extra_info_str.append_fmt("elapsed_us_from_start_scn:%ld;", elapsed_us_from_start_scn))) {
 | 
						|
      LOG_WARN("fail to printf retry time", K(ret));
 | 
						|
    } else {
 | 
						|
      if (OB_SUCCESS == result && is_ready) {
 | 
						|
        if (OB_FAIL(write_server_event_(result, extra_info_str, finish_status))) {
 | 
						|
          LOG_WARN("fail to write server event", K(ret), K(result), K(extra_info_str));
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        if (REACH_TENANT_TIME_INTERVAL(10 * 1000 * 1000)) {
 | 
						|
          if (OB_FAIL(write_server_event_(result, extra_info_str, doing_status))) {
 | 
						|
            LOG_WARN("fail to write server event", K(ret), K(result), K(extra_info_str));
 | 
						|
          } else if (elapsed_us_from_start_scn > TASK_EXECUTE_LONG_WARNING_THRESHOLD) {
 | 
						|
            LOG_ERROR("transfer task stuck at doing stage for too long, may be wait log replay or transfer backfill too slow",
 | 
						|
                K(start_scn), K(elapsed_us_from_start_scn), K_(task_id));
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObTxFinishTransfer::write_server_event_(const int32_t result, const ObSqlString &extra_info, const share::ObTransferStatus &status) const
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  SERVER_EVENT_ADD("storage_ha", "transfer",
 | 
						|
      "tenant_id", tenant_id_,
 | 
						|
      "trace_id", *ObCurTraceId::get_trace_id(),
 | 
						|
      "src_ls", src_ls_id_.id(),
 | 
						|
      "dest_ls", dest_ls_id_.id(),
 | 
						|
      "status", status.str(),
 | 
						|
      "result", result,
 | 
						|
      extra_info.ptr());
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace storage
 | 
						|
}  // namespace oceanbase
 |