Files
oceanbase/src/clog/ob_ilog_per_file_cache.cpp
oceanbase-admin cea7de1475 init push
2021-05-31 22:56:52 +08:00

550 lines
19 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.
*/
#include "ob_ilog_per_file_cache.h"
#include "ob_raw_entry_iterator.h"
#include "ob_file_id_cache.h"
#include "ob_ilog_storage.h"
namespace oceanbase {
using namespace common;
namespace clog {
int ObIlogPerFileCache::init(const file_id_t file_id, const int64_t array_size, PageArena<>* pf_allocator)
{
int ret = OB_SUCCESS;
if (is_inited_) {
ret = OB_INIT_TWICE;
} else if (OB_UNLIKELY(OB_INVALID_FILE_ID == file_id) || OB_UNLIKELY(array_size <= 0) ||
OB_UNLIKELY(NULL == pf_allocator)) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "ObIlogPerFileCache init error", K(ret), K(file_id), K(array_size), KP(pf_allocator));
} else {
pf_allocator_ = pf_allocator;
char* buf = reinterpret_cast<char*>(pf_allocator_->alloc(sizeof(ObLogCursorExt) * array_size));
if (OB_UNLIKELY(NULL == buf)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
} else {
cursor_arr_ = reinterpret_cast<ObLogCursorExt*>(buf);
file_id_ = file_id;
arr_size_ = array_size;
tail_pos_ = 0;
is_inited_ = true;
}
}
if (OB_SUCCESS != ret && OB_INIT_TWICE != ret) {
destroy();
}
return ret;
}
void ObIlogPerFileCache::destroy()
{
if (is_inited_) {
is_inited_ = false;
file_id_ = OB_INVALID_FILE_ID;
pf_allocator_->free();
pf_allocator_ = NULL;
cursor_arr_ = NULL;
arr_size_ = 0;
tail_pos_ = 0;
}
}
int ObIlogPerFileCache::append_cursor(const ObLogCursorExt& cursor)
{
int ret = OB_SUCCESS;
if (IS_NOT_INIT) {
ret = OB_NOT_INIT;
} else if (OB_UNLIKELY(!cursor.is_valid())) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "append cursor error, invalid cursor", K(ret), K(cursor));
} else if (OB_UNLIKELY(tail_pos_ >= arr_size_)) {
ret = OB_ERR_UNEXPECTED;
// inited arr_size_ error
CSR_LOG(ERROR, "append too many cursors", K(ret), K(tail_pos_), K(arr_size_));
} else {
cursor_arr_[tail_pos_] = cursor;
tail_pos_++;
}
return ret;
}
int ObIlogPerFileCache::query_cursor(const ObPartitionKey& pkey, const uint64_t query_log_id, const uint64_t min_log_id,
const uint64_t max_log_id, const offset_t start_offset_index, ObGetCursorResult& result)
{
int ret = OB_SUCCESS;
if (OB_UNLIKELY(!pkey.is_valid()) || OB_UNLIKELY(OB_INVALID_ID == query_log_id) ||
OB_UNLIKELY(OB_INVALID_ID == min_log_id) || OB_UNLIKELY(OB_INVALID_ID == max_log_id) ||
OB_UNLIKELY(min_log_id > query_log_id) || OB_UNLIKELY(max_log_id < query_log_id) ||
OB_UNLIKELY(start_offset_index < 0) || OB_UNLIKELY(start_offset_index >= tail_pos_) ||
OB_UNLIKELY(!result.is_valid())) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR,
"invald argument",
K(ret),
K(pkey),
K(query_log_id),
K(min_log_id),
K(max_log_id),
K(start_offset_index),
K(tail_pos_));
} else {
const int64_t target_offset_index = static_cast<int64_t>(start_offset_index + (query_log_id - min_log_id));
const int64_t ret_len = std::min(static_cast<int64_t>(max_log_id - query_log_id + 1), result.arr_len_);
const ObLogCursorExt* start_cursor = cursor_arr_ + target_offset_index;
memcpy(result.csr_arr_, start_cursor, sizeof(ObLogCursorExt) * ret_len);
result.ret_len_ = ret_len;
CSR_LOG(DEBUG,
"[ILOG_PER_FILE_CACHE] query cursor success",
K_(file_id),
K(pkey),
K(query_log_id),
K(min_log_id),
K(max_log_id),
K(target_offset_index),
K(result));
}
return ret;
}
// Return value
// 1. OB_SUCCESS SUCCESS target_log is the target log
// 2. other error code FAILURE target_log is invalid
int ObIlogPerFileCache::locate_by_timestamp(const common::ObPartitionKey& pkey, const int64_t start_ts,
const uint64_t min_log_id, const uint64_t max_log_id, const offset_t start_offset_index, uint64_t& target_log_id,
int64_t& target_log_timestamp)
{
int ret = OB_SUCCESS;
if (!pkey.is_valid() || OB_INVALID_TIMESTAMP == start_ts || OB_INVALID_ID == min_log_id || min_log_id <= 0 ||
OB_INVALID_ID == max_log_id || max_log_id <= 0 || min_log_id > max_log_id || start_offset_index < 0 ||
start_offset_index >= tail_pos_) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(
ERROR, "invalid argument", K(ret), K(pkey), K(start_ts), K(min_log_id), K(max_log_id), K(start_offset_index));
} else {
const ObLogCursorExt* pkey_csr_arr = cursor_arr_ + start_offset_index;
const int64_t array_len = max_log_id - min_log_id + 1;
const ObLogCursorExt* target_csr = NULL;
// Binary search timestamp from array, the target_csr is the first log which log_ts
// is greater than start_ts
if (OB_FAIL(binary_search_timestamp_from_cursor_array_(pkey_csr_arr, array_len, start_ts, target_csr))) {
CSR_LOG(WARN,
"binary_search_timestamp_from_cursor_array_ fail",
K(ret),
K(pkey),
K(start_ts),
K(min_log_id),
K(max_log_id),
K(start_offset_index));
} else if (OB_ISNULL(target_csr)) {
ret = OB_ERR_UNEXPECTED;
CSR_LOG(ERROR,
"binary_search_timestamp_from_cursor_array_ fail, target cursor is NULL",
K(ret),
K(pkey),
K(start_ts),
K(min_log_id),
K(max_log_id),
K(start_offset_index));
} else {
target_log_id = min_log_id + target_csr - pkey_csr_arr;
target_log_timestamp = target_csr->get_submit_timestamp();
}
CSR_LOG(INFO,
"[ILOG_PER_FILE_CACHE] locate_by_timestamp finish",
K(ret),
K(pkey),
K(start_ts),
K(min_log_id),
K(max_log_id),
K(start_offset_index),
K(target_log_id),
K(target_log_timestamp));
}
return ret;
}
// Binary search a cursor which log_ts is greater than target_ts
int ObIlogPerFileCache::binary_search_timestamp_from_cursor_array_(const ObLogCursorExt* csr_arr,
const int64_t array_len, const int64_t start_ts, const ObLogCursorExt*& target_cursor)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(csr_arr) || OB_UNLIKELY(array_len <= 0) || OB_UNLIKELY(OB_INVALID_TIMESTAMP == start_ts)) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "invalid argument", K(ret), K(csr_arr), K(array_len));
} else {
int64_t begin = 0;
int64_t end = array_len - 1;
while (begin <= end) {
int64_t middle = ((begin + end) / 2);
if (csr_arr[middle].get_submit_timestamp() >= start_ts) {
end = middle - 1;
} else {
begin = middle + 1;
}
}
if (end + 1 < array_len) {
target_cursor = csr_arr + end + 1;
} else {
ret = OB_ERR_OUT_OF_UPPER_BOUND;
}
}
return ret;
}
int ObIlogPerFileCacheBuilder::init(ObIlogStorage* ilog_storage, ObFileIdCache* file_id_cache)
{
int ret = OB_SUCCESS;
if (OB_UNLIKELY(NULL == ilog_storage) || OB_UNLIKELY(NULL == file_id_cache)) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "init error", K(ret), KP(ilog_storage), KP(file_id_cache));
} else {
ilog_storage_ = ilog_storage;
file_id_cache_ = file_id_cache;
}
return ret;
}
// NOTE: this operation is time-consumed
int ObIlogPerFileCacheBuilder::build_cache(
const file_id_t file_id, ObIlogPerFileCache* pf_cache, PageArena<>& pf_page_arena, ObIlogStorageQueryCost& csr_cost)
{
int ret = OB_SUCCESS;
CSR_LOG(TRACE, "[ILOG_PER_FILE_CACHE] start build", K(file_id), KP(pf_cache));
if (OB_ISNULL(ilog_storage_)) {
ret = OB_NOT_INIT;
} else if (OB_UNLIKELY(OB_INVALID_FILE_ID == file_id) || OB_UNLIKELY(NULL == pf_cache)) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "build cache error", K(ret), K(file_id), KP(pf_cache));
} else {
ObIRawIndexIterator* raw_ilog_iter = NULL;
RawArray raw_array; // to buffer raw ilog entry from ilog file
ObArenaAllocator raw_entry_allocator(ObModIds::OB_INDEX_ITERATOR_ID); // tmp_allocator
const offset_t start_offset = 0;
if (OB_UNLIKELY(
NULL == (raw_ilog_iter = ilog_storage_->alloc_raw_index_iterator(file_id, file_id, start_offset)))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
CSR_LOG(ERROR, "alloc raw ilog iter", K(ret), K(file_id), KP(ilog_storage_), KP(raw_ilog_iter));
} else if (OB_FAIL(prepare_sorted_raw_array_(file_id, raw_ilog_iter, raw_entry_allocator, raw_array, csr_cost))) {
CSR_LOG(WARN, "prepare sorted raw_array error", K(ret), K(file_id), K(raw_array));
} else if (raw_array.count_ > 0) {
if (OB_FAIL(pf_cache->init(file_id, raw_array.count_, &pf_page_arena))) {
CSR_LOG(WARN, "init pf_cache error", K(ret), K(file_id), K(raw_array), KP(pf_cache));
} else if (OB_FAIL(build_pf_cache_(file_id, raw_array, pf_cache))) {
CSR_LOG(WARN, "build pf_cache error", K(ret), K(file_id), KP(pf_cache));
} else {
CSR_LOG(INFO, "[ILOG_PER_FILE_CACHE] build_cache success", K(ret), K(file_id), "entry_count", raw_array.count_);
}
} else {
CSR_LOG(ERROR, "[ILOG_PER_FILE_CACHE] build_cache ilog raw_array count is 0", K(file_id), K(raw_array));
}
if (OB_LIKELY(NULL != raw_ilog_iter)) {
ilog_storage_->revert_raw_index_iterator(raw_ilog_iter);
raw_ilog_iter = NULL;
}
raw_entry_allocator.clear();
}
CSR_LOG(TRACE, "[ILOG_PER_FILE_CACHE] build cache finish", K(ret), K(file_id), KP(pf_cache));
return ret;
}
// compare ret:
// e1 < e2 => -1
// e1 > e2 => 1
// e1 == e2 => 0
int ilog_entry_comparator(const void* e1, const void* e2)
{
int cmp_ret = 0;
if (OB_ISNULL(e1) || OB_ISNULL(e2)) {
int tmp_ret = common::OB_ERR_UNEXPECTED;
CSR_LOG(ERROR, "ilog_entry_comparator error, null element", K(tmp_ret), KP(e1), KP(e2));
} else {
const ObIndexEntry* i1 = reinterpret_cast<const ObIndexEntry*>(e1);
const ObIndexEntry* i2 = reinterpret_cast<const ObIndexEntry*>(e2);
if (i1->get_partition_key() != i2->get_partition_key()) {
if (i1->get_partition_key() < i2->get_partition_key()) {
cmp_ret = -1;
} else {
cmp_ret = 1;
}
} else if (i1->get_log_id() < i2->get_log_id()) {
cmp_ret = -1;
} else if (i1->get_log_id() > i2->get_log_id()) {
cmp_ret = 1;
} else {
cmp_ret = 0;
}
}
return cmp_ret;
}
int ObIlogPerFileCacheBuilder::prepare_sorted_raw_array_(const file_id_t file_id, ObIRawIndexIterator* raw_ilog_iter,
ObIAllocator& raw_entry_allocator, RawArray& raw_array, ObIlogStorageQueryCost& csr_cost)
{
// alloc -> fill -> sort
UNUSED(file_id);
int ret = OB_SUCCESS;
if (OB_ISNULL(raw_ilog_iter)) {
ret = OB_ERR_UNEXPECTED;
} else {
raw_array.arr_ =
reinterpret_cast<ObIndexEntry*>(raw_entry_allocator.alloc(sizeof(ObIndexEntry) * ILOG_ENTRY_COUNT_PER_FILE));
if (OB_UNLIKELY(NULL == raw_array.arr_)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
CSR_LOG(ERROR, "alloc memory for raw_array error", K(ret), K(file_id));
} else {
ObIndexEntry ilog_entry;
int64_t count = 0;
ObReadParam param;
int64_t persist_len = 0;
while (OB_SUCC(ret) && OB_SUCC(raw_ilog_iter->next_entry(ilog_entry, param, persist_len)) &&
file_id == param.file_id_) {
ret = raw_array.arr_[count].shallow_copy(ilog_entry);
count++;
}
// always update csr_cost no matter success or not
csr_cost.read_ilog_disk_count_ += raw_ilog_iter->get_read_cost().read_disk_count_;
if (OB_ITER_END == ret) {
ret = OB_SUCCESS;
}
if (OB_SUCC(ret)) {
raw_array.count_ = count;
CSR_LOG(TRACE, "raw_array size", K(count), K(file_id));
} else {
CSR_LOG(WARN, "iterate ilog file error", K(ret), K(file_id), K(raw_array));
}
}
if (OB_SUCC(ret)) {
qsort(raw_array.arr_, raw_array.count_, sizeof(ObIndexEntry), ilog_entry_comparator);
}
}
CSR_LOG(TRACE, "prepare_sorted_raw_array finish", K(ret), K(file_id), K(raw_array), K(csr_cost));
return ret;
}
ObLogCursorExt ObIlogPerFileCacheBuilder::trim_to_cursor_(const ObIndexEntry& entry)
{
ObLogCursorExt ret_cursor;
ret_cursor.reset(entry.get_file_id(),
entry.get_offset(),
entry.get_size(),
entry.get_accum_checksum(),
entry.get_submit_timestamp(),
entry.is_batch_committed());
return ret_cursor;
}
int ObIlogPerFileCacheBuilder::BackfillGenerator::init(const file_id_t file_id, ObFileIdCache* file_id_cache)
{
int ret = OB_SUCCESS;
if (OB_UNLIKELY(OB_INVALID_FILE_ID == file_id) || OB_UNLIKELY(NULL == file_id_cache)) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "DamGenerator init error", K(ret), K(file_id), KP(file_id_cache));
} else {
file_id_ = file_id;
file_id_cache_ = file_id_cache;
cur_offset_ = OB_INVALID_OFFSET;
cur_pkey_.reset();
cur_min_log_id_ = OB_INVALID_ID;
cur_max_log_id_ = OB_INVALID_ID;
cur_start_offset_index_ = OB_INVALID_OFFSET;
CSR_LOG(TRACE, "DamGenerator init success", K(file_id_), KP(file_id_cache_));
}
return ret;
}
void ObIlogPerFileCacheBuilder::BackfillGenerator::reset()
{
file_id_ = OB_INVALID_FILE_ID;
file_id_cache_ = NULL;
cur_offset_ = OB_INVALID_OFFSET;
cur_pkey_.reset();
cur_min_log_id_ = OB_INVALID_ID;
cur_max_log_id_ = OB_INVALID_ID;
cur_start_offset_index_ = OB_INVALID_OFFSET;
}
int ObIlogPerFileCacheBuilder::BackfillGenerator::end_cur_pkey_()
{
int ret = OB_SUCCESS;
if (OB_ISNULL(file_id_cache_)) {
ret = OB_NOT_INIT;
CSR_LOG(ERROR, "invalid file_id_cache", K(file_id_cache_));
} else if (OB_FAIL(file_id_cache_->backfill(cur_pkey_, cur_min_log_id_, file_id_, cur_start_offset_index_))) {
CSR_LOG(WARN,
"backfill file id cache fail",
K(ret),
K(cur_pkey_),
K(cur_min_log_id_),
K(file_id_),
K(cur_start_offset_index_));
} else {
CSR_LOG(TRACE,
"[ILOG_PER_FILE_CACHE] backfill success",
K(ret),
K(cur_pkey_),
K(cur_min_log_id_),
K(file_id_),
K(cur_start_offset_index_));
}
return ret;
}
int ObIlogPerFileCacheBuilder::BackfillGenerator::do_track_(
const ObPartitionKey& pkey, const uint64_t log_id, const int32_t array_idx)
{
int ret = OB_SUCCESS;
// NOTE: travse sequentially
if (OB_UNLIKELY(OB_INVALID_OFFSET == cur_offset_ && 0 != array_idx) || OB_UNLIKELY(array_idx != (cur_offset_ + 1))) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(ERROR, "array index is not expected", K(ret), K(array_idx), K(cur_offset_));
} else if (OB_UNLIKELY(0 == array_idx)) {
cur_pkey_ = pkey;
cur_min_log_id_ = log_id;
cur_max_log_id_ = log_id;
cur_start_offset_index_ = array_idx;
} else {
const bool is_same_pkey = (cur_pkey_ == pkey);
const bool is_next_log = (cur_max_log_id_ + 1 == log_id);
if (is_same_pkey && is_next_log) {
cur_max_log_id_ = log_id;
// NOTE: logs in the same partition must be continous
} else if (is_same_pkey && !is_next_log) {
ret = OB_ERR_UNEXPECTED;
CLOG_LOG(ERROR,
"is_same_pkey, but not is_next_log",
K(ret),
K(pkey),
K(file_id_),
K(cur_min_log_id_),
K(cur_max_log_id_),
K(log_id));
} else if (OB_FAIL(end_cur_pkey_())) {
CSR_LOG(WARN,
"end_cur_pkey_ fail",
K(ret),
K(cur_pkey_),
K(pkey),
K(file_id_),
K(cur_min_log_id_),
K(cur_max_log_id_),
K(log_id));
} else {
cur_pkey_ = pkey;
cur_min_log_id_ = log_id;
cur_max_log_id_ = log_id;
cur_start_offset_index_ = array_idx;
}
}
if (OB_SUCCESS == ret) {
// update index
cur_offset_ = array_idx;
}
return ret;
}
int ObIlogPerFileCacheBuilder::BackfillGenerator::track(const ObIndexEntry& ilog_entry, const int32_t array_idx)
{
int ret = OB_SUCCESS;
const ObPartitionKey& pkey = ilog_entry.get_partition_key();
const uint64_t log_id = ilog_entry.get_log_id();
if (OB_UNLIKELY(!pkey.is_valid()) || OB_UNLIKELY(OB_INVALID_ID == log_id) || OB_UNLIKELY(array_idx < 0)) {
ret = OB_INVALID_ARGUMENT;
CSR_LOG(WARN, "invalid ilog_entry", K(ret), K(pkey), K(log_id), K(ilog_entry), K(array_idx));
} else if (OB_FAIL(do_track_(pkey, log_id, array_idx))) {
CSR_LOG(WARN, "dam_generator do_track error", K(ret), K(pkey), K(log_id), K(array_idx));
} else {
// success, do nothing
}
return ret;
}
int ObIlogPerFileCacheBuilder::BackfillGenerator::close()
{
int ret = OB_SUCCESS;
if (cur_offset_ >= 0) {
if (OB_FAIL(end_cur_pkey_())) {
CSR_LOG(WARN,
"BackfillGenerator end_cur_pkey_ fail",
K(ret),
K(file_id_),
K(cur_offset_),
K(cur_pkey_),
K(cur_min_log_id_),
K(cur_max_log_id_),
K(cur_start_offset_index_));
} else {
CSR_LOG(TRACE,
"DamGenerator close success",
K(ret),
K(file_id_),
K(cur_offset_),
K(cur_pkey_),
K(cur_min_log_id_),
K(cur_max_log_id_),
K(cur_start_offset_index_));
reset();
}
}
return ret;
}
int ObIlogPerFileCacheBuilder::build_pf_cache_(
const file_id_t file_id, const RawArray& raw_array, ObIlogPerFileCache* pf_cache)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(pf_cache)) {
ret = OB_INVALID_ARGUMENT;
} else if (OB_UNLIKELY(!raw_array.is_valid())) {
ret = OB_ERR_UNEXPECTED;
CSR_LOG(ERROR, "invalid raw_array", K(ret), K(raw_array));
} else {
BackfillGenerator backfill_generator;
if (OB_FAIL(backfill_generator.init(file_id, file_id_cache_))) {
CSR_LOG(WARN, "backfill_generator init error", K(ret), K(file_id), K(pf_cache));
}
for (int32_t idx = 0; OB_SUCC(ret) && (idx < raw_array.count_); idx++) {
const ObIndexEntry& ilog_entry = raw_array.arr_[idx];
if (OB_FAIL(backfill_generator.track(ilog_entry, idx))) {
CSR_LOG(WARN, "backfill_generator track cur ilog_entry error", K(ret), K(ilog_entry), K(idx));
} else if (OB_FAIL(pf_cache->append_cursor(trim_to_cursor_(ilog_entry)))) {
CSR_LOG(WARN, "pf_cache append_cursor", K(ret), K(ilog_entry));
}
} // end for
// close the loop
if (OB_SUCC(ret)) {
if (OB_FAIL(backfill_generator.close())) {
CSR_LOG(WARN, "backfill_generator close error", K(ret), K(file_id), KP(pf_cache));
}
}
}
CSR_LOG(TRACE, "[ILOG_PER_FILE_CACHE] build_pf_cache finish", K(ret), K(file_id), "entry_count", raw_array.count_);
return ret;
}
} // namespace clog
} // namespace oceanbase