This is an automated email from the ASF dual-hosted git repository.

zhaoliwei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pegasus.git


The following commit(s) were added to refs/heads/master by this push:
     new b26a1130c feat(new_metrics): support filters for selected metric 
fields in response (#1237)
b26a1130c is described below

commit b26a1130cd2d79f9c4fa3d203182115cbe032ad8
Author: Dan Wang <[email protected]>
AuthorDate: Fri Nov 18 10:26:40 2022 +0800

    feat(new_metrics): support filters for selected metric fields in response 
(#1237)
---
 src/utils/enum_helper.h         |   1 +
 src/utils/metrics.cpp           |   9 -
 src/utils/metrics.h             | 217 +++++++++++++++++------
 src/utils/test/metrics_test.cpp | 368 ++++++++++++++++++++++++++++++++--------
 4 files changed, 459 insertions(+), 136 deletions(-)

diff --git a/src/utils/enum_helper.h b/src/utils/enum_helper.h
index e5d83e0d0..25d78b909 100644
--- a/src/utils/enum_helper.h
+++ b/src/utils/enum_helper.h
@@ -49,6 +49,7 @@
 #define ENUM_BEGIN(type, invalid_value) ENUM_BEGIN2(type, type, invalid_value)
 
 #define ENUM_REG2(type, name) helper->register_enum(#name, type::name);
+#define ENUM_REG_WITH_CUSTOM_NAME(type, name) helper->register_enum(#name, 
type);
 #define ENUM_REG(e) helper->register_enum(#e, e);
 
 #define ENUM_END2(type, name)                                                  
                    \
diff --git a/src/utils/metrics.cpp b/src/utils/metrics.cpp
index 07a1210f2..2036a135c 100644
--- a/src/utils/metrics.cpp
+++ b/src/utils/metrics.cpp
@@ -24,15 +24,6 @@
 
 namespace dsn {
 
-std::set<kth_percentile_type> get_all_kth_percentile_types()
-{
-    std::set<kth_percentile_type> all_types;
-    for (size_t i = 0; i < static_cast<size_t>(kth_percentile_type::COUNT); 
++i) {
-        all_types.insert(static_cast<kth_percentile_type>(i));
-    }
-    return all_types;
-}
-
 metric_entity::metric_entity(const std::string &id, attr_map &&attrs)
     : _id(id), _lock(), _attrs(std::move(attrs)), _metrics()
 {
diff --git a/src/utils/metrics.h b/src/utils/metrics.h
index 87cbb3dc2..e9490ead2 100644
--- a/src/utils/metrics.h
+++ b/src/utils/metrics.h
@@ -27,6 +27,7 @@
 #include <string>
 #include <type_traits>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -131,6 +132,29 @@
 
 namespace dsn {
 
+using metric_fields_type = std::unordered_set<std::string>;
+
+// This struct includes a set of filters for both entities and metrics 
requested by client.
+struct metric_filters
+{
+    // According to the parameters requested by client, this function will 
filter metric
+    // fields that will be put in the response.
+    bool include_metric_field(const std::string &field_name) const
+    {
+        // NOTICE: empty `with_metric_fields` means every field is required by 
client.
+        if (with_metric_fields.empty()) {
+            return true;
+        }
+
+        return with_metric_fields.find(field_name) != with_metric_fields.end();
+    }
+
+    // `with_metric_fields` includes all the metric fields that are wanted by 
client. If it
+    // is empty, there will be no restriction: in other words, all fields 
owned by the metric
+    // will be put in the response.
+    metric_fields_type with_metric_fields;
+};
+
 class metric_prototype;
 class metric;
 using metric_ptr = ref_ptr<metric>;
@@ -256,10 +280,10 @@ enum class metric_type
 };
 
 ENUM_BEGIN(metric_type, metric_type::kInvalidUnit)
-ENUM_REG(metric_type::kGauge)
-ENUM_REG(metric_type::kCounter)
-ENUM_REG(metric_type::kVolatileCounter)
-ENUM_REG(metric_type::kPercentile)
+ENUM_REG_WITH_CUSTOM_NAME(metric_type::kGauge, gauge)
+ENUM_REG_WITH_CUSTOM_NAME(metric_type::kCounter, counter)
+ENUM_REG_WITH_CUSTOM_NAME(metric_type::kVolatileCounter, volatile_counter)
+ENUM_REG_WITH_CUSTOM_NAME(metric_type::kPercentile, percentile)
 ENUM_END(metric_type)
 
 enum class metric_unit
@@ -273,10 +297,11 @@ enum class metric_unit
 };
 
 ENUM_BEGIN(metric_unit, metric_unit::kInvalidUnit)
-ENUM_REG(metric_unit::kNanoSeconds)
-ENUM_REG(metric_unit::kMicroSeconds)
-ENUM_REG(metric_unit::kMilliSeconds)
-ENUM_REG(metric_unit::kSeconds)
+ENUM_REG_WITH_CUSTOM_NAME(metric_unit::kNanoSeconds, nanoseconds)
+ENUM_REG_WITH_CUSTOM_NAME(metric_unit::kMicroSeconds, microseconds)
+ENUM_REG_WITH_CUSTOM_NAME(metric_unit::kMilliSeconds, milliseconds)
+ENUM_REG_WITH_CUSTOM_NAME(metric_unit::kSeconds, seconds)
+ENUM_REG_WITH_CUSTOM_NAME(metric_unit::kRequests, requests)
 ENUM_END(metric_unit)
 
 class metric_prototype
@@ -331,6 +356,12 @@ private:
     DISALLOW_COPY_AND_ASSIGN(metric_prototype_with);
 };
 
+const std::string kMetricTypeField = "type";
+const std::string kMetricNameField = "name";
+const std::string kMetricUnitField = "unit";
+const std::string kMetricDescField = "desc";
+const std::string kMetricSingleValueField = "value";
+
 // Base class for each type of metric.
 // Every metric class should inherit from this class.
 //
@@ -345,13 +376,73 @@ class metric : public ref_counter
 public:
     const metric_prototype *prototype() const { return _prototype; }
 
-    // Take snapshot of each metric to collect current values as json format.
-    virtual void take_snapshot(dsn::json::JsonWriter &writer) = 0;
+    // Take snapshot of each metric to collect current values as json format 
with fields chosen
+    // by `filters`.
+    virtual void take_snapshot(dsn::json::JsonWriter &writer, const 
metric_filters &filters) = 0;
 
 protected:
     explicit metric(const metric_prototype *prototype);
     virtual ~metric() = default;
 
+    // Encode a metric field specified by `field_name` as json format. 
However, once the field
+    // are not chosen by `filters`, this function will do nothing.
+    template <typename T>
+    inline void encode(dsn::json::JsonWriter &writer,
+                       const std::string &field_name,
+                       const T &value,
+                       const metric_filters &filters) const
+    {
+        if (!filters.include_metric_field(field_name)) {
+            return;
+        }
+
+        writer.Key(field_name.c_str());
+        json::json_encode(writer, value);
+    }
+
+    // Encode the metric type as json format, if it is chosen by `filters`.
+    inline void encode_type(dsn::json::JsonWriter &writer, const 
metric_filters &filters) const
+    {
+        encode(writer, kMetricTypeField, enum_to_string(prototype()->type()), 
filters);
+    }
+
+    // Encode the metric name as json format, if it is chosen by `filters`.
+    inline void encode_name(dsn::json::JsonWriter &writer, const 
metric_filters &filters) const
+    {
+        encode(writer, kMetricNameField, prototype()->name().data(), filters);
+    }
+
+    // Encode the metric unit as json format, if it is chosen by `filters`.
+    inline void encode_unit(dsn::json::JsonWriter &writer, const 
metric_filters &filters) const
+    {
+        encode(writer, kMetricUnitField, enum_to_string(prototype()->unit()), 
filters);
+    }
+
+    // Encode the metric description as json format, if it is chosen by 
`filters`.
+    inline void encode_desc(dsn::json::JsonWriter &writer, const 
metric_filters &filters) const
+    {
+        encode(writer, kMetricDescField, prototype()->description().data(), 
filters);
+    }
+
+    // Encode the metric prototype as json format, if some attributes in it 
are chosen by `filters`.
+    inline void encode_prototype(dsn::json::JsonWriter &writer, const 
metric_filters &filters) const
+    {
+        encode_type(writer, filters);
+        encode_name(writer, filters);
+        encode_unit(writer, filters);
+        encode_desc(writer, filters);
+    }
+
+    // Encode the unique value of a metric as json format, if it is chosen by 
`filters`. Notice
+    // that the metric should have only one value. like gauge and counter.
+    template <typename T>
+    inline void encode_single_value(dsn::json::JsonWriter &writer,
+                                    const T &value,
+                                    const metric_filters &filters) const
+    {
+        encode(writer, kMetricSingleValueField, value, filters);
+    }
+
     const metric_prototype *const _prototype;
 
 private:
@@ -405,15 +496,12 @@ public:
     // where "name" is the name of the gauge in string type, and "value" is 
just current value
     // of the gauge fetched by `value()`, in numeric types (i.e. integral or 
floating-point type,
     // determined by `value_type`).
-    void take_snapshot(json::JsonWriter &writer) override
+    void take_snapshot(json::JsonWriter &writer, const metric_filters 
&filters) override
     {
         writer.StartObject();
 
-        writer.Key("name");
-        json::json_encode(writer, prototype()->name().data());
-
-        writer.Key("value");
-        json::json_encode(writer, value());
+        encode_prototype(writer, filters);
+        encode_single_value(writer, value(), filters);
 
         writer.EndObject();
     }
@@ -525,15 +613,12 @@ public:
     // }
     // where "name" is the name of the counter in string type, and "value" is 
just current value
     // of the counter fetched by `value()`, in integral type (namely int64_t).
-    void take_snapshot(json::JsonWriter &writer) override
+    void take_snapshot(json::JsonWriter &writer, const metric_filters 
&filters) override
     {
         writer.StartObject();
 
-        writer.Key("name");
-        json::json_encode(writer, prototype()->name().data());
-
-        writer.Key("value");
-        json::json_encode(writer, value());
+        encode_prototype(writer, filters);
+        encode_single_value(writer, value(), filters);
 
         writer.EndObject();
     }
@@ -580,40 +665,74 @@ using concurrent_volatile_counter_ptr = 
counter_ptr<concurrent_long_adder, true>
 template <typename Adder = striped_long_adder>
 using volatile_counter_prototype = metric_prototype_with<counter<Adder, true>>;
 
+#define KTH_PERCENTILE(prefix, kth) prefix##kth
+#define KTH_PERCENTILE_TYPE(kth) KTH_PERCENTILE(P, kth)
+#define ENUM_KTH_PERCENTILE_TYPE(qualifier, kth) qualifier 
KTH_PERCENTILE_TYPE(kth)
+#define KTH_PERCENTILE_NAME(kth) KTH_PERCENTILE(p, kth)
+
+#define ENUM_REG_WITH_KTH_PERCENTILE_TYPE(kth)                                 
                    \
+    ENUM_REG_WITH_CUSTOM_NAME(ENUM_KTH_PERCENTILE_TYPE(kth_percentile_type::, 
kth),                \
+                              KTH_PERCENTILE_NAME(kth))
+
+struct kth_percentile_property
+{
+    std::string name;
+    double decimal;
+};
+
+#define STRINGIFY_HELPER(x) #x
+#define STRINGIFY(x) STRINGIFY_HELPER(x)
+#define STRINGIFY_KTH_PERCENTILE_NAME(kth) STRINGIFY(KTH_PERCENTILE_NAME(kth))
+#define KTH_TO_DECIMAL(kth) 0.##kth
+#define KTH_PERCENTILE_PROPERTY_LIST(kth)                                      
                    \
+    {                                                                          
                    \
+        STRINGIFY_KTH_PERCENTILE_NAME(kth), KTH_TO_DECIMAL(kth)                
                    \
+    }
+
 // All supported kinds of kth percentiles. User can configure required kth 
percentiles for
 // each percentile. Only configured kth percentiles will be computed. This can 
reduce CPU
 // consumption.
+#define ALL_KTH_PERCENTILE_TYPES(qualifier)                                    
                    \
+    ENUM_KTH_PERCENTILE_TYPE(qualifier, 50)                                    
                    \
+    , ENUM_KTH_PERCENTILE_TYPE(qualifier, 90), 
ENUM_KTH_PERCENTILE_TYPE(qualifier, 95),            \
+        ENUM_KTH_PERCENTILE_TYPE(qualifier, 99), 
ENUM_KTH_PERCENTILE_TYPE(qualifier, 999)
+
 enum class kth_percentile_type : size_t
 {
-    P50,
-    P90,
-    P95,
-    P99,
-    P999,
+    ALL_KTH_PERCENTILE_TYPES(),
     COUNT,
-    INVALID
+    INVALID,
 };
 
 // Support to load from configuration files for percentiles.
 ENUM_BEGIN(kth_percentile_type, kth_percentile_type::INVALID)
-ENUM_REG(kth_percentile_type::P50)
-ENUM_REG(kth_percentile_type::P90)
-ENUM_REG(kth_percentile_type::P95)
-ENUM_REG(kth_percentile_type::P99)
-ENUM_REG(kth_percentile_type::P999)
+ENUM_REG_WITH_KTH_PERCENTILE_TYPE(50)
+ENUM_REG_WITH_KTH_PERCENTILE_TYPE(90)
+ENUM_REG_WITH_KTH_PERCENTILE_TYPE(95)
+ENUM_REG_WITH_KTH_PERCENTILE_TYPE(99)
+ENUM_REG_WITH_KTH_PERCENTILE_TYPE(999)
 ENUM_END(kth_percentile_type)
 
-struct kth_percentile
-{
-    std::string name;
-    double decimal;
-};
+// Generate decimals from kth percentiles.
+const std::vector<kth_percentile_property> kAllKthPercentiles = 
{KTH_PERCENTILE_PROPERTY_LIST(50),
+                                                                 
KTH_PERCENTILE_PROPERTY_LIST(90),
+                                                                 
KTH_PERCENTILE_PROPERTY_LIST(95),
+                                                                 
KTH_PERCENTILE_PROPERTY_LIST(99),
+                                                                 
KTH_PERCENTILE_PROPERTY_LIST(999)};
+
+const std::set<kth_percentile_type> kAllKthPercentileTypes = {
+    ALL_KTH_PERCENTILE_TYPES(kth_percentile_type::)};
 
-const std::vector<kth_percentile> kAllKthPercentiles = {
-    {"p50", 0.5}, {"p90", 0.9}, {"p95", 0.95}, {"p99", 0.99}, {"p999", 0.999}};
+inline std::string kth_percentile_to_name(const kth_percentile_type &type)
+{
+    auto index = static_cast<size_t>(type);
+    CHECK_LT(index, kAllKthPercentiles.size());
+    return kAllKthPercentiles[index].name;
+}
 
 inline size_t kth_percentile_to_nth_index(size_t size, size_t kth_index)
 {
+    CHECK_LT(kth_index, kAllKthPercentiles.size());
     auto decimal = kAllKthPercentiles[kth_index].decimal;
     // Since the kth percentile is the value that is greater than k percent of 
the data values after
     // ranking them 
(https://people.richland.edu/james/ictcm/2001/descriptive/helpposition.html),
@@ -626,16 +745,6 @@ inline size_t kth_percentile_to_nth_index(size_t size, 
kth_percentile_type type)
     return kth_percentile_to_nth_index(size, static_cast<size_t>(type));
 }
 
-std::set<kth_percentile_type> get_all_kth_percentile_types();
-const std::set<kth_percentile_type> kAllKthPercentileTypes = 
get_all_kth_percentile_types();
-
-inline std::string kth_percentile_to_name(const kth_percentile_type &type)
-{
-    auto index = static_cast<size_t>(type);
-    CHECK_LT(index, kAllKthPercentiles.size());
-    return kAllKthPercentiles[index].name;
-}
-
 // `percentile_timer` is a timer class that encapsulates the details how each 
percentile is
 // computed periodically.
 //
@@ -736,20 +845,18 @@ public:
     // where "name" is the name of the percentile in string type, with each 
configured kth
     // percentile followed, such as "p50", "p90", "p95", etc. All of them are 
in numeric types
     // (i.e. integral or floating-point type, determined by `value_type`).
-    void take_snapshot(json::JsonWriter &writer) override
+    void take_snapshot(json::JsonWriter &writer, const metric_filters 
&filters) override
     {
         writer.StartObject();
 
-        writer.Key("name");
-        json::json_encode(writer, prototype()->name().data());
+        encode_prototype(writer, filters);
 
         for (size_t i = 0; i < 
static_cast<size_t>(kth_percentile_type::COUNT); ++i) {
             if (!_kth_percentile_bitset.test(i)) {
                 continue;
             }
 
-            writer.Key(kAllKthPercentiles[i].name.c_str());
-            json::json_encode(writer, value(i));
+            encode(writer, kAllKthPercentiles[i].name, value(i), filters);
         }
 
         writer.EndObject();
diff --git a/src/utils/test/metrics_test.cpp b/src/utils/test/metrics_test.cpp
index 511ecbfc0..36bf79f78 100644
--- a/src/utils/test/metrics_test.cpp
+++ b/src/utils/test/metrics_test.cpp
@@ -34,7 +34,7 @@ class my_gauge : public metric
 public:
     int64_t value() { return _value; }
 
-    virtual void take_snapshot(json::JsonWriter &) override {}
+    void take_snapshot(json::JsonWriter &, const metric_filters &) override {}
 
 protected:
     explicit my_gauge(const metric_prototype *prototype) : metric(prototype), 
_value(0) {}
@@ -906,13 +906,13 @@ TEST(metrics_test, percentile_double)
                          
floating_checker<value_type>>(METRIC_test_percentile_double);
 }
 
-std::string take_snapshot_and_get_json_string(metric *m)
+std::string take_snapshot_and_get_json_string(metric *m, const metric_filters 
&filters)
 {
     std::stringstream out;
     rapidjson::OStreamWrapper wrapper(out);
     json::JsonWriter writer(wrapper);
 
-    m->take_snapshot(writer);
+    m->take_snapshot(writer, filters);
 
     return out.str();
 }
@@ -921,24 +921,42 @@ template <typename T, typename = typename 
std::enable_if<std::is_arithmetic<T>::
 using metric_value_map = std::map<std::string, T>;
 
 template <typename T, typename = typename 
std::enable_if<std::is_arithmetic<T>::value>::type>
-void check_and_extract_metric_value_map_from_json_string(const std::string 
&json_string,
-                                                         const std::string 
&metric_name,
-                                                         const bool 
is_integral,
-                                                         metric_value_map<T> 
&value_map)
+void check_prototype_and_extract_value_map_from_json_string(
+    metric *my_metric,
+    const std::string &json_string,
+    const bool is_integral,
+    const metric_fields_type &expected_metric_fields,
+    metric_value_map<T> &value_map)
 {
     rapidjson::Document doc;
     rapidjson::ParseResult result = doc.Parse(json_string.c_str());
     ASSERT_FALSE(result.IsError());
 
+    metric_fields_type actual_metric_fields;
+
+    // The json format for each metric should be an object.
     ASSERT_TRUE(doc.IsObject());
     for (const auto &elem : doc.GetObject()) {
+        // Each metric name must be a string.
         ASSERT_TRUE(elem.name.IsString());
 
         if (elem.value.IsString()) {
-            ASSERT_STREQ(elem.name.GetString(), "name");
-
-            ASSERT_STREQ(elem.value.GetString(), metric_name.c_str());
+            // Must be a field of metric prototype.
+            if (std::strcmp(elem.name.GetString(), kMetricTypeField.c_str()) 
== 0) {
+                ASSERT_STREQ(elem.value.GetString(),
+                             enum_to_string(my_metric->prototype()->type()));
+            } else if (std::strcmp(elem.name.GetString(), 
kMetricNameField.c_str()) == 0) {
+                ASSERT_STREQ(elem.value.GetString(), 
my_metric->prototype()->name().data());
+            } else if (std::strcmp(elem.name.GetString(), 
kMetricUnitField.c_str()) == 0) {
+                ASSERT_STREQ(elem.value.GetString(),
+                             enum_to_string(my_metric->prototype()->unit()));
+            } else if (std::strcmp(elem.name.GetString(), 
kMetricDescField.c_str()) == 0) {
+                ASSERT_STREQ(elem.value.GetString(), 
my_metric->prototype()->description().data());
+            } else {
+                ASSERT_TRUE(false);
+            }
         } else {
+            // Must be a field of metric value.
             T value;
             if (is_integral) {
                 ASSERT_TRUE(elem.value.IsInt64());
@@ -949,17 +967,25 @@ void 
check_and_extract_metric_value_map_from_json_string(const std::string &json
             }
             value_map[elem.name.GetString()] = value;
         }
+
+        actual_metric_fields.emplace(elem.name.GetString());
     }
+
+    // Check if the fields of metric prototype have been provided to the 
client as expected.
+    ASSERT_EQ(actual_metric_fields, expected_metric_fields);
 }
 
+// Take snapshot as json format, then decode to a value map.
 template <typename T, typename = typename 
std::enable_if<std::is_arithmetic<T>::value>::type>
 void generate_metric_value_map(metric *my_metric,
                                const bool is_integral,
+                               const metric_filters &filters,
+                               const metric_fields_type 
&expected_metric_fields,
                                metric_value_map<T> &value_map)
 {
-    auto json_string = take_snapshot_and_get_json_string(my_metric);
-    check_and_extract_metric_value_map_from_json_string(
-        json_string, my_metric->prototype()->name().data(), is_integral, 
value_map);
+    auto json_string = take_snapshot_and_get_json_string(my_metric, filters);
+    check_prototype_and_extract_value_map_from_json_string(
+        my_metric, json_string, is_integral, expected_metric_fields, 
value_map);
 }
 
 template <typename T, typename = typename 
std::enable_if<std::is_integral<T>::value>::type>
@@ -984,67 +1010,154 @@ void compare_floating_metric_value_map(const 
metric_value_map<T> &actual_value_m
     }
 }
 
-#define TEST_METRIC_SNAPSHOT_WITH_SINGLE_VALUE(                                
                    \
-    metric_prototype, updater, value_type, is_integral, value_map_comparator)  
                    \
+#define TEST_METRIC_SNAPSHOT_WITH_SINGLE_VALUE(metric_prototype,               
                    \
+                                               updater,                        
                    \
+                                               value_type,                     
                    \
+                                               is_integral,                    
                    \
+                                               metric_fields,                  
                    \
+                                               expected_metric_fields,         
                    \
+                                               value_map_comparator)           
                    \
     do {                                                                       
                    \
         auto my_server_entity = 
METRIC_ENTITY_my_server.instantiate(test.entity_id);               \
         auto my_metric = metric_prototype.instantiate(my_server_entity);       
                    \
         my_metric->updater(test.expected_value);                               
                    \
                                                                                
                    \
-        const metric_value_map<value_type> expected_value_map = {{"value", 
test.expected_value}};  \
+        metric_filters filters;                                                
                    \
+        filters.with_metric_fields = metric_fields;                            
                    \
+                                                                               
                    \
+        metric_value_map<value_type> expected_value_map;                       
                    \
+        if (expected_metric_fields.find(kMetricSingleValueField) !=            
                    \
+            expected_metric_fields.end()) {                                    
                    \
+            expected_value_map[kMetricSingleValueField] = test.expected_value; 
                    \
+        }                                                                      
                    \
                                                                                
                    \
         metric_value_map<value_type> actual_value_map;                         
                    \
-        generate_metric_value_map(my_metric.get(), is_integral, 
actual_value_map);                 \
+        generate_metric_value_map(                                             
                    \
+            my_metric.get(), is_integral, filters, expected_metric_fields, 
actual_value_map);      \
                                                                                
                    \
         value_map_comparator(actual_value_map, expected_value_map);            
                    \
     } while (0)
 
-TEST(metrics_test, take_snapshot_gauge_int64)
-{
-    struct test_case
-    {
-        std::string entity_id;
-        int64_t expected_value;
-    } tests[]{{"server_60", 5}};
-
-    for (const auto &test : tests) {
-        TEST_METRIC_SNAPSHOT_WITH_SINGLE_VALUE(
-            METRIC_test_gauge_int64, set, int64_t, true, 
compare_integral_metric_value_map);
-    }
-}
+const metric_fields_type kAllPrototypeMetricFields = {
+    kMetricTypeField, kMetricNameField, kMetricUnitField, kMetricDescField};
 
-TEST(metrics_test, take_snapshot_gauge_double)
+metric_fields_type get_all_single_value_metric_fields()
 {
-    struct test_case
-    {
-        std::string entity_id;
-        double expected_value;
-    } tests[]{{"server_60", 6.789}};
-
-    for (const auto &test : tests) {
-        TEST_METRIC_SNAPSHOT_WITH_SINGLE_VALUE(
-            METRIC_test_gauge_double, set, double, false, 
compare_floating_metric_value_map);
-    }
+    auto fields = kAllPrototypeMetricFields;
+    fields.insert(kMetricSingleValueField);
+    return fields;
 }
 
-#define TEST_METRIC_SNAPSHOT_WITH_COUNTER(metric_prototype)                    
                    \
-    do {                                                                       
                    \
-        TEST_METRIC_SNAPSHOT_WITH_SINGLE_VALUE(                                
                    \
-            metric_prototype, increment_by, int64_t, true, 
compare_integral_metric_value_map);     \
-    } while (0)
-
-#define RUN_CASES_WITH_COUNTER_SNAPSHOT(metric_prototype)                      
                    \
+// Test cases:
+// - with_metric_fields is empty
+// - with_metric_fields has a field of prototype that exists
+// - with_metric_fields has a field of value that exists
+// - with_metric_fields has 2 fields of prototype that exist
+// - with_metric_fields has 2 fields that exist where there is a field of 
prototype and a field
+// of value
+// - with_metric_fields has 3 fields that exist where there is 2 fields of 
prototype and a field
+// of value
+// - with_metric_fields has all fields which exist
+// - with_metric_fields has a field that does not exist
+// - with_metric_fields has 2 fields both of which does not exist
+// - with_metric_fields has a field that does not exist and another field of 
prototype that
+// exists
+// - with_metric_fields has a field that does not exist and another field of 
value that exists
+// - with_metric_fields has a field that does not exist and another 2 fields 
of prototype that
+// exists
+// - with_metric_fields has a field that does not exist and another 2 fields 
that exist where
+// there is a field of prototype and a field of value
+// - with_metric_fields has a field that does not exist and another 3 fields 
that exist where
+// there is 2 fields of prototype and a field of value
+// - with_metric_fields has 2 fields that does not exist and another 3 fields 
that exist where
+// there is 2 fields of prototype and a field of value
+#define RUN_CASES_WITH_SINGLE_VALUE_SNAPSHOT(                                  
                    \
+    metric_prototype, updater, value_type, is_integral, value, 
value_map_comparator)               \
     do {                                                                       
                    \
+        static const metric_fields_type kAllSingleValueMetricFields =          
                    \
+            get_all_single_value_metric_fields();                              
                    \
         struct test_case                                                       
                    \
         {                                                                      
                    \
             std::string entity_id;                                             
                    \
-            int64_t expected_value;                                            
                    \
-        } tests[]{{"server_60", 10}};                                          
                    \
+            value_type expected_value;                                         
                    \
+            metric_fields_type with_metric_fields;                             
                    \
+            metric_fields_type expected_metric_fields;                         
                    \
+        } tests[] = {                                                          
                    \
+            {"server_60", value, {}, kAllSingleValueMetricFields},             
                    \
+            {"server_61", value, {kMetricNameField}, {kMetricNameField}},      
                    \
+            {"server_62", value, {kMetricSingleValueField}, 
{kMetricSingleValueField}},            \
+            {"server_63",                                                      
                    \
+             value,                                                            
                    \
+             {kMetricNameField, kMetricDescField},                             
                    \
+             {kMetricNameField, kMetricDescField}},                            
                    \
+            {"server_64",                                                      
                    \
+             value,                                                            
                    \
+             {kMetricNameField, kMetricSingleValueField},                      
                    \
+             {kMetricNameField, kMetricSingleValueField}},                     
                    \
+            {"server_65",                                                      
                    \
+             value,                                                            
                    \
+             {kMetricTypeField, kMetricNameField, kMetricSingleValueField},    
                    \
+             {kMetricTypeField, kMetricNameField, kMetricSingleValueField}},   
                    \
+            {"server_66", value, kAllSingleValueMetricFields, 
kAllSingleValueMetricFields},        \
+            {"server_67", value, {"field_not_exist"}, {}},                     
                    \
+            {"server_68", value, {"field_not_exist", 
"another_field_not_exist"}, {}},              \
+            {"server_69", value, {"field_not_exist", kMetricNameField}, 
{kMetricNameField}},       \
+            {"server_70",                                                      
                    \
+             value,                                                            
                    \
+             {"field_not_exist", kMetricSingleValueField},                     
                    \
+             {kMetricSingleValueField}},                                       
                    \
+            {"server_71",                                                      
                    \
+             value,                                                            
                    \
+             {"field_not_exist", kMetricTypeField, kMetricUnitField},          
                    \
+             {kMetricTypeField, kMetricUnitField}},                            
                    \
+            {"server_72",                                                      
                    \
+             value,                                                            
                    \
+             {"field_not_exist", kMetricDescField, kMetricSingleValueField},   
                    \
+             {kMetricDescField, kMetricSingleValueField}},                     
                    \
+            {"server_73",                                                      
                    \
+             value,                                                            
                    \
+             {"field_not_exist", kMetricTypeField, kMetricNameField, 
kMetricSingleValueField},     \
+             {kMetricTypeField, kMetricNameField, kMetricSingleValueField}},   
                    \
+            {"server_74",                                                      
                    \
+             value,                                                            
                    \
+             {"field_not_exist",                                               
                    \
+              "another_field_not_exist",                                       
                    \
+              kMetricUnitField,                                                
                    \
+              kMetricDescField,                                                
                    \
+              kMetricSingleValueField},                                        
                    \
+             {kMetricUnitField, kMetricDescField, kMetricSingleValueField}}};  
                    \
                                                                                
                    \
         for (const auto &test : tests) {                                       
                    \
-            TEST_METRIC_SNAPSHOT_WITH_COUNTER(metric_prototype);               
                    \
+            TEST_METRIC_SNAPSHOT_WITH_SINGLE_VALUE(metric_prototype,           
                    \
+                                                   updater,                    
                    \
+                                                   value_type,                 
                    \
+                                                   is_integral,                
                    \
+                                                   test.with_metric_fields,    
                    \
+                                                   
test.expected_metric_fields,                    \
+                                                   value_map_comparator);      
                    \
         }                                                                      
                    \
-    } while (0)
+    } while (0);
+
+#define RUN_CASES_WITH_GAUGE_SNAPSHOT(                                         
                    \
+    metric_prototype, value_type, is_integral, value, value_map_comparator)    
                    \
+    RUN_CASES_WITH_SINGLE_VALUE_SNAPSHOT(                                      
                    \
+        metric_prototype, set, value_type, is_integral, value, 
value_map_comparator)
+
+TEST(metrics_test, take_snapshot_gauge_int64)
+{
+    RUN_CASES_WITH_GAUGE_SNAPSHOT(
+        METRIC_test_gauge_int64, int64_t, true, 5, 
compare_integral_metric_value_map);
+}
+
+TEST(metrics_test, take_snapshot_gauge_double)
+{
+    RUN_CASES_WITH_GAUGE_SNAPSHOT(
+        METRIC_test_gauge_double, double, false, 6.789, 
compare_floating_metric_value_map);
+}
+
+#define RUN_CASES_WITH_COUNTER_SNAPSHOT(metric_prototype)                      
                    \
+    RUN_CASES_WITH_SINGLE_VALUE_SNAPSHOT(                                      
                    \
+        metric_prototype, increment_by, int64_t, true, 10, 
compare_integral_metric_value_map)
 
 TEST(metrics_test, take_snapshot_counter) { 
RUN_CASES_WITH_COUNTER_SNAPSHOT(METRIC_test_counter); }
 
@@ -1063,12 +1176,15 @@ TEST(metrics_test, 
take_snapshot_concurrent_volatile_counter)
     RUN_CASES_WITH_COUNTER_SNAPSHOT(METRIC_test_concurrent_volatile_counter);
 }
 
+// Set to percentile metric with values output by case generator, and generate 
the expected
+// value map.
 template <typename MetricType, typename CaseGenerator>
 void generate_metric_value_map(MetricType *my_metric,
                                CaseGenerator &generator,
                                const uint64_t interval_ms,
                                const uint64_t exec_ms,
                                const std::set<kth_percentile_type> 
&kth_percentiles,
+                               const metric_fields_type 
&expected_metric_fields,
                                metric_value_map<typename 
MetricType::value_type> &value_map)
 {
     using value_type = typename MetricType::value_type;
@@ -1089,54 +1205,162 @@ void generate_metric_value_map(MetricType *my_metric,
     auto value = values.begin();
     for (const auto &type : kth_percentiles) {
         auto name = kth_percentile_to_name(type);
-        value_map[name] = *value++;
+        // Only add the chosen fields to the expected value map.
+        if (expected_metric_fields.find(name) != expected_metric_fields.end()) 
{
+            value_map[name] = *value;
+        }
+        ++value;
     }
 }
 
-#define TEST_METRIC_SNAPSHOT_WITH_PERCENTILE(                                  
                    \
-    metric_prototype, case_generator, is_integral, value_map_comparator)       
                    \
+#define TEST_METRIC_SNAPSHOT_WITH_PERCENTILE(metric_prototype,                 
                    \
+                                             case_generator,                   
                    \
+                                             is_integral,                      
                    \
+                                             metric_fields,                    
                    \
+                                             expected_metric_fields,           
                    \
+                                             value_map_comparator)             
                    \
     do {                                                                       
                    \
         using value_type = typename case_generator::value_type;                
                    \
                                                                                
                    \
+        const uint64_t interval_ms = 50;                                       
                    \
+        const auto kth_percentiles = kAllKthPercentileTypes;                   
                    \
+        const size_t sample_size = 4096;                                       
                    \
+        const size_t data_size = 4096;                                         
                    \
+        const uint64_t exec_ms = 10;                                           
                    \
+                                                                               
                    \
         auto my_server_entity = 
METRIC_ENTITY_my_server.instantiate(test.entity_id);               \
         auto my_metric = metric_prototype.instantiate(                         
                    \
-            my_server_entity, test.interval_ms, test.kth_percentiles, 
test.sample_size);           \
+            my_server_entity, interval_ms, kth_percentiles, sample_size);      
                    \
                                                                                
                    \
-        case_generator generator(test.data_size,                               
                    \
-                                 value_type() /* initial_value */,             
                    \
-                                 5 /* range_size */,                           
                    \
-                                 test.kth_percentiles);                        
                    \
+        metric_filters filters;                                                
                    \
+        filters.with_metric_fields = metric_fields;                            
                    \
+                                                                               
                    \
+        case_generator generator(                                              
                    \
+            data_size, value_type() /* initial_value */, 5 /* range_size */, 
kth_percentiles);     \
                                                                                
                    \
         metric_value_map<value_type> expected_value_map;                       
                    \
         generate_metric_value_map(my_metric.get(),                             
                    \
                                   generator,                                   
                    \
-                                  test.interval_ms,                            
                    \
-                                  test.exec_ms,                                
                    \
-                                  test.kth_percentiles,                        
                    \
+                                  interval_ms,                                 
                    \
+                                  exec_ms,                                     
                    \
+                                  kth_percentiles,                             
                    \
+                                  expected_metric_fields,                      
                    \
                                   expected_value_map);                         
                    \
                                                                                
                    \
         metric_value_map<value_type> actual_value_map;                         
                    \
-        generate_metric_value_map(my_metric.get(), is_integral, 
actual_value_map);                 \
+        generate_metric_value_map(                                             
                    \
+            my_metric.get(), is_integral, filters, expected_metric_fields, 
actual_value_map);      \
                                                                                
                    \
         value_map_comparator(actual_value_map, expected_value_map);            
                    \
     } while (0)
 
+metric_fields_type get_all_kth_percentile_fields()
+{
+    auto fields = kAllPrototypeMetricFields;
+    for (const auto &kth : kAllKthPercentiles) {
+        fields.insert(kth.name);
+    }
+    return fields;
+}
+
+// Test cases:
+// - with_metric_fields is empty
+// - with_metric_fields has a field of prototype that exists
+// - with_metric_fields has a field of value that exists
+// - with_metric_fields has 2 fields of prototype that exist
+// - with_metric_fields has 2 fields of value that exist
+// - with_metric_fields has 2 fields that exist where there is a field of 
prototype and a field
+// of value
+// - with_metric_fields has 3 fields that exist where there is 2 fields of 
prototype and a field
+// of value
+// - with_metric_fields has 3 fields that exist where there is a field of 
prototype and 2 fields
+// of value
+// - with_metric_fields has 4 fields that exist where there is 2 fields of 
prototype and 2 fields
+// of value
+// - with_metric_fields has all fields that exist
+// - with_metric_fields has a field that does not exist
+// - with_metric_fields has 2 fields both of which does not exist
+// - with_metric_fields has a field that does not exist and another field of 
prototype that
+// exists
+// - with_metric_fields has a field that does not exist and another field of 
value that
+// exists
+// - with_metric_fields has a field that does not exist and another 2 fields 
of prototype that
+// exist
+// - with_metric_fields has a field that does not exist and another 2 fields 
of value that
+// exist
+// - with_metric_fields has a field that does not exist and another 2 fields 
that exist where
+// there is a field of prototype and a field of value
+// - with_metric_fields has a field that does not exist and another 3 fields 
that exist where
+// there is 2 fields of prototype and a field of value
+// - with_metric_fields has a field that does not exist and another 3 fields 
that exist where
+// there is a field of prototype and 2 fields of value
+// - with_metric_fields has a field that does not exist and another 4 fields 
that exist where
+// there is 2 fields of prototype and 2 fields of value
+// - with_metric_fields has 2 fields that does not exist and another 4 fields 
that exist where
+// there is 2 fields of prototype and 2 fields of value
 #define RUN_CASES_WITH_PERCENTILE_SNAPSHOT(                                    
                    \
     metric_prototype, case_generator, is_integral, value_map_comparator)       
                    \
     do {                                                                       
                    \
+        static const metric_fields_type kAllKthPercentileFields = 
get_all_kth_percentile_fields(); \
+                                                                               
                    \
         struct test_case                                                       
                    \
         {                                                                      
                    \
             std::string entity_id;                                             
                    \
-            uint64_t interval_ms;                                              
                    \
-            std::set<kth_percentile_type> kth_percentiles;                     
                    \
-            size_t sample_size;                                                
                    \
-            size_t data_size;                                                  
                    \
-            uint64_t exec_ms;                                                  
                    \
-        } tests[]{{"server_60", 50, kAllKthPercentileTypes, 4096, 4096, 10}};  
                    \
+            metric_fields_type with_metric_fields;                             
                    \
+            metric_fields_type expected_metric_fields;                         
                    \
+        } tests[] = {                                                          
                    \
+            {"server_60", {}, kAllKthPercentileFields},                        
                    \
+            {"server_61", {kMetricNameField}, {kMetricNameField}},             
                    \
+            {"server_62", {"p999"}, {"p999"}},                                 
                    \
+            {"server_63",                                                      
                    \
+             {kMetricTypeField, kMetricDescField},                             
                    \
+             {kMetricTypeField, kMetricDescField}},                            
                    \
+            {"server_64", {"p50", "p999"}, {"p50", "p999"}},                   
                    \
+            {"server_65", {kMetricUnitField, "p99"}, {kMetricUnitField, 
"p99"}},                   \
+            {"server_66",                                                      
                    \
+             {kMetricNameField, kMetricUnitField, "p99"},                      
                    \
+             {kMetricNameField, kMetricUnitField, "p99"}},                     
                    \
+            {"server_67", {kMetricDescField, "p95", "p999"}, 
{kMetricDescField, "p95", "p999"}},   \
+            {"server_68",                                                      
                    \
+             {kMetricTypeField, kMetricNameField, "p90", "p99"},               
                    \
+             {kMetricTypeField, kMetricNameField, "p90", "p99"}},              
                    \
+            {"server_69", kAllKthPercentileFields, kAllKthPercentileFields},   
                    \
+            {"server_70", {"field_not_exist"}, {}},                            
                    \
+            {"server_71", {"field_not_exist", "another_field_not_exist"}, {}}, 
                    \
+            {"server_72", {"file_not_exist", kMetricTypeField}, 
{kMetricTypeField}},               \
+            {"server_73", {"file_not_exist", "p99"}, {"p99"}},                 
                    \
+            {"server_74",                                                      
                    \
+             {"field_not_exist", kMetricUnitField, kMetricDescField},          
                    \
+             {kMetricUnitField, kMetricDescField}},                            
                    \
+            {"server_75", {"file_not_exist", "p50", "p99"}, {"p50", "p99"}},   
                    \
+            {"server_76",                                                      
                    \
+             {"field_not_exist", kMetricNameField, "p999"},                    
                    \
+             {kMetricNameField, "p999"}},                                      
                    \
+            {"server_77",                                                      
                    \
+             {"field_not_exist", kMetricTypeField, kMetricUnitField, "p99"},   
                    \
+             {kMetricTypeField, kMetricUnitField, "p99"}},                     
                    \
+            {"server_78",                                                      
                    \
+             {"field_not_exist", kMetricDescField, "p90", "p999"},             
                    \
+             {kMetricDescField, "p90", "p999"}},                               
                    \
+            {"server_79",                                                      
                    \
+             {"field_not_exist", kMetricNameField, kMetricUnitField, "p95", 
"p99"},                \
+             {kMetricNameField, kMetricUnitField, "p95", "p99"}},              
                    \
+            {"server_80",                                                      
                    \
+             {"field_not_exist",                                               
                    \
+              "another_field_not_exist",                                       
                    \
+              kMetricTypeField,                                                
                    \
+              kMetricDescField,                                                
                    \
+              "p50",                                                           
                    \
+              "p999"},                                                         
                    \
+             {kMetricTypeField, kMetricDescField, "p50", "p999"}}};            
                    \
                                                                                
                    \
         for (const auto &test : tests) {                                       
                    \
-            TEST_METRIC_SNAPSHOT_WITH_PERCENTILE(                              
                    \
-                metric_prototype, case_generator, is_integral, 
value_map_comparator);              \
+            TEST_METRIC_SNAPSHOT_WITH_PERCENTILE(metric_prototype,             
                    \
+                                                 case_generator,               
                    \
+                                                 is_integral,                  
                    \
+                                                 test.with_metric_fields,      
                    \
+                                                 test.expected_metric_fields,  
                    \
+                                                 value_map_comparator);        
                    \
         }                                                                      
                    \
     } while (0)
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to