[liboblog] support storage work mode
This commit is contained in:
committed by
LINxiansheng
parent
8b88f1f1bd
commit
463064375a
782
tools/obcdc/src/ob_log_trans_ctx.cpp
Normal file
782
tools/obcdc/src/ob_log_trans_ctx.cpp
Normal file
@ -0,0 +1,782 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
#include "ob_log_trans_ctx.h"
|
||||
|
||||
#include "lib/allocator/ob_mod_define.h" // ObModIds
|
||||
|
||||
#include "ob_log_part_mgr.h" // IObLogPartMgr
|
||||
#include "ob_log_trans_ctx_mgr.h" // IObLogTransCtxMgr
|
||||
|
||||
#define _TCTX_STAT(level, tag, args...) _OBLOG_LOG(level, "[STAT] [TRANS_CTX] " tag, ##args)
|
||||
#define _TCTX_ISTAT(tag, args...) _TCTX_STAT(INFO, tag, ##args)
|
||||
#define _TCTX_DSTAT(tag, args...) _TCTX_STAT(DEBUG, tag, ##args)
|
||||
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::transaction;
|
||||
|
||||
namespace oceanbase
|
||||
{
|
||||
namespace liboblog
|
||||
{
|
||||
TransCtxSortElement::TransCtxSortElement() :
|
||||
trans_ctx_host_(NULL),
|
||||
trans_id_(),
|
||||
global_trans_version_(OB_INVALID_VERSION)
|
||||
{
|
||||
}
|
||||
|
||||
TransCtxSortElement::~TransCtxSortElement()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void TransCtxSortElement::reset()
|
||||
{
|
||||
trans_ctx_host_ = NULL;
|
||||
trans_id_.reset();
|
||||
global_trans_version_ = OB_INVALID_VERSION;
|
||||
}
|
||||
|
||||
int TransCtxSortElement::init(TransCtx &trans_ctx_host,
|
||||
const transaction::ObTransID &trans_id,
|
||||
const int64_t global_trans_version)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
if (OB_UNLIKELY(! trans_id.is_valid())
|
||||
|| OB_UNLIKELY(OB_INVALID_VERSION == global_trans_version)) {
|
||||
LOG_ERROR("invalid argument", K(trans_id), K(global_trans_version));
|
||||
} else {
|
||||
trans_ctx_host_ = &trans_ctx_host;
|
||||
trans_id_ = trans_id;
|
||||
global_trans_version_ = global_trans_version;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TransCtxSortElement::TransCtxCmp::operator() (const TransCtxSortElement &tx1, const TransCtxSortElement &tx2) const
|
||||
{
|
||||
bool bool_ret = false;
|
||||
|
||||
if (tx1.global_trans_version_ != tx2.global_trans_version_) {
|
||||
bool_ret = tx1.global_trans_version_ > tx2.global_trans_version_;
|
||||
} else {
|
||||
bool_ret = tx1.trans_id_.compare(tx2.trans_id_);
|
||||
}
|
||||
|
||||
return bool_ret;
|
||||
}
|
||||
|
||||
TransCtx::TransCtx() :
|
||||
host_(NULL),
|
||||
state_(TRANS_CTX_STATE_INVALID),
|
||||
trans_id_(),
|
||||
trans_id_str_(),
|
||||
major_version_str_(),
|
||||
trx_sort_elem_(),
|
||||
seq_(0),
|
||||
participants_(NULL),
|
||||
participant_count_(0),
|
||||
ready_participant_objs_(NULL),
|
||||
ready_participant_count_(0),
|
||||
br_committer_queue_cond_(),
|
||||
total_br_count_(0),
|
||||
committed_br_count_(0),
|
||||
valid_part_trans_task_count_(0),
|
||||
revertable_participant_count_(0),
|
||||
allocator_(ObModIds::OB_LOG_TRANS_CTX, PAGE_SIZE),
|
||||
lock_()
|
||||
{}
|
||||
|
||||
TransCtx::~TransCtx()
|
||||
{
|
||||
}
|
||||
|
||||
void TransCtx::reset()
|
||||
{
|
||||
destroy_participant_array_();
|
||||
|
||||
host_ = NULL;
|
||||
state_ = TRANS_CTX_STATE_INVALID;
|
||||
trans_id_.reset();
|
||||
trans_id_str_.reset();
|
||||
major_version_str_.reset();
|
||||
trx_sort_elem_.reset();
|
||||
seq_ = 0;
|
||||
|
||||
participants_ = NULL;
|
||||
participant_count_ = 0;
|
||||
ready_participant_objs_ = NULL;
|
||||
ready_participant_count_ = 0;
|
||||
|
||||
total_br_count_ = 0;
|
||||
committed_br_count_ = 0;
|
||||
valid_part_trans_task_count_ = 0;
|
||||
revertable_participant_count_ = 0;
|
||||
|
||||
allocator_.reset();
|
||||
}
|
||||
|
||||
int TransCtx::set_trans_id(const transaction::ObTransID &trans_id)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (! trans_id.is_valid()) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
trans_id_ = trans_id;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::get_state() const
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
int TransCtx::set_state(const int target_state)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (! is_state_valid(target_state)) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
state_ = static_cast<int8_t>(target_state);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::set_ready_participant_count(const int64_t count)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (OB_UNLIKELY(count < 0)) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
ready_participant_count_ = count;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t TransCtx::get_revertable_participant_count() const
|
||||
{
|
||||
return revertable_participant_count_;
|
||||
}
|
||||
|
||||
int TransCtx::set_ready_participant_objs(PartTransTask *part_trans_task)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (OB_ISNULL(part_trans_task)) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
ready_participant_objs_ = part_trans_task;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::init(IObLogTransCtxMgr *host)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
if (OB_ISNULL(host)) {
|
||||
LOG_ERROR("IObLogTransCtxMgr is NULL");
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
host_ = host;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::prepare(PartTransTask &part_trans_task,
|
||||
IObLogPartMgr &part_mgr,
|
||||
const bool print_participant_not_serve_info,
|
||||
volatile bool &stop_flag,
|
||||
bool &need_discard)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
const ObTransID &trans_id = part_trans_task.get_trans_id();
|
||||
|
||||
// Requires locking conditions
|
||||
ObSpinLockGuard guard(lock_);
|
||||
need_discard = false;
|
||||
|
||||
if (state_ < TRANS_CTX_STATE_INVALID) {
|
||||
// Context is deprecated and can no longer be used
|
||||
ret = OB_INVALID_ERROR;
|
||||
}
|
||||
// PREPARE is required only if the status is INVALID
|
||||
else if (TRANS_CTX_STATE_INVALID == state_) {
|
||||
// Requires that the partitioned transaction responsible for Prepare must be serviced, otherwise the transaction context will be deprecated
|
||||
if (OB_FAIL(prepare_(part_trans_task, part_mgr, print_participant_not_serve_info,
|
||||
stop_flag))) {
|
||||
if (OB_INVALID_ERROR == ret) {
|
||||
// The partition transaction is not serviced and the transaction context is discarded
|
||||
need_discard = true;
|
||||
state_ = TRANS_CTX_STATE_DISCARDED;
|
||||
_TCTX_ISTAT("[DISCARD] TRANS_ID=%s PART=%s TRANS_CTX=%p", to_cstring(trans_id),
|
||||
to_cstring(part_trans_task.get_partition()), this);
|
||||
// reset return value
|
||||
ret = OB_SUCCESS;
|
||||
} else if (OB_IN_STOP_STATE != ret) {
|
||||
LOG_ERROR("prepare trans_ctx fail", KR(ret), K(part_trans_task));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Other cases indicate that it has been Prepared and returns normal
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::add_participant(PartTransTask &part_trans_task,
|
||||
bool &is_part_trans_served,
|
||||
bool &is_all_participants_ready)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
const ObTransID &trans_id = part_trans_task.get_trans_id();
|
||||
|
||||
// Requires locking conditions
|
||||
ObSpinLockGuard guard(lock_);
|
||||
|
||||
is_part_trans_served = true;
|
||||
is_all_participants_ready = false;
|
||||
|
||||
if (state_ <= TRANS_CTX_STATE_INVALID) {
|
||||
LOG_ERROR("state not match which is not PREPARED or higher", "state", print_state());
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else if (state_ >= TRANS_CTX_STATE_PARTICIPANT_READY) {
|
||||
// Participants have been added, no later arrivals will be served
|
||||
is_part_trans_served = false;
|
||||
_TCTX_DSTAT("[PART_NOT_SERVE] [DELAY_COMING] TRANS_ID=%s PART=%s "
|
||||
"LOG_ID=%ld LOG_TSTAMP=%ld TRNAS_CTX_STATE=%s",
|
||||
to_cstring(trans_id), to_cstring(part_trans_task.get_partition()),
|
||||
part_trans_task.get_prepare_log_id(), part_trans_task.get_timestamp(),
|
||||
print_state());
|
||||
}
|
||||
// Attempt to add participants to the participant READY list
|
||||
else if (OB_FAIL(add_ready_participant_(part_trans_task, is_part_trans_served, is_all_participants_ready))) {
|
||||
LOG_ERROR("add_ready_participant_ fail", KR(ret), K(part_trans_task), K(is_part_trans_served),
|
||||
K(is_all_participants_ready));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::prepare_(PartTransTask &host,
|
||||
IObLogPartMgr &part_mgr,
|
||||
const bool print_participant_not_serve_info,
|
||||
volatile bool &stop_flag)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
if (OB_UNLIKELY(TRANS_CTX_STATE_INVALID != state_)) {
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else if (OB_UNLIKELY(NULL != participants_) || OB_UNLIKELY(participant_count_ > 0)) {
|
||||
LOG_ERROR("participants info are not empty", K(participants_), K(participant_count_));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else {
|
||||
const ObTransID &trans_id = host.get_trans_id();
|
||||
const ObPartitionKey &host_pkey = host.get_partition();
|
||||
int64_t host_prepare_log_timestmap = host.get_timestamp();
|
||||
int64_t host_prepare_log_id = host.get_prepare_log_id();
|
||||
const ObPartitionLogInfo *part_array = host.get_participants();
|
||||
int64_t part_count = host.get_participant_count();
|
||||
const int64_t global_trans_version = host.get_global_trans_version();
|
||||
// default serve
|
||||
bool is_serving_host_part = true;
|
||||
|
||||
if (OB_UNLIKELY(part_count <= 0) || OB_ISNULL(part_array)) {
|
||||
LOG_ERROR("invalid participant array", K(part_count), K(part_array), K(host));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
}
|
||||
// first determine whether to service the host partition transaction
|
||||
// if not, simply discard the transaction context.
|
||||
// This ensures that a valid transaction context must have been created by a valid participant, and thus ensure that the transaction context must be processed
|
||||
else if (OB_FAIL(inc_part_trans_count_on_serving_(is_serving_host_part,
|
||||
part_mgr,
|
||||
trans_id,
|
||||
host_pkey,
|
||||
host_prepare_log_id,
|
||||
host_prepare_log_timestmap,
|
||||
print_participant_not_serve_info,
|
||||
stop_flag))) {
|
||||
if (OB_IN_STOP_STATE != ret) {
|
||||
LOG_ERROR("inc_part_trans_count_on_serving_ fail", KR(ret), K(host_pkey),
|
||||
K(host_prepare_log_id), K(host_prepare_log_timestmap));
|
||||
}
|
||||
} else if (! is_serving_host_part) {
|
||||
// host partition not serve
|
||||
ret = OB_INVALID_ERROR;
|
||||
}
|
||||
// Initialize the participant array on the premise of the host partition service
|
||||
else if (OB_FAIL(init_participant_array_(part_count))) {
|
||||
LOG_ERROR("init participant array fail", KR(ret), K(part_count));
|
||||
} else if (OB_FAIL(trx_sort_elem_.init(*this, trans_id, global_trans_version))) {
|
||||
LOG_ERROR("trx_sort_elem_ init fail", KR(ret), K(trans_id), K(global_trans_version));
|
||||
} else {
|
||||
trans_id_ = trans_id;
|
||||
|
||||
// Prepare the list of participants in the case of a host partitioned transaction service, the list of participants must contain host partitioned transactions
|
||||
// Check if each participant is served and add the served participants to the participant list
|
||||
for (int64_t index = 0; OB_SUCC(ret) && index < part_count; index++) {
|
||||
const ObPartitionLogInfo &pinfo = part_array[index];
|
||||
|
||||
if (! pinfo.is_valid()) {
|
||||
LOG_ERROR("participant info is invalid", K(pinfo), K(index), K(part_array));
|
||||
ret = OB_INVALID_DATA;
|
||||
} else {
|
||||
bool is_host_pkey = (host_pkey == pinfo.get_partition());
|
||||
|
||||
// host partition must serve
|
||||
bool is_serving = is_host_pkey;
|
||||
|
||||
if (! is_serving && OB_FAIL(inc_part_trans_count_on_serving_(is_serving,
|
||||
part_mgr,
|
||||
trans_id,
|
||||
pinfo.get_partition(),
|
||||
pinfo.get_log_id(),
|
||||
pinfo.get_log_timestamp(),
|
||||
print_participant_not_serve_info,
|
||||
stop_flag))) {
|
||||
if (OB_IN_STOP_STATE != ret) {
|
||||
LOG_ERROR("inc_part_trans_count_on_serving_ fail", KR(ret), K(pinfo));
|
||||
}
|
||||
} else if (is_serving) {
|
||||
// Add participant information for the service
|
||||
new (participants_ + participant_count_) TransPartInfo(pinfo.get_partition());
|
||||
participant_count_++;
|
||||
}
|
||||
|
||||
if (! is_serving) {
|
||||
if (print_participant_not_serve_info) {
|
||||
_TCTX_ISTAT("[PREPARE_PART] [PART_NOT_SERVE] TRANS_ID=%s PART=%s LOG_ID=%ld "
|
||||
"LOG_TSTAMP=%ld PARTICIPANT_INDEX=%ld/%ld",
|
||||
to_cstring(trans_id), to_cstring(pinfo.get_partition()), pinfo.get_log_id(),
|
||||
pinfo.get_log_timestamp(), index + 1, part_count);
|
||||
} else if (REACH_TIME_INTERVAL(PRINT_LOG_INTERVAL)) {
|
||||
_TCTX_ISTAT("[PREPARE_PART] [PART_NOT_SERVE] TRANS_ID=%s PART=%s LOG_ID=%ld "
|
||||
"LOG_TSTAMP=%ld PARTICIPANT_INDEX=%ld/%ld",
|
||||
to_cstring(trans_id), to_cstring(pinfo.get_partition()), pinfo.get_log_id(),
|
||||
pinfo.get_log_timestamp(), index + 1, part_count);
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
} else {
|
||||
_TCTX_DSTAT("[PREPARE_PART] TRANS_ID=%s PART=%s LOG_ID=%ld LOG_TSTAMP=%ld "
|
||||
"PARTICIPANT_INDEX=%ld/%ld IS_SERVING=%d",
|
||||
to_cstring(trans_id), to_cstring(pinfo.get_partition()), pinfo.get_log_id(),
|
||||
pinfo.get_log_timestamp(), index + 1, part_count, is_serving);
|
||||
}
|
||||
}
|
||||
} // for
|
||||
|
||||
// If the configuration requires participants to be sorted, the sorting operation is performed
|
||||
// The purpose is to ensure that the list of participants iterated out is the same in a restart cluster scenario
|
||||
if (OB_SUCC(ret)
|
||||
&& OB_UNLIKELY(NULL != host_ && host_->need_sort_participant())
|
||||
&& participant_count_ > 0) {
|
||||
std::sort(participants_, participants_ + participant_count_, TransPartInfoCompare());
|
||||
}
|
||||
|
||||
_TCTX_DSTAT("[PREPARE] TRANS_ID=%s SERVED_PARTICIPANTS=%ld/%ld",
|
||||
to_cstring(trans_id), participant_count_, part_count);
|
||||
}
|
||||
|
||||
// None of the partition participants are served, additional case
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_UNLIKELY(participant_count_ <= 0)) {
|
||||
LOG_ERROR("get participants count unexpected failed", K(participant_count_));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else if (OB_FAIL(init_trans_id_str_(trans_id))) {
|
||||
LOG_ERROR("init_trans_id_str_ fail", KR(ret), K(trans_id));
|
||||
} else {
|
||||
switch_state_(TRANS_CTX_STATE_PREPARED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Initialize trans_id_str per transaction context to avoid Committer to_cstring ObTransID impacting commit performance
|
||||
int TransCtx::init_trans_id_str_(const transaction::ObTransID &trans_id)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
// Use the maximum value for the buf length of the ObTransID, 1024 is large enough
|
||||
static const int64_t TRANS_ID_BUF_LENGTH = 1024;
|
||||
char trans_id_buf[TRANS_ID_BUF_LENGTH];
|
||||
int64_t pos = 0;
|
||||
|
||||
if (OB_FAIL(common::databuff_printf(trans_id_buf, TRANS_ID_BUF_LENGTH, pos, "%s", to_cstring(trans_id)))) {
|
||||
LOG_ERROR("databuff_printf fail", K(ret), K(trans_id), K(trans_id_buf), K(TRANS_ID_BUF_LENGTH), K(pos));
|
||||
} else if (OB_UNLIKELY(pos <= 0 || pos >= TRANS_ID_BUF_LENGTH)) {
|
||||
LOG_ERROR("local buf pos is not valid", K(pos), K(TRANS_ID_BUF_LENGTH), K(trans_id));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else {
|
||||
int64_t buf_len = pos + 1;
|
||||
char *buf = static_cast<char*>(allocator_.alloc(buf_len));
|
||||
|
||||
if (OB_ISNULL(buf)) {
|
||||
LOG_ERROR("allocator_ alloc for trans id str fail", K(buf), K(buf_len));
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
} else {
|
||||
MEMCPY(buf, trans_id_buf, pos);
|
||||
buf[pos] = '\0';
|
||||
|
||||
trans_id_str_.assign(buf, static_cast<int32_t>(pos));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TransCtx::switch_state_(const int target_state)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
state_ = static_cast<int8_t>(target_state);
|
||||
|
||||
if (NULL != host_) {
|
||||
if (OB_FAIL(host_->update_stat_info(target_state))) {
|
||||
LOG_ERROR("update stat info failed", KR(ret), K(target_state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TransCtx::add_ready_participant_(PartTransTask &part_trans_task,
|
||||
bool &is_part_trans_served,
|
||||
bool &is_all_participants_ready)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
if (OB_UNLIKELY(TRANS_CTX_STATE_PREPARED != state_)) {
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else {
|
||||
is_all_participants_ready = false;
|
||||
|
||||
// Check if the partition is in the participants list and if it exists, set the participants to the participants array
|
||||
bool existed = false;
|
||||
for (int64_t index = 0; OB_SUCC(ret) && ! existed && index < participant_count_; index++) {
|
||||
if (part_trans_task.get_partition() == participants_[index].pkey_) {
|
||||
existed = true;
|
||||
participants_[index].obj_ = &part_trans_task;
|
||||
}
|
||||
}
|
||||
|
||||
if (OB_SUCC(ret)) {
|
||||
// If the partitioned transaction is not in the list of participants, the partitioned transaction is no longer in service
|
||||
if (! existed) {
|
||||
is_part_trans_served = false;
|
||||
}
|
||||
// If the partition transaction is in the participant list, add it to the READY list
|
||||
else {
|
||||
ready_participant_count_++;
|
||||
is_all_participants_ready = (ready_participant_count_ == participant_count_);
|
||||
|
||||
// If all participants are clustered, link them together in order to construct a list of participant objects
|
||||
if (is_all_participants_ready && OB_FAIL(build_ready_participants_list_())) {
|
||||
LOG_ERROR("build_ready_participants_list_ fail", KR(ret), K(is_all_participants_ready),
|
||||
K(ready_participant_count_), K(participant_count_));
|
||||
} else {
|
||||
// All participants are READY to advance to the current state
|
||||
if (is_all_participants_ready) {
|
||||
switch_state_(TRANS_CTX_STATE_PARTICIPANT_READY);
|
||||
}
|
||||
|
||||
_TCTX_DSTAT("[ADD_PART] TRANS_ID=%s READY_PARTS=%ld/%ld READY=%d",
|
||||
to_cstring(trans_id_), ready_participant_count_,
|
||||
participant_count_, is_all_participants_ready);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::build_ready_participants_list_()
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (OB_ISNULL(participants_)) {
|
||||
LOG_ERROR("invalid participants", K(participants_));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else {
|
||||
// Linked list of participants
|
||||
for (int64_t index = 0; OB_SUCCESS == ret && index < participant_count_; index++) {
|
||||
if (OB_ISNULL(participants_[index].obj_)) {
|
||||
LOG_ERROR("participant object is NULL", K(participants_[index]), K(index),
|
||||
K(participant_count_), K(state_), K(trans_id_));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else if (index < participant_count_ - 1) {
|
||||
participants_[index].obj_->set_next_task(participants_[index + 1].obj_);
|
||||
} else {
|
||||
participants_[index].obj_->set_next_task(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// set ready obj list
|
||||
if (OB_SUCCESS == ret) {
|
||||
ready_participant_objs_ = participants_[0].obj_;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::inc_part_trans_count_on_serving_(bool &is_serving,
|
||||
IObLogPartMgr &part_mgr,
|
||||
const ObTransID &trans_id,
|
||||
const ObPartitionKey &key,
|
||||
const uint64_t prepare_log_id,
|
||||
const int64_t prepare_log_timestamp,
|
||||
const bool print_participant_not_serve_info,
|
||||
volatile bool &stop_flag)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (OB_UNLIKELY(!trans_id.is_valid())
|
||||
|| OB_UNLIKELY(!key.is_valid())
|
||||
|| OB_UNLIKELY(prepare_log_timestamp <= 0)) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
RETRY_FUNC(stop_flag, (part_mgr), inc_part_trans_count_on_serving,
|
||||
is_serving,
|
||||
key,
|
||||
prepare_log_id,
|
||||
prepare_log_timestamp,
|
||||
print_participant_not_serve_info,
|
||||
DATA_OP_TIMEOUT);
|
||||
|
||||
if (OB_SUCC(ret)) {
|
||||
_TCTX_DSTAT("[INC_TRANS_COUNT] IS_SERVING=%d TRANS_ID=%s PART=%s LOG_ID=%ld LOG_TSTAMP=%ld",
|
||||
is_serving, to_cstring(trans_id), to_cstring(key), prepare_log_id, prepare_log_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::sequence(const int64_t seq)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObSpinLockGuard guard(lock_);
|
||||
|
||||
if (OB_UNLIKELY(seq < 0)) {
|
||||
LOG_ERROR("invalid argument", K(seq));
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else if (TRANS_CTX_STATE_PARTICIPANT_READY != state_) {
|
||||
LOG_ERROR("state not match which is not DEP_PARSED", "state", print_state());
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else {
|
||||
seq_ = seq;
|
||||
switch_state_(TRANS_CTX_STATE_SEQUENCED);
|
||||
|
||||
// Update the global sequence number and Schema version for each participant
|
||||
PartTransTask *task = ready_participant_objs_;
|
||||
while (NULL != task) {
|
||||
task->set_global_trans_seq(seq);
|
||||
task = task->next_task();
|
||||
}
|
||||
|
||||
_TCTX_DSTAT("[SEQUENCE] COMMIT_VERSION=%ld TRANS_ID=%s SEQ=%ld",
|
||||
trx_sort_elem_.get_global_trans_version(),
|
||||
to_cstring(trans_id_),
|
||||
seq_);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::wait_data_ready(const int64_t timeout,
|
||||
volatile bool &stop_flag)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
int64_t end_time = get_timestamp() + timeout;
|
||||
|
||||
if (OB_UNLIKELY(TRANS_CTX_STATE_SEQUENCED != state_)) {
|
||||
LOG_ERROR("state is not match which is not SEQUENCED", "state", print_state());
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else {
|
||||
PartTransTask *part = ready_participant_objs_;
|
||||
while (OB_SUCC(ret) && NULL != part) {
|
||||
RETRY_FUNC(stop_flag, (*part), wait_data_ready, DATA_OP_TIMEOUT);
|
||||
|
||||
int64_t left_time = end_time - get_timestamp();
|
||||
if (left_time <= 0) {
|
||||
//_TCTX_ISTAT()
|
||||
OBLOG_LOG(INFO, "wait_data_ready timeout", KPC(this), KPC(part));
|
||||
}
|
||||
|
||||
if (OB_SUCC(ret)) {
|
||||
part = part->next_task();
|
||||
}
|
||||
} // while
|
||||
}
|
||||
|
||||
if (OB_SUCC(ret)) {
|
||||
switch_state_(TRANS_CTX_STATE_DATA_READY);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::commit()
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObSpinLockGuard guard(lock_);
|
||||
|
||||
if (OB_ISNULL(ready_participant_objs_)) {
|
||||
LOG_ERROR("ready participant objs is null",
|
||||
KPC(ready_participant_objs_), KPC(this));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else {
|
||||
const bool is_ddl_trans = ready_participant_objs_->is_ddl_trans();
|
||||
|
||||
if (TRANS_CTX_STATE_DATA_READY != state_) {
|
||||
LOG_ERROR("state not match which is not DATA_READY", "state", print_state(), K(is_ddl_trans));
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else {
|
||||
_TCTX_DSTAT("[COMMIT] TRANS_ID=%s/%ld PARTICIPANTS=%ld SEQ=%ld ",
|
||||
to_cstring(trans_id_), trx_sort_elem_.get_global_trans_version(),
|
||||
ready_participant_count_, seq_);
|
||||
|
||||
switch_state_(TRANS_CTX_STATE_COMMITTED);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::inc_revertable_participant_count(bool &all_participant_revertable)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObSpinLockGuard guard(lock_);
|
||||
|
||||
if (OB_UNLIKELY(TRANS_CTX_STATE_COMMITTED != state_)) {
|
||||
LOG_ERROR("state is not match which is not COMMITTED", "state", print_state());
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else {
|
||||
int64_t result_count = ATOMIC_AAF(&revertable_participant_count_, 1);
|
||||
all_participant_revertable = (result_count == ready_participant_count_);
|
||||
|
||||
if (result_count > ready_participant_count_) {
|
||||
LOG_ERROR("revertable participant count is larger than ready participant count",
|
||||
"revertable_participant_count", result_count,
|
||||
K(ready_participant_count_),
|
||||
K(*this));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::revert_participants()
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObSpinLockGuard guard(lock_);
|
||||
|
||||
if (OB_UNLIKELY(TRANS_CTX_STATE_COMMITTED != state_)) {
|
||||
LOG_ERROR("TransCtx has not been sequenced, can not revert participants",
|
||||
"state", print_state());
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else if (OB_UNLIKELY(revertable_participant_count_ != ready_participant_count_)) {
|
||||
LOG_ERROR("revertable_participant_count does not equal to participant count",
|
||||
K(revertable_participant_count_), K(ready_participant_count_));
|
||||
ret = OB_STATE_NOT_MATCH;
|
||||
} else {
|
||||
_TCTX_DSTAT("[REVERT_PARTICIPANTS] TRANS_ID=%s PARTICIPANTS=%ld SEQ=%ld",
|
||||
to_cstring(trans_id_), participant_count_, seq_);
|
||||
|
||||
// Note: All participants are recalled by external modules
|
||||
ready_participant_objs_ = NULL;
|
||||
ready_participant_count_ = 0;
|
||||
revertable_participant_count_ = 0;
|
||||
|
||||
switch_state_(TRANS_CTX_STATE_PARTICIPANT_REVERTED);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::lock()
|
||||
{
|
||||
return lock_.lock();
|
||||
}
|
||||
|
||||
int TransCtx::unlock()
|
||||
{
|
||||
return lock_.unlock();
|
||||
}
|
||||
|
||||
int TransCtx::get_tenant_id(uint64_t &tenant_id) const
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObSpinLockGuard guard(lock_);
|
||||
if (OB_ISNULL(ready_participant_objs_)) {
|
||||
LOG_ERROR("ready participant objs is null, can not decide tenant id",
|
||||
KPC(ready_participant_objs_), KPC(this));
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
} else {
|
||||
// The tenant ID of the first participant is used as the tenant ID of the distributed transaction
|
||||
// TODO: support for cross-tenant transactions
|
||||
tenant_id = ready_participant_objs_->get_tenant_id();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TransCtx::init_participant_array_(const int64_t part_count)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
if (OB_UNLIKELY(part_count <= 0)) {
|
||||
LOG_ERROR("invalid argument", K(part_count));
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
} else if (OB_UNLIKELY(NULL != participants_)) {
|
||||
LOG_ERROR("participant array has been initialized", K(participants_), K(participant_count_));
|
||||
ret = OB_INIT_TWICE;
|
||||
} else {
|
||||
int64_t parts_alloc_size = part_count * sizeof(participants_[0]);
|
||||
|
||||
participants_ = static_cast<TransPartInfo *>(allocator_.alloc(parts_alloc_size));
|
||||
|
||||
if (OB_ISNULL(participants_)) {
|
||||
LOG_ERROR("allocate memory for participant array fail", K(part_count), K(parts_alloc_size));
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
} else {
|
||||
// Number of valid participants is 0
|
||||
participant_count_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TransCtx::destroy_participant_array_()
|
||||
{
|
||||
if (NULL != participants_ && participant_count_ > 0) {
|
||||
for (int64_t index = 0; index < participant_count_; index++) {
|
||||
participants_[index].~TransPartInfo();
|
||||
}
|
||||
|
||||
allocator_.free(participants_);
|
||||
participants_ = NULL;
|
||||
participant_count_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace liboblog
|
||||
} // namespace oceanbase
|
||||
Reference in New Issue
Block a user