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

318 lines
10 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 STORAGE
#include "ob_scan_merge_loser_tree.h"
namespace oceanbase {
using namespace common;
namespace storage {
void ObScanMergeLoserTreeCmp::reset()
{
cmp_funcs_.reset();
rowkey_size_ = 0;
error_ = OB_SUCCESS;
reverse_ = false;
is_inited_ = false;
}
template <typename T>
int ObScanMergeLoserTreeCmp::make_rowkey_cmp_funcs(
const int64_t rowkey_size, const ObColDescIArray& col_descs, ObIAllocator& allocator)
{
int ret = OB_SUCCESS;
void* buf = NULL;
T* cmp_funcs = NULL;
cmp_funcs_.set_allocator(&allocator);
cmp_funcs_.set_capacity(rowkey_size);
if (OB_ISNULL(buf = allocator.alloc(sizeof(T) * rowkey_size))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("Failed to allocate memory for compare func", K_(rowkey_size), K(ret));
} else {
cmp_funcs = reinterpret_cast<T*>(new (buf) T[rowkey_size]);
for (int64_t i = 0; OB_SUCC(ret) && i < rowkey_size; i++) {
if (OB_FAIL(cmp_funcs[i].init_compare_func(col_descs.at(i).col_type_))) {
LOG_WARN("Failed to init compare func", K(i), K(ret));
} else if (OB_FAIL(cmp_funcs_.push_back(&cmp_funcs[i]))) {
LOG_WARN("Failed to push back compare func", K(i), K(ret));
}
}
}
return ret;
}
int ObScanMergeLoserTreeCmp::init(const int64_t rowkey_size, const ObColDescIArray& col_descs, const bool reverse,
const bool is_oracle_mode, const bool use_cmp_nullsafe, ObIAllocator& allocator)
{
int ret = OB_SUCCESS;
if (IS_INIT) {
ret = OB_INIT_TWICE;
LOG_WARN("init twice", K(ret));
} else if (rowkey_size <= 0 || col_descs.count() <= 0) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(rowkey_size), K(col_descs));
} else {
if (is_oracle_mode && use_cmp_nullsafe) {
ret = make_rowkey_cmp_funcs<ObRowkeyObjComparerNullsafeOracle>(rowkey_size, col_descs, allocator);
} else if (is_oracle_mode && !use_cmp_nullsafe) {
ret = make_rowkey_cmp_funcs<ObRowkeyObjComparerOracle>(rowkey_size, col_descs, allocator);
} else if (!is_oracle_mode && use_cmp_nullsafe) {
ret = make_rowkey_cmp_funcs<ObRowkeyObjComparerNullsafeMysql>(rowkey_size, col_descs, allocator);
} else {
make_rowkey_cmp_funcs<ObRowkeyObjComparer>(rowkey_size, col_descs, allocator);
}
if (OB_SUCC(ret)) {
rowkey_size_ = rowkey_size;
reverse_ = reverse;
is_inited_ = true;
}
}
return ret;
}
int ObScanMergeLoserTreeCmp::compare_rowkey(const ObStoreRow& l_row, const ObStoreRow& r_row,
const int64_t& rowkey_size, RowkeyCmpFuncArray& cmp_funcs, int32_t& cmp_result)
{
int ret = OB_SUCCESS;
if (!l_row.is_valid() || !r_row.is_valid() || rowkey_size < 0) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(l_row), K(r_row), K(rowkey_size));
} else {
ObStoreRowkey l_key(l_row.row_val_.cells_, rowkey_size);
ObStoreRowkey r_key(r_row.row_val_.cells_, rowkey_size);
cmp_result = ObSSTableRowkeyHelper::compare(l_key, r_key, cmp_funcs);
}
return ret;
}
int64_t ObScanMergeLoserTreeCmp::operator()(const ObScanMergeLoserTreeItem& l, const ObScanMergeLoserTreeItem& r)
{
int32_t cmp_result = 0;
error_ = OB_SUCCESS;
if (IS_NOT_INIT) {
error_ = OB_NOT_INIT;
LOG_WARN("not init", K(error_));
} else if (nullptr == l.row_ || nullptr == r.row_ || l.row_->scan_index_ < 0 || r.row_->scan_index_ < 0) {
error_ = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(error_), KP(l.row_), KP(r.row_));
} else {
cmp_result = static_cast<int32_t>(l.row_->scan_index_ - r.row_->scan_index_);
if (0 == cmp_result) {
if (OB_SUCCESS != (error_ = compare_rowkey(*l.row_, *r.row_, rowkey_size_, cmp_funcs_, cmp_result))) {
LOG_WARN("compare rowkey error", K(error_));
} else if (reverse_) {
cmp_result = -cmp_result;
}
}
}
return cmp_result;
}
int ObScanMergeLoserTree::init(const int64_t total_player_cnt, ObIAllocator& allocator)
{
int ret = OB_SUCCESS;
if (IS_INIT) {
ret = OB_INIT_TWICE;
LOG_WARN("init twice", K(ret));
} else {
if (OB_FAIL(ObScanMergeLoserTreeBase::init(total_player_cnt, allocator))) {
LOG_WARN("init ObScanMergeLoserTreeBase fail", K(ret));
} else {
has_king_ = false;
is_king_eq_champion_ = false;
}
}
return ret;
}
void ObScanMergeLoserTree::reset()
{
has_king_ = false;
is_king_eq_champion_ = false;
ObScanMergeLoserTreeBase::reset();
}
int ObScanMergeLoserTree::top(const ObScanMergeLoserTreeItem*& player)
{
int ret = OB_SUCCESS;
if (!IS_INIT) {
ret = OB_NOT_INIT;
LOG_WARN("not init", K(ret));
} else if (has_king_) {
player = &king_;
} else if (OB_FAIL(ObScanMergeLoserTreeBase::top(player))) {
LOG_WARN("get top from base tree fail", K(ret));
}
return ret;
}
int ObScanMergeLoserTree::pop()
{
int ret = OB_SUCCESS;
if (!IS_INIT) {
ret = OB_NOT_INIT;
LOG_WARN("not init", K(ret));
} else if (need_rebuild_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("new players has been push, please rebuild", K(ret));
} else if (has_king_) {
has_king_ = false;
is_king_eq_champion_ = false;
} else if (OB_FAIL(ObScanMergeLoserTreeBase::pop())) {
LOG_WARN("pop base tree fail", K(ret));
}
return ret;
}
int ObScanMergeLoserTree::push(const ObScanMergeLoserTreeItem& player)
{
int ret = OB_SUCCESS;
if (!IS_INIT) {
ret = OB_NOT_INIT;
LOG_WARN("not init", K(ret));
} else if (has_king_ && cur_free_cnt_ <= 1) {
ret = OB_SIZE_OVERFLOW;
LOG_WARN("player is full", K(ret), K(player_cnt_), K(cur_free_cnt_), K(has_king_));
} else if (OB_FAIL(ObScanMergeLoserTreeBase::push(player))) {
LOG_WARN("push base tree fail", K(ret));
}
return ret;
}
int ObScanMergeLoserTree::rebuild()
{
int ret = OB_SUCCESS;
if (!IS_INIT) {
ret = OB_NOT_INIT;
LOG_WARN("not init", K(ret));
} else if (!need_rebuild_) {
// push_top will arrange the player automatically, so whatever has_king_, need_rebuild_
// will tell us if the tree has new players
} else if (empty()) {
ret = OB_EMPTY_RESULT;
LIB_LOG(WARN, "the tree is already empty", K(ret));
} else {
if (has_king_) {
if (OB_FAIL(ObScanMergeLoserTreeBase::push(king_))) {
LOG_WARN("fail to push king", K(ret));
} else {
has_king_ = false;
is_king_eq_champion_ = false;
}
}
if (OB_SUCC(ret) && OB_FAIL(ObScanMergeLoserTreeBase::rebuild())) {
LOG_WARN("build base tree fail", K(ret), K(has_king_), K(is_king_eq_champion_));
}
}
return ret;
}
int ObScanMergeLoserTree::push_top(const ObScanMergeLoserTreeItem& player)
{
int ret = OB_SUCCESS;
if (!IS_INIT) {
ret = OB_NOT_INIT;
LOG_WARN("not init", K(ret));
} else if (has_king_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("has old king", K(ret), K(has_king_));
} else if (cur_free_cnt_ <= 0) {
ret = OB_SIZE_OVERFLOW;
LOG_WARN("player is full", K(ret), K(player_cnt_), K(cur_free_cnt_), K(has_king_));
} else if (need_rebuild_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("tree need rebuild", K(ret), K(need_rebuild_));
} else if (0 == ObScanMergeLoserTreeBase::count()) {
king_ = player;
has_king_ = true;
is_king_eq_champion_ = false;
} else {
const int64_t champion = matches_[0].winner_idx_;
if (champion < 0 || champion > player_cnt_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid champion idx", K(ret), K(champion), K(player_cnt_), K(cur_free_cnt_));
} else if (player.iter_idx_ == players_[champion].iter_idx_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("rows from same iterator", K(ret), K(player.iter_idx_), K(players_[champion].iter_idx_));
}
// if left only one player, we can compare them by rebuild directly without trying
// thus can save one time compare
if (OB_SUCC(ret) && ObScanMergeLoserTreeBase::count() > 1) {
const int64_t king_cmp = cmp_(players_[champion], player);
if (OB_FAIL(cmp_.get_error_code())) {
LOG_WARN("compare champion fail",
K(ret),
K(players_[champion].iter_idx_),
K(player.iter_idx_),
K(*players_[champion].row_),
K(*player.row_));
} else {
if (king_cmp > 0) {
king_ = player;
has_king_ = true;
is_king_eq_champion_ = false;
} else if (0 == king_cmp && player.iter_idx_ < players_[champion].iter_idx_) {
king_ = player;
has_king_ = true;
is_king_eq_champion_ = true;
}
}
}
if (OB_SUCC(ret) && !has_king_) {
if (OB_FAIL(ObScanMergeLoserTreeBase::push(player))) {
LOG_WARN("push player fail", K(ret));
} else if (OB_FAIL(ObScanMergeLoserTreeBase::rebuild())) {
LOG_WARN("build base tree fail", K(ret));
} else {
has_king_ = false;
is_king_eq_champion_ = false;
}
}
}
return ret;
}
int ObScanMergeLoserTree::duel(ObScanMergeLoserTreeItem& offender, ObScanMergeLoserTreeItem& defender,
const int64_t match_idx, bool& is_offender_win)
{
int ret = OB_SUCCESS;
if (match_idx < 0 || match_idx >= leaf_offset_) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid match_idx", K(ret), K(match_idx));
} else if (offender.iter_idx_ == defender.iter_idx_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("rows from same iterator",
K(ret),
K(offender.iter_idx_),
K(defender.iter_idx_),
K(*offender.row_),
K(*defender.row_));
} else {
int64_t cmp_ret = cmp_(offender, defender);
if (OB_FAIL(cmp_.get_error_code())) {
LOG_WARN(
"compare fail", K(ret), K(offender.iter_idx_), K(defender.iter_idx_), K(*offender.row_), K(*defender.row_));
} else {
matches_[match_idx].is_draw_ = (0 == cmp_ret);
if (0 == cmp_ret) {
cmp_ret = (offender.iter_idx_ > defender.iter_idx_) ? 1 : -1;
}
is_offender_win = cmp_ret < 0;
}
}
return ret;
}
} // namespace storage
} // namespace oceanbase