Files
oceanbase/src/share/ob_global_autoinc_service.cpp
2023-07-20 12:12:38 +00:00

514 lines
21 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 SHARE
#include "lib/mysqlclient/ob_mysql_result.h"
#include "lib/mysqlclient/ob_mysql_transaction.h"
#include "observer/ob_server_struct.h"
#include "observer/ob_sql_client_decorator.h"
#include "observer/ob_srv_network_frame.h"
#include "share/ob_autoincrement_service.h"
#include "share/ob_define.h"
#include "share/ob_global_autoinc_service.h"
#include "storage/ls/ob_ls_tx_service.h"
#include "storage/slog/ob_storage_logger.h"
#include "storage/slog/ob_storage_log.h"
#include "storage/slog/ob_storage_log_replayer.h"
#include "storage/tx_storage/ob_ls_service.h"
#include "share/schema/ob_schema_struct.h"
#include "share/schema/ob_schema_getter_guard.h"
#include "share/schema/ob_multi_version_schema_service.h"
namespace oceanbase
{
using namespace oceanbase::common;
using namespace oceanbase::share;
namespace share
{
int ObAutoIncCacheNode::init(const uint64_t sequence_value,
const uint64_t last_available_value,
const uint64_t sync_value,
const int64_t autoinc_version)
{
int ret = OB_SUCCESS;
if (sequence_value <= 0 || last_available_value < sequence_value || sync_value > sequence_value || autoinc_version < OB_INVALID_VERSION) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(sequence_value), K(last_available_value), K(sync_value));
} else {
sequence_value_ = sequence_value;
last_available_value_ = last_available_value;
sync_value_ = sync_value;
autoinc_version_ = autoinc_version;
}
return ret;
}
bool ObAutoIncCacheNode::need_fetch_next_node(const uint64_t base_value,
const uint64_t desired_cnt,
const uint64_t max_value) const
{
bool bret = false;
if (OB_UNLIKELY(last_available_value_ == max_value)) {
bret = false;
} else if (OB_LIKELY(last_available_value_ >= desired_cnt)) {
uint64_t new_base_value = std::max(base_value, sequence_value_);
bret = new_base_value > last_available_value_ - desired_cnt + 1;
} else {
bret = true;
}
return bret;
}
int ObAutoIncCacheNode::update_sequence_value(const uint64_t sequence_value)
{
int ret = OB_SUCCESS;
if (OB_UNLIKELY(!is_valid())) {
ret = OB_NOT_INIT;
LOG_WARN("update invalid cache is not allowed", K(ret));
} else if (OB_UNLIKELY(sequence_value > last_available_value_ ||
sequence_value < sequence_value_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(sequence_value),
K(sequence_value_), K(last_available_value_));
} else {
sequence_value_ = sequence_value;
}
return ret;
}
int ObAutoIncCacheNode::update_available_value(const uint64_t available_value)
{
int ret = OB_SUCCESS;
if (OB_UNLIKELY(!is_valid())) {
ret = OB_NOT_INIT;
LOG_WARN("update invalid cache is not allowed", K(ret));
} else if (OB_UNLIKELY(available_value < last_available_value_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(available_value), K(last_available_value_));
} else {
last_available_value_ = available_value;
}
return ret;
}
int ObAutoIncCacheNode::update_sync_value(const uint64_t sync_value)
{
int ret = OB_SUCCESS;
if (OB_UNLIKELY(!is_valid())) {
ret = OB_NOT_INIT;
LOG_WARN("update invalid cache is not allowed", K(ret));
} else if (OB_UNLIKELY(sync_value > sequence_value_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(sync_value), K(sequence_value_));
} else {
sync_value_ = sync_value;
}
return ret;
}
int ObGlobalAutoIncService::init(const ObAddr &addr, ObMySQLProxy *mysql_proxy)
{
int ret = OB_SUCCESS;
ObMemAttr attr(MTL_ID(), ObModIds::OB_AUTOINCREMENT);
if (OB_ISNULL(mysql_proxy)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), KP(mysql_proxy));
} else if (OB_FAIL(inner_table_proxy_.init(mysql_proxy))) {
LOG_WARN("init inner table proxy failed", K(ret));
} else if (OB_FAIL(autoinc_map_.create(ObGlobalAutoIncService::INIT_HASHMAP_SIZE,
attr,
attr))) {
LOG_WARN("init autoinc_map_ failed", K(ret));
} else {
for (int64_t i = 0; i < MUTEX_NUM; ++i) {
op_mutex_[i].set_latch_id(common::ObLatchIds::AUTO_INCREMENT_GAIS_LOCK);
}
self_ = addr;
is_inited_ = true;
}
return ret;
}
int ObGlobalAutoIncService::mtl_init(ObGlobalAutoIncService *&gais)
{
int ret = OB_SUCCESS;
ObMySQLProxy *mysql_proxy = GCTX.sql_proxy_;
const ObAddr &self = GCTX.self_addr();
ret = gais->init(self, mysql_proxy);
return ret;
}
void ObGlobalAutoIncService::destroy()
{
autoinc_map_.destroy();
inner_table_proxy_.reset();
ObSpinLockGuard lock(cache_ls_lock_);
cache_ls_ = NULL;
is_leader_ = false;
is_inited_ = false;
}
int ObGlobalAutoIncService::clear()
{
int ret = OB_SUCCESS;
if (autoinc_map_.size() > 0) {
ret = autoinc_map_.clear();
}
return ret;
}
int ObGlobalAutoIncService::handle_next_autoinc_request(
const ObGAISNextAutoIncValReq &request,
obrpc::ObGAISNextValRpcResult &result)
{
int ret = OB_SUCCESS;
const AutoincKey &key = request.autoinc_key_;
const uint64_t desired_count = request.desired_cnt_;
bool is_leader = false;
lib::ObMutex &mutex = op_mutex_[key.hash() % MUTEX_NUM];
if (OB_UNLIKELY(!is_inited_)) {
ret = OB_NOT_INIT;
LOG_WARN("gloabl service is not init", K(ret));
} else if (OB_UNLIKELY(!request.is_valid())) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(request));
} else if (OB_FAIL(check_leader_(key.tenant_id_, is_leader))) {
LOG_WARN("check leader failed", K(ret), K(request.sender_), K(self_));
} else if (OB_UNLIKELY(!is_leader)) {
ret = OB_NOT_MASTER;
LOG_WARN("gais service is not leader", K(ret));
} else if (OB_FAIL(mutex.lock())) {
LOG_WARN("fail to get lock", K(ret));
} else {
ObAutoIncCacheNode cache_node;
int err = autoinc_map_.get_refactored(key, cache_node);
const int64_t tenant_id = key.tenant_id_;
const int64_t request_version = request.autoinc_version_;
LOG_TRACE("begin handle req autoinc request", K(request), K(cache_node));
if (OB_UNLIKELY(OB_SUCCESS != err && OB_HASH_NOT_EXIST != err)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("failed to get seq value", K(ret), K(key));
} else if (OB_UNLIKELY(!cache_node.is_valid()
|| (request_version == cache_node.autoinc_version_
&& cache_node.need_fetch_next_node(
request.base_value_, desired_count, request.max_value_)))) {
OZ(fetch_next_node_(request, cache_node));
} else if (OB_UNLIKELY(request_version > cache_node.autoinc_version_)) {
LOG_INFO("start to reset old global table node", K(tenant_id), K(key.table_id_),
K(request_version), K(cache_node.autoinc_version_));
cache_node.reset();
OZ(fetch_next_node_(request, cache_node));
} else if (OB_UNLIKELY(request_version < cache_node.autoinc_version_)) {
ret = OB_AUTOINC_CACHE_NOT_EQUAL;
LOG_WARN("request autoinc_version is less than autoinc_version_ in table_node, it should retry", KR(ret), K(tenant_id), K(key.table_id_),
K(request_version), K(cache_node.autoinc_version_));
}
if (OB_SUCC(ret)) {
if (OB_UNLIKELY(!cache_node.is_valid())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Unexpected cache node", K(ret), K(cache_node));
} else {
const uint64_t start_inclusive = std::max(cache_node.sequence_value_, request.base_value_);
const uint64_t max_value = request.max_value_;
uint64_t end_inclusive = 0;
if (max_value >= request.desired_cnt_ &&
start_inclusive <= max_value - request.desired_cnt_ + 1) {
end_inclusive = start_inclusive + request.desired_cnt_ - 1;
if (OB_UNLIKELY(end_inclusive > cache_node.last_available_value_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected value", K(ret), K(end_inclusive), K(cache_node));
} else if (OB_UNLIKELY(end_inclusive == cache_node.last_available_value_)) {
// the cache node is run out
cache_node.reset();
} else if (OB_FAIL(cache_node.update_sequence_value(end_inclusive + 1))) {
LOG_WARN("fail to update sequence value", K(ret), K(cache_node), K(end_inclusive));
}
} else if (OB_FAIL(cache_node.update_sequence_value(max_value))) {
LOG_WARN("fail to update sequence value", K(ret), K(cache_node), K(max_value));
} else {
end_inclusive = max_value;
}
if (OB_SUCC(ret)) {
uint64_t sync_value = cache_node.sync_value_;
if (request.base_value_ != 0 && request.base_value_ - 1 > sync_value) {
sync_value = request.base_value_ - 1;
}
if (OB_FAIL(result.init(start_inclusive, end_inclusive, sync_value))) {
LOG_WARN("init result failed", K(ret), K(cache_node));
} else if (OB_FAIL(autoinc_map_.set_refactored(key, cache_node, 1))) {
LOG_WARN("set autoinc_map_ failed", K(ret));
}
}
LOG_TRACE("after handle req autoinc request", K(request), K(cache_node));
}
}
mutex.unlock();
}
return ret;
}
int ObGlobalAutoIncService::handle_curr_autoinc_request(const ObGAISAutoIncKeyArg &request,
obrpc::ObGAISCurrValRpcResult &result)
{
int ret = OB_SUCCESS;
const AutoincKey &key = request.autoinc_key_;
uint64_t sequence_value = 0;
uint64_t sync_value = 0;
bool is_leader = false;
lib::ObMutex &mutex = op_mutex_[key.hash() % MUTEX_NUM];
if (OB_UNLIKELY(!is_inited_)) {
ret = OB_NOT_INIT;
LOG_WARN("gloabl service is not init", K(ret));
} else if (OB_UNLIKELY(!request.is_valid())) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(request));
} else if (OB_FAIL(check_leader_(key.tenant_id_, is_leader))) {
LOG_WARN("check leader failed", K(ret), K(request.sender_), K(self_));
} else if (OB_FAIL(mutex.lock())) {
LOG_WARN("fail to get lock", K(ret));
} else {
ObAutoIncCacheNode cache_node;
const int64_t tenant_id = key.tenant_id_;
int err = autoinc_map_.get_refactored(key, cache_node);
const int64_t request_version = request.autoinc_version_;
LOG_TRACE("start handle get autoinc request", K(request), K(cache_node));
if (OB_UNLIKELY(OB_SUCCESS != err && OB_HASH_NOT_EXIST != err)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("failed to get seq value", K(ret), K(key));
} else if (is_leader
&& OB_LIKELY(cache_node.is_valid())
&& request_version == cache_node.autoinc_version_) {
// get autoinc values from cache
sequence_value = cache_node.sequence_value_;
sync_value = cache_node.sync_value_;
// hash not exist, cache node is non-valid or service is not leader,
// read value from inner table
} else if (OB_FAIL(read_value_from_inner_table_(key, request_version, sequence_value, sync_value))) {
LOG_WARN("fail to read value from inner table", KR(ret), K(tenant_id), K_(key.table_id));
}
if (OB_SUCC(ret)) {
if (OB_FAIL(result.init(sequence_value, sync_value))) {
LOG_WARN("failed to init result", KR(ret), K(tenant_id), K_(key.table_id), K(request_version), K(cache_node));
}
}
mutex.unlock();
}
return ret;
}
int ObGlobalAutoIncService::handle_push_autoinc_request(
const ObGAISPushAutoIncValReq &request,
uint64_t &sync_value)
{
int ret = OB_SUCCESS;
const AutoincKey &key = request.autoinc_key_;
bool is_leader = false;
lib::ObMutex &mutex = op_mutex_[key.hash() % MUTEX_NUM];
if (OB_UNLIKELY(!is_inited_)) {
ret = OB_NOT_INIT;
LOG_WARN("gloabl service is not init", K(ret));
} else if (OB_UNLIKELY(!request.is_valid())) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(request));
} else if (OB_FAIL(check_leader_(key.tenant_id_, is_leader))) {
LOG_WARN("check leader failed", K(ret), K(request.sender_), K(self_));
} else if (OB_UNLIKELY(!is_leader)) {
ret = OB_NOT_MASTER;
LOG_WARN("gais service is not leader", K(ret));
} else if (OB_FAIL(mutex.lock())) {
LOG_WARN("fail to get lock", K(ret));
} else {
ObAutoIncCacheNode cache_node;
const int64_t tenant_id = key.tenant_id_;
int err = autoinc_map_.get_refactored(key, cache_node);
const int64_t request_version = request.autoinc_version_;
LOG_TRACE("start handle push global autoinc request", K(request), K(cache_node));
if (OB_UNLIKELY(OB_SUCCESS != err && OB_HASH_NOT_EXIST != err)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("failed to get seq value", K(ret), K(key));
} else if (OB_UNLIKELY(OB_HASH_NOT_EXIST == err
|| (request_version == cache_node.autoinc_version_
&& cache_node.need_sync(request.base_value_))
// cache node is expired
|| (request_version > cache_node.autoinc_version_))) {
if (request_version > cache_node.autoinc_version_) {
cache_node.reset();
}
if (OB_FAIL(sync_value_to_inner_table_(request, cache_node, sync_value))) {
LOG_WARN("sync to inner table failed", K(ret));
} else if (OB_FAIL(autoinc_map_.set_refactored(key, cache_node, 1))) {
LOG_WARN("set autoinc_map_ failed", K(ret));
}
// old request just ignore
} else if (OB_UNLIKELY(request_version < cache_node.autoinc_version_)) {
ret = OB_AUTOINC_CACHE_NOT_EQUAL;
LOG_WARN("request autoinc_version is less than cache_node autoinc_version", KR(ret), K(tenant_id), K_(key.table_id),
K(request_version), K(cache_node.autoinc_version_));
} else if (OB_LIKELY(request_version == cache_node.autoinc_version_)) {
sync_value = cache_node.sync_value_;
}
mutex.unlock();
}
return ret;
}
int ObGlobalAutoIncService::handle_clear_autoinc_cache_request(const ObGAISAutoIncKeyArg &request)
{
int ret = OB_SUCCESS;
const AutoincKey &key = request.autoinc_key_;
bool is_leader = false;
lib::ObMutex &mutex = op_mutex_[key.hash() % MUTEX_NUM];
if (OB_UNLIKELY(!is_inited_)) {
ret = OB_NOT_INIT;
LOG_WARN("gloabl service is not init", K(ret));
} else if (OB_UNLIKELY(!request.is_valid())) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(request));
} else if (OB_FAIL(check_leader_(key.tenant_id_, is_leader))) {
LOG_WARN("check leader failed", K(ret), K(request.sender_), K(self_));
} else if (OB_UNLIKELY(!is_leader)) {
ret = OB_NOT_MASTER;
LOG_WARN("gais service is not leader", K(ret));
} else if (OB_FAIL(mutex.lock())) {
LOG_WARN("fail to get lock", K(ret));
} else {
LOG_TRACE("start clear autoinc cache request", K(request));
if (OB_FAIL(autoinc_map_.erase_refactored(key))) {
LOG_WARN("fail to erase autoinc cache map key", K(ret));
}
if (ret == OB_HASH_NOT_EXIST) {
ret = OB_SUCCESS;
}
mutex.unlock();
}
return ret;
}
int ObGlobalAutoIncService::check_leader_(const uint64_t tenant_id, bool &is_leader)
{
int ret = OB_SUCCESS;
is_leader = ATOMIC_LOAD(&is_leader_);
if (OB_LIKELY(is_leader)) {
} else {
// try to get role from logstream
ObRole role = ObRole::INVALID_ROLE;
int64_t proposal_id = 0;
ObSpinLockGuard lock(cache_ls_lock_);
if (OB_ISNULL(cache_ls_)) {
ret = OB_NOT_MASTER;
LOG_WARN("cache ls is null", K(ret));
} else if (OB_FAIL(cache_ls_->get_log_handler()->get_role(role, proposal_id))) {
int tmp_ret = ret;
ret = OB_NOT_MASTER;
LOG_WARN("get ls role fail", K(ret), K(tmp_ret));
} else if (common::ObRole::LEADER == role) {
is_leader = true;
} else {
is_leader = false;
}
}
return ret;
}
int ObGlobalAutoIncService::fetch_next_node_(const ObGAISNextAutoIncValReq &request,
ObAutoIncCacheNode &node)
{
int ret = OB_SUCCESS;
uint64_t desired_count = std::max(request.cache_size_, request.desired_cnt_);
uint64_t start_inclusive = 0;
uint64_t end_inclusive = 0;
uint64_t sync_value = 0;
const int64_t autoinc_version = request.autoinc_version_;
if (OB_FAIL(inner_table_proxy_.next_autoinc_value(request.autoinc_key_,
request.offset_,
request.increment_,
request.base_value_,
request.max_value_,
desired_count,
autoinc_version,
start_inclusive,
end_inclusive,
sync_value))) {
LOG_WARN("fail to require autoinc value from inner table", K(ret));
} else if (OB_LIKELY(node.is_valid() && node.last_available_value_ == start_inclusive - 1)) {
if (OB_FAIL(node.update_available_value(end_inclusive))) {
LOG_WARN("fail to update available value", K(ret), K(node), K(end_inclusive));
} else {
LOG_TRACE("fetch next node done", K(request), K(node));
}
} else if (OB_FAIL(node.init(start_inclusive, end_inclusive, sync_value, autoinc_version))){
LOG_WARN("fail to init node", K(ret), K(start_inclusive), K(end_inclusive), K(sync_value));
} else {
LOG_TRACE("fetch next node done", K(request), K(node));
}
return ret;
}
int ObGlobalAutoIncService::read_value_from_inner_table_(const share::AutoincKey &key,
const int64_t &autoinc_version,
uint64_t &sequence_val,
uint64_t &sync_val)
{
return inner_table_proxy_.get_autoinc_value(key, autoinc_version, sequence_val, sync_val);
}
int ObGlobalAutoIncService::sync_value_to_inner_table_(
const ObGAISPushAutoIncValReq &request,
ObAutoIncCacheNode &node,
uint64_t &sync_value)
{
int ret = OB_SUCCESS;
const uint64_t insert_value = request.base_value_;
uint64_t seq_value = node.is_valid() ? node.sequence_value_ : 0;
const int64_t autoinc_version = request.autoinc_version_;
if (OB_FAIL(inner_table_proxy_.sync_autoinc_value(request.autoinc_key_,
insert_value,
request.max_value_,
autoinc_version,
seq_value,
sync_value))) {
LOG_WARN("fail to sync autoinc value to inner table", K(ret));
} else if (OB_LIKELY(node.is_valid())) {
if (seq_value > node.last_available_value_) {
// the node is expired.
node.reset();
node.sync_value_ = sync_value; // update sync value for next sync
} else if (sync_value == request.max_value_) {
if (node.last_available_value_ != request.max_value_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected error", K(ret), K(node), K(request.max_value_));
} else if (OB_FAIL(node.init(request.max_value_, request.max_value_, request.max_value_, autoinc_version))) {
LOG_WARN("fail to init node", K(ret), K(request.max_value_));
}
} else if (OB_FAIL(node.update_sequence_value(seq_value))) {
LOG_WARN("fail to update sequence value", K(ret), K(seq_value));
} else if (OB_FAIL(node.update_sync_value(sync_value))) {
LOG_WARN("fail to update sync value", K(ret), K(sync_value));
}
} else {
node.reset();
node.sync_value_ = sync_value; // update sync value for next sync
}
if (OB_SUCC(ret)) {
node.autoinc_version_ = autoinc_version > node.autoinc_version_ ? autoinc_version : node.autoinc_version_;
}
return ret;
}
} // share
} // oceanbase