github-actions[bot] commented on code in PR #68005:
URL: https://github.com/apache/doris/pull/68005#discussion_r4059150311
##########
cloud/src/recycler/recycler.h:
##########
@@ -153,105 +154,136 @@ struct RowsetDeleteTask {
class RecyclerMetricsContext {
public:
- RecyclerMetricsContext() = default;
+ enum class MetricType {
+ SCANNED_NUM,
+ EXPIRED_NUM,
+ RECYCLED_NUM,
+ RECYCLED_BYTES,
+ };
- RecyclerMetricsContext(std::string instance_id, std::string operation_type)
- : operation_type(std::move(operation_type)),
instance_id(std::move(instance_id)) {
- start();
- }
+ class MetricValue {
+ public:
+ // Concurrent workers only update atomics. Batch boundaries publish
their snapshots.
+ MetricValue& operator+=(uint64_t delta) {
+ value_.fetch_add(delta, std::memory_order_relaxed);
+ return *this;
+ }
+
+ MetricValue& operator++() {
+ *this += 1;
+ return *this;
+ }
- ~RecyclerMetricsContext() = default;
+ uint64_t operator++(int) { return value_.fetch_add(1,
std::memory_order_relaxed); }
- std::atomic_ullong total_need_recycle_data_size = 0;
- std::atomic_ullong total_need_recycle_num = 0;
+ void reset() { value_.store(0, std::memory_order_relaxed); }
- std::atomic_ullong total_recycled_data_size = 0;
- std::atomic_ullong total_recycled_num = 0;
+ void set(uint64_t v) { value_.store(v, std::memory_order_relaxed); }
- std::string operation_type;
- std::string instance_id;
+ uint64_t value() const { return
value_.load(std::memory_order_relaxed); }
+
+ private:
+ std::atomic_ullong value_ = 0;
+ };
- double start_time = 0;
+ RecyclerMetricsContext() = delete;
- void start() {
- start_time = duration_cast<std::chrono::milliseconds>(
-
std::chrono::system_clock::now().time_since_epoch())
- .count();
+ explicit RecyclerMetricsContext(std::string instance_id, std::string
operation_type)
+ : operation_type(std::move(operation_type)),
+ instance_id(std::move(instance_id)),
+ start_time_(std::chrono::steady_clock::now()) {
+ reset();
}
- double duration() const {
- return duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch())
- .count() -
- start_time;
+ // Each context has one publisher; workers may update its MetricValues
concurrently.
+ void update_metrics() {
+ auto cost =
duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() -
+ start_time_)
+ .count();
+
g_bvar_recycler_instance_current_round_task_elapsed_ms.put({instance_id,
operation_type},
+ cost);
+ put(MetricType::SCANNED_NUM, kv_scanned_num.value());
+ put(MetricType::EXPIRED_NUM, kv_expired_num.value());
+ put(MetricType::RECYCLED_NUM, kv_recycled_num.value());
+ put(MetricType::RECYCLED_BYTES, kv_recycled_bytes.value());
}
+ ~RecyclerMetricsContext() { finish(); }
+
+ MetricValue kv_scanned_num;
+ MetricValue kv_expired_num;
+ MetricValue kv_recycled_num;
+ MetricValue kv_recycled_bytes;
+
+ std::string operation_type;
+ std::string instance_id;
+
+private:
+ std::chrono::steady_clock::time_point 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();
+ start_time_ = std::chrono::steady_clock::now();
+ kv_scanned_num.reset();
+ kv_expired_num.reset();
+ kv_recycled_num.reset();
+ kv_recycled_bytes.reset();
+ put(MetricType::SCANNED_NUM, 0);
+ put(MetricType::EXPIRED_NUM, 0);
+ put(MetricType::RECYCLED_NUM, 0);
+ put(MetricType::RECYCLED_BYTES, 0);
+
g_bvar_recycler_instance_current_round_task_elapsed_ms.put({instance_id,
operation_type},
+ 0);
}
- 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());
+ void finish() {
+ update_metrics();
+ if (auto num = kv_recycled_num.value(); num > 0) {
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);
- }
+ {instance_id, operation_type}, static_cast<int64_t>(num));
+ }
+ if (auto bytes = kv_recycled_bytes.value(); bytes > 0) {
+ g_bvar_recycler_instance_recycle_total_bytes_since_started.put(
+ {instance_id, operation_type},
static_cast<int64_t>(bytes));
}
+ g_bvar_recycler_instance_last_round_recycled_num.put(
+ {instance_id, operation_type},
static_cast<int64_t>(kv_recycled_num.value()));
+ g_bvar_recycler_instance_last_round_recycled_bytes.put(
+ {instance_id, operation_type},
static_cast<int64_t>(kv_recycled_bytes.value()));
+ reset();
Review Comment:
[P2] Publish current metrics before final reset. The stream contexts and the
member tablet/segment contexts update these atomics but never call
update_metrics() in production. Their only publication is this finalizer, where
update_metrics() is immediately followed by reset(), so a scrape during a long
round sees zero and cannot observe the work at all. Please publish at joined
batch boundaries (and advance/reset the instance current elapsed gauge) rather
than relying on destruction.
##########
cloud/src/recycler/recycler.h:
##########
@@ -153,105 +154,136 @@ struct RowsetDeleteTask {
class RecyclerMetricsContext {
public:
- RecyclerMetricsContext() = default;
+ enum class MetricType {
+ SCANNED_NUM,
+ EXPIRED_NUM,
+ RECYCLED_NUM,
+ RECYCLED_BYTES,
+ };
- RecyclerMetricsContext(std::string instance_id, std::string operation_type)
- : operation_type(std::move(operation_type)),
instance_id(std::move(instance_id)) {
- start();
- }
+ class MetricValue {
+ public:
+ // Concurrent workers only update atomics. Batch boundaries publish
their snapshots.
+ MetricValue& operator+=(uint64_t delta) {
+ value_.fetch_add(delta, std::memory_order_relaxed);
+ return *this;
+ }
+
+ MetricValue& operator++() {
+ *this += 1;
+ return *this;
+ }
- ~RecyclerMetricsContext() = default;
+ uint64_t operator++(int) { return value_.fetch_add(1,
std::memory_order_relaxed); }
- std::atomic_ullong total_need_recycle_data_size = 0;
- std::atomic_ullong total_need_recycle_num = 0;
+ void reset() { value_.store(0, std::memory_order_relaxed); }
- std::atomic_ullong total_recycled_data_size = 0;
- std::atomic_ullong total_recycled_num = 0;
+ void set(uint64_t v) { value_.store(v, std::memory_order_relaxed); }
- std::string operation_type;
- std::string instance_id;
+ uint64_t value() const { return
value_.load(std::memory_order_relaxed); }
+
+ private:
+ std::atomic_ullong value_ = 0;
+ };
- double start_time = 0;
+ RecyclerMetricsContext() = delete;
- void start() {
- start_time = duration_cast<std::chrono::milliseconds>(
-
std::chrono::system_clock::now().time_since_epoch())
- .count();
+ explicit RecyclerMetricsContext(std::string instance_id, std::string
operation_type)
+ : operation_type(std::move(operation_type)),
+ instance_id(std::move(instance_id)),
+ start_time_(std::chrono::steady_clock::now()) {
+ reset();
}
- double duration() const {
- return duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch())
- .count() -
- start_time;
+ // Each context has one publisher; workers may update its MetricValues
concurrently.
+ void update_metrics() {
+ auto cost =
duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() -
+ start_time_)
+ .count();
+
g_bvar_recycler_instance_current_round_task_elapsed_ms.put({instance_id,
operation_type},
+ cost);
+ put(MetricType::SCANNED_NUM, kv_scanned_num.value());
+ put(MetricType::EXPIRED_NUM, kv_expired_num.value());
+ put(MetricType::RECYCLED_NUM, kv_recycled_num.value());
+ put(MetricType::RECYCLED_BYTES, kv_recycled_bytes.value());
}
+ ~RecyclerMetricsContext() { finish(); }
+
+ MetricValue kv_scanned_num;
+ MetricValue kv_expired_num;
+ MetricValue kv_recycled_num;
+ MetricValue kv_recycled_bytes;
+
+ std::string operation_type;
+ std::string instance_id;
+
+private:
+ std::chrono::steady_clock::time_point 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();
+ start_time_ = std::chrono::steady_clock::now();
+ kv_scanned_num.reset();
+ kv_expired_num.reset();
+ kv_recycled_num.reset();
+ kv_recycled_bytes.reset();
+ put(MetricType::SCANNED_NUM, 0);
+ put(MetricType::EXPIRED_NUM, 0);
+ put(MetricType::RECYCLED_NUM, 0);
Review Comment:
[P2] Do not reset a shared series from an uncoordinated publisher. The
manual recycle_copy_jobs endpoint can construct a second InstanceRecycler for
an instance already in recycling_instance_map_ because it uses a separate
s_worker guard. That constructor resets the scheduled round's tablet/segment
series, and its destructor later overwrites their last-round values with zero
even though the manual worker never ran those tasks. Please share the
scheduler's per-instance exclusion or make publication generation-aware.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -5732,7 +5628,9 @@ int InstanceRecycler::recycle_versioned_tablet(int64_t
tablet_id,
}
if (ret == 0) {
- // All object files under tablet have been deleted
+ // All object files under tablet have been deleted, and all KV keys
have been deleted
+ tablet_metrics_context_.kv_recycled_num += 1;
+ segment_metrics_context_.kv_recycled_num += recycle_segments_number;
Review Comment:
[P2] Aggregate only physical deletion results, exactly once.
recycle_segments_number includes ref-count>1 rowsets whose files are
deliberately retained, while a unique batch-deleted rowset has already
incremented segment_metrics_context_ inside delete_rowset_data(), so this line
counts it a second time. The pre-aggregated tablet bytes above have the same
retained-rowset problem. Please collect per-rowset delete outcomes after
classification for the tablet/segment totals.
##########
regression-test/plugins/cloud_recycler_plugin.groovy:
##########
@@ -510,92 +510,31 @@ Suite.metaClass.checkRecycleMetrics = { String
recyclerHttpPort, String recycleJ
int retryCount = 0
while (true) {
- def metricDataBeforeRecycle = getRecyclerMetricsMethod.call(
- recyclerHttpPort,
- "recycler_instance_last_round_to_recycle_bytes",
- recycleJobType
- )
-
- def metricDataAftereRecycle = getRecyclerMetricsMethod.call(
+ def recycledBytesMetric = getRecyclerMetricsMethod.call(
recyclerHttpPort,
"recycler_instance_last_round_recycled_bytes",
recycleJobType
)
-
- // not all resource types have bytes metrics
- def validResourceTypes = ["recycle_indexes", "recycle_partitions",
"recycle_tmp_rowsets", "recycle_rowsets", "recycle_tablet", "recycle_segment"]
-
- boolean checkFlag1 = false
- boolean checkFlag2 = false
-
- if (validResourceTypes.contains(recycleJobType)) {
- checkFlag1 = true
- }
-
- if (metricDataBeforeRecycle && metricDataAftereRecycle && !checkFlag1)
{
- if (metricDataBeforeRecycle.value ==
metricDataAftereRecycle.value) {
- logger.info("--- Recycle Success ---")
- logger.info("Metric Name:
recycler_instance_last_round_recycled_bytes")
- logger.info("Value: ${metricDataBeforeRecycle.value}")
- logger.info("Resource Type:
${metricDataBeforeRecycle.labels?.resource_type}")
- logger.info("--------------------------------------")
- checkFlag1 = true
- } else {
- logger.info("--- Recycle failed ---")
- logger.info("Metric Name:
recycler_instance_last_round_to_recycle_bytes")
- logger.info("Value: ${metricDataBeforeRecycle.value}")
- logger.info("Resource Type:
${metricDataBeforeRecycle.labels?.resource_type}")
- logger.info("--------------------------------------")
- logger.info("Metric Name:
recycler_instance_last_round_recycled_bytes")
- logger.info("Value: ${metricDataAftereRecycle.value}")
- logger.info("Resource Type:
${metricDataAftereRecycle.labels?.resource_type}")
- logger.info("--------------------------------------")
- }
- }
-
- metricDataBeforeRecycle = getRecyclerMetricsMethod.call(
- recyclerHttpPort,
- "recycler_instance_last_round_to_recycle_num",
- recycleJobType
- )
-
- metricDataAftereRecycle = getRecyclerMetricsMethod.call(
+ def recycledNumMetric = getRecyclerMetricsMethod.call(
recyclerHttpPort,
"recycler_instance_last_round_recycled_num",
recycleJobType
)
- if (metricDataBeforeRecycle && metricDataAftereRecycle && !checkFlag2)
{
- if (metricDataBeforeRecycle.value ==
metricDataAftereRecycle.value) {
- logger.info("--- Recycle Success ---")
- logger.info("Metric Name:
recycler_instance_last_round_recycled_num")
- logger.info("Value: ${metricDataBeforeRecycle.value}")
- logger.info("Resource Type:
${metricDataBeforeRecycle.labels?.resource_type}")
- logger.info("--------------------------------------")
- checkFlag2 = true
- } else {
- logger.info("--- Recycle failed ---")
- logger.info("Metric Name:
recycler_instance_last_round_to_recycle_num")
- logger.info("Value: ${metricDataBeforeRecycle.value}")
- logger.info("Resource Type:
${metricDataBeforeRecycle.labels?.resource_type}")
- logger.info("--------------------------------------")
- logger.info("Metric Name:
recycler_instance_last_round_recycled_num")
- logger.info("Value: ${metricDataAftereRecycle.value}")
- logger.info("Resource Type:
${metricDataAftereRecycle.labels?.resource_type}")
- logger.info("--------------------------------------")
- }
- }
-
- if (checkFlag1 && checkFlag2) {
- break;
+ if (recycledBytesMetric && recycledNumMetric) {
Review Comment:
[P2] Exercise the new inline counters here. This condition only checks that
two legacy last-round records exist; every RecyclerMetricsContext destructor
creates them even when their values are zero, so the test passes with the
missing publishers and wrong task accounting introduced in this change. Please
assert expected values/deltas for workloads with recyclable data and cover at
least one failed/partial path.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -7822,8 +7762,9 @@ int InstanceRecycler::recycle_expired_stage_objects() {
ret = -1;
continue;
}
- metrics_context.total_recycled_num++;
- metrics_context.report();
+ ++num_expired;
Review Comment:
[P2] Do not publish this object-store operation as a recycled TxnKV. This
loop scans instance stages and delete_all() removes S3 objects; the following
comment even says no KV is recycled, but these locals are copied into
kv_expired_num/kv_recycled_num. A success therefore reports one nonexistent KV
deletion, while a storage failure also hides the eligible stage. Use an
object-operation/byte metric here (and mark eligibility before deletion if an
expired counter is retained).
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -5968,6 +5866,12 @@ int InstanceRecycler::recycle_rowsets() {
};
auto loop_done = [&]() -> int {
+ DORIS_CLOUD_DEFER {
+ metrics_context.kv_scanned_num.set(num_scanned);
+ metrics_context.kv_expired_num.set(num_expired);
+ metrics_context.kv_recycled_num.set(num_recycled);
Review Comment:
[P2] Record the physical deletion for PREPARE rowsets. On this prefix path
the worker can delete the objects and recycle KV successfully, but it only
increments num_recycled; the known total_disk_size and num_segments never reach
the task or segment metrics. Carry those values into the completion callback
and update them only after the object deletion succeeds, including the
abort/recheck and versioned variants.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -2791,8 +2796,8 @@ int
InstanceRecycler::recycle_table_stream_offset_prefix(std::string prefix,
.tag("num_keys", num_keys);
return -1;
}
- metrics_context->total_recycled_num += num_keys;
- metrics_context->report();
+ metrics_context->kv_expired_num += keys.size();
Review Comment:
[P2] Advance expired at the eligibility boundary, not after commit. If
txn_remove fails for a page of N scanned offsets, this code publishes
scanned=N, expired=0, recycled=0 even though all N are still eligible for
retry. Increment expired while collecting the offsets and keep recycled here
after the successful removal so the two gauges distinguish backlog from
completed work.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -3417,6 +3428,7 @@ int InstanceRecycler::recycle_versions() {
if (iter->has_next()) { // Table is useful, should not recycle table
and partition versions
return 0;
}
+ ++num_expired;
Review Comment:
[P2] Keep all three counters in the same unit. For a dropped table with N
partition-version keys, this branch makes scanned=N and recycled=N but expired
remains 1 because only the first key increments it. The range delete can also
remove keys beyond the materialized page, so recycled can undercount the
committed removal. Count exact eligible/committed KVs, or expose these as
table-level metrics under different names.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -2881,9 +2883,8 @@ int
InstanceRecycler::recycle_partition_table_stream_offsets(
int64_t db_id, int64_t table_id, int64_t partition_id,
const google::protobuf::RepeatedPtrField<TableStreamIdentityPB>&
table_streams) {
RecyclerMetricsContext metrics_context(instance_id_,
"recycle_stream_partition_offsets");
- DORIS_CLOUD_DEFER {
- metrics_context.finish_report();
- };
+ metrics_context.kv_scanned_num += table_streams.size();
Review Comment:
[P2] Do not pre-count bindings as scanned/expired KVs before a transaction
exists. If create_txn() fails, this publishes scanned=N and expired=N without
touching TxnKV at all. Even on success no KV is scanned and each binding
removes two keyspaces. Account only completed work and either measure actual
KVs or expose this as a logical-binding metric.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -5345,17 +5254,11 @@ int InstanceRecycler::recycle_tablet(int64_t tablet_id,
RecyclerMetricsContext&
return ret;
}
- tablet_metrics_context_.total_recycled_data_size +=
+ tablet_metrics_context_.kv_recycled_bytes +=
Review Comment:
[P2] Preserve bytes from successful vault deletions on partial failure. A
tablet may span storage vaults, and these directory deletes run independently;
if vault A succeeds and vault B fails, the aggregate return above skips this
update even though A's objects are already gone. A retry then attributes those
bytes to a later idempotent delete. Track rowset bytes per resource and publish
each successful task's physical deletion before reducing the overall tablet
status.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -5391,6 +5294,8 @@ int InstanceRecycler::recycle_tablet(int64_t tablet_id,
RecyclerMetricsContext&
if (ret == 0) {
// All object files under tablet have been deleted
+ tablet_metrics_context_.kv_recycled_num += 1;
Review Comment:
[P2] Count a versioned tablet only once. recycle_versioned_tablet() already
increments tablet_metrics_context_.kv_recycled_num after its metadata commit,
then this wrapper unconditionally runs the legacy tail and increments the same
counter again. The mode predicate uses the InstanceRecycler's fixed
instance_info_ snapshot, so the comment about a mid-call mode change does not
prevent the normal 2-per-tablet result. Give one layer sole ownership of the
tablet count.
##########
cloud/src/recycler/recycler.h:
##########
@@ -153,105 +154,136 @@ struct RowsetDeleteTask {
class RecyclerMetricsContext {
public:
- RecyclerMetricsContext() = default;
+ enum class MetricType {
+ SCANNED_NUM,
+ EXPIRED_NUM,
+ RECYCLED_NUM,
+ RECYCLED_BYTES,
+ };
- RecyclerMetricsContext(std::string instance_id, std::string operation_type)
- : operation_type(std::move(operation_type)),
instance_id(std::move(instance_id)) {
- start();
- }
+ class MetricValue {
+ public:
+ // Concurrent workers only update atomics. Batch boundaries publish
their snapshots.
+ MetricValue& operator+=(uint64_t delta) {
+ value_.fetch_add(delta, std::memory_order_relaxed);
+ return *this;
+ }
+
+ MetricValue& operator++() {
+ *this += 1;
+ return *this;
+ }
- ~RecyclerMetricsContext() = default;
+ uint64_t operator++(int) { return value_.fetch_add(1,
std::memory_order_relaxed); }
- std::atomic_ullong total_need_recycle_data_size = 0;
- std::atomic_ullong total_need_recycle_num = 0;
+ void reset() { value_.store(0, std::memory_order_relaxed); }
- std::atomic_ullong total_recycled_data_size = 0;
- std::atomic_ullong total_recycled_num = 0;
+ void set(uint64_t v) { value_.store(v, std::memory_order_relaxed); }
- std::string operation_type;
- std::string instance_id;
+ uint64_t value() const { return
value_.load(std::memory_order_relaxed); }
+
+ private:
+ std::atomic_ullong value_ = 0;
+ };
- double start_time = 0;
+ RecyclerMetricsContext() = delete;
- void start() {
- start_time = duration_cast<std::chrono::milliseconds>(
-
std::chrono::system_clock::now().time_since_epoch())
- .count();
+ explicit RecyclerMetricsContext(std::string instance_id, std::string
operation_type)
+ : operation_type(std::move(operation_type)),
+ instance_id(std::move(instance_id)),
+ start_time_(std::chrono::steady_clock::now()) {
+ reset();
}
- double duration() const {
- return duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch())
- .count() -
- start_time;
+ // Each context has one publisher; workers may update its MetricValues
concurrently.
+ void update_metrics() {
+ auto cost =
duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() -
+ start_time_)
+ .count();
+
g_bvar_recycler_instance_current_round_task_elapsed_ms.put({instance_id,
operation_type},
+ cost);
+ put(MetricType::SCANNED_NUM, kv_scanned_num.value());
+ put(MetricType::EXPIRED_NUM, kv_expired_num.value());
+ put(MetricType::RECYCLED_NUM, kv_recycled_num.value());
+ put(MetricType::RECYCLED_BYTES, kv_recycled_bytes.value());
}
+ ~RecyclerMetricsContext() { finish(); }
Review Comment:
[P2] Do not finalize a context that never started. InstanceRecycler
constructs the tablet/segment members before init() and recycle-job
preparation; if either preflight step fails, destruction reaches this call and
writes zero last-round values even though do_recycle() never began. That erases
the last completed round for ordinary init/lease failures. Activate/finalize
these contexts only after the round starts, or guard finish() with explicit
started/used state.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -6610,10 +6517,12 @@ int InstanceRecycler::recycle_tmp_rowsets() {
register_recycle_task(task_name, start_time);
DORIS_CLOUD_DEFER {
+ metrics_context.kv_scanned_num.set(num_scanned);
+ metrics_context.kv_expired_num.set(num_expired);
+ metrics_context.kv_recycled_num.set(num_recycled);
Review Comment:
[P2] Count the primary KV once its removal commits. Both tmp-rowset workers
first delete the objects and primary tmp-rowset KV, then attempt ref-count
cleanup in a second txn and return before incrementing num_recycled if that
cleanup fails. The worker error is not propagated and the primary key is gone,
so this round can report recycled=0 permanently. Combine the removals or
advance the primary count before the secondary cleanup and surface that failure
separately.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -7499,8 +7414,8 @@ int InstanceRecycler::recycle_copy_jobs() {
if (!config::force_immediate_recycle && current_time <=
copy_job.timeout_time_ms()) {
return 0;
}
- ++num_expired;
}
+ ++num_expired;
Review Comment:
[P2] Account for the internal job before returning from this branch.
BatchObjStoreAccessor later deletes its objects and commits removal of the
copy-file and copy-job KVs, but this early return bypasses both num_expired and
num_recycled, and consume() has no outcome channel back to them. Successful
internal jobs therefore remain scanned-only. Please record eligibility when
queued and report recycled only after each batch KV commit succeeds.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -6577,6 +6483,7 @@ int InstanceRecycler::recycle_rowset_meta_and_data(const
RowsetDeleteTask& task)
LOG_WARNING("failed to recycle rowset meta and data").tag("err",
err);
return -1;
}
+ metrics_context.kv_recycled_bytes += recycled_bytes;
Review Comment:
[P2] Preserve the physical-delete result across the metadata retry. If
object deletion succeeds but this commit conflicts, the loop starts again with
recycled_bytes=0; a later shared-ref path can return success while reporting no
deleted bytes. A non-conflict commit error also drops the already completed
deletion. Publish the byte result once at object-delete success (or retain it
across retries) without allowing a retry to double-add it.
##########
cloud/src/recycler/recycler.cpp:
##########
@@ -4730,13 +4761,12 @@ int InstanceRecycler::delete_rowset_data(
if (rs_meta != rowsets.end() &&
!deleted_rowset_id.contains(rowset_id)) {
deleted_rowset_id.emplace(rowset_id);
- metrics_context.total_recycled_data_size
+=
+ metrics_context.kv_recycled_bytes +=
Review Comment:
[P2] Do not charge object bytes again on a metadata-only retry. This line
adds total_disk_size whenever delete_files() succeeds, but
cleanup_rowset_metadata() can then fail its separate transaction and
deliberately leave the recycle key for the next round. Deleting already-absent
files is also treated as success, so every retry re-adds the same metadata size
to last-round and cumulative bytes. Persist an object-deleted/accounted phase
or use a delete result that distinguishes files actually removed.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]