/** * 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. */ #include "ob_multi_data_source.h" #include "lib/ob_abort.h" #include "lib/ob_errno.h" #include "ob_trans_define.h" #include "share/ob_errno.h" #include "storage/ddl/ob_ddl_clog.h" #include "storage/tx/ob_trans_part_ctx.h" #include "storage/memtable/ob_memtable_context.h" #include "storage/tablelock/ob_table_lock_common.h" #include "storage/tablet/ob_tablet_binding_helper.h" #include "share/ls/ob_ls_operator.h" #include "storage/multi_data_source/runtime_utility/mds_factory.h" #define NEED_MDS_REGISTER_DEFINE #include "storage/multi_data_source/compile_utility/mds_register.h" #undef NEED_MDS_REGISTER_DEFINE #include "share/ob_standby_upgrade.h" // ObStandbyUpgrade namespace oceanbase { using namespace common; using namespace memtable; using namespace storage; using namespace transaction::tablelock; namespace transaction { //##################################################### // ObTxBufferNode //##################################################### OB_SERIALIZE_MEMBER(ObTxBufferNode, type_, data_); int ObTxBufferNode::init(const ObTxDataSourceType type, const ObString &data, const share::SCN &base_scn, mds::BufferCtx *ctx) { int ret = OB_SUCCESS; if (OB_UNLIKELY(type <= ObTxDataSourceType::UNKNOWN || type >= ObTxDataSourceType::MAX_TYPE)) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid argument", KR(ret), K(type)); } else { reset(); type_ = type; data_ = data; mds_base_scn_ = base_scn; buffer_ctx_node_.set_ctx(ctx); } return ret; } void ObTxBufferNode::replace_data(const common::ObString &data) { if (nullptr != data_.ptr()) { ob_free(data_.ptr()); data_.assign_ptr(nullptr, 0); } data_ = data; has_submitted_ = false; has_synced_ = false; } bool ObTxBufferNode::operator==(const ObTxBufferNode &buffer_node) const { bool is_same = false; if (has_submitted_ == buffer_node.has_submitted_ && has_synced_ == buffer_node.has_synced_ && mds_base_scn_ == buffer_node.mds_base_scn_ && type_ == buffer_node.type_ && data_ == buffer_node.data_) { is_same = true; } return is_same; } //##################################################### // ObMulSourceTxDataNotifier //##################################################### int ObMulSourceTxDataNotifier::notify_table_lock(const ObTxBufferNodeArray &array, const ObMulSourceDataNotifyArg &arg, ObPartTransCtx *part_ctx, int64_t &total_time) { int ret = OB_SUCCESS; ObMemtableCtx *mt_ctx = nullptr; ObMulSourceDataNotifyArg tmp_notify_arg = arg; const NotifyType notify_type = NotifyType::TX_END; if (OB_ISNULL(part_ctx)) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid argument", KR(ret), K(part_ctx)); } else if (OB_ISNULL(mt_ctx = part_ctx->get_memtable_ctx())) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "memtable ctx should not null", KR(ret), K(part_ctx)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < array.count(); ++i) { ObTimeGuard notify_time; const ObTxBufferNode &node = array.at(i); const char *buf = node.data_.ptr(); const int64_t len = node.data_.length(); tmp_notify_arg.redo_submitted_ = node.is_submitted(); tmp_notify_arg.redo_synced_ = node.is_synced(); if (ObTxDataSourceType::TABLE_LOCK == node.type_) { if (OB_FAIL(notify_table_lock(notify_type, buf, len, tmp_notify_arg, mt_ctx))) { TRANS_LOG(WARN, "notify table lock failed", KR(ret), K(node)); } } if (notify_time.get_diff() > 1 * 1000 * 1000) { TRANS_LOG(INFO, "notify one data source with too much time", K(ret), K(notify_type), K(i), K(node), K(arg), K(notify_time.get_diff())); } total_time += notify_time.get_diff(); } } return ret; } int ObMulSourceTxDataNotifier::notify(const ObTxBufferNodeArray &array, const NotifyType notify_type, const ObMulSourceDataNotifyArg &arg, ObPartTransCtx *part_ctx, int64_t &total_time) { int ret = OB_SUCCESS; ObMemtableCtx *mt_ctx = nullptr; ObMulSourceDataNotifyArg tmp_notify_arg = arg; if (OB_ISNULL(part_ctx)) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid argument", KR(ret), K(part_ctx)); } else if (OB_ISNULL(mt_ctx = part_ctx->get_memtable_ctx())) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "memtable ctx should not null", KR(ret), K(part_ctx)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < array.count(); ++i) { ObTimeGuard notify_time; const ObTxBufferNode &node = array.at(i); const char *buf = node.data_.ptr(); const int64_t len = node.data_.length(); tmp_notify_arg.redo_submitted_ = node.is_submitted(); tmp_notify_arg.redo_synced_ = node.is_synced(); OB_ASSERT(node.type_ != ObTxDataSourceType::BEFORE_VERSION_4_1); if (node.type_ < ObTxDataSourceType::BEFORE_VERSION_4_1 && ObTxDataSourceType::CREATE_TABLET_NEW_MDS != node.type_ && ObTxDataSourceType::DELETE_TABLET_NEW_MDS != node.type_ && ObTxDataSourceType::UNBIND_TABLET_NEW_MDS != node.type_) { switch (node.type_) { case ObTxDataSourceType::TABLE_LOCK: { ret = notify_table_lock(notify_type, buf, len, tmp_notify_arg, mt_ctx); break; } case ObTxDataSourceType::LS_TABLE: { ret = notify_ls_table(notify_type, buf, len, tmp_notify_arg); break; } case ObTxDataSourceType::DDL_TRANS: { ret = notify_ddl_trans(notify_type, buf, len, tmp_notify_arg); break; } case ObTxDataSourceType::DDL_BARRIER: { ret = notify_ddl_barrier(notify_type, buf, len, tmp_notify_arg); break; } case ObTxDataSourceType::STANDBY_UPGRADE: { ret = notify_standby_upgrade(notify_type, buf, len, tmp_notify_arg); break; } default: { ret = OB_ERR_UNEXPECTED; break; } } } else { switch (node.type_) { #define NEED_GENERATE_MDS_FRAME_CODE_FOR_TRANSACTION #define _GENERATE_MDS_FRAME_CODE_FOR_TRANSACTION_(HELPER_CLASS, BUFFER_CTX_TYPE, ID, ENUM_NAME) \ case ObTxDataSourceType::ENUM_NAME:\ switch (notify_type) {\ case NotifyType::REGISTER_SUCC:\ {\ if (!arg.for_replay_) {\ if (OB_FAIL(HELPER_CLASS::on_register(buf, len, *const_cast(node.get_buffer_ctx_node().get_ctx())))) {\ MDS_LOG(WARN, "call user helper on_register failed", KR(ret));\ } else {\ MDS_LOG(TRACE, "call user helper on_register success", K(node));\ }\ } else {\ if (OB_FAIL(HELPER_CLASS::on_replay(buf, len, arg.scn_, *const_cast(node.get_buffer_ctx_node().get_ctx())))) {\ MDS_LOG(WARN, "call user helper on_replay failed", KR(ret));\ } else {\ MDS_LOG(TRACE, "call user helper on_replay success", K(node));\ }\ }\ }\ break;\ case NotifyType::ON_REDO:\ node.get_buffer_ctx_node().on_redo(arg.scn_);\ break;\ case NotifyType::TX_END:\ node.get_buffer_ctx_node().before_prepare();\ break;\ case NotifyType::ON_PREPARE:\ node.get_buffer_ctx_node().on_prepare(arg.trans_version_);\ break;\ case NotifyType::ON_COMMIT:\ if (OB_FAIL(common::meta::MdsCommitForOldMdsWrapper::\ on_commit_for_old_mds(buf,\ len,\ tmp_notify_arg))) {\ MDS_LOG(WARN, "fail to on_commit_for_old_mds", KR(ret));\ } else if (arg.for_replay_ && !common::meta::MdsCheckCanReplayWrapper::\ check_can_replay_commit(buf,\ len,\ arg.scn_,\ *const_cast(node.get_buffer_ctx_node().get_ctx()))) {\ ret = OB_EAGAIN;\ MDS_LOG(INFO, "check can replay commit return false", KR(ret), K(node));\ } else {\ node.get_buffer_ctx_node().on_commit(arg.trans_version_, arg.scn_);\ }\ break;\ case NotifyType::ON_ABORT:\ node.get_buffer_ctx_node().on_abort(arg.scn_);\ break;\ default:\ ob_abort();\ }\ break; #include "storage/multi_data_source/compile_utility/mds_register.h" #undef _GENERATE_MDS_FRAME_CODE_FOR_TRANSACTION_ #undef NEED_GENERATE_MDS_FRAME_CODE_FOR_TRANSACTION default: ob_abort(); } } if (OB_FAIL(ret)) { TRANS_LOG(WARN, "notify data source failed", KR(ret), K(node)); } if (notify_time.get_diff() > 1 * 1000 * 1000) { TRANS_LOG(INFO, "notify one data source with too much time", K(ret), K(notify_type), K(i), K(node), K(arg), K(notify_time.get_diff())); } total_time += notify_time.get_diff(); } } return ret; } void ObMulSourceTxDataNotifier::ob_abort_log_cb_notify_(const NotifyType type, int err_code, bool for_replay) { bool need_core = false; if (NotifyType::ON_REDO == type || NotifyType::ON_PREPARE == type || NotifyType::ON_COMMIT == type || NotifyType::ON_ABORT == type) { if (for_replay) { if (OB_SUCCESS != err_code && OB_TENANT_OUT_OF_MEM != err_code && OB_ALLOCATE_MEMORY_FAILED != err_code && OB_EAGAIN != err_code) { need_core = true; } } else { if (OB_SUCCESS != err_code) { need_core = true; } } } if (need_core) { TRANS_LOG_RET(ERROR, OB_ERROR, "data source can not return error in log_cb on_success", K(type), K(err_code), K(for_replay)); ob_usleep(1000 * 1000); ob_abort(); } } int ObMulSourceTxDataNotifier::notify_table_lock( const NotifyType type, const char *buf, const int64_t len, const ObMulSourceDataNotifyArg &arg, ObMemtableCtx *mt_ctx) { int ret = OB_SUCCESS; tablelock::ObTableLockOp lock_op; bool need_replay = false; const bool is_replay_multi_source = true; const bool is_committed = true; int64_t pos = 0; const int64_t curr_timestamp = ObTimeUtility::current_time(); if (OB_ISNULL(buf) || OB_UNLIKELY(len <= 0) || OB_ISNULL(mt_ctx)) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid argument", K(ret), K(buf), K(len), K(mt_ctx)); } else if (NotifyType::TX_END != type) { // TABLELOCK only deal with tx_end type, but not redo/prepare/commit/abort. } else if (!arg.for_replay_) { // TABLELOCK only need deal with replay process, but not apply. // the replay process will produce a lock op and will be dealt at trans end. } else if (OB_FAIL(lock_op.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "deserialize lock op failed", K(ret), K(len), KP(buf)); } else if (FALSE_IT(lock_op.create_timestamp_ = OB_MIN(curr_timestamp, lock_op.create_timestamp_))) { } else if (OB_FAIL(mt_ctx->replay_lock(lock_op, arg.scn_))) { TRANS_LOG(WARN, "replay lock failed", K(ret)); } else { // do nothing } TABLELOCK_LOG(DEBUG, "ObMulSourceTxDataNotifier::notify_table_lock", K(ret), K(type), K(arg.for_replay_), K(lock_op)); ob_abort_log_cb_notify_(type, ret, arg.for_replay_); return ret; } int ObMulSourceTxDataNotifier::notify_ls_table(const NotifyType type, const char *buf, const int64_t len, const ObMulSourceDataNotifyArg &arg) { int ret = OB_SUCCESS; //TODO by msy164651 just for check share::ObLSAttr ls_attr; int64_t pos = 0; if (OB_FAIL(ls_attr.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "failed to deserialize ls attr", KR(ret), K(buf), K(len)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } TRANS_LOG(INFO, "ls table notify", KR(ret), K(ls_attr)); ob_abort_log_cb_notify_(type, ret, arg.for_replay_); return ret; } int ObMulSourceTxDataNotifier::notify_standby_upgrade(const NotifyType type, const char *buf, const int64_t len, const ObMulSourceDataNotifyArg &arg) { int ret = OB_SUCCESS; share::ObStandbyUpgrade data_version; int64_t pos = 0; if (OB_ISNULL(buf) || OB_UNLIKELY(len <= 0)) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid argument", KR(ret), K(buf), K(len), K(type)); } else if (OB_FAIL(data_version.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "failed to deserialize data_version", KR(ret), K(buf), K(len)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } TRANS_LOG(INFO, "standby upgrade notify", KR(ret), K(data_version), K(len), K(type)); ob_abort_log_cb_notify_(type, ret, arg.for_replay_); return ret; } int ObMulSourceTxDataNotifier::notify_ddl_trans(const NotifyType type, const char *buf, const int64_t len, const ObMulSourceDataNotifyArg &arg) { int ret = OB_SUCCESS; TRANS_LOG(DEBUG, "ddl trans commit notify", KR(ret), K(type), KP(buf), K(len)); ob_abort_log_cb_notify_(type, ret, arg.for_replay_); return ret; } int ObMulSourceTxDataNotifier::notify_ddl_barrier(const NotifyType type, const char *buf, const int64_t len, const ObMulSourceDataNotifyArg &arg) { int ret = OB_SUCCESS; int64_t pos = 0; ObDDLBarrierLog log; if (OB_FAIL(log.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "failed to deserialize buf", K(ret)); } else if (OB_UNLIKELY(!log.is_valid())) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(ERROR, "invalid ddl barrier log", K(ret), K(type), K(arg)); } else { TRANS_LOG(INFO, "notify ddl barrier", K(type), K(arg), K(log)); } ob_abort_log_cb_notify_(type, ret, arg.for_replay_); return ret; } //##################################################### // ObMulSourceTxDataDump //##################################################### const char * ObMulSourceTxDataDump::dump_buf(ObTxDataSourceType source_type, const char *buf, const int64_t len) { int ret = OB_SUCCESS; const char *dump_str = "Unkown Multi Data Source"; int64_t pos = 0; if (OB_ISNULL(buf) || OB_UNLIKELY(len <= 0)) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid argument", K(ret), K(buf), K(len)); } else { switch (source_type) { case ObTxDataSourceType::TABLE_LOCK: { tablelock::ObTableLockOp lock_op; if (OB_FAIL(lock_op.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "deserialize lock op failed", K(ret), K(len), KP(buf)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } else { dump_str = to_cstring(lock_op); } break; } case ObTxDataSourceType::LS_TABLE: { share::ObLSAttr ls_attr; if (OB_FAIL(ls_attr.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "failed to deserialize ls attr", KR(ret), K(buf), K(len)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } else { dump_str = to_cstring(ls_attr); } break; } case ObTxDataSourceType::CREATE_TABLET_NEW_MDS: { obrpc::ObBatchCreateTabletArg create_arg; if (OB_FAIL(create_arg.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "deserialize failed for ls_member trans", KR(ret)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } else { dump_str = to_cstring(create_arg); } break; } case ObTxDataSourceType::DELETE_TABLET_NEW_MDS: { obrpc::ObBatchRemoveTabletArg remove_arg; if (OB_FAIL(remove_arg.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "deserialize failed for ls_member trans", KR(ret)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } else { dump_str = to_cstring(remove_arg); } break; } case ObTxDataSourceType::DDL_BARRIER: { ObDDLBarrierLog log; if (OB_FAIL(log.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "failed to deserialize buf", K(ret)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } else { dump_str = to_cstring(log); } break; } case ObTxDataSourceType::UNBIND_TABLET_NEW_MDS: { ObBatchUnbindTabletArg modify_arg; if (OB_FAIL(modify_arg.deserialize(buf, len, pos))) { TRANS_LOG(WARN, "failed to deserialize arg", K(ret)); } else if (pos > len) { ret = OB_ERR_UNEXPECTED; TRANS_LOG(WARN, "deserialize error", KR(ret), K(pos), K(len)); } else { dump_str = to_cstring(modify_arg); } break; } default: { // TRANS_LOG(ERROR, "Unkown Multi Data Source", K(source_type), KP(buf), K(len)); } } } return dump_str; } //##################################################### // ObRegisterMdsArg //##################################################### OB_SERIALIZE_MEMBER(ObRegisterMdsFlag, need_flush_redo_instantly_, mds_base_scn_); //##################################################### // ObMDSStr //##################################################### OB_SERIALIZE_MEMBER(ObMDSInnerSQLStr, mds_str_, type_, ls_id_, register_flag_); ObMDSInnerSQLStr::ObMDSInnerSQLStr() { reset(); } ObMDSInnerSQLStr::~ObMDSInnerSQLStr() {} void ObMDSInnerSQLStr::reset() { mds_str_.reset(); type_ = ObTxDataSourceType::UNKNOWN; ls_id_.reset(); register_flag_.reset(); } int ObMDSInnerSQLStr::set(const char *msd_buf, const int64_t msd_buf_len, const ObTxDataSourceType &type, const share::ObLSID ls_id, const ObRegisterMdsFlag ®ister_flag) { int ret = OB_SUCCESS; if (OB_ISNULL(msd_buf) || 0 == msd_buf_len || ObTxDataSourceType::UNKNOWN == type || !ls_id.is_valid()) { ret = OB_INVALID_ARGUMENT; TRANS_LOG(WARN, "invalid arguments", K(ret), KP(msd_buf), K(msd_buf_len), K(type), K(ls_id)); } else if (!mds_str_.empty()) { TRANS_LOG(WARN, "MSD str is not empty", K(ret), K(*this)); } else { mds_str_.assign_ptr(msd_buf, msd_buf_len); type_ = type; ls_id_ = ls_id; register_flag_ = register_flag; } return ret; } } // transaction } // oceanbase