/** * 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_max_log_meta_info.h" namespace oceanbase { using namespace common; namespace clog { ObMaxLogMetaInfo::ObMaxLogMetaInfo() { saved_last_gts_ = 0; saved_last_get_gts_ts_ = 0; total_log_cnt_ = 0; total_commit_log_cnt_ = 0; reset(); } ObMaxLogMetaInfo::~ObMaxLogMetaInfo() { reset(); } int ObMaxLogMetaInfo::init(const common::ObPartitionKey& partition_key, const uint64_t log_id, const int64_t timestamp) { int ret = OB_SUCCESS; if (!partition_key.is_valid() || OB_INVALID_ID == log_id || OB_INVALID_TIMESTAMP == timestamp) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(ERROR, "invalid arguments", K(ret), K(partition_key), K(log_id), K(timestamp)); } else if (log_id >= LOG_ID_LIMIT) { ret = OB_ERR_UNEXPECTED; CLOG_LOG(ERROR, "log_id is larger than LOG_ID_LIMIT", K(ret), K(partition_key), K(log_id)); } else { check_log_id_range_(log_id); partition_key_ = partition_key; // meta_info_.log_id_ = log_id; // meta_info_.offset_ = AGGRE_BUFFER_FLAG; // meta_info_.ts_ = timestamp; meta_info_.v128_.lo = ((log_id & MASK) << 16) + AGGRE_BUFFER_FLAG; meta_info_.v128_.hi = timestamp; } return ret; } void ObMaxLogMetaInfo::reset() { // meta_info_.log_id_ = 0; // meta_info_.offset_ = 0; // meta_info_.ts_ = 0; meta_info_.v128_.lo = 0; meta_info_.v128_.hi = 0; } uint64_t ObMaxLogMetaInfo::get_log_id() const { MetaInfo meta_info; LOAD128(meta_info, &meta_info_); return meta_info.log_id_; } int64_t ObMaxLogMetaInfo::get_timestamp() const { MetaInfo meta_info; LOAD128(meta_info, &meta_info_); return meta_info.ts_; } int ObMaxLogMetaInfo::get_log_id_and_timestamp(uint64_t& log_id, int64_t& timestamp) const { int ret = OB_SUCCESS; MetaInfo meta_info; LOAD128(meta_info, &meta_info_); log_id = meta_info.log_id_; timestamp = meta_info.ts_; return ret; } int ObMaxLogMetaInfo::alloc_log_id_ts( const int64_t base_timestamp, const uint64_t log_id_limit, uint64_t& log_id, int64_t& timestamp, int64_t& offset) { int ret = OB_SUCCESS; if (base_timestamp < 0 || log_id_limit >= LOG_ID_LIMIT) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(WARN, "invalid arguments", K(ret), K(partition_key_), K(base_timestamp), K(log_id_limit)); } else { check_log_id_range_(log_id_limit); MetaInfo last; MetaInfo next; while (true) { const int64_t base_ts = base_timestamp ? base_timestamp : ObTimeUtility::current_time(); LOAD128(last, &meta_info_); offset = last.offset_; // next.log_id_ = last.log_id_ + 1; // next.offset_ = AGGRE_BUFFER_FLAG; // next.ts_ = (last.ts_ >= (static_cast(base_ts))) ? (last.ts_ + 1) : base_ts; next.v128_.lo = (((last.log_id_ + 1) & MASK) << 16) + AGGRE_BUFFER_FLAG; next.v128_.hi = (last.ts_ >= (static_cast(base_ts))) ? (last.ts_ + 1) : base_ts; if (next.log_id_ >= log_id_limit) { ret = OB_EAGAIN; break; } else if (CAS128(&meta_info_, last, next)) { break; } else { PAUSE(); } } log_id = next.log_id_; timestamp = next.ts_; if (timestamp > ObTimeUtility::current_time() + 3600 * 1000 * 1000l) { ret = OB_ERR_UNEXPECTED; CLOG_LOG( ERROR, "alloc_log_id_ts return unexpected value", K(last.log_id_), K(last.ts_), K(next.log_id_), K(next.ts_)); } } CLOG_LOG(DEBUG, "alloc log id ts", K(ret), K(partition_key_), K(log_id)); return ret; } int ObMaxLogMetaInfo::alloc_log_id_ts(const int64_t base_timestamp, const int64_t size, const uint64_t log_id_limit, uint64_t& log_id, int64_t& timestamp, int64_t& offset) { int ret = OB_SUCCESS; if (base_timestamp < 0 || size <= 0 || size >= AGGRE_BUFFER_LIMIT || log_id_limit >= LOG_ID_LIMIT) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(WARN, "invalid arguments", K(ret), K(partition_key_), K(base_timestamp), K(size), K(log_id_limit)); } else { check_log_id_range_(log_id_limit); ATOMIC_INC(&total_commit_log_cnt_); MetaInfo last; MetaInfo next; while (true) { const int64_t base_ts = base_timestamp ? base_timestamp : ObTimeUtility::current_time(); LOAD128(last, &meta_info_); offset = last.offset_; if (last.offset_ + size < AGGRE_BUFFER_LIMIT) { // next.log_id_ = last.log_id_; // next.offset_ = last.offset_ + size; // next.ts_ = (last.ts_ >= (static_cast(base_ts))) ? (last.ts_ + 1) : base_ts; next.v128_.lo = ((last.log_id_ & MASK) << 16) + last.offset_ + size; next.v128_.hi = (last.ts_ >= (static_cast(base_ts))) ? (last.ts_ + 1) : base_ts; ret = OB_SUCCESS; } else { // next.log_id_ = last.log_id_ + 1; // next.offset_ = size; // next.ts_ = (last.ts_ >= (static_cast(base_ts))) ? (last.ts_ + 1) : base_ts; next.v128_.lo = (((last.log_id_ + 1) & MASK) << 16) + size; next.v128_.hi = (last.ts_ >= (static_cast(base_ts))) ? (last.ts_ + 1) : base_ts; ret = OB_BLOCK_SWITCHED; } if (next.log_id_ >= log_id_limit) { ret = OB_EAGAIN; break; } else if (CAS128(&meta_info_, last, next)) { break; } else { PAUSE(); } } if (OB_BLOCK_SWITCHED == ret) { ATOMIC_INC(&total_log_cnt_); } if (REACH_TIME_INTERVAL(10 * 1000 * 1000)) { CLOG_LOG(INFO, "alloc_log_id_ts statistics", K(total_log_cnt_), K(total_commit_log_cnt_)); ATOMIC_STORE(&total_log_cnt_, 0); ATOMIC_STORE(&total_commit_log_cnt_, 0); } log_id = last.log_id_; timestamp = next.ts_; if (timestamp > ObTimeUtility::current_time() + 3600 * 1000 * 1000l) { ret = OB_ERR_UNEXPECTED; CLOG_LOG( ERROR, "alloc_log_id_ts return unexpected value", K(last.log_id_), K(last.ts_), K(next.log_id_), K(next.ts_)); } } CLOG_LOG(DEBUG, "alloc log id ts", K(ret), K(partition_key_), K(log_id)); return ret; } int ObMaxLogMetaInfo::try_freeze(const uint64_t log_id, int64_t& offset) { int ret = OB_SUCCESS; if (log_id >= LOG_ID_LIMIT) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(WARN, "invalid arguments", K(ret), K(partition_key_), K(log_id)); } else { check_log_id_range_(log_id); MetaInfo last; MetaInfo next; while (true) { LOAD128(last, &meta_info_); offset = last.offset_; if (last.log_id_ != log_id || last.offset_ == AGGRE_BUFFER_FLAG) { // no need freeze ret = OB_SUCCESS; break; } else { // next.log_id_ = last.log_id_; // next.offset_ = AGGRE_BUFFER_FLAG; // next.ts_ = last.ts_; next.v128_.lo = ((last.log_id_ & MASK) << 16) + AGGRE_BUFFER_FLAG; next.v128_.hi = last.ts_; if (CAS128(&meta_info_, last, next)) { ret = OB_BLOCK_SWITCHED; break; } } } } return ret; } int ObMaxLogMetaInfo::try_update_timestamp(const int64_t base_timestamp) { int ret = OB_SUCCESS; if (base_timestamp < 0) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(WARN, "invalid arguments", K(ret), K(partition_key_), K(base_timestamp)); } else { MetaInfo last; MetaInfo next; while (true) { LOAD128(last, &meta_info_); // next.log_id_ = last.log_id_; // next.offset_ = last.offset_; // next.ts_ = base_timestamp; next.v128_.lo = ((last.log_id_ & MASK) << 16) + last.offset_; next.v128_.hi = base_timestamp; if (next.ts_ < last.ts_) { break; } else if (CAS128(&meta_info_, last, next)) { break; } else { PAUSE(); } } } return ret; } int ObMaxLogMetaInfo::try_update_log_id(const uint64_t log_id) { int ret = OB_SUCCESS; if (log_id >= LOG_ID_LIMIT) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(WARN, "invalid arguments", K(ret), K(partition_key_), K(log_id)); } else { check_log_id_range_(log_id); MetaInfo last; MetaInfo next; while (true) { LOAD128(last, &meta_info_); // next.log_id_ = log_id; // next.offset_ = AGGRE_BUFFER_FLAG; // next.ts_ = last.ts_; next.v128_.lo = ((log_id & MASK) << 16) + AGGRE_BUFFER_FLAG; next.v128_.hi = last.ts_; if (log_id < last.log_id_) { break; } else if (CAS128(&meta_info_, last, next)) { if (log_id - last.log_id_ >= 50000000) { CLOG_LOG(WARN, "too large arg log_id", K(partition_key_), "increased by:", log_id - last.log_id_, K(log_id), K(last.log_id_)); } break; } else { PAUSE(); } } } return ret; } void ObMaxLogMetaInfo::check_log_id_range_(const uint64_t log_id) const { if (log_id >= LOG_ID_CHECK_RANGE) { if (REACH_TIME_INTERVAL(1000 * 1000)) { CLOG_LOG(ERROR, "log id may be too large, please attention!", K(log_id), K(partition_key_)); } } } ObAggreBuffer::ObAggreBuffer() { reset(); } ObAggreBuffer::~ObAggreBuffer() { destroy(); } int ObAggreBuffer::init(const uint64_t id, const uint64_t tenant_id) { int ret = OB_SUCCESS; ObMemAttr mem_attr(tenant_id, ObModIds::OB_LOG_AGGRE_BUFFER); if (OB_INVALID_ID == id) { ret = OB_INVALID_ARGUMENT; CLOG_LOG(ERROR, "invalid arguments", K(ret), K(id)); } else if (NULL == (data_ = static_cast(ob_malloc(AGGRE_BUFFER_SIZE, mem_attr)))) { ret = OB_ALLOCATE_MEMORY_FAILED; CLOG_LOG(ERROR, "alloc memory failed", K(ret), K(id)); } else { id_ = id; ref_ = 0; // data_ has been initialized data_size_ = 0; max_submit_timestamp_ = 0; last_access_timestamp_ = ObTimeUtility::current_time(); } if (OB_FAIL(ret) && NULL != data_) { ob_free(data_); data_ = NULL; } return ret; } void ObAggreBuffer::reset() { id_ = OB_INVALID_ID; ref_ = 0; data_ = NULL; data_size_ = 0; max_submit_timestamp_ = 0; last_access_timestamp_ = ObTimeUtility::current_time(); ObSpinLockGuard guard(lock_); cb_list_head_ = NULL; cb_list_tail_ = NULL; } void ObAggreBuffer::destroy() { id_ = OB_INVALID_ID; ref_ = 0; if (NULL != data_) { ob_free(data_); data_ = NULL; } data_size_ = 0; max_submit_timestamp_ = 0; ObSpinLockGuard guard(lock_); cb_list_head_ = NULL; cb_list_tail_ = NULL; } const char* ObAggreBuffer::get_data() const { return data_; } int64_t ObAggreBuffer::get_data_size() const { return data_size_; } int64_t ObAggreBuffer::get_max_submit_timestamp() const { return max_submit_timestamp_; } ObISubmitLogCb* ObAggreBuffer::get_submit_cb() const { return cb_list_head_; } int64_t ObAggreBuffer::ref(const int64_t delta) { if (delta < 0) { data_size_ = (-delta); } return ATOMIC_AAF(&ref_, delta); } void ObAggreBuffer::fill( const int64_t offset, const char* data, const int64_t data_size, const int64_t submit_timestamp, ObISubmitLogCb* cb) { int ret = OB_SUCCESS; const int32_t next_log_offset = static_cast((offset + data_size + AGGRE_LOG_RESERVED_SIZE) & 0xffffffff); int64_t pos = 0; if (OB_FAIL(serialization::encode_i32(data_ + offset, AGGRE_LOG_RESERVED_SIZE, pos, next_log_offset))) { CLOG_LOG(ERROR, "serialization::encode_i32 failed", K(ret)); } else if (OB_FAIL(serialization::encode_i64(data_ + offset, AGGRE_LOG_RESERVED_SIZE, pos, submit_timestamp))) { CLOG_LOG(ERROR, "serialization::encode_i64 failed", K(ret)); } MEMCPY(data_ + offset + AGGRE_LOG_RESERVED_SIZE, data, data_size); inc_update(max_submit_timestamp_, submit_timestamp); add_callback_to_list_(cb); last_access_timestamp_ = ObTimeUtility::current_time(); } void ObAggreBuffer::wait(const uint64_t id, int64_t& wait_times) { uint64_t real_id = 0; wait_times = 0; const int64_t MAX_SLEEP_US = 100; while ((real_id = ATOMIC_LOAD(&id_)) < id) { wait_times++; // Avoid busy waiting here // There will be no deadlock here, but if you use PAUSE, it may cause an avalanche int64_t sleep_us = wait_times * 10; if (sleep_us > MAX_SLEEP_US) { sleep_us = MAX_SLEEP_US; } usleep(sleep_us); } } void ObAggreBuffer::reuse(const uint64_t id) { ref_ = 0; // data_ no need reset data_size_ = 0; max_submit_timestamp_ = 0; ObSpinLockGuard guard(lock_); cb_list_head_ = NULL; cb_list_tail_ = NULL; // Must put it at the end ATOMIC_STORE(&id_, id); } void ObAggreBuffer::add_callback_to_list_(ObISubmitLogCb* cb) { if (NULL != cb) { const bool is_high_priority = cb->is_high_priority(); ObSpinLockGuard guard(lock_); if (OB_ISNULL(cb_list_head_) || OB_ISNULL(cb_list_tail_)) { cb->next_ = NULL; cb_list_head_ = cb; cb_list_tail_ = cb; } else { if (is_high_priority) { cb->next_ = cb_list_head_; cb_list_head_ = cb; } else { cb->next_ = NULL; cb_list_tail_->next_ = cb; cb_list_tail_ = cb; } } } } } // namespace clog } // namespace oceanbase