blob: d196fe9942d196d4a18f62dfc4bde1427995a01e [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.
#include <gtest/gtest.h>
#include <chrono>
#include <cstdint>
#include <map>
#include <memory>
#include <tuple>
#include <vector>
#include "cloud/cloud_tablet_hotspot.h"
namespace doris {
namespace {
using SystemTimePoint = std::chrono::system_clock::time_point;
using PartitionSnapshot =
std::map<std::tuple<int64_t, int64_t, int64_t>, std::pair<uint64_t, uint64_t>>;
size_t slot_idx(int64_t tablet_id) {
return tablet_id % TabletHotspot::s_slot_size;
}
HotspotCounterPtr insert_counter(TabletHotspot* hotspot, int64_t tablet_id, int64_t table_id,
int64_t index_id, int64_t partition_id,
SystemTimePoint last_access_time, uint64_t cur_counter,
uint64_t day_history_counter, uint64_t week_history_counter) {
auto counter = std::make_shared<HotspotCounter>(table_id, index_id, partition_id);
counter->last_access_time = last_access_time;
counter->cur_counter.store(cur_counter, std::memory_order_relaxed);
counter->day_history_counter.store(day_history_counter, std::memory_order_relaxed);
counter->week_history_counter.store(week_history_counter, std::memory_order_relaxed);
auto& slot = hotspot->_tablets_hotspot[slot_idx(tablet_id)];
std::lock_guard lock(slot.mtx);
slot.map[tablet_id] = counter;
return counter;
}
PartitionSnapshot export_snapshot(TabletHotspot* hotspot) {
std::vector<THotTableMessage> hot_tables;
hotspot->get_top_n_hot_partition(&hot_tables);
PartitionSnapshot snapshot;
for (const auto& table_msg : hot_tables) {
for (const auto& partition : table_msg.hot_partitions) {
snapshot[std::make_tuple(table_msg.table_id, table_msg.index_id,
partition.partition_id)] =
std::make_pair(partition.query_per_day, partition.query_per_week);
}
}
return snapshot;
}
} // namespace
TEST(TabletHotspotGcTest, GcEligibilityRequiresExpiredAndZeroContribution) {
const auto now = std::chrono::system_clock::now();
HotspotCounter expired_zero_counter(1, 2, 3);
expired_zero_counter.last_access_time = now - std::chrono::hours(24 * 8);
HotspotCounter recent_zero_counter(1, 2, 3);
recent_zero_counter.last_access_time = now - std::chrono::hours(24 * 6);
HotspotCounter expired_week_counter(1, 2, 3);
expired_week_counter.last_access_time = now - std::chrono::hours(24 * 8);
expired_week_counter.week_history_counter.store(1, std::memory_order_relaxed);
EXPECT_TRUE(TabletHotspot::is_gc_eligible(expired_zero_counter, now));
EXPECT_FALSE(TabletHotspot::is_gc_eligible(recent_zero_counter, now));
EXPECT_FALSE(TabletHotspot::is_gc_eligible(expired_week_counter, now));
}
TEST(TabletHotspotGcTest, RunMaintenanceEvictsExpiredZeroCounter) {
TabletHotspot hotspot(false);
const auto now = std::chrono::system_clock::now();
const int64_t tablet_id = 1001;
insert_counter(&hotspot, tablet_id, 11, 12, 13, now - std::chrono::hours(24 * 8), 0, 0, 0);
const auto stats = hotspot.run_maintenance_once(now);
auto& slot = hotspot._tablets_hotspot[slot_idx(tablet_id)];
std::lock_guard lock(slot.mtx);
EXPECT_EQ(1u, stats.total_counters_before_gc);
EXPECT_EQ(0u, stats.total_counters_after_gc);
EXPECT_EQ(1u, stats.evicted_counters);
EXPECT_EQ(slot.map.end(), slot.map.find(tablet_id));
}
TEST(TabletHotspotGcTest, RunMaintenanceKeepsRecentZeroCounter) {
TabletHotspot hotspot(false);
const auto now = std::chrono::system_clock::now();
const int64_t tablet_id = 1002;
insert_counter(&hotspot, tablet_id, 21, 22, 23, now - std::chrono::hours(24 * 6), 0, 0, 0);
const auto stats = hotspot.run_maintenance_once(now);
auto& slot = hotspot._tablets_hotspot[slot_idx(tablet_id)];
std::lock_guard lock(slot.mtx);
EXPECT_EQ(1u, stats.total_counters_before_gc);
EXPECT_EQ(1u, stats.total_counters_after_gc);
EXPECT_EQ(0u, stats.evicted_counters);
ASSERT_NE(slot.map.end(), slot.map.find(tablet_id));
}
TEST(TabletHotspotGcTest, RunMaintenanceRemovesColdCounterAndCountRecreatesIt) {
TabletHotspot hotspot(false);
const auto now = std::chrono::system_clock::now();
const int64_t tablet_id = 1003;
insert_counter(&hotspot, tablet_id, 31, 32, 33, now - std::chrono::hours(24 * 8), 0, 0, 0);
const auto stats = hotspot.run_maintenance_once(now);
EXPECT_EQ(1u, stats.evicted_counters);
hotspot.count(tablet_id, 31, 32, 33);
auto& slot = hotspot._tablets_hotspot[slot_idx(tablet_id)];
std::lock_guard lock(slot.mtx);
auto iter = slot.map.find(tablet_id);
ASSERT_NE(slot.map.end(), iter);
EXPECT_EQ(31, iter->second->table_id);
EXPECT_EQ(32, iter->second->index_id);
EXPECT_EQ(33, iter->second->partition_id);
EXPECT_EQ(1u, iter->second->cur_counter.load(std::memory_order_relaxed));
}
TEST(TabletHotspotGcTest, RunMaintenanceCompactsSparseShard) {
TabletHotspot hotspot(false);
const auto now = std::chrono::system_clock::now();
const int64_t slot_seed = 17;
for (int i = 0; i < 256; ++i) {
const int64_t tablet_id = slot_seed + static_cast<int64_t>(i) * TabletHotspot::s_slot_size;
insert_counter(&hotspot, tablet_id, 41, 42, 43, now - std::chrono::hours(24 * 8), 0, 0, 0);
}
auto& slot = hotspot._tablets_hotspot[slot_idx(slot_seed)];
size_t bucket_count_before = 0;
{
std::lock_guard lock(slot.mtx);
bucket_count_before = slot.map.bucket_count();
ASSERT_GE(slot.map.size(), 256u);
}
const auto stats = hotspot.run_maintenance_once(now);
std::lock_guard lock(slot.mtx);
EXPECT_EQ(256u, stats.evicted_counters);
EXPECT_EQ(1u, stats.compacted_slots);
EXPECT_TRUE(slot.map.empty());
EXPECT_LT(slot.map.bucket_count(), bucket_count_before);
}
TEST(TabletHotspotGcTest, GcKeepsHotspotExportStable) {
const auto now = std::chrono::system_clock::now();
TabletHotspot baseline(false);
insert_counter(&baseline, 2001, 51, 52, 53, now - std::chrono::hours(1), 0, 7, 11);
insert_counter(&baseline, 2002, 51, 52, 54, now - std::chrono::hours(24 * 8), 0, 0, 0);
TabletHotspot with_gc(false);
insert_counter(&with_gc, 2001, 51, 52, 53, now - std::chrono::hours(1), 0, 7, 11);
insert_counter(&with_gc, 2002, 51, 52, 54, now - std::chrono::hours(24 * 8), 0, 0, 0);
const PartitionSnapshot before_gc = export_snapshot(&baseline);
const auto stats = with_gc.run_maintenance_once(now);
EXPECT_EQ(1u, stats.evicted_counters);
const PartitionSnapshot after_gc = export_snapshot(&with_gc);
EXPECT_EQ(before_gc, after_gc);
ASSERT_EQ(1u, after_gc.size());
const auto iter = after_gc.find(std::make_tuple(51, 52, 53));
ASSERT_NE(after_gc.end(), iter);
EXPECT_EQ(7u, iter->second.first);
EXPECT_EQ(11u, iter->second.second);
}
} // namespace doris