Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cloud/src/common/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ CONF_Int32(parallel_txn_lazy_commit_num_threads, "0"); // hardware concurrency i
CONF_mInt64(txn_lazy_max_rowsets_per_batch, "1000");
CONF_mBool(txn_lazy_commit_shuffle_partitions, "true");
CONF_Int64(txn_lazy_commit_shuffle_seed, "0"); // 0 means generate a random seed
CONF_mBool(enable_recycler_check_lazy_txn_finished, "true");
// max TabletIndexPB num for batch get
CONF_Int32(max_tablet_index_num_per_batch, "1000");
CONF_Int32(max_restore_job_rowsets_per_batch, "1000");
Expand Down
6 changes: 4 additions & 2 deletions cloud/src/common/sync_executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class SyncExecutor {
T t = _callback();
// We'll return this task result to user even if this task return error
// So we don't set _valid to false here
if (_cancel(t)) {
bool bypass_cancel = false;
TEST_SYNC_POINT_CALLBACK("SyncExecutor::Task::bypass_cancel", &bypass_cancel);
if (!bypass_cancel && _cancel(t)) {
stop_token = true;
}
_pro.set_value(std::move(t));
Expand Down Expand Up @@ -146,4 +148,4 @@ class SyncExecutor {
std::function<bool(const T&)> _cancel;
std::string _name_tag;
};
} // namespace doris::cloud
} // namespace doris::cloud
139 changes: 65 additions & 74 deletions cloud/src/recycler/recycler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2868,135 +2868,125 @@ int InstanceRecycler::recycle_tablets(int64_t table_id, int64_t index_id,
.tag("num_recycled", num_recycled);
};

// The first string_view represents the tablet key which has been recycled
// The second bool represents whether the following fdb's tablet key deletion could be done using range move or not
using TabletKeyPair = std::pair<std::string_view, bool>;
SyncExecutor<TabletKeyPair> sync_executor(
// The tablet key and id which have been recycled.
struct TabletInfo {
std::string_view tablet_meta_key;
int64_t tablet_id;
};
SyncExecutor<TabletInfo> sync_executor(
_thread_pool_group.recycle_tablet_pool,
fmt::format("recycle tablets, tablet id {}, index id {}, partition id {}", table_id,
index_id, partition_id),
[](const TabletKeyPair& k) { return k.first.empty(); });
[](const TabletInfo& k) { return k.tablet_meta_key.empty(); });

// Elements in `tablet_keys` has the same lifetime as `it` in `scan_and_recycle`
std::vector<std::string> tablet_idx_keys;
std::vector<std::string> restore_job_keys;
// Elements in `tablets_info` has the same lifetime as `it` in `scan_and_recycle`
std::vector<std::string> init_rs_keys;
std::vector<std::string> tablet_compact_stats_keys;
std::vector<std::string> tablet_load_stats_keys;
std::vector<std::string> versioned_meta_tablet_keys;
bool has_failure = false;
auto recycle_func = [&, this](std::string_view k, std::string_view v) -> int {
bool use_range_remove = true;
++num_scanned;
doris::TabletMetaCloudPB tablet_meta_pb;
if (!tablet_meta_pb.ParseFromArray(v.data(), v.size())) {
LOG_WARNING("malformed tablet meta").tag("key", hex(k));
use_range_remove = false;
has_failure = true;
return -1;
}
int64_t tablet_id = tablet_meta_pb.tablet_id();

if (!check_lazy_txn_finished(txn_kv_, instance_id_, tablet_meta_pb.tablet_id())) {
if (config::enable_recycler_check_lazy_txn_finished &&
!check_lazy_txn_finished(txn_kv_, instance_id_, tablet_meta_pb.tablet_id())) {
LOG(WARNING) << "lazy txn not finished tablet_id=" << tablet_meta_pb.tablet_id();
has_failure = true;
return -1;
}

tablet_idx_keys.push_back(meta_tablet_idx_key({instance_id_, tablet_id}));
restore_job_keys.push_back(job_restore_tablet_key({instance_id_, tablet_id}));
if (is_multi_version) {
// The tablet index/inverted index are recycled in recycle_versioned_tablet.
tablet_compact_stats_keys.push_back(
versioned::tablet_compact_stats_key({instance_id_, tablet_id}));
tablet_load_stats_keys.push_back(
versioned::tablet_load_stats_key({instance_id_, tablet_id}));
versioned_meta_tablet_keys.push_back(
versioned::meta_tablet_key({instance_id_, tablet_id}));
}
TEST_SYNC_POINT_RETURN_WITH_VALUE("recycle_tablet::bypass_check", false);
sync_executor.add([this, &num_recycled, tid = tablet_id, range_move = use_range_remove,
&metrics_context, k]() mutable -> TabletKeyPair {
if (recycle_tablet(tid, metrics_context) != 0) {
LOG_WARNING("failed to recycle tablet")
.tag("instance_id", instance_id_)
.tag("tablet_id", tid);
range_move = false;
return {std::string_view(), range_move};
}
++num_recycled;
LOG(INFO) << "recycle_tablets scan, key=" << (k.empty() ? "(empty)" : hex(k));
return {k, range_move};
});
sync_executor.add(
[this, &num_recycled, tid = tablet_id, &metrics_context, k]() -> TabletInfo {
if (recycle_tablet(tid, metrics_context) != 0) {
LOG_WARNING("failed to recycle tablet")
.tag("instance_id", instance_id_)
.tag("tablet_id", tid);
return {.tablet_meta_key = std::string_view(), .tablet_id = tid};
}
++num_recycled;
LOG(INFO) << "recycle_tablets scan, key=" << (k.empty() ? "(empty)" : hex(k));
return {.tablet_meta_key = k, .tablet_id = tid};
});
return 0;
};

// TODO(AlexYue): Add one ut to cover use_range_remove = false
auto loop_done = [&, this]() -> int {
int ret = 0;
bool finished = true;
auto tablet_keys = sync_executor.when_all(&finished);
bool has_empty_key = false;
DORIS_CLOUD_DEFER {
init_rs_keys.clear();
has_failure = false;
};
auto tablets_info = sync_executor.when_all(&finished);
Comment thread
wyxxxcat marked this conversation as resolved.
if (!finished) {
LOG_WARNING("failed to recycle tablet").tag("instance_id", instance_id_);
return -1;
}
if (tablet_keys.empty() && tablet_idx_keys.empty()) return 0;
if (!tablet_keys.empty() &&
std::ranges::all_of(tablet_keys, [](const auto& k) { return k.first.empty(); })) {
return -1;

size_t size_before_erase = tablets_info.size();
std::erase_if(tablets_info, [](const TabletInfo& t) { return t.tablet_meta_key.empty(); });
if (tablets_info.empty()) {
return size_before_erase == 0 ? 0 : -1;
} else if (size_before_erase != tablets_info.size()) {
has_empty_key = true;
}

ret = has_empty_key ? -1 : 0;
// sort the vector using key's order
std::sort(tablet_keys.begin(), tablet_keys.end(),
[](const auto& prev, const auto& last) { return prev.first < last.first; });
bool use_range_remove = true;
for (auto& [_, remove] : tablet_keys) {
if (!remove) {
use_range_remove = remove;
break;
}
}
DORIS_CLOUD_DEFER {
tablet_idx_keys.clear();
restore_job_keys.clear();
init_rs_keys.clear();
tablet_compact_stats_keys.clear();
tablet_load_stats_keys.clear();
versioned_meta_tablet_keys.clear();
};
std::ranges::sort(tablets_info, [](const auto& prev, const auto& last) {
return prev.tablet_meta_key < last.tablet_meta_key;
});
std::unique_ptr<Transaction> txn;
if (txn_kv_->create_txn(&txn) != TxnErrorCode::TXN_OK) {
LOG(WARNING) << "failed to delete tablet meta kv, instance_id=" << instance_id_;
return -1;
}
std::string tablet_key_end;
if (!tablet_keys.empty()) {
if (use_range_remove) {
tablet_key_end = std::string(tablet_keys.back().first) + '\x00';
txn->remove(tablet_keys.front().first, tablet_key_end);
if (!tablets_info.empty()) {
if (!has_empty_key && !has_failure) {
tablet_key_end = std::string(tablets_info.back().tablet_meta_key) + '\x00';
txn->remove(tablets_info.front().tablet_meta_key, tablet_key_end);
} else {
for (auto& [k, _] : tablet_keys) {
txn->remove(k);
for (auto& tablet_info : tablets_info) {
txn->remove(tablet_info.tablet_meta_key);
}
}
}
if (is_multi_version) {
for (auto& k : tablet_compact_stats_keys) {
for (auto& tablet_info : tablets_info) {
// Remove all versions of tablet compact stats for recycled tablet
auto k = versioned::tablet_compact_stats_key({instance_id_, tablet_info.tablet_id});
LOG_INFO("remove versioned tablet compact stats key")
.tag("compact_stats_key", hex(k));
versioned_remove_all(txn.get(), k);
}
for (auto& k : tablet_load_stats_keys) {
for (auto& tablet_info : tablets_info) {
// Remove all versions of tablet load stats for recycled tablet
auto k = versioned::tablet_load_stats_key({instance_id_, tablet_info.tablet_id});
LOG_INFO("remove versioned tablet load stats key").tag("load_stats_key", hex(k));
versioned_remove_all(txn.get(), k);
}
for (auto& k : versioned_meta_tablet_keys) {
for (auto& tablet_info : tablets_info) {
// Remove all versions of meta tablet for recycled tablet
auto k = versioned::meta_tablet_key({instance_id_, tablet_info.tablet_id});
LOG_INFO("remove versioned meta tablet key").tag("meta_tablet_key", hex(k));
versioned_remove_all(txn.get(), k);
}
}
for (auto& k : tablet_idx_keys) {
for (auto& tablet_info : tablets_info) {
std::string k;
meta_tablet_idx_key({instance_id_, tablet_info.tablet_id}, &k);
txn->remove(k);
}
for (auto& k : restore_job_keys) {
for (auto& tablet_info : tablets_info) {
std::string k;
job_restore_tablet_key({instance_id_, tablet_info.tablet_id}, &k);
txn->remove(k);
}
for (auto& k : init_rs_keys) {
Expand All @@ -3007,7 +2997,7 @@ int InstanceRecycler::recycle_tablets(int64_t table_id, int64_t index_id,
<< ", err=" << err;
return -1;
}
return 0;
return ret;
};

int ret = scan_and_recycle(tablet_key_begin, tablet_key_end, std::move(recycle_func),
Expand Down Expand Up @@ -4107,7 +4097,8 @@ int InstanceRecycler::scan_tablets_and_statistics(int64_t table_id, int64_t inde
}
int64_t tablet_id = tablet_meta_pb.tablet_id();

if (!check_lazy_txn_finished(txn_kv_, instance_id_, tablet_meta_pb.tablet_id())) {
if (config::enable_recycler_check_lazy_txn_finished &&
!check_lazy_txn_finished(txn_kv_, instance_id_, tablet_meta_pb.tablet_id())) {
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion cloud/test/mock_accessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ inline auto MockAccessor::get_prefix_range(const std::string& path_prefix) {
}

inline int MockAccessor::delete_prefix_impl(const std::string& path_prefix) {
TEST_SYNC_POINT_RETURN_WITH_VALUE("MockAccessor::delete_prefix", (int)0);
TEST_SYNC_POINT_RETURN_WITH_VALUE("MockAccessor::delete_prefix", (int)0, &path_prefix);
LOG(INFO) << "delete object of prefix=" << path_prefix;
std::lock_guard lock(mtx_);

Expand Down
12 changes: 6 additions & 6 deletions cloud/test/recycler_operation_log_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,9 +1099,9 @@ TEST(RecycleOperationLogTest, RecycleCompactionLog) {
ret->first = true;
ret->second = true;
});
sp->set_call_back("recycle_tablet::bypass_check", [&](auto&& args) {
auto* ret = doris::try_any_cast_ret<bool>(args);
ret->first = false;
sp->set_call_back("recycle_tablet::begin", [&](auto&& args) {
auto* ret = doris::try_any_cast_ret<int>(args);
ret->first = 0;
ret->second = true;
});
sp->enable_processing();
Expand Down Expand Up @@ -1622,9 +1622,9 @@ TEST(RecycleOperationLogTest, RecycleSchemaChangeLog) {
ret->first = true;
ret->second = true;
});
sp->set_call_back("recycle_tablet::bypass_check", [&](auto&& args) {
auto* ret = doris::try_any_cast_ret<bool>(args);
ret->first = false;
sp->set_call_back("recycle_tablet::begin", [&](auto&& args) {
auto* ret = doris::try_any_cast_ret<int>(args);
ret->first = 0;
ret->second = true;
});
sp->enable_processing();
Expand Down
Loading
Loading