diff --git a/include/envoy/stats/stats.h b/include/envoy/stats/stats.h index 7f5114fb1b4a5..4c48b2e88bb06 100644 --- a/include/envoy/stats/stats.h +++ b/include/envoy/stats/stats.h @@ -39,6 +39,9 @@ class Metric { * Indicates whether this metric has been updated since the server was started. */ virtual bool used() const PURE; + + virtual uint64_t hash() const PURE; + virtual bool operator==(const Metric& rhs) const PURE; }; /** diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 8764402069e97..d4d00705bef20 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -18,6 +18,8 @@ class StatName { public: virtual ~StatName(){}; virtual std::string toString() const PURE; + virtual uint64_t hash() const PURE; + virtual bool operator==(const StatName& rhs) const PURE; }; using StatNamePtr = std::unique_ptr; diff --git a/source/common/common/hash.h b/source/common/common/hash.h index 67c68a2bb85af..9a14edc5ffbc8 100644 --- a/source/common/common/hash.h +++ b/source/common/common/hash.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "absl/strings/ascii.h" #include "absl/strings/string_view.h" @@ -35,6 +36,15 @@ class HashUtil { }; return hash; } + + // https://stackoverflow.com/a/27216842 + static uint64_t hashVector(std::vector const& vec) { + std::size_t seed = vec.size(); + for (auto& i : vec) { + seed ^= i + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + return seed; + } }; } // namespace Envoy diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index da193a341d6ca..0fe2422138881 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -14,6 +14,7 @@ envoy_cc_library( hdrs = ["heap_stat_data.h"], deps = [ ":stat_data_allocator_lib", + ":symbol_table_lib", "//source/common/common:assert_lib", "//source/common/common:hash_lib", "//source/common/common:thread_annotations", @@ -125,6 +126,9 @@ envoy_cc_library( deps = [ "//include/envoy/stats:symbol_table_interface", "//source/common/common:assert_lib", + "//source/common/common:hash_lib", + "//source/common/common:lock_guard_lib", + "//source/common/common:thread_lib", "//source/common/common:utility_lib", ], ) diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index 7a31fa71396d9..64d9734c73e79 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -6,7 +6,7 @@ namespace Envoy { namespace Stats { -HeapStatData::HeapStatData(absl::string_view key) : name_(key.data(), key.size()) {} +HeapStatData::HeapStatData(StatNamePtr name_ptr) : name_ptr_(std::move(name_ptr)) {} HeapStatDataAllocator::HeapStatDataAllocator() {} @@ -15,8 +15,8 @@ HeapStatDataAllocator::~HeapStatDataAllocator() { ASSERT(stats_.empty()); } HeapStatData* HeapStatDataAllocator::alloc(absl::string_view name) { // Any expected truncation of name is done at the callsite. No truncation is // required to use this allocator. - auto data = std::make_unique(name); Thread::ReleasableLockGuard lock(mutex_); + auto data = std::make_unique(table_.encode(name)); auto ret = stats_.insert(data.get()); HeapStatData* existing_data = *ret.first; lock.release(); diff --git a/source/common/stats/heap_stat_data.h b/source/common/stats/heap_stat_data.h index d87a784673352..a12a6250bac62 100644 --- a/source/common/stats/heap_stat_data.h +++ b/source/common/stats/heap_stat_data.h @@ -5,9 +5,11 @@ #include #include "common/common/hash.h" +#include "common/common/lock_guard.h" #include "common/common/thread.h" #include "common/common/thread_annotations.h" #include "common/stats/stat_data_allocator_impl.h" +#include "common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { @@ -17,18 +19,24 @@ namespace Stats { * so that it can be allocated efficiently from the heap on demand. */ struct HeapStatData { - explicit HeapStatData(absl::string_view key); + explicit HeapStatData(StatNamePtr name_ptr); /** * @returns absl::string_view the name as a string_view. */ - absl::string_view key() const { return name_; } + std::string key() const { return name(); } + std::string name() const { return name_ptr_->toString(); } + + uint64_t hash() const { return name_ptr_->hash(); } + bool operator==(const HeapStatData& rhs) const { + return *(name_ptr_.get()) == *(rhs.name_ptr_.get()); + } std::atomic value_{0}; std::atomic pending_increment_{0}; std::atomic flags_{0}; std::atomic ref_count_{1}; - std::string name_; + StatNamePtr name_ptr_; }; /** @@ -47,20 +55,24 @@ class HeapStatDataAllocator : public StatDataAllocatorImpl { // StatDataAllocator bool requiresBoundedStatNameSize() const override { return false; } + // SymbolTable + StatNamePtr encode(absl::string_view sv) { + Thread::LockGuard lock(mutex_); + return table_.encode(sv); + } + private: - struct HeapStatHash_ { - size_t operator()(const HeapStatData* a) const { return HashUtil::xxHash64(a->key()); } + struct HeapStatDataHash_ { + size_t operator()(const HeapStatData* a) const { return a->hash(); } }; - struct HeapStatCompare_ { - bool operator()(const HeapStatData* a, const HeapStatData* b) const { - return (a->key() == b->key()); - } + struct HeapStatDataCompare_ { + bool operator()(const HeapStatData* a, const HeapStatData* b) const { return (*a == *b); } }; // TODO(jmarantz): See https://github.com/envoyproxy/envoy/pull/3927 and // https://github.com/envoyproxy/envoy/issues/3585, which can help reorganize // the heap stats using a ref-counted symbol table to compress the stat strings. - typedef std::unordered_set StatSet; + typedef std::unordered_set StatSet; // An unordered set of HeapStatData pointers which keys off the key() // field in each object. This necessitates a custom comparator and hasher. @@ -69,6 +81,8 @@ class HeapStatDataAllocator : public StatDataAllocatorImpl { // Although alloc() operations are called under existing locking, free() operations are made from // the destructors of the individual stat objects, which are not protected by locks. Thread::MutexBasicLockable mutex_; + + SymbolTableImpl table_ GUARDED_BY(mutex_); }; } // namespace Stats diff --git a/source/common/stats/histogram_impl.h b/source/common/stats/histogram_impl.h index 28cb1e0931b71..2dc46b4c64d05 100644 --- a/source/common/stats/histogram_impl.h +++ b/source/common/stats/histogram_impl.h @@ -7,6 +7,7 @@ #include "envoy/stats/stats.h" #include "envoy/stats/store.h" +#include "common/common/hash.h" #include "common/common/non_copyable.h" #include "common/stats/metric_impl.h" @@ -53,6 +54,13 @@ class HistogramImpl : public Histogram, public MetricImpl { bool used() const override { return true; } + // Metric + uint64_t hash() const { return HashUtil::xxHash64(name()); } + bool operator==(const Metric& rhs) const { + const HistogramImpl& r = dynamic_cast(rhs); + return (name() == r.name()); + } + private: // This is used for delivering the histogram data to sinks. Store& parent_; diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index d85d38ef03213..1a743f1efbc53 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -13,19 +13,26 @@ namespace Envoy { namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() - : counters_([this](const std::string& name) -> CounterSharedPtr { - std::string tag_extracted_name = name; - std::vector tags; - return alloc_.makeCounter(name, std::move(tag_extracted_name), std::move(tags)); - }), - gauges_([this](const std::string& name) -> GaugeSharedPtr { - std::string tag_extracted_name = name; - std::vector tags; - return alloc_.makeGauge(name, std::move(tag_extracted_name), std::move(tags)); - }), - histograms_([this](const std::string& name) -> HistogramSharedPtr { - return std::make_shared(name, *this, std::string(name), std::vector()); - }) {} + : counters_( + [this](const std::string& name) -> CounterSharedPtr { + std::string tag_extracted_name = name; + std::vector tags; + return alloc_.makeCounter(name, std::move(tag_extracted_name), std::move(tags)); + }, + alloc_), + gauges_( + [this](const std::string& name) -> GaugeSharedPtr { + std::string tag_extracted_name = name; + std::vector tags; + return alloc_.makeGauge(name, std::move(tag_extracted_name), std::move(tags)); + }, + alloc_), + histograms_( + [this](const std::string& name) -> HistogramSharedPtr { + return std::make_shared(name, *this, std::string(name), + std::vector()); + }, + alloc_) {} struct IsolatedScopeImpl : public Scope { IsolatedScopeImpl(IsolatedStoreImpl& parent, const std::string& prefix) diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 7765fd50e3e72..ffcef17197c9d 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -8,10 +8,12 @@ #include "envoy/stats/stats.h" #include "envoy/stats/stats_options.h" #include "envoy/stats/store.h" +#include "envoy/stats/symbol_table.h" #include "common/common/utility.h" #include "common/stats/heap_stat_data.h" #include "common/stats/stats_options_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/utility.h" namespace Envoy { @@ -24,16 +26,18 @@ template class IsolatedStatsCache { public: typedef std::function(const std::string& name)> Allocator; - IsolatedStatsCache(Allocator alloc) : alloc_(alloc) {} + IsolatedStatsCache(Allocator alloc, HeapStatDataAllocator& heap_alloc) + : alloc_(alloc), heap_alloc_(heap_alloc) {} Base& get(const std::string& name) { - auto stat = stats_.find(name); + StatNamePtr ptr = heap_alloc_.encode(name); + auto stat = stats_.find(ptr); if (stat != stats_.end()) { return *stat->second; } std::shared_ptr new_stat = alloc_(name); - stats_.emplace(name, new_stat); + stats_.emplace(std::move(ptr), new_stat); return *new_stat; } @@ -48,8 +52,10 @@ template class IsolatedStatsCache { } private: - std::unordered_map> stats_; + std::unordered_map, StatNamePtrHash_, StatNamePtrCompare_> + stats_; Allocator alloc_; + HeapStatDataAllocator& heap_alloc_; }; class IsolatedStoreImpl : public Store { diff --git a/source/common/stats/raw_stat_data.h b/source/common/stats/raw_stat_data.h index da000fb277753..72a7b28cbc620 100644 --- a/source/common/stats/raw_stat_data.h +++ b/source/common/stats/raw_stat_data.h @@ -63,6 +63,11 @@ struct RawStatData { * Returns a hash of the key. This is required by BlockMemoryHashSet. */ static uint64_t hash(absl::string_view key) { return HashUtil::xxHash64(key); } + uint64_t hash() { return HashUtil::xxHash64(absl::string_view(name_)); } + + bool operator==(const RawStatData& rhs) const { + return (name_ == rhs.name_); + } /** * Returns true if object is in use. @@ -73,6 +78,7 @@ struct RawStatData { * Returns the name as a string_view with no truncation. */ absl::string_view key() const { return absl::string_view(name_); } + std::string name() const { return name_; } std::atomic value_; std::atomic pending_increment_; diff --git a/source/common/stats/stat_data_allocator_impl.h b/source/common/stats/stat_data_allocator_impl.h index 7d4a96154a123..8bdff2bf24c82 100644 --- a/source/common/stats/stat_data_allocator_impl.h +++ b/source/common/stats/stat_data_allocator_impl.h @@ -63,7 +63,7 @@ template class CounterImpl : public Counter, public MetricImpl public: CounterImpl(StatData& data, StatDataAllocatorImpl& alloc, std::string&& tag_extracted_name, std::vector&& tags) - : MetricImpl(data.name_, std::move(tag_extracted_name), std::move(tags)), data_(data), + : MetricImpl(data.name(), std::move(tag_extracted_name), std::move(tags)), data_(data), alloc_(alloc) {} ~CounterImpl() { alloc_.free(data_); } @@ -80,11 +80,41 @@ template class CounterImpl : public Counter, public MetricImpl bool used() const override { return data_.flags_ & Flags::Used; } uint64_t value() const override { return data_.value_; } + uint64_t hash() const { return data_.hash(); } + bool operator==(const Metric& rhs) const { + const CounterImpl& r = dynamic_cast(rhs); + return (data_ == r.data_); + } + private: StatData& data_; StatDataAllocatorImpl& alloc_; }; +struct MetricSharedPtrHash_ { + uint64_t operator()(const std::shared_ptr a) const { + return a.get()->hash(); + } +}; + +struct MetricSharedPtrCompare_ { + bool operator()(const std::shared_ptr a, const std::shared_ptr b) const { + return (*a.get() == *b.get()); + } +}; + +////struct CounterSharedPtrHash_ { +//// uint64_t operator()(const CounterSharedPtr a) const { +//// return a.get()->hash(); +//// } +////}; +//// +////struct CounterSharedPtrCompare_ { +//// bool operator()(const CounterSharedPtr a, const CounterSharedPtr b) const { +//// return (*a.get() == *b.get()); +//// } +////}; + /** * Gauge implementation that wraps a StatData. */ @@ -92,7 +122,7 @@ template class GaugeImpl : public Gauge, public MetricImpl { public: GaugeImpl(StatData& data, StatDataAllocatorImpl& alloc, std::string&& tag_extracted_name, std::vector&& tags) - : MetricImpl(data.name_, std::move(tag_extracted_name), std::move(tags)), data_(data), + : MetricImpl(data.name(), std::move(tag_extracted_name), std::move(tags)), data_(data), alloc_(alloc) {} ~GaugeImpl() { alloc_.free(data_); } @@ -115,11 +145,29 @@ template class GaugeImpl : public Gauge, public MetricImpl { virtual uint64_t value() const override { return data_.value_; } bool used() const override { return data_.flags_ & Flags::Used; } + uint64_t hash() const { return data_.hash(); } + bool operator==(const Metric& rhs) const { + const GaugeImpl& r = dynamic_cast(rhs); + return (data_ == r.data_); + } + private: StatData& data_; StatDataAllocatorImpl& alloc_; }; +////struct GaugeSharedPtrHash_ { +//// uint64_t operator()(const GaugeSharedPtr a) const { +//// return a.get()->hash(); +//// } +////}; +//// +////struct GaugeSharedPtrCompare_ { +//// bool operator()(const GaugeSharedPtr a, const GaugeSharedPtr b) const { +//// return (*a.get() == *b.get()); +//// } +////}; + template CounterSharedPtr StatDataAllocatorImpl::makeCounter(absl::string_view name, std::string&& tag_extracted_name, diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 2ee7269d2cb15..01c7f1ebf8dbd 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -31,6 +31,7 @@ std::string SymbolTableImpl::decode(const SymbolVec& symbol_vec) const { } void SymbolTableImpl::free(const SymbolVec& symbol_vec) { + Thread::LockGuard lock(lock_); for (const Symbol symbol : symbol_vec) { auto decode_search = decode_map_.find(symbol); ASSERT(decode_search != decode_map_.end()); diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index ab9ea86d38a97..c3f7a41efcb12 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -11,6 +11,9 @@ #include "envoy/stats/symbol_table.h" #include "common/common/assert.h" +#include "common/common/hash.h" +#include "common/common/lock_guard.h" +#include "common/common/thread.h" #include "common/common/utility.h" #include "absl/strings/str_join.h" @@ -115,6 +118,8 @@ class SymbolTableImpl : public SymbolTable { // TODO(ambuc): There might be an optimization here relating to storing ranges of freed symbols // using an Envoy::IntervalSet. std::stack pool_; + + mutable Thread::MutexBasicLockable lock_; // This must be called during free() }; /** @@ -127,6 +132,11 @@ class StatNameImpl : public StatName { : symbol_vec_(symbol_vec), symbol_table_(symbol_table) {} ~StatNameImpl() override { symbol_table_.free(symbol_vec_); } std::string toString() const override { return symbol_table_.decode(symbol_vec_); } + uint64_t hash() const override { return HashUtil::hashVector(symbol_vec_); } + bool operator==(const StatName& rhs) const override { + const StatNameImpl& r = dynamic_cast(rhs); + return symbol_vec_ == r.symbol_vec_; + } private: friend class StatNameTest; @@ -135,5 +145,16 @@ class StatNameImpl : public StatName { SymbolTableImpl& symbol_table_; }; +struct StatNamePtrHash_ { + size_t operator()(const StatNamePtr& a) const { return a->hash(); } +}; + +struct StatNamePtrCompare_ { + bool operator()(const StatNamePtr& a, const StatNamePtr& b) const { + // We really need to compare the underlying StatNames. + return (*a.get() == *b.get()); + } +}; + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index de98b3aaff3c0..0ba8ff869dce6 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -38,10 +38,17 @@ std::vector ThreadLocalStoreImpl::counters() const { std::vector ret; std::unordered_set names; Thread::LockGuard lock(lock_); + ////for (ScopeImpl* scope : scopes_) { + //// for (auto& counter : scope->central_cache_.counters_) { + //// if (names.insert(counter.first->toString()).second) { + //// ret.push_back(counter.second); + //// } + //// } + ////} for (ScopeImpl* scope : scopes_) { - for (auto& counter : scope->central_cache_.counters_) { - if (names.insert(counter.first).second) { - ret.push_back(counter.second); + for (auto counter_shared_ptr: scope->central_cache_.counters_) { + if (names.insert(counter_shared_ptr.get()->name()).second) { + ret.push_back(counter_shared_ptr); } } } @@ -61,10 +68,17 @@ std::vector ThreadLocalStoreImpl::gauges() const { std::vector ret; std::unordered_set names; Thread::LockGuard lock(lock_); + ////for (ScopeImpl* scope : scopes_) { + //// for (auto& gauge : scope->central_cache_.gauges_) { + //// if (names.insert(gauge.first->toString()).second) { + //// ret.push_back(gauge.second); + //// } + //// } + ////} for (ScopeImpl* scope : scopes_) { - for (auto& gauge : scope->central_cache_.gauges_) { - if (names.insert(gauge.first).second) { - ret.push_back(gauge.second); + for (auto gauge_shared_ptr : scope->central_cache_.gauges_) { + if (names.insert(gauge_shared_ptr.get()->name()).second) { + ret.push_back(gauge_shared_ptr); } } } @@ -190,7 +204,8 @@ ThreadLocalStoreImpl::ScopeImpl::~ScopeImpl() { parent_.releaseScopeCrossThread( template StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( const std::string& name, - std::unordered_map>& central_cache_map, + //std::unordered_map, StatNamePtrHash_, StatNamePtrCompare_>& central_cache_map, + std::unordered_set, MetricSharedPtrHash_, MetricSharedPtrCompare_>& central_cache_set, MakeStatFn make_stat, std::shared_ptr* tls_ref) { // If we have a valid cache entry, return it. @@ -201,7 +216,7 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( // We must now look in the central store so we must be locked. We grab a reference to the // central store location. It might contain nothing. In this case, we allocate a new stat. Thread::LockGuard lock(parent_.lock_); - std::shared_ptr& central_ref = central_cache_map[name]; + std::shared_ptr& central_ref = central_cache_set[std::move(parent_.heap_allocator_.encode(name))]; if (!central_ref) { std::vector tags; @@ -238,8 +253,7 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counter(const std::string& name) { // is no cache entry. CounterSharedPtr* tls_ref = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { - tls_ref = - &parent_.tls_->getTyped().scope_cache_[this->scope_id_].counters_[final_name]; + tls_ref = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].counters_[std::move(parent_.heap_allocator_.encode(final_name))]; } return safeMakeStat( @@ -273,7 +287,7 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gauge(const std::string& name) { std::string final_name = prefix_ + name; GaugeSharedPtr* tls_ref = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { - tls_ref = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].gauges_[final_name]; + tls_ref = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].gauges_[std::move(parent_.heap_allocator_.encode(final_name))]; } return safeMakeStat( @@ -292,9 +306,7 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogram(const std::string& name) { ParentHistogramSharedPtr* tls_ref = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { - tls_ref = &parent_.tls_->getTyped() - .scope_cache_[this->scope_id_] - .parent_histograms_[final_name]; + tls_ref = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].parent_histograms_[std::move(parent_.heap_allocator_.encode(final_name))]; } if (tls_ref && *tls_ref) { @@ -302,7 +314,9 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogram(const std::string& name) { } Thread::LockGuard lock(parent_.lock_); - ParentHistogramImplSharedPtr& central_ref = central_cache_.histograms_[final_name]; + // // ParentHistogramImplSharedPtr& central_ref = central_cache_.histograms_[final_name]; + ParentHistogramImplSharedPtr& central_ref = + central_cache_.histograms_[std::move(parent_.heap_allocator_.encode(final_name))]; if (!central_ref) { std::vector tags; std::string tag_extracted_name = parent_.getTagsForName(final_name, tags); @@ -324,7 +338,7 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(const std::string& name // during recordValue, the prefix is already attached to the name. TlsHistogramSharedPtr* tls_ref = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { - tls_ref = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].histograms_[name]; + tls_ref = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].histograms_[parent_.heap_allocator_.encode(name)]; } if (tls_ref && *tls_ref) { diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index ab464672a1744..5b3cf13663dc4 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -8,11 +8,13 @@ #include #include +#include "envoy/stats/symbol_table.h" #include "envoy/thread_local/thread_local.h" #include "common/stats/heap_stat_data.h" #include "common/stats/histogram_impl.h" #include "common/stats/source_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/utility.h" #include "circllhist.h" @@ -47,6 +49,13 @@ class ThreadLocalHistogramImpl : public Histogram, public MetricImpl { void recordValue(uint64_t value) override; bool used() const override { return flags_ & Flags::Used; } + // Metric + uint64_t hash() const { return HashUtil::xxHash64(name()); } + bool operator==(const Metric& rhs) const { + const ThreadLocalHistogramImpl& r = dynamic_cast(rhs); + return (name() == r.name()); + } + private: uint64_t otherHistogramIndex() const { return 1 - current_active_; } uint64_t current_active_; @@ -86,6 +95,13 @@ class ParentHistogramImpl : public ParentHistogram, public MetricImpl { } const std::string summary() const override; + // Metric + uint64_t hash() const { return HashUtil::xxHash64(name()); } + bool operator==(const Metric& rhs) const { + const ParentHistogramImpl& r = dynamic_cast(rhs); + return (name() == r.name()); + } + private: bool usedLockHeld() const EXCLUSIVE_LOCKS_REQUIRED(merge_lock_); @@ -204,18 +220,21 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo private: struct TlsCacheEntry { - std::unordered_map counters_; - std::unordered_map gauges_; - std::unordered_map histograms_; - std::unordered_map parent_histograms_; + std::unordered_map counters_; + std::unordered_map gauges_; + std::unordered_map histograms_; + std::unordered_map parent_histograms_; }; struct CentralCacheEntry { - std::unordered_map counters_; - std::unordered_map gauges_; - std::unordered_map histograms_; + //std::unordered_map counters_; + //std::unordered_map gauges_; + std::unordered_set counters_; + std::unordered_set gauges_; + std::unordered_map histograms_; }; + struct ScopeImpl : public TlsScope { ScopeImpl(ThreadLocalStoreImpl& parent, const std::string& prefix) : scope_id_(next_scope_id_++), parent_(parent), @@ -245,7 +264,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo * result, creating it with the heap allocator. * * @param name the full name of the stat (not tag extracted). - * @param central_cache_map a map from name to the desired object in the central cache. + * @param central_cache_set a map from name to the desired object in the central cache. * @param make_stat a function to generate the stat object, called if it's not in cache. * @param tls_ref possibly null reference to a cache entry for this stat, which will be * used if non-empty, or filled in if empty (and non-null). @@ -253,7 +272,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo template StatType& safeMakeStat(const std::string& name, - std::unordered_map>& central_cache_map, + //std::unordered_map, StatNamePtrHash_, StatNamePtrCompare_>& central_cache_map, + std::unordered_set, MetricSharedPtrHash_, MetricSharedPtrCompare_>& central_cache_map, MakeStatFn make_stat, std::shared_ptr* tls_ref); static std::atomic next_scope_id_; @@ -284,8 +304,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo const Stats::StatsOptions& stats_options_; StatDataAllocator& alloc_; Event::Dispatcher* main_thread_dispatcher_{}; - ThreadLocal::SlotPtr tls_; mutable Thread::MutexBasicLockable lock_; + HeapStatDataAllocator heap_allocator_ GUARDED_BY(lock_); + ThreadLocal::SlotPtr tls_; std::unordered_set scopes_ GUARDED_BY(lock_); ScopePtr default_scope_; std::list> timer_sinks_; @@ -293,7 +314,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo std::atomic shutting_down_{}; std::atomic merge_in_progress_{}; Counter& num_last_resort_stats_; - HeapStatDataAllocator heap_allocator_; SourceImpl source_; }; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 9ec20c8dec6c0..ff1344c300d4b 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -3,12 +3,12 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::_; using testing::Invoke; using testing::NiceMock; using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; -using testing::_; namespace Envoy { namespace Stats { @@ -20,6 +20,8 @@ MockCounter::MockCounter() { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); + ON_CALL(*this, hash()).WillByDefault(Return(HashUtil::xxHash64(name_))); + // ON_CALL(*this, operator_eq(const MockCounter& rhs)).WillByDefault(Return(*this == rhs)); } MockCounter::~MockCounter() {} @@ -29,6 +31,8 @@ MockGauge::MockGauge() { ON_CALL(*this, tags()).WillByDefault(ReturnRef(tags_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); + ON_CALL(*this, hash()).WillByDefault(Return(HashUtil::xxHash64(name_))); + // ON_CALL(*this, operator_eq(const MockCounter& rhs)).WillByDefault(Return(*this == rhs)); } MockGauge::~MockGauge() {} @@ -40,6 +44,8 @@ MockHistogram::MockHistogram() { })); ON_CALL(*this, tagExtractedName()).WillByDefault(ReturnRef(name_)); ON_CALL(*this, tags()).WillByDefault(ReturnRef(tags_)); + ON_CALL(*this, hash()).WillByDefault(Return(HashUtil::xxHash64(name_))); + // ON_CALL(*this, operator_eq(const MockHistogram& rhs)).WillByDefault(Return(*this == rhs)); } MockHistogram::~MockHistogram() {} @@ -55,6 +61,8 @@ MockParentHistogram::MockParentHistogram() { ON_CALL(*this, intervalStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); + ON_CALL(*this, hash()).WillByDefault(Return(HashUtil::xxHash64(name_))); + // ON_CALL(*this, operator_eq(const MockParentHistogram& rhs)).WillByDefault(Return(*this == rhs)); } MockParentHistogram::~MockParentHistogram() {} diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index af3f073db03e4..f1f95d42ba152 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -36,6 +36,13 @@ class MockCounter : public Counter { MOCK_METHOD0(reset, void()); MOCK_CONST_METHOD0(used, bool()); MOCK_CONST_METHOD0(value, uint64_t()); + MOCK_CONST_METHOD0(hash, uint64_t()); + MOCK_CONST_METHOD1(operator_eq, bool(const MockCounter& rhs)); + + bool operator==(const Metric& rhs) const override { + const MockCounter& r = dynamic_cast(rhs); + return operator_eq(r); + } bool used_; uint64_t value_; @@ -59,6 +66,13 @@ class MockGauge : public Gauge { MOCK_METHOD1(sub, void(uint64_t amount)); MOCK_CONST_METHOD0(used, bool()); MOCK_CONST_METHOD0(value, uint64_t()); + MOCK_CONST_METHOD0(hash, uint64_t()); + MOCK_CONST_METHOD1(operator_eq, bool(const MockGauge& rhs)); + + bool operator==(const Metric& rhs) const override { + const MockGauge& r = dynamic_cast(rhs); + return operator_eq(r); + } bool used_; uint64_t value_; @@ -79,6 +93,13 @@ class MockHistogram : public Histogram { MOCK_CONST_METHOD0(tags, const std::vector&()); MOCK_METHOD1(recordValue, void(uint64_t value)); MOCK_CONST_METHOD0(used, bool()); + MOCK_CONST_METHOD0(hash, uint64_t()); + MOCK_CONST_METHOD1(operator_eq, bool(const MockHistogram& rhs)); + + bool operator==(const Metric& rhs) const override { + const MockHistogram& r = dynamic_cast(rhs); + return operator_eq(r); + } std::string name_; std::vector tags_; @@ -102,6 +123,13 @@ class MockParentHistogram : public ParentHistogram { MOCK_METHOD1(recordValue, void(uint64_t value)); MOCK_CONST_METHOD0(cumulativeStatistics, const HistogramStatistics&()); MOCK_CONST_METHOD0(intervalStatistics, const HistogramStatistics&()); + MOCK_CONST_METHOD0(hash, uint64_t()); + MOCK_CONST_METHOD1(operator_eq, bool(const MockParentHistogram& rhs)); + + bool operator==(const Metric& rhs) const override { + const MockParentHistogram& r = dynamic_cast(rhs); + return operator_eq(r); + } std::string name_; std::vector tags_;