275 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) 2021 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 OBLOG_PARSER
 | 
						|
 | 
						|
#include "ob_log_dml_parser.h"
 | 
						|
 | 
						|
#include "ob_log_formatter.h"           // IObLogFormatter
 | 
						|
#include "ob_log_instance.h"            // IObLogErrHandler
 | 
						|
#include "ob_log_part_trans_parser.h"   // IObLogPartTransParser
 | 
						|
#include "ob_ms_queue_thread.h"         // BitSet
 | 
						|
 | 
						|
using namespace oceanbase::common;
 | 
						|
 | 
						|
namespace oceanbase
 | 
						|
{
 | 
						|
namespace liboblog
 | 
						|
{
 | 
						|
 | 
						|
ObLogDmlParser::ObLogDmlParser() :
 | 
						|
    inited_(false),
 | 
						|
    formatter_(NULL),
 | 
						|
    err_handler_(NULL),
 | 
						|
    part_trans_parser_(NULL),
 | 
						|
    log_entry_task_count_(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ObLogDmlParser::~ObLogDmlParser()
 | 
						|
{
 | 
						|
  destroy();
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::init(const int64_t parser_thread_num,
 | 
						|
    const int64_t parser_queue_size,
 | 
						|
    IObLogFormatter &formatter,
 | 
						|
    IObLogErrHandler &err_handler,
 | 
						|
    IObLogPartTransParser &part_trans_parser)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
 | 
						|
  if (OB_UNLIKELY(inited_)) {
 | 
						|
    LOG_ERROR("parser has been initialized");
 | 
						|
    ret = OB_INIT_TWICE;
 | 
						|
  } else if (OB_UNLIKELY(parser_thread_num <= 0)
 | 
						|
      || OB_UNLIKELY(parser_thread_num > MAX_THREAD_NUM)
 | 
						|
      || OB_UNLIKELY(parser_queue_size <= 0)) {
 | 
						|
    LOG_ERROR("invalid argument", K(parser_thread_num), LITERAL_K(MAX_THREAD_NUM),
 | 
						|
        K(parser_queue_size));
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
  } else if (OB_FAIL(DmlParserThread::init(parser_thread_num, parser_queue_size))) {
 | 
						|
    LOG_ERROR("init parser queue thread fail", KR(ret), K(parser_thread_num), K(parser_queue_size));
 | 
						|
  } else {
 | 
						|
    formatter_ = &formatter;
 | 
						|
    err_handler_ = &err_handler;
 | 
						|
    part_trans_parser_ = &part_trans_parser;
 | 
						|
    log_entry_task_count_ = 0;
 | 
						|
    inited_ = true;
 | 
						|
 | 
						|
    LOG_INFO("init DML parser succ", K(parser_thread_num), K(parser_queue_size));
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void ObLogDmlParser::destroy()
 | 
						|
{
 | 
						|
  DmlParserThread::destroy();
 | 
						|
 | 
						|
  inited_ = false;
 | 
						|
  formatter_ =  NULL;
 | 
						|
  err_handler_ = NULL;
 | 
						|
  part_trans_parser_ = NULL;
 | 
						|
  log_entry_task_count_ = 0;
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::start()
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
 | 
						|
  if (OB_UNLIKELY(! inited_)) {
 | 
						|
    LOG_ERROR("parser has not been initialized");
 | 
						|
    ret = OB_NOT_INIT;
 | 
						|
  } else if (OB_FAIL(DmlParserThread::start())) {
 | 
						|
    LOG_ERROR("start parser thread fail", KR(ret), "thread_num", get_thread_num());
 | 
						|
  } else {
 | 
						|
    LOG_INFO("start DML parser threads succ", "thread_num", get_thread_num());
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void ObLogDmlParser::stop()
 | 
						|
{
 | 
						|
  if (inited_) {
 | 
						|
    DmlParserThread::stop();
 | 
						|
    LOG_INFO("stop DML parser threads succ", "thread_num", get_thread_num());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::push(ObLogEntryTask &task, const int64_t timeout)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  PartTransTask *part_trans_task = NULL;
 | 
						|
 | 
						|
  if (OB_UNLIKELY(! inited_)) {
 | 
						|
    LOG_ERROR("DML parser has not been initialized");
 | 
						|
    ret = OB_NOT_INIT;
 | 
						|
  }
 | 
						|
  // Verify that the task information is valid
 | 
						|
  else if (OB_UNLIKELY(! task.is_valid())) {
 | 
						|
    LOG_ERROR("invalid task", K(task));
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
  } else if (OB_ISNULL(part_trans_task = static_cast<PartTransTask *>(task.get_host()))) {
 | 
						|
    LOG_ERROR("part_trans_task is NULL", K(part_trans_task), K(task));
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
  } else if (OB_UNLIKELY(DmlParserThread::is_stoped())) {
 | 
						|
    LOG_INFO("DML parser has been stoped");
 | 
						|
    ret = OB_IN_STOP_STATE;
 | 
						|
  } else {
 | 
						|
    // Hash rules ensure that tasks for the same partition transaction are assigned to the same queue
 | 
						|
    const uint64_t hash_value = part_trans_task->get_partition().hash();
 | 
						|
 | 
						|
    if (OB_FAIL(DmlParserThread::push(&task, hash_value, timeout))) {
 | 
						|
      if (OB_TIMEOUT != ret) {
 | 
						|
        LOG_ERROR("push task into DML queue thread fail", KR(ret), K(task));
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      ATOMIC_INC(&log_entry_task_count_);
 | 
						|
      LOG_DEBUG("push task into DML parser", K(task));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::get_log_entry_task_count(int64_t &task_num)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
 | 
						|
  task_num = ATOMIC_LOAD(&log_entry_task_count_);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::handle(void *data,
 | 
						|
    const int64_t thread_index,
 | 
						|
    volatile bool &stop_flag)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  ObLogEntryTask *task = (ObLogEntryTask *)(data);
 | 
						|
  PartTransTask *part_trans_task = NULL;
 | 
						|
 | 
						|
  if (OB_UNLIKELY(! inited_) || OB_ISNULL(part_trans_parser_)) {
 | 
						|
    LOG_ERROR("DML parser has not been initialized", K(part_trans_parser_));
 | 
						|
    ret = OB_NOT_INIT;
 | 
						|
  } else if (OB_ISNULL(task)) {
 | 
						|
    LOG_ERROR("invalid argument", KPC(task));
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
  } else if (OB_ISNULL(part_trans_task = static_cast<PartTransTask *>(task->get_host()))) {
 | 
						|
    LOG_ERROR("part_trans_task is NULL", K(part_trans_task), KPC(task));
 | 
						|
    ret = OB_ERR_UNEXPECTED;
 | 
						|
  } else {
 | 
						|
    LOG_DEBUG("DML parser handle task", K(thread_index), KPC(task));
 | 
						|
    const uint64_t tenant_id = part_trans_task->get_tenant_id();
 | 
						|
    share::ObWorker::CompatMode compat_mode = share::ObWorker::CompatMode::INVALID;
 | 
						|
 | 
						|
    if (OB_FAIL(get_tenant_compat_mode(tenant_id, compat_mode, stop_flag))) {
 | 
						|
      LOG_ERROR("get_tenant_compat_mode fail", KR(ret), K(tenant_id),
 | 
						|
          "compat_mode", print_compat_mode(compat_mode), KPC(task));
 | 
						|
    } else {
 | 
						|
      share::CompatModeGuard g(compat_mode);
 | 
						|
 | 
						|
      if (OB_FAIL(part_trans_parser_->parse(*task, stop_flag))) {
 | 
						|
        LOG_ERROR("parse task fail", KR(ret), KPC(task), "compat_mode", print_compat_mode(compat_mode));
 | 
						|
      } else if (OB_FAIL(dispatch_task_(*task, *part_trans_task, stop_flag))) {
 | 
						|
        if (OB_IN_STOP_STATE != ret) {
 | 
						|
          LOG_ERROR("dispatch_task_ fail", KR(ret), KPC(task), KPC(part_trans_task));
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (OB_SUCC(ret)) {
 | 
						|
      ATOMIC_DEC(&log_entry_task_count_);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Failure to exit
 | 
						|
  if (OB_SUCCESS != ret && OB_IN_STOP_STATE != ret && NULL != err_handler_) {
 | 
						|
    err_handler_->handle_error(ret, "DML parser thread exits, thread_index=%ld, err=%d",
 | 
						|
        thread_index, ret);
 | 
						|
    stop_flag = true;
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int ObLogDmlParser::dispatch_task_(ObLogEntryTask &log_entry_task,
 | 
						|
    PartTransTask &part_trans_task,
 | 
						|
    volatile bool &stop_flag)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  const int64_t stmt_count = log_entry_task.get_stmt_list().num_;
 | 
						|
 | 
						|
  if (stmt_count <= 0 ) {
 | 
						|
    if (OB_FAIL(handle_empty_stmt_(log_entry_task, part_trans_task, stop_flag))) {
 | 
						|
      LOG_ERROR("handle_empty_stmt_ fail", KR(ret), K(log_entry_task), K(part_trans_task));
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    if (OB_FAIL(push_task_into_formatter_(log_entry_task, stop_flag))) {
 | 
						|
      if (OB_IN_STOP_STATE != ret) {
 | 
						|
        LOG_ERROR("push_task_into_formatter_ fail", KR(ret), K(log_entry_task), K(part_trans_task));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::handle_empty_stmt_(ObLogEntryTask &log_entry_task,
 | 
						|
    PartTransTask &part_trans_task,
 | 
						|
    volatile bool &stop_flag)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
  bool is_unserved_part_trans_task_can_be_recycled = false;
 | 
						|
 | 
						|
  if (OB_FAIL(part_trans_task.handle_log_entry_task_callback(ObLogEntryTask::DML_PARSER_CB,
 | 
						|
      log_entry_task,
 | 
						|
      is_unserved_part_trans_task_can_be_recycled))) {
 | 
						|
    LOG_ERROR("handle_log_entry_task_callback fail", KR(ret), K(log_entry_task),
 | 
						|
        K(is_unserved_part_trans_task_can_be_recycled), K(part_trans_task), K(stop_flag));
 | 
						|
  } else if (is_unserved_part_trans_task_can_be_recycled) {
 | 
						|
    LOG_DEBUG("handle_log_entry_task_callback: part_trans_task is revert", K(part_trans_task));
 | 
						|
    part_trans_task.revert();
 | 
						|
  } else {}
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int ObLogDmlParser::push_task_into_formatter_(ObLogEntryTask &task, volatile bool &stop_flag)
 | 
						|
{
 | 
						|
  int ret = OB_SUCCESS;
 | 
						|
 | 
						|
  if (OB_ISNULL(formatter_)) {
 | 
						|
    LOG_ERROR("invalid formatter", K(formatter_));
 | 
						|
    ret = OB_INVALID_ARGUMENT;
 | 
						|
  } else {
 | 
						|
    const StmtList &stmt_list = task.get_stmt_list();
 | 
						|
    IStmtTask *stmt = stmt_list.head_;
 | 
						|
 | 
						|
    if (OB_FAIL(formatter_->push(stmt, stop_flag))) {
 | 
						|
      if (OB_IN_STOP_STATE != ret) {
 | 
						|
        LOG_ERROR("formatter_ push fail", KR(ret), K(task));
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      // succ
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace liboblog
 | 
						|
} // namespace oceanbase
 |