| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "storage/compaction/binlog_compaction.h" |
| |
| #include <cpp/sync_point.h> |
| |
| #include <mutex> |
| #include <string> |
| #include <vector> |
| |
| #include "common/logging.h" |
| #include "runtime/thread_context.h" |
| #include "storage/compaction/binlog_compaction_policy.h" |
| #include "storage/rowset/rowset.h" |
| #include "storage/rowset/rowset_meta.h" |
| #include "storage/tablet/tablet.h" |
| #include "util/defer_op.h" |
| #include "util/time.h" |
| #include "util/trace.h" |
| |
| namespace doris { |
| using namespace ErrorCode; |
| |
| BinlogCompaction::BinlogCompaction(StorageEngine& engine, const TabletSharedPtr& tablet, |
| int8_t compaction_level) |
| : CompactionMixin(engine, tablet, |
| "BinlogCompaction:" + std::to_string(tablet->tablet_id())), |
| _compaction_level(compaction_level) {} |
| |
| BinlogCompaction::~BinlogCompaction() = default; |
| |
| Status BinlogCompaction::prepare_compact() { |
| Status st; |
| Defer defer_set_st([&] { |
| if (!st.ok()) { |
| tablet()->set_last_binlog_compaction_status(st.to_string()); |
| } |
| }); |
| |
| if (!tablet()->init_succeeded()) { |
| st = Status::Error<BINLOG_COMPACTION_INVALID_PARAMETERS, false>("_tablet init failed"); |
| return st; |
| } |
| |
| std::unique_lock<std::mutex> lock(tablet()->get_binlog_compaction_lock(), std::try_to_lock); |
| if (!lock.owns_lock()) { |
| st = Status::Error<TRY_LOCK_FAILED, false>( |
| "The tablet is under binlog compaction. tablet={}", _tablet->tablet_id()); |
| return st; |
| } |
| |
| st = pick_rowsets_to_compact(); |
| RETURN_IF_ERROR(st); |
| |
| COUNTER_UPDATE(_input_rowsets_counter, _input_rowsets.size()); |
| |
| st = Status::OK(); |
| return st; |
| } |
| |
| Status BinlogCompaction::execute_compact() { |
| Status st; |
| Defer defer_set_st([&] { |
| tablet()->set_last_binlog_compaction_status(st.to_string()); |
| if (!st.ok()) { |
| tablet()->set_last_binlog_compaction_failure_time(UnixMillis()); |
| } else { |
| tablet()->set_last_binlog_compaction_success_time(_compaction_level, UnixMillis()); |
| } |
| }); |
| |
| std::unique_lock<std::mutex> lock(tablet()->get_binlog_compaction_lock(), std::try_to_lock); |
| if (!lock.owns_lock()) { |
| st = Status::Error<TRY_LOCK_FAILED, false>( |
| "The tablet is under binlog compaction. tablet={}", _tablet->tablet_id()); |
| return st; |
| } |
| |
| SCOPED_ATTACH_TASK(_mem_tracker); |
| |
| st = CompactionMixin::execute_compact(); |
| RETURN_IF_ERROR(st); |
| |
| TEST_SYNC_POINT_RETURN_WITH_VALUE("binlog_compaction::BinlogCompaction::execute_compact", |
| Status::OK()); |
| |
| DCHECK_EQ(_state, CompactionState::SUCCESS); |
| |
| st = Status::OK(); |
| return st; |
| } |
| |
| Status BinlogCompaction::pick_rowsets_to_compact() { |
| auto candidate_rowsets = tablet()->pick_candidate_rowsets_to_binlog_compaction(); |
| if (candidate_rowsets.empty()) { |
| return Status::Error<BINLOG_COMPACTION_NO_SUITABLE_VERSION>("candidate_rowsets is empty"); |
| } |
| |
| // candidate_rowsets may not be continuous |
| // So we need to choose the longest continuous path from it. |
| std::vector<Version> missing_versions; |
| find_longest_consecutive_version(&candidate_rowsets, &missing_versions); |
| if (!missing_versions.empty()) { |
| DCHECK(missing_versions.size() % 2 == 0); |
| LOG(WARNING) << "There are missed versions among binlog rowsets. " |
| << "total missed version size: " << missing_versions.size() / 2 |
| << ", first missed version prev rowset verison=" << missing_versions[0] |
| << ", first missed version next rowset version=" << missing_versions[1] |
| << ", tablet=" << _tablet->tablet_id(); |
| } |
| |
| tablet()->binlog_compaction_policy()->pick_input_rowsets(tablet(), candidate_rowsets, |
| _compaction_level, &_input_rowsets); |
| |
| // Binlog compaction will process with at least 1 rowset. |
| // So when there is no rowset being chosen, we should return Status::Error<BINLOG_COMPACTION_NO_SUITABLE_VERSION>(): |
| if (_input_rowsets.empty()) { |
| VLOG_DEBUG << "binlog compaction can't get input rowsets, tablet=" << _tablet->tablet_id(); |
| return Status::Error<BINLOG_COMPACTION_NO_SUITABLE_VERSION>("_input_rowsets is empty"); |
| } |
| |
| return Status::OK(); |
| } |
| |
| Status BinlogCompaction::modify_rowsets() { |
| std::vector<RowsetSharedPtr> output_rowsets; |
| output_rowsets.push_back(_output_rowset); |
| |
| if (_is_ordered_data_compaction && |
| _compaction_level == BinlogCompactionPolicy::kBinlogCompactionMaxLevel - 1) { |
| _output_rowset->rowset_meta()->set_segments_overlap(OVERLAPPING); |
| } |
| tablet()->binlog_compaction_policy()->update_compaction_level(tablet(), _input_rowsets, |
| _output_rowset); |
| { |
| std::lock_guard<std::mutex> wrlock_(tablet()->get_rowset_update_lock()); |
| std::lock_guard<std::shared_mutex> wrlock(_tablet->get_header_lock()); |
| SCOPED_SIMPLE_TRACE_IF_TIMEOUT(TRACE_TABLET_LOCK_THRESHOLD); |
| RETURN_IF_ERROR(tablet()->modify_row_binlog_rowsets(output_rowsets, _input_rowsets)); |
| } |
| { |
| std::shared_lock rlock(_tablet->get_header_lock()); |
| tablet()->save_meta(); |
| } |
| return Status::OK(); |
| } |
| |
| } // namespace doris |