blob: fb5c9c3e7e6de40ed6e9b63337323f7dbd35e2a5 [file]
// 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.
#pragma once
#include <gen_cpp/cloud.pb.h>
#include <glog/logging.h>
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <deque>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "common/bvars.h"
#include "meta-service/delete_bitmap_lock_white_list.h"
#include "meta-service/txn_lazy_committer.h"
#include "meta-store/versionstamp.h"
#include "recycler/snapshot_chain_compactor.h"
#include "recycler/snapshot_data_migrator.h"
#include "recycler/storage_vault_accessor.h"
#include "snapshot/snapshot_manager.h"
namespace brpc {
class Server;
} // namespace brpc
namespace doris::cloud {
class TxnKv;
class InstanceRecycler;
class StorageVaultAccessor;
class Checker;
class SimpleThreadPool;
class RecyclerMetricsContext;
class TabletRecyclerMetricsContext;
class SegmentRecyclerMetricsContext;
struct RecyclerThreadPoolGroup {
RecyclerThreadPoolGroup() = default;
RecyclerThreadPoolGroup(std::shared_ptr<SimpleThreadPool> s3_producer_pool,
std::shared_ptr<SimpleThreadPool> recycle_tablet_pool,
std::shared_ptr<SimpleThreadPool> group_recycle_function_pool)
: s3_producer_pool(std::move(s3_producer_pool)),
recycle_tablet_pool(std::move(recycle_tablet_pool)),
group_recycle_function_pool(std::move(group_recycle_function_pool)) {}
~RecyclerThreadPoolGroup() = default;
RecyclerThreadPoolGroup(const RecyclerThreadPoolGroup&) = default;
RecyclerThreadPoolGroup& operator=(RecyclerThreadPoolGroup& other) = default;
RecyclerThreadPoolGroup& operator=(RecyclerThreadPoolGroup&& other) = default;
RecyclerThreadPoolGroup(RecyclerThreadPoolGroup&&) = default;
// used for accessor.delete_files, accessor.delete_directory
std::shared_ptr<SimpleThreadPool> s3_producer_pool;
// used for InstanceRecycler::recycle_tablet
std::shared_ptr<SimpleThreadPool> recycle_tablet_pool;
std::shared_ptr<SimpleThreadPool> group_recycle_function_pool;
};
class Recycler {
public:
explicit Recycler(std::shared_ptr<TxnKv> txn_kv);
~Recycler();
// returns 0 for success otherwise error
int start(brpc::Server* server);
void stop();
bool stopped() const { return stopped_.load(std::memory_order_acquire); }
RecyclerThreadPoolGroup& thread_pool_group() { return _thread_pool_group; }
private:
void recycle_callback();
void instance_scanner_callback();
void lease_recycle_jobs();
void check_recycle_tasks();
private:
friend class RecyclerServiceImpl;
std::shared_ptr<TxnKv> txn_kv_;
std::atomic_bool stopped_ {false};
std::vector<std::thread> workers_;
std::mutex mtx_;
// notify recycle workers
std::condition_variable pending_instance_cond_;
std::deque<InstanceInfoPB> pending_instance_queue_;
std::unordered_set<std::string> pending_instance_set_;
std::unordered_map<std::string, std::shared_ptr<InstanceRecycler>> recycling_instance_map_;
// notify instance scanner and lease thread
std::condition_variable notifier_;
std::string ip_port_;
std::unique_ptr<Checker> checker_;
RecyclerThreadPoolGroup _thread_pool_group;
std::shared_ptr<TxnLazyCommitter> txn_lazy_committer_;
std::shared_ptr<SnapshotManager> snapshot_manager_;
std::shared_ptr<SnapshotDataMigrator> snapshot_data_migrator_;
std::shared_ptr<SnapshotChainCompactor> snapshot_chain_compactor_;
};
enum class RowsetRecyclingState {
FORMAL_ROWSET,
TMP_ROWSET,
};
// Represents a single rowset deletion task for batch delete
struct RowsetDeleteTask {
RowsetMetaCloudPB rowset_meta;
std::string recycle_rowset_key; // Primary key marking "pending recycle"
std::string non_versioned_rowset_key; // Legacy non-versioned rowset meta key
std::string versioned_rowset_key; // Versioned meta rowset key
Versionstamp versionstamp;
std::string rowset_ref_count_key;
};
class RecyclerMetricsContext {
public:
RecyclerMetricsContext() = default;
RecyclerMetricsContext(std::string instance_id, std::string operation_type)
: operation_type(std::move(operation_type)), instance_id(std::move(instance_id)) {
start();
}
~RecyclerMetricsContext() = default;
std::atomic_ullong total_need_recycle_data_size = 0;
std::atomic_ullong total_need_recycle_num = 0;
std::atomic_ullong total_recycled_data_size = 0;
std::atomic_ullong total_recycled_num = 0;
std::string operation_type;
std::string instance_id;
double start_time = 0;
void start() {
start_time = duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
double duration() const {
return duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count() -
start_time;
}
void reset() {
total_need_recycle_data_size = 0;
total_need_recycle_num = 0;
total_recycled_data_size = 0;
total_recycled_num = 0;
start_time = duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
void finish_report() {
if (!operation_type.empty()) {
double cost = duration();
g_bvar_recycler_instance_last_round_recycle_elpased_ts.put(
{instance_id, operation_type}, cost);
g_bvar_recycler_instance_recycle_round.put({instance_id, operation_type}, 1);
g_bvar_recycler_instance_recycle_total_bytes_since_started.put(
{instance_id, operation_type}, total_recycled_data_size.load());
g_bvar_recycler_instance_recycle_total_num_since_started.put(
{instance_id, operation_type}, total_recycled_num.load());
LOG(INFO) << "recycle instance: " << instance_id
<< ", operation type: " << operation_type << ", cost: " << cost
<< " ms, total recycled num: " << total_recycled_num.load()
<< ", total recycled data size: " << total_recycled_data_size.load()
<< " bytes";
if (cost != 0) {
if (total_recycled_num.load() != 0) {
g_bvar_recycler_instance_recycle_time_per_resource.put(
{instance_id, operation_type}, cost / total_recycled_num.load());
}
g_bvar_recycler_instance_recycle_bytes_per_ms.put(
{instance_id, operation_type}, total_recycled_data_size.load() / cost);
}
}
}
// `is_begin` is used to initialize total num of items need to be recycled
void report(bool is_begin = false) {
if (!operation_type.empty()) {
// is init
if (is_begin) {
auto value = total_need_recycle_num.load();
g_bvar_recycler_instance_last_round_to_recycle_bytes.put(
{instance_id, operation_type}, total_need_recycle_data_size.load());
g_bvar_recycler_instance_last_round_to_recycle_num.put(
{instance_id, operation_type}, value);
} else {
g_bvar_recycler_instance_last_round_recycled_bytes.put(
{instance_id, operation_type}, total_recycled_data_size.load());
g_bvar_recycler_instance_last_round_recycled_num.put({instance_id, operation_type},
total_recycled_num.load());
}
}
}
};
class TabletRecyclerMetricsContext : public RecyclerMetricsContext {
public:
TabletRecyclerMetricsContext() : RecyclerMetricsContext("global_recycler", "recycle_tablet") {}
};
class SegmentRecyclerMetricsContext : public RecyclerMetricsContext {
public:
SegmentRecyclerMetricsContext()
: RecyclerMetricsContext("global_recycler", "recycle_segment") {}
};
struct OplogRecycleStats;
class InstanceRecycler {
public:
struct PackedFileRecycleStats {
int64_t num_scanned = 0; // packed-file kv scanned
int64_t num_corrected = 0; // packed-file kv corrected
int64_t num_deleted = 0; // packed-file kv deleted
int64_t num_failed = 0; // packed-file kv failed
int64_t bytes_deleted = 0; // packed-file kv bytes deleted from txn-kv
int64_t num_object_deleted = 0; // packed-file objects deleted from storage (vault/HDFS)
int64_t bytes_object_deleted = 0; // bytes deleted from storage objects
int64_t rowset_scan_count = 0; // rowset metas scanned during correction
};
explicit InstanceRecycler(std::shared_ptr<TxnKv> txn_kv, const InstanceInfoPB& instance,
RecyclerThreadPoolGroup thread_pool_group,
std::shared_ptr<TxnLazyCommitter> txn_lazy_committer);
~InstanceRecycler();
std::string_view instance_id() const { return instance_id_; }
const InstanceInfoPB& instance_info() const { return instance_info_; }
// returns 0 for success otherwise error
int init();
void stop() { stopped_.store(true, std::memory_order_release); }
bool stopped() const { return stopped_.load(std::memory_order_acquire); }
// returns 0 for success otherwise error
int do_recycle();
// remove all kv and data in this instance, ONLY be called when instance has been deleted
// returns 0 for success otherwise error
int recycle_deleted_instance();
// scan and recycle expired indexes:
// 1. dropped table, dropped mv
// 2. half-successtable/index when create
// returns 0 for success otherwise error
int recycle_indexes();
// scan and recycle expired partitions:
// 1. dropped parttion
// 2. half-success partition when create
// returns 0 for success otherwise error
int recycle_partitions();
// scan and recycle expired rowsets:
// 1. prepare_rowset will produce recycle_rowset before uploading data to remote storage (memo)
// 2. compaction will change the input rowsets to recycle_rowset
// returns 0 for success otherwise error
int recycle_rowsets();
// like `recycle_rowsets`, but for versioned rowsets.
int recycle_versioned_rowsets();
// scan and recycle expired tmp rowsets:
// 1. commit_rowset will produce tmp_rowset when finish upload data (load or compaction) to remote storage
// returns 0 for success otherwise error
int recycle_tmp_rowsets();
/**
* recycle all tablets belonging to the index specified by `index_id`
*
* @param partition_id if positive, only recycle tablets in this partition belonging to the specified index
* @return 0 for success otherwise error
*/
int recycle_tablets(int64_t table_id, int64_t index_id, RecyclerMetricsContext& ctx,
int64_t partition_id = -1);
/**
* recycle all rowsets belonging to the tablet specified by `tablet_id`
*
* @return 0 for success otherwise error
*/
int recycle_tablet(int64_t tablet_id, RecyclerMetricsContext& metrics_context);
/**
* like `recycle_tablet`, but for versioned tablet
*/
int recycle_versioned_tablet(int64_t tablet_id, RecyclerMetricsContext& metrics_context);
// scan and recycle useless partition version kv
int recycle_versions();
// scan and recycle the orphan partitions
int recycle_orphan_partitions();
// scan and abort timeout txn label
// returns 0 for success otherwise error
int abort_timeout_txn();
//scan and recycle expire txn label
// returns 0 for success otherwise error
int recycle_expired_txn_label();
// scan and recycle finished or timeout copy jobs
// returns 0 for success otherwise error
int recycle_copy_jobs();
// scan and recycle dropped internal stage
// returns 0 for success otherwise error
int recycle_stage();
// scan and recycle expired stage objects
// returns 0 for success otherwise error
int recycle_expired_stage_objects();
// scan and recycle operation logs
// returns 0 for success otherwise error
int recycle_operation_logs();
// scan and recycle expired restore jobs
// returns 0 for success otherwise error
int recycle_restore_jobs();
/**
* Scan packed-file metadata, correct reference counters, and recycle unused packed files.
*
* @return 0 on success, non-zero error code otherwise
*/
int recycle_packed_files();
// scan and recycle snapshots
// returns 0 for success otherwise error
int recycle_cluster_snapshots();
// scan and recycle ref rowsets for deleted instance
// returns 0 for success otherwise error
int recycle_ref_rowsets(bool* has_unrecycled_rowsets);
bool check_recycle_tasks();
int scan_and_statistics_indexes();
int scan_and_statistics_partitions();
int scan_and_statistics_rowsets();
int scan_and_statistics_tmp_rowsets();
int scan_and_statistics_abort_timeout_txn();
int scan_and_statistics_expired_txn_label();
int scan_and_statistics_copy_jobs();
int scan_and_statistics_stage();
int scan_and_statistics_expired_stage_objects();
int scan_and_statistics_versions();
int scan_and_statistics_restore_jobs();
void scan_and_statistics_operation_logs();
/**
* Decode the key of a packed-file metadata record into the persisted object path.
*
* @param key raw key persisted in txn-kv
* @param packed_path output object storage path referenced by the key
* @return true if decoding succeeds, false otherwise
*/
static bool decode_packed_file_key(std::string_view key, std::string* packed_path);
void TEST_add_accessor(std::string_view id, std::shared_ptr<StorageVaultAccessor> accessor) {
accessor_map_.insert({std::string(id), std::move(accessor)});
}
// Recycle snapshot meta and data, return 0 for success otherwise error.
int recycle_snapshot_meta_and_data(const std::string& instance_id,
const std::string& resource_id,
Versionstamp snapshot_version,
const SnapshotPB& snapshot_pb);
private:
// returns 0 for success otherwise error
int init_obj_store_accessors();
// returns 0 for success otherwise error
int init_storage_vault_accessors();
/**
* Scan key-value pairs between [`begin`, `end`) with multiple rounds of range get(`RangeGetIterator`),
* and perform `recycle_func` on each key-value pair.
*
* @param recycle_func defines how to recycle resources corresponding to a key-value pair.
* The scan will stop if recycle_func() returns non-zero.
* recycle_func() returns 0 if the recycling is successful or the scan can continue with ignorable errors.
* @param loop_done is called after a round (`RangeGetIterator`) in the scan has no next kv. Usually used to perform a batch recycling.
* The scan will stop if loop_done() returns non-zero.
* loop_done() returns 0 if the recycling is successful or the scan can continue with ignorable errors.
* @return 0 if all corresponding resources are recycled successfully, otherwise non-zero
*/
int scan_and_recycle(std::string begin, std::string_view end,
std::function<int(std::string_view k, std::string_view v)> recycle_func,
std::function<int()> loop_done = nullptr,
std::function<bool(std::string*)> next_begin_getter = nullptr);
static int next_recycle_rowset_tablet_key(const std::string& instance_id, int64_t tablet_id,
std::string* next_key);
int scan_recycle_rowsets_by_tablet(
std::string begin, std::string_view end,
std::function<int(std::string_view k, std::string_view v)> recycle_func,
std::function<int()> loop_done = nullptr);
// return 0 for success otherwise error
int delete_rowset_data(const doris::RowsetMetaCloudPB& rs_meta_pb);
// return 0 for success otherwise error
// NOTE: this function ONLY be called when the file paths cannot be calculated
int delete_rowset_data(const std::string& resource_id, int64_t tablet_id,
const std::string& rowset_id);
// return 0 for success otherwise error
int delete_rowset_data(const std::map<std::string, doris::RowsetMetaCloudPB>& rowsets,
RowsetRecyclingState type, RecyclerMetricsContext& metrics_context);
// Decrement packed file ref counts for rowset segments.
// Returns 0 for success, -1 for error.
int decrement_packed_file_ref_counts(const doris::RowsetMetaCloudPB& rs_meta_pb);
enum class DeleteBitmapStorageType {
NOT_FOUND,
IN_FDB,
STANDALONE_FILE,
PACKED_FILE,
};
// Process delete bitmap storage and decrement packed file ref count when needed.
// Returns 0 for success, -1 for error.
// out_storage_type: if not null, will be set to the delete bitmap storage type.
int decrement_delete_bitmap_packed_file_ref_counts(int64_t tablet_id,
const std::string& rowset_id,
DeleteBitmapStorageType* out_storage_type);
int delete_packed_file_and_kv(const std::string& packed_file_path,
const std::string& packed_key,
const cloud::PackedFileInfoPB& packed_info);
/**
* Get stage storage info from instance and init StorageVaultAccessor
* @return 0 if accessor is successfully inited, 1 if stage not found, negative for error
*/
int init_copy_job_accessor(const std::string& stage_id, const StagePB::StageType& stage_type,
std::shared_ptr<StorageVaultAccessor>* accessor);
void register_recycle_task(const std::string& task_name, int64_t start_time);
void unregister_recycle_task(const std::string& task_name);
// for scan all tablets and statistics metrics
int scan_tablets_and_statistics(int64_t tablet_id, int64_t index_id,
RecyclerMetricsContext& metrics_context,
int64_t partition_id = -1, bool is_empty_tablet = false);
// for scan all rs of tablet and statistics metrics
int scan_tablet_and_statistics(int64_t tablet_id, RecyclerMetricsContext& metrics_context);
// Recycle operation log and the log keys. The log keys are specified by `raw_keys`.
//
// Both `operation_log` and `raw_keys` will be removed in the same transaction, to ensure atomicity.
int recycle_operation_log(Versionstamp log_version, const std::vector<std::string>& raw_keys,
OperationLogPB operation_log,
OplogRecycleStats* oplog_stats = nullptr);
// Recycle rowset meta and data, return 0 for success otherwise error
//
// This function will decrease the rowset ref count and remove the rowset meta and data if the ref count is 1.
int recycle_rowset_meta_and_data(const RowsetDeleteTask& task);
// Classify rowset task by ref_count, return 0 to add to batch delete, 1 if handled (ref>1), -1 on error
int classify_rowset_task_by_ref_count(RowsetDeleteTask& task,
std::vector<RowsetDeleteTask>& batch_delete_tasks);
// Cleanup metadata for deleted rowsets, return 0 for success otherwise error
int cleanup_rowset_metadata(const std::vector<RowsetDeleteTask>& tasks);
// Whether the instance has any snapshots, return 0 for success otherwise error.
int has_cluster_snapshots(bool* any);
// Whether need to recycle versioned keys
bool should_recycle_versioned_keys() const;
/**
* Parse the path of a packed-file fragment and output the owning tablet and rowset identifiers.
*
* @param path packed-file fragment path to decode
* @param tablet_id output tablet identifier extracted from the path
* @param rowset_id output rowset identifier extracted from the path
* @return true if both identifiers are successfully parsed, false otherwise
*/
static bool parse_packed_slice_path(std::string_view path, int64_t* tablet_id,
std::string* rowset_id);
// Check whether a rowset referenced by a packed file still exists in metadata.
// @param stats optional recycle statistics collector.
int check_rowset_exists(int64_t tablet_id, const std::string& rowset_id, bool* exists,
PackedFileRecycleStats* stats = nullptr);
int check_recycle_and_tmp_rowset_exists(int64_t tablet_id, const std::string& rowset_id,
int64_t txn_id, bool* recycle_exists, bool* tmp_exists);
/**
* Resolve which storage accessor should be used for a packed file.
*
* @param hint preferred storage resource identifier persisted with the file
* @return pair of the resolved resource identifier and accessor; the accessor can be null if unavailable
*/
std::pair<std::string, std::shared_ptr<StorageVaultAccessor>> resolve_packed_file_accessor(
const std::string& hint);
// Recompute packed-file counters and lifecycle state after validating contained fragments.
// @param stats optional recycle statistics collector.
int correct_packed_file_info(cloud::PackedFileInfoPB* packed_info, bool* changed,
const std::string& packed_file_path,
PackedFileRecycleStats* stats = nullptr);
// Correct and recycle a single packed-file record, updating metadata and accounting statistics.
// @param stats optional recycle statistics collector.
int process_single_packed_file(const std::string& packed_key,
const std::string& packed_file_path,
PackedFileRecycleStats* stats);
// Process a packed-file KV while scanning and aggregate recycling statistics.
int handle_packed_file_kv(std::string_view key, std::string_view value,
PackedFileRecycleStats* stats, int* ret);
// Abort the transaction/job associated with a rowset that is about to be recycled.
// This function is called during rowset recycling to prevent data loss by ensuring that
// the transaction/job cannot be committed after its rowset data has been deleted.
//
// Scenario:
// When recycler detects an expired prepared rowset (e.g., from a failed load transaction/job),
// it needs to recycle the rowset data. However, if the transaction/job is still active and gets
// committed after the data is deleted, it would lead to data loss - the transaction/job would
// reference non-existent data.
//
// Solution:
// Before recycling the rowset data, this function aborts the associated transaction/job to ensure
// it cannot be committed. This guarantees that:
// 1. The transaction/job state is marked as ABORTED
// 2. Any subsequent commit_rowset/commit_txn attempts will fail
// 3. The rowset data can be safely deleted without risk of data loss
//
// Parameters:
// txn_id: The transaction/job ID associated with the rowset to be recycled
//
// Returns:
// 0 on success, -1 on failure
int abort_txn_for_related_rowset(int64_t txn_id);
int abort_job_for_related_rowset(const RowsetMetaCloudPB& rowset_meta);
template <typename T>
int batch_abort_txn_or_job_for_recycle(const std::vector<std::string>& keys,
bool skip_base_version);
private:
std::atomic_bool stopped_ {false};
std::shared_ptr<TxnKv> txn_kv_;
std::string instance_id_;
InstanceInfoPB instance_info_;
// TODO(plat1ko): Add new accessor to map in runtime for new created storage vaults
std::unordered_map<std::string, std::shared_ptr<StorageVaultAccessor>> accessor_map_;
using InvertedIndexInfo =
std::pair<InvertedIndexStorageFormatPB, std::vector<std::pair<int64_t, std::string>>>;
class InvertedIndexIdCache;
std::unique_ptr<InvertedIndexIdCache> inverted_index_id_cache_;
std::mutex recycled_tablets_mtx_;
// Store recycled tablets, we can skip deleting rowset data of these tablets because these data has already been deleted.
std::unordered_set<int64_t> recycled_tablets_;
std::mutex recycle_tasks_mutex;
// <task_name, start_time>>
std::map<std::string, int64_t> running_recycle_tasks;
RecyclerThreadPoolGroup _thread_pool_group;
std::shared_ptr<TxnLazyCommitter> txn_lazy_committer_;
std::shared_ptr<SnapshotManager> snapshot_manager_;
std::shared_ptr<DeleteBitmapLockWhiteList> delete_bitmap_lock_white_list_;
std::shared_ptr<ResourceManager> resource_mgr_;
TabletRecyclerMetricsContext tablet_metrics_context_;
SegmentRecyclerMetricsContext segment_metrics_context_;
};
struct OperationLogReferenceInfo {
bool referenced_by_instance = false;
bool referenced_by_snapshot = false;
Versionstamp referenced_snapshot_timestamp;
};
struct OplogRecycleStats {
// Total oplog count scanned per round
std::atomic<int64_t> total_num {0};
// Oplogs not recycled this round (per round, written to mBvarStatus)
std::atomic<int64_t> not_recycled_num {0};
// Recycle failures (per round, accumulated to mBvarIntAdder at end)
std::atomic<int64_t> failed_num {0};
// Per-oplog-type recycled counts (incremented after successful commit)
std::atomic<int64_t> recycled_commit_partition {0};
std::atomic<int64_t> recycled_drop_partition {0};
std::atomic<int64_t> recycled_commit_index {0};
std::atomic<int64_t> recycled_drop_index {0};
std::atomic<int64_t> recycled_update_tablet {0};
std::atomic<int64_t> recycled_compaction {0};
std::atomic<int64_t> recycled_schema_change {0};
std::atomic<int64_t> recycled_commit_txn {0};
};
// Helper class to check if operation logs can be recycled based on snapshots and versionstamps
class OperationLogRecycleChecker {
public:
OperationLogRecycleChecker(std::string_view instance_id, TxnKv* txn_kv,
const InstanceInfoPB& instance_info)
: instance_id_(instance_id), txn_kv_(txn_kv), instance_info_(instance_info) {}
// Initialize the checker by loading snapshots and setting max version stamp
int init();
// Check if an operation log can be recycled
bool can_recycle(const Versionstamp& log_versionstamp, int64_t log_min_timestamp,
OperationLogReferenceInfo* reference_info) const;
Versionstamp max_versionstamp() const { return max_versionstamp_; }
const std::vector<std::pair<SnapshotPB, Versionstamp>>& get_snapshots() const {
return snapshots_;
}
private:
std::string_view instance_id_;
TxnKv* txn_kv_;
const InstanceInfoPB& instance_info_;
Versionstamp max_versionstamp_;
Versionstamp source_snapshot_versionstamp_;
std::map<Versionstamp, size_t> snapshot_indexes_;
std::vector<std::pair<SnapshotPB, Versionstamp>> snapshots_;
};
class SnapshotDataSizeCalculator {
public:
SnapshotDataSizeCalculator(std::string_view instance_id, std::shared_ptr<TxnKv> txn_kv)
: instance_id_(instance_id), txn_kv_(std::move(txn_kv)) {}
void init(const std::vector<std::pair<SnapshotPB, Versionstamp>>& snapshots);
int calculate_operation_log_data_size(const std::string_view& log_key,
OperationLogPB& operation_log,
OperationLogReferenceInfo& reference_info);
int save_snapshot_data_size_with_retry();
private:
int get_all_index_partitions(int64_t db_id, int64_t table_id, int64_t index_id,
std::vector<int64_t>* partition_ids);
int get_index_partition_data_size(int64_t db_id, int64_t table_id, int64_t index_id,
int64_t partition_id, int64_t* data_size);
int save_operation_log(const std::string_view& log_key, OperationLogPB& operation_log);
int save_snapshot_data_size();
std::string_view instance_id_;
std::shared_ptr<TxnKv> txn_kv_;
int64_t instance_retained_data_size_ = 0;
std::map<Versionstamp, int64_t> retained_data_size_;
std::set<std::string> calculated_partitions_;
};
} // namespace doris::cloud