diff --git a/kv_cache_manager/service/meta_service_impl.cc b/kv_cache_manager/service/meta_service_impl.cc index 0865c79ac..3f89fa8d6 100644 --- a/kv_cache_manager/service/meta_service_impl.cc +++ b/kv_cache_manager/service/meta_service_impl.cc @@ -964,8 +964,10 @@ void MetaServiceImpl::GetHostCacheState(RequestContext *request_context, const proto::meta::GetHostCacheStateRequest *request, proto::meta::GetHostCacheStateResponse *response) { SPAN_TRACER(request_context); + // This is a best-effort read of the node's local ReportEvent view. Followers may + // serve a stale view, while ReportEvent mutations remain leader-only. API_CALL_GUARD_WITH_DEBUG("GetHostCacheState", - true, + false, BuildGetHostCacheStateRequestAccessLogSummary(request), BuildGetHostCacheStateResponseAccessLogSummary(response)); auto *header = response->mutable_header(); diff --git a/kv_cache_manager/service/test/BUILD b/kv_cache_manager/service/test/BUILD index f2c934838..3c2674cbc 100644 --- a/kv_cache_manager/service/test/BUILD +++ b/kv_cache_manager/service/test/BUILD @@ -15,6 +15,21 @@ cc_test( ], ) +cc_test( + name = "MetaServiceImplTest", + srcs = [ + "meta_service_impl_test.cc", + ], + copts = ["-fno-access-control"], + deps = [ + "//kv_cache_manager/common:unittest", + "//kv_cache_manager/config", + "//kv_cache_manager/manager:cache_manager", + "//kv_cache_manager/metrics:metrics_registry", + "//kv_cache_manager/service:meta_service_impl", + ], +) + cc_test( name = "CommandLineTest", srcs = [ diff --git a/kv_cache_manager/service/test/meta_service_impl_test.cc b/kv_cache_manager/service/test/meta_service_impl_test.cc new file mode 100644 index 000000000..0051897c9 --- /dev/null +++ b/kv_cache_manager/service/test/meta_service_impl_test.cc @@ -0,0 +1,51 @@ +#include + +#include "kv_cache_manager/common/request_context.h" +#include "kv_cache_manager/common/unittest.h" +#include "kv_cache_manager/config/registry_manager.h" +#include "kv_cache_manager/manager/cache_manager.h" +#include "kv_cache_manager/metrics/metrics_registry.h" +#include "kv_cache_manager/protocol/protobuf/meta_service.pb.h" +#include "kv_cache_manager/service/meta_service_impl.h" + +namespace kv_cache_manager { + +class MetaServiceImplTest : public TESTBASE { +protected: + void SetUp() override { + metrics_registry_ = std::make_shared(); + registry_manager_ = std::make_shared("", metrics_registry_); + cache_manager_ = std::make_shared(metrics_registry_, registry_manager_); + service_ = std::make_unique(cache_manager_, nullptr, nullptr); + service_->DisableLeaderOnlyRequests(); + } + + std::shared_ptr metrics_registry_; + std::shared_ptr registry_manager_; + std::shared_ptr cache_manager_; + std::unique_ptr service_; +}; + +TEST_F(MetaServiceImplTest, FollowerAllowsReportEventQueryButRejectsWrite) { + RequestContext query_context("follower-query"); + proto::meta::GetHostCacheStateRequest query_request; + query_request.set_trace_id(query_context.trace_id()); + proto::meta::GetHostCacheStateResponse query_response; + + service_->GetHostCacheState(&query_context, &query_request, &query_response); + + // INVALID_ARGUMENT proves the read passed the follower gate and reached + // normal request validation instead of being rejected as SERVER_NOT_LEADER. + EXPECT_EQ(proto::meta::INVALID_ARGUMENT, query_response.header().status().code()); + + RequestContext write_context("follower-write"); + proto::meta::ReportEventRequest write_request; + write_request.set_trace_id(write_context.trace_id()); + proto::meta::ReportEventResponse write_response; + + service_->ReportEvent(&write_context, &write_request, &write_response); + + EXPECT_EQ(proto::meta::SERVER_NOT_LEADER, write_response.header().status().code()); +} + +} // namespace kv_cache_manager