1010#include < memory>
1111#include < optional>
1212#include < set>
13+ #include < shared_mutex>
1314#include < string>
1415#include < string_view>
1516#include < thread>
@@ -89,6 +90,82 @@ namespace kv_cache_manager {
8990 } while (0 )
9091
9192namespace {
93+ class LocationLookupMetricsGuard {
94+ public:
95+ LocationLookupMetricsGuard (MetricsRegistry *registry,
96+ std::string api_name,
97+ std::string instance_id,
98+ std::size_t request_key_count)
99+ : registry_(registry)
100+ , api_name_(std::move(api_name))
101+ , instance_id_(std::move(instance_id))
102+ , request_key_count_(request_key_count) {}
103+
104+ ~LocationLookupMetricsGuard () {
105+ if (!completed_) {
106+ Complete (0 , 0 , 0 , request_key_count_);
107+ }
108+ }
109+
110+ void SetRequestKeyCount (std::size_t count) { request_key_count_ = count; }
111+
112+ void Complete (std::size_t hit_count,
113+ std::size_t miss_count,
114+ std::size_t filtered_count,
115+ std::size_t error_count) {
116+ try {
117+ if (registry_ != nullptr ) {
118+ const MetricsTags base_tags{{" api_name" , api_name_}, {" instance_id" , instance_id_}};
119+ ++registry_->GetCounter (" manager.location_lookup.requests_total" , base_tags);
120+ if (request_key_count_ > 0 ) {
121+ registry_->GetCounter (" manager.location_lookup.request_keys_total" , base_tags) +=
122+ request_key_count_;
123+ const auto record = [this , &base_tags](const char *result, std::size_t count) {
124+ if (count == 0 ) {
125+ return ;
126+ }
127+ auto tags = base_tags;
128+ tags.emplace (" result" , result);
129+ registry_->GetCounter (" manager.location_lookup.keys_total" , tags) += count;
130+ };
131+ record (" hit" , hit_count);
132+ record (" miss" , miss_count);
133+ record (" filtered" , filtered_count);
134+ record (" error" , error_count);
135+ }
136+ }
137+ } catch (...) {
138+ // Monitoring must never change the location-query result.
139+ }
140+ completed_ = true ;
141+ }
142+
143+ private:
144+ MetricsRegistry *registry_;
145+ std::string api_name_;
146+ std::string instance_id_;
147+ std::size_t request_key_count_;
148+ bool completed_ = false ;
149+ };
150+
151+ std::size_t CountUnmaskedKeys (const CacheManager::KeyVector &keys, const BlockMask &block_mask) {
152+ std::size_t count = 0 ;
153+ for (std::size_t index = 0 ; index < keys.size (); ++index) {
154+ if (!IsIndexInMaskRange (block_mask, index)) {
155+ ++count;
156+ }
157+ }
158+ return count;
159+ }
160+
161+ std::size_t CountLocations (const CacheLocationVector &locations, bool require_location_spec = false ) {
162+ return static_cast <std::size_t >(
163+ std::count_if (locations.begin (), locations.end (), [require_location_spec](const auto &location) {
164+ return location && !location->id ().empty () &&
165+ (!require_location_spec || !location->location_specs ().empty ());
166+ }));
167+ }
168+
92169CacheManager::KeyVector GenKeyVector (const CacheManager::TokenIdsVector &tokens, int64_t block_size) {
93170 std::vector<int64_t > block_keys;
94171 size_t total_blocks = tokens.size () / block_size;
@@ -760,22 +837,40 @@ ErrorCode CacheManager::PerformCacheLocationQuery(RequestContext *request_contex
760837 const BlockMask &block_mask,
761838 int32_t sw_size,
762839 KeyVector &query_keys,
763- CacheLocationVector &cache_locations) const {
840+ CacheLocationVector &cache_locations,
841+ std::size_t *out_lookup_error_key_count) const {
764842 SPAN_TRACER (request_context);
765843 const std::string &trace_id = request_context->trace_id ();
844+ if (out_lookup_error_key_count != nullptr ) {
845+ *out_lookup_error_key_count = 0 ;
846+ }
766847 ErrorCode ec = EC_ERROR ;
767848 if (!keys.empty ()) {
768849 KVCM_METRICS_COLLECTOR_SET_METRICS (service_metrics_collector, manager, request_key_count, keys.size ());
769- ec = GetCacheLocationByQueryType (
770- meta_searcher, request_context, instance_id, query_type, keys, block_mask, sw_size, cache_locations);
850+ ec = GetCacheLocationByQueryType (meta_searcher,
851+ request_context,
852+ instance_id,
853+ query_type,
854+ keys,
855+ block_mask,
856+ sw_size,
857+ cache_locations,
858+ out_lookup_error_key_count);
771859 } else {
772860 auto [ec_temp, block_size] = GetBlockSize (request_context, instance_id);
773861 RETURN_IF_EC_NOT_OK_WITH_LOG (WARN , ec_temp, " get block_size failed" );
774862 auto gen_keys = GenKeyVector (tokens, block_size);
775863 KVCM_METRICS_COLLECTOR_SET_METRICS (service_metrics_collector, manager, request_key_count, gen_keys.size ());
776864 query_keys = gen_keys;
777- ec = GetCacheLocationByQueryType (
778- meta_searcher, request_context, instance_id, query_type, gen_keys, block_mask, sw_size, cache_locations);
865+ ec = GetCacheLocationByQueryType (meta_searcher,
866+ request_context,
867+ instance_id,
868+ query_type,
869+ gen_keys,
870+ block_mask,
871+ sw_size,
872+ cache_locations,
873+ out_lookup_error_key_count);
779874 }
780875 return ec;
781876}
@@ -791,9 +886,19 @@ CacheManager::GetCacheLocation(RequestContext *request_context,
791886 const std::vector<std::string> &location_spec_names) {
792887 SPAN_TRACER (request_context);
793888 const std::string &trace_id = request_context->trace_id ();
889+ // Keep a valid instance alive until the dynamic metric write completes.
890+ // RemoveInstance holds the same lifecycle fence exclusively before it
891+ // purges instance-tagged series.
892+ std::shared_lock<std::shared_mutex> metrics_lifecycle_guard (metrics_lifecycle_->mut_ );
794893 auto *service_metrics_collector = dynamic_cast <ServiceMetricsCollector *>(request_context->metrics_collector ());
795894 auto [ec, meta_searcher] = CheckInputAndGetMetaSearcher (request_context, instance_id, keys, tokens);
796895 RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (WARN , ec, CacheLocationViewVecWrapper, " check input or get meta searcher failed" );
896+ if (registry_manager_->GetInstanceInfo (request_context, instance_id) == nullptr ) {
897+ RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (
898+ WARN , EC_INSTANCE_NOT_EXIST , CacheLocationViewVecWrapper, " instance not found" );
899+ }
900+ LocationLookupMetricsGuard location_metrics (
901+ metrics_registry_.get (), " GetCacheLocation" , instance_id, keys.size ());
797902 if (query_type == QueryType::QT_UNSPECIFIED ) {
798903 RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (WARN , EC_ERROR , CacheLocationViewVecWrapper, " unknown query type" );
799904 }
@@ -802,6 +907,7 @@ CacheManager::GetCacheLocation(RequestContext *request_context,
802907 : KVCM_METRICS_COLLECTOR_CHRONO_SCOPE (service_metrics_collector, ManagerPrefixMatch);
803908 CacheLocationVector cache_locations;
804909 KeyVector query_keys = keys;
910+ std::size_t lookup_error_key_count = 0 ;
805911 ec = PerformCacheLocationQuery (request_context,
806912 service_metrics_collector,
807913 meta_searcher,
@@ -812,8 +918,13 @@ CacheManager::GetCacheLocation(RequestContext *request_context,
812918 block_mask,
813919 sw_size,
814920 query_keys,
815- cache_locations);
921+ cache_locations,
922+ &lookup_error_key_count);
816923 query_scope = ChronoScopeGuard{};
924+ const std::size_t lookup_key_count = query_type == QueryType::QT_PREFIX_MATCH
925+ ? CountUnmaskedKeys (query_keys, block_mask)
926+ : query_keys.size ();
927+ location_metrics.SetRequestKeyCount (lookup_key_count);
817928 // prefix_match_len: count actual hits (non-empty id), not total returned entries.
818929 // BatchGet/ReverseRollSW pad misses with empty CacheLocation objects.
819930 {
@@ -847,7 +958,16 @@ CacheManager::GetCacheLocation(RequestContext *request_context,
847958 query_counter += query_count;
848959 hit_counter += hit_count;
849960 }
961+ const std::size_t pre_filter_hit_count = CountLocations (cache_locations);
850962 FilterLocationSpecByName (cache_locations, location_spec_names);
963+ const std::size_t hit_count = CountLocations (cache_locations, !location_spec_names.empty ());
964+ const std::size_t filtered_count = pre_filter_hit_count - hit_count;
965+ const std::size_t unresolved_count = lookup_key_count > pre_filter_hit_count
966+ ? lookup_key_count - pre_filter_hit_count
967+ : 0 ;
968+ const std::size_t error_count = std::min (lookup_error_key_count, unresolved_count);
969+ const std::size_t miss_count = unresolved_count - error_count;
970+ location_metrics.Complete (hit_count, miss_count, filtered_count, error_count);
851971
852972 auto cache_get_event = std::make_shared<CacheGetEvent>(instance_id);
853973 cache_get_event->SetEventTriggerTime ();
@@ -886,9 +1006,16 @@ CacheManager::GetCacheLocationsByBackend(RequestContext *request_context,
8861006 const std::vector<BackendSelector> &backend_selectors) {
8871007 SPAN_TRACER (request_context);
8881008 const std::string &trace_id = request_context->trace_id ();
1009+ std::shared_lock<std::shared_mutex> metrics_lifecycle_guard (metrics_lifecycle_->mut_ );
8891010 auto *service_metrics_collector = dynamic_cast <ServiceMetricsCollector *>(request_context->metrics_collector ());
8901011 auto [ec, meta_searcher] = CheckInputAndGetMetaSearcher (request_context, instance_id, keys, tokens);
8911012 RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (WARN , ec, BatchLocationsView, " check input or get meta searcher failed" );
1013+ auto instance_info = registry_manager_->GetInstanceInfo (request_context, instance_id);
1014+ if (instance_info == nullptr ) {
1015+ RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (WARN , EC_INSTANCE_NOT_EXIST , BatchLocationsView, " instance not found" );
1016+ }
1017+ LocationLookupMetricsGuard location_metrics (
1018+ metrics_registry_.get (), " GetCacheLocationsByBackend" , instance_id, keys.size ());
8921019 if (query_type != QueryType::QT_BATCH_GET ) {
8931020 request_context->error_tracer ()->AddErrorMsg (" GetCacheLocationsByBackend only supports QT_BATCH_GET" );
8941021 RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (
@@ -915,6 +1042,8 @@ CacheManager::GetCacheLocationsByBackend(RequestContext *request_context,
9151042 RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (
9161043 WARN , EC_BADARGS , BatchLocationsView, " block_mask must match the number of query keys" );
9171044 }
1045+ const std::size_t lookup_key_count = CountUnmaskedKeys (query_keys, block_mask);
1046+ location_metrics.SetRequestKeyCount (lookup_key_count);
9181047
9191048 if (!location_spec_names.empty ()) {
9201049 if (location_spec_names.size () != query_keys.size () ||
@@ -966,13 +1095,15 @@ CacheManager::GetCacheLocationsByBackend(RequestContext *request_context,
9661095 }
9671096
9681097 LocationsPerKey locations_per_key;
1098+ std::vector<bool > had_usable_locations;
9691099 ec = meta_searcher->BatchGetBestLocationByBackend (request_context,
9701100 query_keys,
9711101 locations_per_key,
9721102 policy.get (),
9731103 backend_selectors,
9741104 location_spec_names,
975- block_mask);
1105+ block_mask,
1106+ &had_usable_locations);
9761107 query_scope = ChronoScopeGuard{};
9771108 // prefix_match_len: count keys with at least one hit (non-empty id).
9781109 // Miss keys have empty CacheLocation objects with no id.
@@ -990,11 +1121,6 @@ CacheManager::GetCacheLocationsByBackend(RequestContext *request_context,
9901121 }
9911122 RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (WARN , ec, BatchLocationsView, " batch get multi locations failed" );
9921123
993- auto instance_info = registry_manager_->GetInstanceInfo (request_context, instance_id);
994- if (instance_info == nullptr ) {
995- request_context->error_tracer ()->AddErrorMsg (" instance not found" );
996- RETURN_IF_EC_NOT_OK_WITH_TYPE_LOG (WARN , EC_INSTANCE_NOT_EXIST , BatchLocationsView, " instance not found" );
997- }
9981124 for (auto &key_locs : locations_per_key) {
9991125 FillEmptyLocationSpecs (instance_info->location_spec_infos (), key_locs);
10001126 }
@@ -1004,6 +1130,24 @@ CacheManager::GetCacheLocationsByBackend(RequestContext *request_context,
10041130 }
10051131 }
10061132
1133+ std::size_t hit_count = 0 ;
1134+ std::size_t filtered_count = 0 ;
1135+ for (std::size_t i = 0 ; i < locations_per_key.size (); ++i) {
1136+ if (IsIndexInMaskRange (block_mask, i)) {
1137+ continue ;
1138+ }
1139+ if (CountLocations (locations_per_key[i], !location_spec_names.empty ()) > 0 ) {
1140+ ++hit_count;
1141+ } else if (i < had_usable_locations.size () && had_usable_locations[i]) {
1142+ ++filtered_count;
1143+ }
1144+ }
1145+ const std::size_t classified_count = hit_count + filtered_count;
1146+ const std::size_t miss_count = lookup_key_count > classified_count
1147+ ? lookup_key_count - classified_count
1148+ : 0 ;
1149+ location_metrics.Complete (hit_count, miss_count, filtered_count, 0 );
1150+
10071151 auto cache_get_event = std::make_shared<CacheGetEvent>(instance_id);
10081152 cache_get_event->SetEventTriggerTime ();
10091153 cache_get_event->SetAddtionalArgs (
@@ -4279,7 +4423,8 @@ ErrorCode CacheManager::GetCacheLocationByQueryType(MetaSearcher *meta_searcher,
42794423 const KeyVector &keys,
42804424 const BlockMask &block_mask,
42814425 int32_t sw_size,
4282- CacheLocationVector &cache_locations) const {
4426+ CacheLocationVector &cache_locations,
4427+ std::size_t *out_prefix_error_key_count) const {
42834428 SPAN_TRACER (request_context);
42844429 const std::string &trace_id = request_context->trace_id ();
42854430 auto policy = genSelectLocationPolicy (request_context, instance_id);
@@ -4293,7 +4438,8 @@ ErrorCode CacheManager::GetCacheLocationByQueryType(MetaSearcher *meta_searcher,
42934438 break ;
42944439 }
42954440 case QueryType::QT_PREFIX_MATCH : {
4296- ec = meta_searcher->PrefixMatch (request_context, keys, block_mask, cache_locations, policy.get ());
4441+ ec = meta_searcher->PrefixMatch (
4442+ request_context, keys, block_mask, cache_locations, policy.get (), out_prefix_error_key_count);
42974443 break ;
42984444 }
42994445 case QueryType::QT_REVERSE_ROLL_SW_MATCH : {
0 commit comments