diff --git a/docs/cn/mbvar_c++.md b/docs/cn/mbvar_c++.md index 8a81e71473..1ce1364f24 100644 --- a/docs/cn/mbvar_c++.md +++ b/docs/cn/mbvar_c++.md @@ -412,6 +412,7 @@ public: ``` ### get_stats + 根据指定label获取对应的单维度统计项bvar。 get_stats除了支持(默认)std::list参数类型,也支持自定义参数类型,满足以下条件: @@ -421,6 +422,12 @@ get_stats除了支持(默认)std::list参数类型,也支持 推荐使用不需要分配内存的容器(例如,std::array、absl::InlinedVector)和不需要拷贝字符串的数据结构(例如,const char*、std::string_view、butil::StringPieces),可以提高性能。 +bvar::MultiDimension的模板参数Shared,默认为false: +1. 如果Shared等于false,get_stats返回模板参数T的指针,例如bvar::Adder*。 +2. 如果Shared等于true,get_stats返回模板参数T的shared_ptr,例如std::shared_ptr\\>。 + +**注意**:因为shared_ptr的开销,Shared等于true的性能会比Shared等于false的性能差一些。 + ```c++ #include #include @@ -523,6 +530,20 @@ int request_count = get_request_count(request_label_list); * store(bvar) * return bvar +### delete_stats + +根据指定label删除对应的单维度统计项bvar。 + +bvar::MultiDimension的模板参数Shared,默认为false: +1. 如果Shared等于false,get_stats返回的是模板参数T的指针,delete_stats无法保证没有使用者,所以无法安全删除bvar。 +2. 如果Shared等于true,get_stats返回的是模板参数T的shared_ptr,delete_stats可以安全删除bvar。 + +### clear_stats + +清理所有单维度统计项bvar。 + +安全性同样受bvar::MultiDimension的模板参数Shared的影响,见delete_stats一节说明。 + **bvar的生命周期** label对应的单维度统计项bvar存储在多维度统计项(mbvar)中,当mbvar析构的时候会释放自身所有bvar,所以用户必须保证在mbvar的生命周期之内操作bvar,在mbvar生命周期外访问bvar的行为未定义,极有可能出core。 diff --git a/src/bvar/detail/combiner.h b/src/bvar/detail/combiner.h index 3b4421809c..cae1b8ea8f 100644 --- a/src/bvar/detail/combiner.h +++ b/src/bvar/detail/combiner.h @@ -205,12 +205,10 @@ friend class GlobalValue; // // NOTE: Only available to non-atomic types. template - void merge_global(const Op &op, self_shared_type c = NULL) { - if (NULL == c) { - c = combiner.lock(); - } - if (NULL != c) { - GlobalValue g(this, c.get()); + void merge_global(const Op &op, self_shared_type& c) { + const self_shared_type& c_ref = NULL != c ? c : combiner.lock(); + if (NULL != c_ref) { + GlobalValue g(this, c_ref.get()); element.merge_global(op, g); } } diff --git a/src/bvar/multi_dimension.h b/src/bvar/multi_dimension.h index 9163e3462e..ad352a02c9 100644 --- a/src/bvar/multi_dimension.h +++ b/src/bvar/multi_dimension.h @@ -20,6 +20,8 @@ #ifndef BVAR_MULTI_DIMENSION_H #define BVAR_MULTI_DIMENSION_H +#include +#include #include "butil/logging.h" // LOG #include "butil/macros.h" // BAIDU_CASSERT #include "butil/scoped_lock.h" // BAIDU_SCOPE_LOCK @@ -35,8 +37,15 @@ namespace bvar { // 2. KeyType::value_type must be std::string. // 3. KeyType::size() returns the number of labels. // 4. KeyType::push_back() adds a label to the end of the container. -template > +// +// If `Shared' is false, `get_stats' returns a raw pointer, +// `delete_stats' and `clear_stats' are not thread safe. +// If `Shared' is true, `get_stats` returns a shared_ptr, +// `delete_stats' and `clear_stats' are thread safe. +// Note: The shared mode may be less performant than the non-shared mode. +template , bool Shared = false> class MultiDimension : public MVariable { + typedef std::shared_ptr shared_value_type; public: enum STATS_OP { READ_ONLY, @@ -45,7 +54,7 @@ class MultiDimension : public MVariable { typedef KeyType key_type; typedef T value_type; - typedef T* value_ptr_type; + typedef typename std::conditional::type value_ptr_type; typedef MVariable Base; struct KeyHash { @@ -102,11 +111,15 @@ class MultiDimension : public MVariable { // 2. K::value_type must be able to convert to std::string and butil::StringPiece // through operator std::string() function and operator butil::StringPiece() function. // 3. K::value_type must be able to compare with std::string. + // + // Returns a shared_ptr if `Shared' is true, otherwise returns a raw pointer. template - T* get_stats(const K& labels_value) { + value_ptr_type get_stats(const K& labels_value) { return get_stats_impl(labels_value, READ_OR_INSERT); } + // `delete_stats' and `clear_stats' are thread safe + // if `Shared' is true, otherwise not. // Remove stat so those not count and dump template void delete_stats(const K& labels_value); @@ -133,7 +146,7 @@ class MultiDimension : public MVariable { // Return real bvar pointer if labels_name exist, NULL otherwise. // CAUTION!!! Just For Debug!!! template - T* get_stats_read_only(const K& labels_value) { + value_ptr_type get_stats_read_only(const K& labels_value) { return get_stats_impl(labels_value); } @@ -141,17 +154,18 @@ class MultiDimension : public MVariable { // Return real bvar pointer if labels_name exist, otherwise(not exist) create bvar pointer. // CAUTION!!! Just For Debug!!! template - T* get_stats_read_or_insert(const K& labels_value, bool* do_write = NULL) { + value_ptr_type get_stats_read_or_insert(const K& labels_value, bool* do_write = NULL) { return get_stats_impl(labels_value, READ_OR_INSERT, do_write); } #endif private: template - T* get_stats_impl(const K& labels_value); + value_ptr_type get_stats_impl(const K& labels_value); template - T* get_stats_impl(const K& labels_value, STATS_OP stats_op, bool* do_write = NULL); + value_ptr_type get_stats_impl( + const K& labels_value, STATS_OP stats_op, bool* do_write = NULL); template static typename std::enable_if::value>::type @@ -162,11 +176,8 @@ class MultiDimension : public MVariable { template static typename std::enable_if::value>::type insert_metrics_map(MetricMap& bg, const K& labels_value, op_value_type metric) { - key_type labels_value_str; - for (auto& label : labels_value) { - // key_type::value_type must be able to convert to std::string. - labels_value_str.push_back(std::string(label)); - } + // key_type::value_type must be able to convert to std::string. + key_type labels_value_str(labels_value.cbegin(), labels_value.cend()); bg.insert(labels_value_str, metric); } @@ -192,7 +203,27 @@ class MultiDimension : public MVariable { void delete_stats(); static size_t init_flatmap(MetricMap& bg); - + + // If Shared is true, return std::shared_ptr, otherwise return raw pointer. + template + typename std::enable_if::type new_value() { + return std::make_shared(); + } + template + typename std::enable_if::type new_value() { + return new value_type(); + } + + // If Shared is true, reset std::shared_ptr, otherwise delete raw pointer. + template + typename std::enable_if::type delete_value(value_ptr_type& v) { + v.reset(); + } + template + typename std::enable_if::type delete_value(value_ptr_type& v) { + delete v; + } + size_t _max_stats_count; MetricMapDBD _metric_map; }; diff --git a/src/bvar/multi_dimension_inl.h b/src/bvar/multi_dimension_inl.h index 15378248b5..c407567c54 100644 --- a/src/bvar/multi_dimension_inl.h +++ b/src/bvar/multi_dimension_inl.h @@ -21,6 +21,7 @@ #define BVAR_MULTI_DIMENSION_INL_H #include +#include "butil/compiler_specific.h" namespace bvar { @@ -34,43 +35,43 @@ static const std::string ALLOW_UNUSED METRIC_TYPE_SUMMARY = "summary"; static const std::string ALLOW_UNUSED METRIC_TYPE_HISTOGRAM = "histogram"; static const std::string ALLOW_UNUSED METRIC_TYPE_GAUGE = "gauge"; -template -MultiDimension::MultiDimension(const key_type& labels) +template +MultiDimension::MultiDimension(const key_type& labels) : Base(labels) , _max_stats_count(FLAGS_max_multi_dimension_stats_count) { _metric_map.Modify(init_flatmap); } -template -MultiDimension::MultiDimension(const butil::StringPiece& name, +template +MultiDimension::MultiDimension(const butil::StringPiece& name, const key_type& labels) : MultiDimension(labels) { this->expose(name); } -template -MultiDimension::MultiDimension(const butil::StringPiece& prefix, +template +MultiDimension::MultiDimension(const butil::StringPiece& prefix, const butil::StringPiece& name, const key_type& labels) : MultiDimension(labels) { this->expose_as(prefix, name); } -template -MultiDimension::~MultiDimension() { +template +MultiDimension::~MultiDimension() { this->hide(); delete_stats(); } -template -size_t MultiDimension::init_flatmap(MetricMap& bg) { +template +size_t MultiDimension::init_flatmap(MetricMap& bg) { // size = 1 << 13 CHECK_EQ(0, bg.init(8192, 80)); - return (size_t)1; + return 1; } -template -size_t MultiDimension::count_stats() { +template +size_t MultiDimension::count_stats() { MetricMapScopedPtr metric_map_ptr; if (_metric_map.Read(&metric_map_ptr) != 0) { LOG(ERROR) << "Fail to read dbd"; @@ -79,9 +80,9 @@ size_t MultiDimension::count_stats() { return metric_map_ptr->size(); } -template +template template -void MultiDimension::delete_stats(const K& labels_value) { +void MultiDimension::delete_stats(const K& labels_value) { if (is_valid_lables_value(labels_value)) { // Because there are two copies(foreground and background) in DBD, // we need to use an empty tmp_metric, get the deleted value of @@ -93,35 +94,35 @@ void MultiDimension::delete_stats(const K& labels_value) { }; _metric_map.Modify(erase_fn); if (tmp_metric) { - delete tmp_metric; + delete_value(tmp_metric); } } } -template -void MultiDimension::delete_stats() { +template +void MultiDimension::delete_stats() { // Because there are two copies(foreground and background) in DBD, we need to use an empty tmp_map, // swap two copies with empty, and get the value of second copy into tmp_map, // then traversal tmp_map and delete bvar object, // which can prevent the bvar object from being deleted twice. MetricMap tmp_map; CHECK_EQ(0, tmp_map.init(8192, 80)); - auto clear_fn = [&tmp_map](MetricMap& map) { + auto clear_fn = [&tmp_map](MetricMap& map) -> size_t { if (!tmp_map.empty()) { tmp_map.clear(); } tmp_map.swap(map); - return (size_t)1; + return 1; }; int ret = _metric_map.Modify(clear_fn); CHECK_EQ(1, ret); - for (auto &kv : tmp_map) { - delete kv.second; + for (auto& kv : tmp_map) { + delete_value(kv.second); } } -template -void MultiDimension::list_stats(std::vector* names) { +template +void MultiDimension::list_stats(std::vector* names) { if (names == NULL) { return; } @@ -137,58 +138,60 @@ void MultiDimension::list_stats(std::vector* names) { } } -template +template template -T* MultiDimension::get_stats_impl(const K& labels_value) { +typename MultiDimension::value_ptr_type +MultiDimension::get_stats_impl(const K& labels_value) { if (!is_valid_lables_value(labels_value)) { - return nullptr; + return NULL; } MetricMapScopedPtr metric_map_ptr; if (_metric_map.Read(&metric_map_ptr) != 0) { LOG(ERROR) << "Fail to read dbd"; - return nullptr; + return NULL; } auto it = metric_map_ptr->seek(labels_value); - if (it == nullptr) { - return nullptr; + if (NULL == it) { + return NULL; } return (*it); } -template +template template -T* MultiDimension::get_stats_impl( +typename MultiDimension::value_ptr_type +MultiDimension::get_stats_impl( const K& labels_value, STATS_OP stats_op, bool* do_write) { if (!is_valid_lables_value(labels_value)) { - return nullptr; + return NULL; } { MetricMapScopedPtr metric_map_ptr; if (0 != _metric_map.Read(&metric_map_ptr)) { LOG(ERROR) << "Fail to read dbd"; - return nullptr; + return NULL; } auto it = metric_map_ptr->seek(labels_value); if (NULL != it) { return (*it); } else if (READ_ONLY == stats_op) { - return nullptr; + return NULL; } if (metric_map_ptr->size() > _max_stats_count) { LOG(ERROR) << "Too many stats seen, overflow detected, max stats count=" << _max_stats_count; - return nullptr; + return NULL; } } - // Because DBD has two copies(foreground and background) MetricMap, both copies need to be modify, + // Because DBD has two copies(foreground and background) MetricMap, both copies need to be modified, // In order to avoid new duplicate bvar object, need use cache_metric to cache the new bvar object, // In this way, when modifying the second copy, can directly use the cache_metric bvar object. op_value_type cache_metric = NULL; - auto insert_fn = [&labels_value, &cache_metric, &do_write](MetricMap& bg) { + auto insert_fn = [this, &labels_value, &cache_metric, &do_write](MetricMap& bg) { auto bg_metric = bg.seek(labels_value); if (NULL != bg_metric) { cache_metric = *bg_metric; @@ -199,7 +202,7 @@ T* MultiDimension::get_stats_impl( } if (NULL == cache_metric) { - cache_metric = new T(); + cache_metric = new_value(); } insert_metrics_map(bg, labels_value, cache_metric); return 1; @@ -208,21 +211,21 @@ T* MultiDimension::get_stats_impl( return cache_metric; } -template -void MultiDimension::clear_stats() { +template +void MultiDimension::clear_stats() { delete_stats(); } -template +template template -bool MultiDimension::has_stats(const K& labels_value) { - return get_stats_impl(labels_value) != nullptr; +bool MultiDimension::has_stats(const K& labels_value) { + return get_stats_impl(labels_value) != NULL; } -template +template template typename std::enable_if::value, size_t>::type -MultiDimension::dump_impl(Dumper* dumper, const DumpOptions* options) { +MultiDimension::dump_impl(Dumper* dumper, const DumpOptions* options) { std::vector label_names; list_stats(&label_names); if (label_names.empty() || !dumper->dump_comment(this->name(), METRIC_TYPE_GAUGE)) { @@ -230,8 +233,8 @@ MultiDimension::dump_impl(Dumper* dumper, const DumpOptions* options } size_t n = 0; for (auto &label_name : label_names) { - T* bvar = get_stats_impl(label_name); - if (!bvar) { + value_ptr_type bvar = get_stats_impl(label_name); + if (NULL == bvar) { continue; } std::ostringstream oss; @@ -246,10 +249,10 @@ MultiDimension::dump_impl(Dumper* dumper, const DumpOptions* options return n; } -template +template template typename std::enable_if::value, size_t>::type -MultiDimension::dump_impl(Dumper* dumper, const DumpOptions*) { +MultiDimension::dump_impl(Dumper* dumper, const DumpOptions*) { std::vector label_names; list_stats(&label_names); if (label_names.empty()) { @@ -299,8 +302,8 @@ MultiDimension::dump_impl(Dumper* dumper, const DumpOptions*) { // max_latency comment dumper->dump_comment(this->name() + "_max_latency", METRIC_TYPE_GAUGE); for (auto &label_name : label_names) { - bvar::LatencyRecorder* bvar = get_stats_impl(label_name); - if (!bvar) { + LatencyRecorder* bvar = get_stats_impl(label_name); + if (NULL == bvar) { continue; } std::ostringstream oss_max_latency_key; @@ -313,8 +316,8 @@ MultiDimension::dump_impl(Dumper* dumper, const DumpOptions*) { // qps comment dumper->dump_comment(this->name() + "_qps", METRIC_TYPE_GAUGE); for (auto &label_name : label_names) { - bvar::LatencyRecorder* bvar = get_stats_impl(label_name); - if (!bvar) { + LatencyRecorder* bvar = get_stats_impl(label_name); + if (NULL == bvar) { continue; } std::ostringstream oss_qps_key; @@ -327,8 +330,8 @@ MultiDimension::dump_impl(Dumper* dumper, const DumpOptions*) { // count comment dumper->dump_comment(this->name() + "_count", METRIC_TYPE_COUNTER); for (auto &label_name : label_names) { - bvar::LatencyRecorder* bvar = get_stats_impl(label_name); - if (!bvar) { + LatencyRecorder* bvar = get_stats_impl(label_name); + if (NULL == bvar) { continue; } std::ostringstream oss_count_key; @@ -340,8 +343,8 @@ MultiDimension::dump_impl(Dumper* dumper, const DumpOptions*) { return n; } -template -void MultiDimension::make_dump_key(std::ostream& os, const key_type& labels_value, +template +void MultiDimension::make_dump_key(std::ostream& os, const key_type& labels_value, const std::string& suffix, int quantile) { os << this->name(); if (!suffix.empty()) { @@ -350,8 +353,8 @@ void MultiDimension::make_dump_key(std::ostream& os, const key_type& make_labels_kvpair_string(os, labels_value, quantile); } -template -void MultiDimension::make_labels_kvpair_string( +template +void MultiDimension::make_labels_kvpair_string( std::ostream& os, const key_type& labels_value, int quantile) { os << "{"; auto label_key = this->_labels.cbegin(); @@ -368,9 +371,9 @@ void MultiDimension::make_labels_kvpair_string( os << "}"; } -template +template template -bool MultiDimension::is_valid_lables_value(const K& labels_value) const { +bool MultiDimension::is_valid_lables_value(const K& labels_value) const { if (this->count_labels() != labels_value.size()) { LOG(ERROR) << "Invalid labels count" << this->count_labels() << " != " << labels_value.size(); @@ -379,8 +382,8 @@ bool MultiDimension::is_valid_lables_value(const K& labels_value) co return true; } -template -void MultiDimension::describe(std::ostream& os) { +template +void MultiDimension::describe(std::ostream& os) { os << "{\"name\" : \"" << this->name() << "\", \"labels\" : ["; char comma[3] = {'\0', ' ', '\0'}; for (auto& label : this->_labels) { diff --git a/test/bvar_multi_dimension_unittest.cpp b/test/bvar_multi_dimension_unittest.cpp index 8d0e479d0d..a04ab4b78e 100644 --- a/test/bvar_multi_dimension_unittest.cpp +++ b/test/bvar_multi_dimension_unittest.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -497,3 +497,169 @@ TEST_F(MultiDimensionTest, test_hash) { LOG(INFO) << "Hash fun performance:\n" << oss.str(); } + +class MyStringView { +public: + MyStringView() : _ptr(NULL), _len(0) {} + MyStringView(const char* str) + : _ptr(str), + _len(str == NULL ? 0 : strlen(str)) {} +#if __cplusplus >= 201703L + MyStringView(const std::string_view& str) + : _ptr(str.data()), _len(str.size()) {} +#endif // __cplusplus >= 201703L + MyStringView(const std::string& str) + : _ptr(str.data()), _len(str.size()) {} + MyStringView(const char* offset, size_t len) + : _ptr(offset), _len(len) {} + + const char* data() const { return _ptr; } + size_t size() const { return _len; } + + // Converts to `std::basic_string`. + explicit operator std::string() const { + if (NULL == _ptr) { + return {}; + } + return {_ptr, size()}; + } + + // Converts to butil::StringPiece. + explicit operator butil::StringPiece() const { + if (NULL == _ptr) { + return {}; + } + return {_ptr, size()}; + } + +private: + const char* _ptr; + size_t _len; +}; + +bool operator==(const MyStringView& x, const std::string& y) { + if (x.size() != y.size()) { + return false; + } + + return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; +} + +bool operator==(const std::string& x, const MyStringView& y) { + if (x.size() != y.size()) { + return false; + } + + return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; +} + +static int g_exposed_count = 0; + +template +static void TestLabels() { + std::string mbvar_name = butil::string_printf("my_madder_%d", g_exposed_count); + KeyType labels{"idc", "method", "status"}; + bvar::MultiDimension, KeyType> my_madder(mbvar_name, labels); + ASSERT_EQ(labels.size(), my_madder.count_labels()); + ASSERT_STREQ(mbvar_name.c_str(), my_madder.name().c_str()); + ASSERT_EQ(labels, my_madder.labels()); + + using ItemType = typename ValueType::value_type; + ValueType labels_value{ItemType("cv"), ItemType("post"), ItemType("200")}; + bvar::Adder* adder = my_madder.get_stats(labels_value); + ASSERT_NE(nullptr, adder); + ASSERT_TRUE(my_madder.has_stats(labels_value)); + ASSERT_EQ((size_t)1, my_madder.count_stats()); + { + // Compatible with old API. + bvar::Adder* temp = my_madder.get_stats({"cv", "post", "200"}); + ASSERT_EQ(adder, temp); + } + *adder << g_exposed_count; + ASSERT_EQ(g_exposed_count, adder->get_value()); + my_madder.delete_stats(labels_value); + ASSERT_FALSE(my_madder.has_stats(labels_value)); + ASSERT_EQ((size_t)0, my_madder.count_stats()); +} + +TEST_F(MultiDimensionTest, labels) { + TestLabels, std::list>(); + TestLabels, std::vector>(); + TestLabels, std::array>(); + + TestLabels, std::list>(); + TestLabels, std::vector>(); + TestLabels, std::array>(); + +#if __cplusplus >= 201703L + TestLabels, std::list>(); + TestLabels, std::vector>(); + TestLabels, std::array>(); +#endif // __cplusplus >= 201703L + + TestLabels, std::list>(); + TestLabels, std::vector>(); + TestLabels, std::array>(); + + TestLabels, std::list>(); + TestLabels, std::vector>(); + TestLabels, std::array>(); + + TestLabels, std::list>(); + TestLabels, std::vector>(); + TestLabels, std::array>(); +} + +std::array g_labels_value{"idc", "post", "200"}; +bool g_shared_stop = false; + +void* get_shared_adder_thread(void* arg) { + auto my_madder = + (bvar::MultiDimension, std::vector, true>*)arg; + while (!g_shared_stop) { + auto adder = my_madder->get_stats(g_labels_value); + EXPECT_NE(nullptr, adder); + *adder << 1; + } + return NULL; +} + +void* delete_shared_adder_thread(void* arg) { + auto my_madder = + (bvar::MultiDimension, std::vector, true>*)arg; + while (!g_shared_stop) { + my_madder->delete_stats(g_labels_value); + } + return NULL; +} + +TEST_F(MultiDimensionTest, shared) { + bvar::MultiDimension, std::vector, true> my_madder( + "my_adder", {"idc", "method", "status"}); + std::shared_ptr> adder = my_madder.get_stats(g_labels_value); + ASSERT_NE(nullptr, adder); + ASSERT_TRUE(my_madder.has_stats(g_labels_value)); + ASSERT_EQ((size_t)1, my_madder.count_stats()); + + *adder << 1; + ASSERT_EQ(1, adder->get_value()); + my_madder.delete_stats(g_labels_value); + *adder << 1; + ASSERT_EQ(2, adder->get_value()); + ASSERT_FALSE(my_madder.has_stats(g_labels_value)); + + const int get_num = 8; + std::vector get_threads(get_num); + for (int i = 0; i < get_num; ++i) { + ASSERT_EQ(0, pthread_create(&get_threads[i], NULL, get_shared_adder_thread, &my_madder)); + } + pthread_t delete_thread; + ASSERT_EQ(0, pthread_create(&delete_thread, NULL, delete_shared_adder_thread, &my_madder)); + usleep(100 * 1000); // 100ms + g_shared_stop = true; + for (int i = 0; i < get_num; ++i) { + ASSERT_EQ(0, pthread_join(get_threads[i], NULL)); + } + ASSERT_EQ(0, pthread_join(delete_thread, NULL)); +} + diff --git a/test/bvar_mvariable_unittest.cpp b/test/bvar_mvariable_unittest.cpp index d72a2e5d09..aca50d5f69 100644 --- a/test/bvar_mvariable_unittest.cpp +++ b/test/bvar_mvariable_unittest.cpp @@ -117,118 +117,6 @@ TEST_F(MVariableTest, expose) { ASSERT_EQ(2, exposed_vars.size()); } -class MyStringView { -public: - MyStringView() : _ptr(NULL), _len(0) {} - MyStringView(const char* str) - : _ptr(str), - _len(str == NULL ? 0 : strlen(str)) {} -#if __cplusplus >= 201703L - MyStringView(const std::string_view& str) - : _ptr(str.data()), _len(str.size()) {} -#endif // __cplusplus >= 201703L - MyStringView(const std::string& str) - : _ptr(str.data()), _len(str.size()) {} - MyStringView(const char* offset, size_t len) - : _ptr(offset), _len(len) {} - - const char* data() const { return _ptr; } - size_t size() const { return _len; } - - // Converts to `std::basic_string`. - explicit operator std::string() const { - if (NULL == _ptr) { - return {}; - } - return {_ptr, size()}; - } - - // Converts to butil::StringPiece. - explicit operator butil::StringPiece() const { - if (NULL == _ptr) { - return {}; - } - return {_ptr, size()}; - } - -private: - const char* _ptr; - size_t _len; -}; - -bool operator==(const MyStringView& x, const std::string& y) { - if (x.size() != y.size()) { - return false; - } - - return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; -} - -bool operator==(const std::string& x, const MyStringView& y) { - if (x.size() != y.size()) { - return false; - } - - return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; -} - -static int g_exposed_count = 0; - -template -static void TestLabels() { - std::string mbvar_name = butil::string_printf("my_madder_%d", g_exposed_count); - KeyType labels{"idc", "method", "status"}; - bvar::MultiDimension, KeyType> my_madder(mbvar_name, labels); - ASSERT_EQ(labels.size(), my_madder.count_labels()); - ASSERT_STREQ(mbvar_name.c_str(), my_madder.name().c_str()); - ASSERT_EQ(labels, my_madder.labels()); - - using ItemType = typename ValueType::value_type; - ValueType labels_value{ItemType("cv"), ItemType("post"), ItemType("200")}; - bvar::Adder* adder = my_madder.get_stats(labels_value); - ASSERT_NE(nullptr, adder); - ASSERT_TRUE(my_madder.has_stats(labels_value)); - ASSERT_EQ((size_t)1, my_madder.count_stats()); - { - // Compatible with old API. - bvar::Adder* temp = my_madder.get_stats({"cv", "post", "200"}); - ASSERT_EQ(adder, temp); - } - *adder << g_exposed_count; - ASSERT_EQ(g_exposed_count, adder->get_value()); - my_madder.delete_stats(labels_value); - ASSERT_FALSE(my_madder.has_stats(labels_value)); - ASSERT_EQ((size_t)0, my_madder.count_stats()); -} - -TEST_F(MVariableTest, labels) { - TestLabels, std::list>(); - TestLabels, std::vector>(); - TestLabels, std::array>(); - - TestLabels, std::list>(); - TestLabels, std::vector>(); - TestLabels, std::array>(); - -#if __cplusplus >= 201703L - TestLabels, std::list>(); - TestLabels, std::vector>(); - TestLabels, std::array>(); -#endif // __cplusplus >= 201703L - - TestLabels, std::list>(); - TestLabels, std::vector>(); - TestLabels, std::array>(); - - TestLabels, std::list>(); - TestLabels, std::vector>(); - TestLabels, std::array>(); - - TestLabels, std::list>(); - TestLabels, std::vector>(); - TestLabels, std::array>(); -} - TEST_F(MVariableTest, dump) { std::string old_bvar_dump_interval; std::string old_mbvar_dump; @@ -302,7 +190,6 @@ TEST_F(MVariableTest, dump) { } TEST_F(MVariableTest, test_describe_exposed) { - std::list labels_value1 {"bj", "get", "200"}; std::string bvar_name("request_count_describe"); bvar::MultiDimension > my_madder1(bvar_name, labels);