Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] Support display datacache's hit rate in app level #48450

Merged
merged 6 commits into from
Jul 19, 2024
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 be/src/block_cache/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(CACHE_FILES
cache_options.cpp
datacache_utils.cpp
disk_space_monitor.cpp
block_cache_hit_rate_counter.hpp
)

if (${WITH_CACHELIB} STREQUAL "ON")
Expand Down
59 changes: 59 additions & 0 deletions be/src/block_cache/block_cache_hit_rate_counter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// Licensed 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
//
// https://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 <bvar/bvar.h>

namespace starrocks {
class BlockCacheHitRateCounter {
public:
static BlockCacheHitRateCounter* instance() {
static BlockCacheHitRateCounter counter;
return &counter;
}
~BlockCacheHitRateCounter() = default;
void update(uint64_t hit_bytes, uint64_t miss_bytes) {
_hit_bytes << hit_bytes;
_miss_bytes << miss_bytes;
}
double hit_rate() const { return hit_rate_calculate(_hit_bytes.get_value(), _miss_bytes.get_value()); }
double hit_rate_last_minute() const {
return hit_rate_calculate(_hit_bytes_last_minute.get_value(), _miss_bytes_last_minute.get_value());
}
ssize_t get_hit_bytes() const { return _hit_bytes.get_value(); }
ssize_t get_miss_bytes() const { return _miss_bytes.get_value(); }
ssize_t get_hit_bytes_last_minute() const { return _hit_bytes_last_minute.get_value(); }
ssize_t get_miss_bytes_last_minute() const { return _miss_bytes_last_minute.get_value(); }
void reset() {
_hit_bytes.reset();
_miss_bytes.reset();
}

private:
static double hit_rate_calculate(ssize_t hit_bytes, ssize_t miss_bytes) {
ssize_t total_bytes = hit_bytes + miss_bytes;
if (total_bytes > 0) {
double hit_rate = std::round(double(hit_bytes) / double(total_bytes) * 100.0) / 100.0;
return hit_rate;
} else {
return 0;
}
}
bvar::Adder<ssize_t> _hit_bytes;
bvar::Adder<ssize_t> _miss_bytes;
bvar::Window<bvar::Adder<ssize_t>> _hit_bytes_last_minute{&_hit_bytes, 60};
bvar::Window<bvar::Adder<ssize_t>> _miss_bytes_last_minute{&_miss_bytes, 60};
};
} // namespace starrocks
3 changes: 3 additions & 0 deletions be/src/exec/hdfs_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "exec/hdfs_scanner.h"

#include "block_cache/block_cache_hit_rate_counter.hpp"
#include "column/column_helper.h"
#include "exec/exec_node.h"
#include "fs/hdfs/fs_hdfs.h"
Expand Down Expand Up @@ -378,6 +379,8 @@ void HdfsScanner::update_counter() {
_runtime_state->update_num_datacache_write_time_ns(stats.write_cache_ns);
_runtime_state->update_num_datacache_count(1);
}

BlockCacheHitRateCounter::instance()->update(stats.read_cache_bytes, _fs_stats.bytes_read);
}
if (_shared_buffered_input_stream) {
COUNTER_UPDATE(profile->shared_buffered_shared_io_count, _shared_buffered_input_stream->shared_io_count());
Expand Down
25 changes: 23 additions & 2 deletions be/src/http/action/datacache_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <string>

#include "block_cache/block_cache.h"
#include "block_cache/block_cache_hit_rate_counter.hpp"
#include "http/http_channel.h"
#include "http/http_headers.h"
#include "http/http_request.h"
Expand All @@ -32,6 +33,7 @@ namespace starrocks {
const static std::string HEADER_JSON = "application/json";
const static std::string ACTION_KEY = "action";
const static std::string ACTION_STAT = "stat";
const static std::string ACTION_APP_STAT = "app_stat";
const static std::string ACTION_INVALIDATE_ALL = "invalidate_all";

std::string cache_status_str(const DataCacheStatus& status) {
Expand All @@ -58,7 +60,7 @@ bool DataCacheAction::_check_request(HttpRequest* req) {
HttpChannel::send_reply(req, HttpStatus::METHOD_NOT_ALLOWED, "Method Not Allowed");
return false;
}
if (req->param(ACTION_KEY) != ACTION_STAT) {
if (req->param(ACTION_KEY) != ACTION_STAT && req->param(ACTION_KEY) != ACTION_APP_STAT) {
HttpChannel::send_reply(req, HttpStatus::NOT_FOUND, "Not Found");
return false;
}
Expand All @@ -75,8 +77,10 @@ void DataCacheAction::handle(HttpRequest* req) {
_handle_error(req, strings::Substitute("Cache system is not ready"));
} else if (block_cache->engine_type() != DataCacheEngineType::STARCACHE) {
_handle_error(req, strings::Substitute("No more metrics for current cache engine type"));
} else {
} else if (req->param(ACTION_KEY) == ACTION_STAT) {
_handle_stat(req, block_cache);
} else {
_handle_app_stat(req);
}
}

Expand Down Expand Up @@ -173,6 +177,23 @@ void DataCacheAction::_handle_stat(HttpRequest* req, BlockCache* cache) {
});
}

void DataCacheAction::_handle_app_stat(HttpRequest* req) {
_handle(req, [=](rapidjson::Document& root) {
#ifdef WITH_STARCACHE
auto& allocator = root.GetAllocator();
BlockCacheHitRateCounter* hit_rate_counter = BlockCacheHitRateCounter::instance();
root.AddMember("hit_bytes", rapidjson::Value(hit_rate_counter->get_hit_bytes()), allocator);
root.AddMember("miss_bytes", rapidjson::Value(hit_rate_counter->get_miss_bytes()), allocator);
root.AddMember("hit_rate", rapidjson::Value(hit_rate_counter->hit_rate()), allocator);
root.AddMember("hit_bytes_last_minute", rapidjson::Value(hit_rate_counter->get_hit_bytes_last_minute()),
allocator);
root.AddMember("miss_bytes_last_minute", rapidjson::Value(hit_rate_counter->get_miss_bytes_last_minute()),
allocator);
root.AddMember("hit_rate_last_minute", rapidjson::Value(hit_rate_counter->hit_rate_last_minute()), allocator);
#endif
});
}

void DataCacheAction::_handle_error(HttpRequest* req, const std::string& err_msg) {
_handle(req, [err_msg](rapidjson::Document& root) {
auto& allocator = root.GetAllocator();
Expand Down
1 change: 1 addition & 0 deletions be/src/http/action/datacache_action.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class DataCacheAction : public HttpHandler {
bool _check_request(HttpRequest* req);
void _handle(HttpRequest* req, const std::function<void(rapidjson::Document& root)>& func);
void _handle_stat(HttpRequest* req, BlockCache* cache);
void _handle_app_stat(HttpRequest* req);
void _handle_error(HttpRequest* req, const std::string& error_msg);

ExecEnv* _exec_env;
Expand Down
1 change: 1 addition & 0 deletions be/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ set(EXEC_FILES
./storage/lake/replication_txn_manager_test.cpp
./storage/lake/persistent_index_sstable_test.cpp
./block_cache/datacache_utils_test.cpp
./block_cache/block_cache_hit_rate_counter_test.cpp
./util/thrift_rpc_helper_test.cpp
)

Expand Down
44 changes: 44 additions & 0 deletions be/test/block_cache/block_cache_hit_rate_counter_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// Licensed 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
//
// https://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 "block_cache/block_cache_hit_rate_counter.hpp"

#include <gtest/gtest.h>

namespace starrocks {

class BlockCacheHitRateCounterTest : public ::testing::Test {
protected:
static void SetUpTestCase() {}

static void TearDownTestCase() {}

void SetUp() override {}
void TearDown() override {}
};

TEST_F(BlockCacheHitRateCounterTest, app_hit_rate) {
BlockCacheHitRateCounter counter{};
EXPECT_EQ(0, counter.hit_rate());
EXPECT_EQ(0, counter.get_hit_bytes_last_minute());
EXPECT_EQ(0, counter.get_miss_bytes_last_minute());
EXPECT_EQ(0, counter.hit_rate_last_minute());

counter.update(3, 10);

EXPECT_EQ(3, counter.get_hit_bytes());
EXPECT_EQ(10, counter.get_miss_bytes());
EXPECT_EQ(0.23, counter.hit_rate());
}
} // namespace starrocks
48 changes: 47 additions & 1 deletion be/test/http/datacache_action_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <rapidjson/document.h>

#include "block_cache/block_cache.h"
#include "gen_cpp/FrontendService_types.h"
#include "block_cache/block_cache_hit_rate_counter.hpp"
#include "gen_cpp/HeartbeatService_types.h"
#include "http/http_channel.h"
#include "http/http_request.h"
Expand Down Expand Up @@ -100,6 +100,52 @@ TEST_F(DataCacheActionTest, stat_success) {
_env._block_cache = nullptr;
}

TEST_F(DataCacheActionTest, app_stat_success) {
BlockCacheHitRateCounter* counter = BlockCacheHitRateCounter::instance();
counter->reset();
auto cache = BlockCache::instance();
ASSERT_TRUE(init_datacache_instance("starcache", cache).ok());
_env._block_cache = cache;

DataCacheAction action(&_env);

{
HttpRequest request(_evhttp_req);
request._method = HttpMethod::GET;
request._params.emplace("action", "app_stat");
request.set_handler(&action);
action.on_header(&request);
action.handle(&request);

rapidjson::Document doc;
doc.Parse(k_response_str.c_str());
EXPECT_EQ(0, doc["hit_bytes"].GetInt64());
EXPECT_EQ(0, doc["miss_bytes"].GetInt64());
EXPECT_EQ(0, doc["hit_rate"].GetDouble());
EXPECT_EQ(0, doc["hit_bytes_last_minute"].GetInt64());
EXPECT_EQ(0, doc["miss_bytes_last_minute"].GetInt64());
EXPECT_EQ(0, doc["hit_rate_last_minute"].GetDouble());
}

counter->update(3, 10);

{
HttpRequest request(_evhttp_req);
request._method = HttpMethod::GET;
request._params.emplace("action", "app_stat");
request.set_handler(&action);
action.on_header(&request);
action.handle(&request);

rapidjson::Document doc;
doc.Parse(k_response_str.c_str());
EXPECT_EQ(3, doc["hit_bytes"].GetInt64());
EXPECT_EQ(10, doc["miss_bytes"].GetInt64());
EXPECT_EQ(0.23, doc["hit_rate"].GetDouble());
}
_env._block_cache = nullptr;
}

TEST_F(DataCacheActionTest, stat_with_uninitialized_cache) {
DataCacheAction action(&_env);

Expand Down
Loading