From f871f2b0e97f3fea58158dcdb846cb865deedcbd Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 1 Sep 2019 17:34:57 -0400 Subject: [PATCH 01/50] initial impl. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.h | 59 ++++++++++++++++++ test/common/stats/recent_lookups_test.cc | 79 ++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 source/common/stats/recent_lookups.h create mode 100644 test/common/stats/recent_lookups_test.cc diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h new file mode 100644 index 000000000000..b4786e6974f4 --- /dev/null +++ b/source/common/stats/recent_lookups.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include "envoy/common/time.h" + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" + +namespace Envoy { +namespace Stats { + +// Remembers the last 'Capacity' items passed to lookup(). +template class RecentLookups { +public: + explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} + + // Records a lookup of an object of type T. Only the last 'Capacity' lookups + // are remembered. + void lookup(T t) { + if (queue_.size() >= Capacity) { + queue_.pop_back(); + } + queue_.push_front(ItemTime(t, time_source_.systemTime())); + } + + using IterFn = std::function; + + // Calls fn(item, timestmp, count) for each of the remembered + // lookups. Duplicates are collated and provided with their timestamp and + // count. + void forEach(IterFn fn) { + using TimeCount = std::pair; + absl::flat_hash_map entry_map; + for (ItemTime& item_time : queue_) { + TimeCount& time_count = entry_map[item_time.first]; + if ((time_count.second == 0) || (item_time.second > time_count.first)) { + time_count.first = item_time.second; + } + ++time_count.second; + } + for (auto& item_time_count : entry_map) { + TimeCount& time_count = item_time_count.second; + fn(item_time_count.first, time_count.first, time_count.second); + } + } + + void clear() { queue_.clear(); } + +private: + using ItemTime = std::pair; + std::deque queue_; + TimeSource& time_source_; +}; + +} // namespace Stats +} // namespace Envoy diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc new file mode 100644 index 000000000000..a5aebba5ddcf --- /dev/null +++ b/test/common/stats/recent_lookups_test.cc @@ -0,0 +1,79 @@ +#include +#include + +#include "common/common/utility.h" +#include "common/stats/recent_lookups.h" + +#include "test/test_common/simulated_time_system.h" + +#include "absl/strings/str_cat.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Stats { +namespace { + +class RecentLookupsTest : public testing::Test { +protected: + RecentLookupsTest() : recent_lookups_(time_system_) { + const uint64_t years = 365 * 24 * 3600; + time_system_.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); + } + + std::string joinLookups() { + std::vector accum; + recent_lookups_.forEach([&accum](std::string item, SystemTime time, size_t count) { + DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); + accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", item, ";Count=", count)); + }); + std::sort(accum.begin(), accum.end()); + return StringUtil::join(accum, " "); + } + + Event::SimulatedTimeSystem time_system_; + RecentLookups recent_lookups_; +}; + +TEST_F(RecentLookupsTest, Empty) { EXPECT_EQ("", joinLookups()); } + +TEST_F(RecentLookupsTest, One) { + recent_lookups_.lookup("Hello"); + EXPECT_EQ("2009-12-22,00:00:00;Item=Hello;Count=1", joinLookups()); +} + +TEST_F(RecentLookupsTest, DropOne) { + for (int i = 0; i < 11; ++i) { + recent_lookups_.lookup(absl::StrCat("lookup", i)); + time_system_.sleep(std::chrono::seconds(1)); + } + EXPECT_EQ("2009-12-22,00:00:01;Item=lookup1;Count=1 " + "2009-12-22,00:00:02;Item=lookup2;Count=1 " + "2009-12-22,00:00:03;Item=lookup3;Count=1 " + "2009-12-22,00:00:04;Item=lookup4;Count=1 " + "2009-12-22,00:00:05;Item=lookup5;Count=1 " + "2009-12-22,00:00:06;Item=lookup6;Count=1 " + "2009-12-22,00:00:07;Item=lookup7;Count=1 " + "2009-12-22,00:00:08;Item=lookup8;Count=1 " + "2009-12-22,00:00:09;Item=lookup9;Count=1 " + "2009-12-22,00:00:10;Item=lookup10;Count=1", + joinLookups()); +} + +TEST_F(RecentLookupsTest, RepeatDrop) { + for (int i = 0; i < 11; ++i) { + recent_lookups_.lookup(absl::StrCat("lookup", i)); + time_system_.sleep(std::chrono::seconds(1)); + recent_lookups_.lookup(absl::StrCat("lookup", i)); + time_system_.sleep(std::chrono::seconds(1)); + } + EXPECT_EQ("2009-12-22,00:00:13;Item=lookup6;Count=2 " + "2009-12-22,00:00:15;Item=lookup7;Count=2 " + "2009-12-22,00:00:17;Item=lookup8;Count=2 " + "2009-12-22,00:00:19;Item=lookup9;Count=2 " + "2009-12-22,00:00:21;Item=lookup10;Count=2", + joinLookups()); +} + +} // namespace +} // namespace Stats +} // namespace Envoy From be02163873649de51a6329be82e270258570f986 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 1 Sep 2019 17:51:34 -0400 Subject: [PATCH 02/50] Add optional recent-lookups tracking to SymbolTable. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 3 ++ source/common/stats/BUILD | 7 +++ source/common/stats/fake_symbol_table_impl.h | 2 + source/common/stats/recent_lookups.h | 6 +++ source/common/stats/symbol_table_impl.cc | 46 +++++++++++++------- source/common/stats/symbol_table_impl.h | 8 ++++ test/common/stats/BUILD | 10 +++++ 7 files changed, 67 insertions(+), 15 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index a4346d69397b..6ec228a74a33 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -5,6 +5,7 @@ #include #include "envoy/common/pure.h" +#include "envoy/common/time.h" #include "absl/strings/string_view.h" @@ -143,6 +144,8 @@ class SymbolTable { virtual void callWithStringView(StatName stat_name, const std::function& fn) const PURE; + virtual void trackRecentLookups(TimeSource&) PURE; + private: friend struct HeapStatData; friend class StatNameStorage; diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index ed5efa052b47..9f8c12b8412c 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -87,6 +87,12 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "recent_lookups_lib", + hdrs = ["recent_lookups.h"], + deps = ["//include/envoy/common:time_interface"], +) + envoy_cc_library( name = "store_impl_lib", hdrs = ["store_impl.h"], @@ -147,6 +153,7 @@ envoy_cc_library( hdrs = ["symbol_table_impl.h"], external_deps = ["abseil_base"], deps = [ + ":recent_lookups_lib", "//include/envoy/stats:symbol_table_interface", "//source/common/common:assert_lib", "//source/common/common:logger_lib", diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 096cb42d4d72..1fdad4601464 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -125,6 +125,8 @@ class FakeSymbolTableImpl : public SymbolTable { fn(toStringView(stat_name)); } + void trackRecentLookups(TimeSource&) override {} + private: absl::string_view toStringView(const StatName& stat_name) const { return {reinterpret_cast(stat_name.data()), diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index b4786e6974f4..2cdbe6585066 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -21,11 +21,15 @@ template class RecentLookups { // are remembered. void lookup(T t) { if (queue_.size() >= Capacity) { + if (free_fn_) { + free_fn_(queue_.back().first); + } queue_.pop_back(); } queue_.push_front(ItemTime(t, time_source_.systemTime())); } + using FreeFn = std::function; using IterFn = std::function; // Calls fn(item, timestmp, count) for each of the remembered @@ -48,11 +52,13 @@ template class RecentLookups { } void clear() { queue_.clear(); } + void setFreeFn(const FreeFn& free_fn) { free_fn_ = free_fn; } private: using ItemTime = std::pair; std::deque queue_; TimeSource& time_source_; + FreeFn free_fn_; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 9a2129eb04f0..3af377842b05 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -187,25 +187,41 @@ void SymbolTableImpl::free(const StatName& stat_name) { Thread::LockGuard lock(lock_); for (Symbol symbol : symbols) { - auto decode_search = decode_map_.find(symbol); - ASSERT(decode_search != decode_map_.end()); + freeSymbolLockHeld(symbol); + } +} - auto encode_search = encode_map_.find(decode_search->second->toStringView()); - ASSERT(encode_search != encode_map_.end()); +void SymbolTableImpl::freeSymbol(Symbol symbol) { + Thread::LockGuard lock(lock_); + freeSymbolLockHeld(symbol); +} - // If that was the last remaining client usage of the symbol, erase the - // current mappings and add the now-unused symbol to the reuse pool. - // - // The "if (--EXPR.ref_count_)" pattern speeds up BM_CreateRace by 20% in - // symbol_table_speed_test.cc, relative to breaking out the decrement into a - // separate step, likely due to the non-trivial dereferences in EXPR. - if (--encode_search->second.ref_count_ == 0) { - decode_map_.erase(decode_search); - encode_map_.erase(encode_search); - pool_.push(symbol); - } +void SymbolTableImpl::freeSymbolLockHeld(Symbol symbol) { + auto decode_search = decode_map_.find(symbol); + ASSERT(decode_search != decode_map_.end()); + + auto encode_search = encode_map_.find(decode_search->second->toStringView()); + ASSERT(encode_search != encode_map_.end()); + + // If that was the last remaining client usage of the symbol, erase the + // current mappings and add the now-unused symbol to the reuse pool. + // + // The "if (--EXPR.ref_count_)" pattern speeds up BM_CreateRace by 20% in + // symbol_table_speed_test.cc, relative to breaking out the decrement into a + // separate step, likely due to the non-trivial dereferences in EXPR. + if (--encode_search->second.ref_count_ == 0) { + decode_map_.erase(decode_search); + encode_map_.erase(encode_search); + pool_.push(symbol); } } + +void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { + Thread::LockGuard lock(lock_); + recent_lookups_ = std::make_unique>(time_source); + recent_lookups_->setFreeFn([this](Symbol symbol) { freeSymbol(symbol); }); +} + Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { Symbol result; auto encode_find = encode_map_.find(sv); diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index dbbb633dcbe8..e1e3fab425ac 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -9,6 +9,7 @@ #include #include "envoy/common/exception.h" +#include "envoy/common/time.h" #include "envoy/stats/symbol_table.h" #include "common/common/assert.h" @@ -18,6 +19,7 @@ #include "common/common/stack_array.h" #include "common/common/thread.h" #include "common/common/utility.h" +#include "common/stats/recent_lookups.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/str_join.h" @@ -159,6 +161,8 @@ class SymbolTableImpl : public SymbolTable { return bytes; } + void trackRecentLookups(TimeSource& timeSource) override; + private: friend class StatName; friend class StatNameTest; @@ -205,6 +209,9 @@ class SymbolTableImpl : public SymbolTable { */ void newSymbol(); + inline void freeSymbol(Symbol symbol); + void freeSymbolLockHeld(Symbol symbol) EXCLUSIVE_LOCKS_REQUIRED(lock_); + /** * Tokenizes name, finds or allocates symbols for each token, and adds them * to encoding. @@ -238,6 +245,7 @@ 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_ GUARDED_BY(lock_); + std::unique_ptr> recent_lookups_ GUARDED_BY(lock_); }; /** diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index ab725c0a2608..64410176aca1 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -39,6 +39,16 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "recent_lookups_test", + srcs = ["recent_lookups_test.cc"], + deps = [ + "//source/common/common:utility_lib", + "//source/common/stats:recent_lookups_lib", + "//test/test_common:simulated_time_system_lib", + ], +) + envoy_cc_test( name = "stat_merger_test", srcs = ["stat_merger_test.cc"], From 7f404a0085d9daedc8b26616ffcdb77559f7857c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 1 Sep 2019 18:04:54 -0400 Subject: [PATCH 03/50] Hook up recent_lookups_ maps into SymbolTable. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 3 +++ source/common/stats/fake_symbol_table_impl.h | 2 ++ source/common/stats/recent_lookups.h | 1 + source/common/stats/symbol_table_impl.cc | 24 +++++++++++++++++++- source/common/stats/symbol_table_impl.h | 9 ++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 6ec228a74a33..c47f6bc02f77 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -23,6 +23,7 @@ class StatName; using StatNameVec = std::vector; class StatNameList; +class StatNameSet; /** * SymbolTable manages a namespace optimized for stat names, exploiting their @@ -145,6 +146,8 @@ class SymbolTable { const std::function& fn) const PURE; virtual void trackRecentLookups(TimeSource&) PURE; + virtual void rememberSet(StatNameSet& stat_name_set) PURE; + virtual void forgetSet(StatNameSet& stat_name_set) PURE; private: friend struct HeapStatData; diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 1fdad4601464..f8023c79ac2a 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -126,6 +126,8 @@ class FakeSymbolTableImpl : public SymbolTable { } void trackRecentLookups(TimeSource&) override {} + void rememberSet(StatNameSet&) override {} + void forgetSet(StatNameSet&) override {} private: absl::string_view toStringView(const StatName& stat_name) const { diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 2cdbe6585066..2d32dd66203b 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -53,6 +53,7 @@ template class RecentLookups { void clear() { queue_.clear(); } void setFreeFn(const FreeFn& free_fn) { free_fn_ = free_fn; } + TimeSource& timeSource() { return time_source_; } private: using ItemTime = std::pair; diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 3af377842b05..ed7b3e233e95 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -222,6 +222,19 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { recent_lookups_->setFreeFn([this](Symbol symbol) { freeSymbol(symbol); }); } +void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { + Thread::LockGuard lock(lock_); + if (recent_lookups_ != nullptr) { + stat_name_set.trackRecentLookups(recent_lookups_->timeSource()); + stat_name_sets_.insert(&stat_name_set); + } +} + +void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { + Thread::LockGuard lock(lock_); + stat_name_sets_.erase(&stat_name_set); +} + Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { Symbol result; auto encode_find = encode_map_.find(sv); @@ -445,10 +458,14 @@ void StatNameList::clear(SymbolTable& symbol_table) { storage_.reset(); } -StatNameSet::StatNameSet(SymbolTable& symbol_table) : pool_(symbol_table) { +StatNameSet::StatNameSet(SymbolTable& symbol_table) + : symbol_table_(symbol_table), pool_(symbol_table) { builtin_stat_names_[""] = StatName(); + symbol_table.rememberSet(*this); } +StatNameSet::~StatNameSet() { symbol_table_.forgetSet(*this); } + void StatNameSet::rememberBuiltin(absl::string_view str) { StatName stat_name; { @@ -475,5 +492,10 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { return stat_name; } +void StatNameSet::trackRecentLookups(TimeSource& time_source) { + absl::MutexLock lock(&mutex_); + recent_lookups_ = std::make_unique>(time_source); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index e1e3fab425ac..0231d57dcf72 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -162,6 +162,8 @@ class SymbolTableImpl : public SymbolTable { } void trackRecentLookups(TimeSource& timeSource) override; + void rememberSet(StatNameSet& stat_name_set) override; + void forgetSet(StatNameSet& stat_name_set) override; private: friend class StatName; @@ -246,6 +248,7 @@ class SymbolTableImpl : public SymbolTable { // using an Envoy::IntervalSet. std::stack pool_ GUARDED_BY(lock_); std::unique_ptr> recent_lookups_ GUARDED_BY(lock_); + absl::flat_hash_set stat_name_sets_ GUARDED_BY(lock_); }; /** @@ -649,6 +652,7 @@ class StatNameStorageSet { class StatNameSet { public: explicit StatNameSet(SymbolTable& symbol_table); + ~StatNameSet(); /** * Adds a string to the builtin map, which is not mutex protected. This map is @@ -679,12 +683,17 @@ class StatNameSet { */ StatName add(absl::string_view str) { return pool_.add(str); } + void trackRecentLookups(TimeSource& time_source); + SymbolTable& symbolTable() { return symbol_table_; } + private: + Stats::SymbolTable& symbol_table_; Stats::StatNamePool pool_; absl::Mutex mutex_; using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); + std::unique_ptr> recent_lookups_ GUARDED_BY(mutex_); }; } // namespace Stats From 4eeb7ebdd34532a06c1de89a03874f6b9cf7a12d Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 06:38:10 -0400 Subject: [PATCH 04/50] full stack of recent-lookups admin access compiles, not tested or tried. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 3 + source/common/stats/fake_symbol_table_impl.h | 1 + source/common/stats/symbol_table_impl.cc | 77 +++++++++++++++++++- source/common/stats/symbol_table_impl.h | 12 ++- source/server/http/admin.cc | 17 +++++ 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index c47f6bc02f77..f5ec0666c656 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -149,6 +149,9 @@ class SymbolTable { virtual void rememberSet(StatNameSet& stat_name_set) PURE; virtual void forgetSet(StatNameSet& stat_name_set) PURE; + using RecentLookupsFn = std::function; + virtual bool getRecentLookups(const RecentLookupsFn&) PURE; + private: friend struct HeapStatData; friend class StatNameStorage; diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index f8023c79ac2a..aede87891fa6 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -128,6 +128,7 @@ class FakeSymbolTableImpl : public SymbolTable { void trackRecentLookups(TimeSource&) override {} void rememberSet(StatNameSet&) override {} void forgetSet(StatNameSet&) override {} + bool getRecentLookups(const RecentLookupsFn&) override { return false; } private: absl::string_view toStringView(const StatName& stat_name) const { diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index ed7b3e233e95..8177152053ad 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -222,16 +222,62 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { recent_lookups_->setFreeFn([this](Symbol symbol) { freeSymbol(symbol); }); } +namespace { +struct LookupData { + std::string name; + SystemTime time; + size_t count; +}; + +} // namespace + +bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { + std::vector lookup_data; + { + Thread::LockGuard lock(lock_); + if (recent_lookups_ == nullptr) { + return false; + } + recent_lookups_->forEach([&lookup_data, this](Symbol symbol, SystemTime time, size_t count) + NO_THREAD_SAFETY_ANALYSIS { + absl::string_view name = fromSymbol(symbol); + lookup_data.push_back({std::string(name), time, count}); + }); + } + { + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->getRecentLookups( + [&lookup_data](absl::string_view name, SystemTime time, size_t count) { + lookup_data.push_back({std::string(name), time, count}); + }); + } + } + + std::sort(lookup_data.begin(), lookup_data.end(), + [](const LookupData& a, const LookupData& b) -> bool { return a.name < b.name; }); + for (const LookupData& lookup : lookup_data) { + iter(lookup.name, lookup.time, lookup.count); + } + return true; +} + void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { - Thread::LockGuard lock(lock_); - if (recent_lookups_ != nullptr) { + { + Thread::LockGuard lock(lock_); + if (recent_lookups_ == nullptr) { + return; + } stat_name_set.trackRecentLookups(recent_lookups_->timeSource()); + } + { + Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.insert(&stat_name_set); } } void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { - Thread::LockGuard lock(lock_); + Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.erase(&stat_name_set); } @@ -488,6 +534,9 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { Stats::StatName& stat_name = dynamic_stat_names_[token]; if (stat_name.empty()) { // Note that builtin_stat_names_ already has one for "". stat_name = pool_.add(token); + if (recent_lookups_ != nullptr) { + recent_lookups_->lookup(stat_name); + } } return stat_name; } @@ -497,5 +546,27 @@ void StatNameSet::trackRecentLookups(TimeSource& time_source) { recent_lookups_ = std::make_unique>(time_source); } +bool StatNameSet::getRecentLookups(const SymbolTable::RecentLookupsFn& iter) { + struct LookupData { + StatName stat_name; + SystemTime time; + size_t count; + }; + std::vector lookup_data; + { + absl::MutexLock lock(&mutex_); + if (recent_lookups_ == nullptr) { + return false; + } + recent_lookups_->forEach([&lookup_data](StatName stat_name, SystemTime time, size_t count) { + lookup_data.push_back({stat_name, time, count}); + }); + } + for (const LookupData& lookup : lookup_data) { + iter(symbol_table_.toString(lookup.stat_name), lookup.time, lookup.count); + } + return true; +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 0231d57dcf72..449792342e50 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -164,6 +164,7 @@ class SymbolTableImpl : public SymbolTable { void trackRecentLookups(TimeSource& timeSource) override; void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; + bool getRecentLookups(const RecentLookupsFn&) override; private: friend class StatName; @@ -179,6 +180,9 @@ class SymbolTableImpl : public SymbolTable { // This must be held during both encode() and free(). mutable Thread::MutexBasicLockable lock_; + // This must be held while updateing stat_name_sets_. + mutable Thread::MutexBasicLockable stat_name_set_mutex_; + /** * Decodes a vector of symbols back into its period-delimited stat name. If * decoding fails on any part of the symbol_vec, we release_assert and crash @@ -248,7 +252,8 @@ class SymbolTableImpl : public SymbolTable { // using an Envoy::IntervalSet. std::stack pool_ GUARDED_BY(lock_); std::unique_ptr> recent_lookups_ GUARDED_BY(lock_); - absl::flat_hash_set stat_name_sets_ GUARDED_BY(lock_); + + absl::flat_hash_set stat_name_sets_ GUARDED_BY(stat_name_set_mutex_); }; /** @@ -339,6 +344,10 @@ class StatName { return HashUtil::xxHash64(absl::string_view(cdata, dataSize())); } + template friend H AbslHashValue(H h, StatName stat_name) { + return H::combine(std::move(h), stat_name.hash()); + } + bool operator==(const StatName& rhs) const { const uint64_t sz = dataSize(); return sz == rhs.dataSize() && memcmp(data(), rhs.data(), sz * sizeof(uint8_t)) == 0; @@ -685,6 +694,7 @@ class StatNameSet { void trackRecentLookups(TimeSource& time_source); SymbolTable& symbolTable() { return symbol_table_; } + bool getRecentLookups(const SymbolTable::RecentLookupsFn&); private: Stats::SymbolTable& symbol_table_; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 71fd23d1118c..dae66a9249c2 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -752,6 +752,23 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Code rc = Http::Code::OK; const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); + if (params.find("recentlookups") != params.end()) { + Stats::SymbolTable& symbol_table = server_.stats().symbolTable(); + response_headers.insertContentType().value().setReference( + Http::Headers::get().ContentTypeValues.Html); + response.add(""); + response.add("\n"); + symbol_table.getRecentLookups( + [&response](absl::string_view name, SystemTime time, size_t count) { + DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); + response.add(absl::StrCat("")); + }); + response.add("
Last LookupNameCount
", formatter.fromTime(time), "", + name, // TODO(jmarantz): ESCAPE THIS BEFORE SUBMITTING + "", count, "
"); + return rc; + } + const bool used_only = params.find("usedonly") != params.end(); const absl::optional regex = filterParam(params); From c1a1a326613cb3fbf3c410813c02d04095e8bb8d Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 07:25:11 -0400 Subject: [PATCH 05/50] cleanup Signed-off-by: Joshua Marantz --- include/envoy/stats/BUILD | 1 + source/common/stats/symbol_table_impl.h | 15 ++------------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/envoy/stats/BUILD b/include/envoy/stats/BUILD index b6a0389265fa..4691fdb2b8b0 100644 --- a/include/envoy/stats/BUILD +++ b/include/envoy/stats/BUILD @@ -40,6 +40,7 @@ envoy_cc_library( name = "symbol_table_interface", hdrs = ["symbol_table.h"], deps = [ + "//include/envoy/common:time_interface", "//source/common/common:hash_lib", ], ) diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 449792342e50..7db0ececde41 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -543,22 +543,11 @@ class StatNameList { SymbolTable::StoragePtr storage_; }; -// Helper class for constructing hash-tables with StatName keys. -struct StatNameHash { - size_t operator()(const StatName& a) const { return a.hash(); } -}; - -// Helper class for constructing hash-tables with StatName keys. -struct StatNameCompare { - bool operator()(const StatName& a, const StatName& b) const { return a == b; } -}; - // Value-templatized hash-map with StatName key. -template -using StatNameHashMap = absl::flat_hash_map; +template using StatNameHashMap = absl::flat_hash_map; // Hash-set of StatNames -using StatNameHashSet = absl::flat_hash_set; +using StatNameHashSet = absl::flat_hash_set; // Helper class for sorting StatNames. struct StatNameLessThan { From e2e384e0cc8c575aa3fabee9c8a64344d2dc39f8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 07:25:52 -0400 Subject: [PATCH 06/50] format Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 12 ++++++------ source/server/http/admin.cc | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 8177152053ad..5bba3b392722 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -239,10 +239,10 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { return false; } recent_lookups_->forEach([&lookup_data, this](Symbol symbol, SystemTime time, size_t count) - NO_THREAD_SAFETY_ANALYSIS { - absl::string_view name = fromSymbol(symbol); - lookup_data.push_back({std::string(name), time, count}); - }); + NO_THREAD_SAFETY_ANALYSIS { + absl::string_view name = fromSymbol(symbol); + lookup_data.push_back({std::string(name), time, count}); + }); } { Thread::LockGuard lock(stat_name_set_mutex_); @@ -559,8 +559,8 @@ bool StatNameSet::getRecentLookups(const SymbolTable::RecentLookupsFn& iter) { return false; } recent_lookups_->forEach([&lookup_data](StatName stat_name, SystemTime time, size_t count) { - lookup_data.push_back({stat_name, time, count}); - }); + lookup_data.push_back({stat_name, time, count}); + }); } for (const LookupData& lookup : lookup_data) { iter(symbol_table_.toString(lookup.stat_name), lookup.time, lookup.count); diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index dae66a9249c2..7cb737aa43c5 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -762,7 +762,7 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo [&response](absl::string_view name, SystemTime time, size_t count) { DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); response.add(absl::StrCat("", formatter.fromTime(time), "", - name, // TODO(jmarantz): ESCAPE THIS BEFORE SUBMITTING + name, // TODO(jmarantz): ESCAPE THIS BEFORE SUBMITTING "", count, "")); }); response.add(""); From 33c0f886ca68fd5bc49b35e4d59e7a7f145be2da Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 07:49:07 -0400 Subject: [PATCH 07/50] Hold fewer locks for less time. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 80 ++++++++++++------------ source/common/stats/symbol_table_impl.h | 3 +- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 5bba3b392722..48e037dd5305 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -222,42 +222,53 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { recent_lookups_->setFreeFn([this](Symbol symbol) { freeSymbol(symbol); }); } -namespace { -struct LookupData { - std::string name; - SystemTime time; - size_t count; -}; - -} // namespace - bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { + struct LookupData { + StatName stat_name; + Symbol symbol; // Used if stat_name is empty, as 0 is a valid symbol. + SystemTime time; + size_t count; + }; std::vector lookup_data; + + // We don't want to hold stat_name_set_mutex and lock_ at the same time. + // StatNameSet::getRecentLookups does not take SymbolTableImpl::lock_, so we + // collect the lookups via StatName, and then later decode the collected data. + { + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->getRecentLookups( + [&lookup_data](StatName stat_name, SystemTime time, size_t count) { + ASSERT(!stat_name.empty()); + lookup_data.push_back({stat_name, Symbol(0), time, count}); + }); + } + } + { Thread::LockGuard lock(lock_); if (recent_lookups_ == nullptr) { return false; } - recent_lookups_->forEach([&lookup_data, this](Symbol symbol, SystemTime time, size_t count) + recent_lookups_->forEach([&lookup_data](Symbol symbol, SystemTime time, size_t count) NO_THREAD_SAFETY_ANALYSIS { - absl::string_view name = fromSymbol(symbol); - lookup_data.push_back({std::string(name), time, count}); + lookup_data.push_back({StatName(), symbol, time, count}); }); } - { - Thread::LockGuard lock(stat_name_set_mutex_); - for (StatNameSet* stat_name_set : stat_name_sets_) { - stat_name_set->getRecentLookups( - [&lookup_data](absl::string_view name, SystemTime time, size_t count) { - lookup_data.push_back({std::string(name), time, count}); - }); - } - } std::sort(lookup_data.begin(), lookup_data.end(), - [](const LookupData& a, const LookupData& b) -> bool { return a.name < b.name; }); + [](const LookupData& a, const LookupData& b) -> bool { return a.time < b.time; }); for (const LookupData& lookup : lookup_data) { - iter(lookup.name, lookup.time, lookup.count); + if (lookup.stat_name.empty()) { + absl::string_view name; + { + Thread::LockGuard lock(lock_); + name = fromSymbol(lookup.symbol); + } + iter(name, lookup.time, lookup.count); // Hold no locks while iterating. + } else { + iter(toString(lookup.stat_name), lookup.time, lookup.count); + } } return true; } @@ -546,25 +557,12 @@ void StatNameSet::trackRecentLookups(TimeSource& time_source) { recent_lookups_ = std::make_unique>(time_source); } -bool StatNameSet::getRecentLookups(const SymbolTable::RecentLookupsFn& iter) { - struct LookupData { - StatName stat_name; - SystemTime time; - size_t count; - }; - std::vector lookup_data; - { - absl::MutexLock lock(&mutex_); - if (recent_lookups_ == nullptr) { - return false; - } - recent_lookups_->forEach([&lookup_data](StatName stat_name, SystemTime time, size_t count) { - lookup_data.push_back({stat_name, time, count}); - }); - } - for (const LookupData& lookup : lookup_data) { - iter(symbol_table_.toString(lookup.stat_name), lookup.time, lookup.count); +bool StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { + absl::MutexLock lock(&mutex_); + if (recent_lookups_ == nullptr) { + return false; } + recent_lookups_->forEach(iter); return true; } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 7db0ececde41..9c7dce9db6ac 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -683,7 +683,8 @@ class StatNameSet { void trackRecentLookups(TimeSource& time_source); SymbolTable& symbolTable() { return symbol_table_; } - bool getRecentLookups(const SymbolTable::RecentLookupsFn&); + + bool getRecentLookups(const RecentLookups::IterFn& iter); private: Stats::SymbolTable& symbol_table_; From 88082583642735166bef45a605cf573643be46f8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 09:08:06 -0400 Subject: [PATCH 08/50] Add test for SymbolTableImpl::getRecentLookups(). Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 60 +++++++++++++-------- source/common/stats/symbol_table_impl.h | 3 +- test/common/stats/BUILD | 1 + test/common/stats/symbol_table_impl_test.cc | 29 ++++++++++ 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 48e037dd5305..415a1067a182 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -127,7 +127,11 @@ void SymbolTableImpl::addTokensToEncoding(const absl::string_view name, Encoding { Thread::LockGuard lock(lock_); for (auto& token : tokens) { - symbols.push_back(toSymbol(token)); + Symbol symbol = toSymbol(token); + symbols.push_back(symbol); + if (recent_lookups_ != nullptr) { + recent_lookups_->lookup(symbol); + } } } @@ -159,7 +163,7 @@ std::string SymbolTableImpl::decodeSymbolVec(const SymbolVec& symbols) const { // Hold the lock only while decoding symbols. Thread::LockGuard lock(lock_); for (Symbol symbol : symbols) { - name_tokens.push_back(fromSymbol(symbol)); + name_tokens.push_back(fromSymbolLockHeld(symbol)); } } return absl::StrJoin(name_tokens, "."); @@ -224,16 +228,23 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { struct LookupData { - StatName stat_name; - Symbol symbol; // Used if stat_name is empty, as 0 is a valid symbol. - SystemTime time; - size_t count; + StatName stat_name_; + Symbol symbol_; // Used if stat_name is empty, as 0 is a valid symbol. + SystemTime time_; + size_t count_; + + std::string name(SymbolTableImpl& symbol_table) const { + if (stat_name_.empty()) { + return std::string(symbol_table.fromSymbol(symbol_)); + } + return symbol_table.toString(stat_name_); + }; }; std::vector lookup_data; // We don't want to hold stat_name_set_mutex and lock_ at the same time. // StatNameSet::getRecentLookups does not take SymbolTableImpl::lock_, so we - // collect the lookups via StatName, and then later decode the collected data. + // collect the lookups via StatName, and decode the collected data below. { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { @@ -257,30 +268,32 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { } std::sort(lookup_data.begin(), lookup_data.end(), - [](const LookupData& a, const LookupData& b) -> bool { return a.time < b.time; }); + [this](const LookupData& a, const LookupData& b) -> bool { + if (a.time_ == b.time_) { + return a.name(*this) < b.name(*this); + } + return a.time_ > b.time_; // Sort latest first. + }); for (const LookupData& lookup : lookup_data) { - if (lookup.stat_name.empty()) { - absl::string_view name; - { - Thread::LockGuard lock(lock_); - name = fromSymbol(lookup.symbol); - } - iter(name, lookup.time, lookup.count); // Hold no locks while iterating. - } else { - iter(toString(lookup.stat_name), lookup.time, lookup.count); - } + iter(lookup.name(*this), lookup.time_, lookup.count_); } return true; } void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { + // We don't want to be holding lock_ when calling + // stat_name_set.trackRecentLookups() below, as that will result in a + // false-positive in absl::Mutex's detection analysis. It is easy enough to + // work around by capturing the TimeSource and then releasing lock_. + TimeSource* time_source; { Thread::LockGuard lock(lock_); if (recent_lookups_ == nullptr) { return; } - stat_name_set.trackRecentLookups(recent_lookups_->timeSource()); + time_source = &recent_lookups_->timeSource(); } + stat_name_set.trackRecentLookups(*time_source); { Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.insert(&stat_name_set); @@ -318,13 +331,18 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { return result; } -absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const +absl::string_view SymbolTableImpl::fromSymbolLockHeld(const Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_) { auto search = decode_map_.find(symbol); RELEASE_ASSERT(search != decode_map_.end(), "no such symbol"); return search->second->toStringView(); } +absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const { + Thread::LockGuard lock(lock_); + return fromSymbolLockHeld(symbol); +} + void SymbolTableImpl::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { if (pool_.empty()) { next_symbol_ = ++monotonic_counter_; @@ -349,7 +367,7 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { Thread::LockGuard lock(lock_); for (uint64_t i = 0, n = std::min(av.size(), bv.size()); i < n; ++i) { if (av[i] != bv[i]) { - bool ret = fromSymbol(av[i]) < fromSymbol(bv[i]); + bool ret = fromSymbolLockHeld(av[i]) < fromSymbolLockHeld(bv[i]); return ret; } } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 9c7dce9db6ac..c069c64f12d4 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -208,7 +208,8 @@ class SymbolTableImpl : public SymbolTable { * @param symbol the individual symbol to be decoded. * @return absl::string_view the decoded string. */ - absl::string_view fromSymbol(Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_); + absl::string_view fromSymbolLockHeld(Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_); + absl::string_view fromSymbol(Symbol symbol) const; /** * Stages a new symbol for use. To be called after a successful insertion. diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 64410176aca1..6abe12a9e22d 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -102,6 +102,7 @@ envoy_cc_test( "//source/common/stats:symbol_table_lib", "//test/mocks/stats:stats_mocks", "//test/test_common:logging_lib", + "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], ) diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index d6a533b092a5..39a088631eb8 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -8,6 +8,7 @@ #include "test/common/stats/stat_test_utility.h" #include "test/test_common/logging.h" +#include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" #include "absl/synchronization/blocking_counter.h" @@ -565,6 +566,34 @@ TEST_P(StatNameTest, StatNameSet) { EXPECT_NE(dynamic2.data(), dynamic.data()); } +TEST_P(StatNameTest, RecentLookups) { + if (GetParam() == SymbolTableType::Fake) { + return; + } + + Event::SimulatedTimeSystem time_system; + const uint64_t years = 365 * 24 * 3600; + time_system.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); + table_->trackRecentLookups(time_system); + StatNameSet set(*table_); + set.getStatName("dynamic.stat"); + time_system.sleep(std::chrono::seconds(1)); + encodeDecode("direct.stat"); + + std::vector accum; + table_->getRecentLookups([&accum](absl::string_view name, SystemTime time, size_t count) { + DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); + accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", name, ";Count=", count)); + }); + std::string recent_lookups_str = StringUtil::join(accum, " "); + + EXPECT_EQ("2009-12-22,00:00:01;Item=direct;Count=1 " + "2009-12-22,00:00:01;Item=stat;Count=2 " + "2009-12-22,00:00:00;Item=dynamic;Count=1 " + "2009-12-22,00:00:00;Item=dynamic.stat;Count=1", + recent_lookups_str); +} + // Tests the memory savings realized from using symbol tables with 1k // clusters. This test shows the memory drops from almost 8M to less than // 2M. Note that only SymbolTableImpl is tested for memory consumption, From 8bc22f6b6ef4be3aa7eec3ee1c82ed78a80a5500 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 19:32:42 -0400 Subject: [PATCH 09/50] simplified; admin flow almost working Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/stats/recent_lookups.h | 21 ++-------- source/common/stats/symbol_table_impl.cc | 44 +++++++++++++-------- source/common/stats/symbol_table_impl.h | 19 ++++++--- source/server/http/admin.cc | 14 +++---- source/server/server.cc | 1 + test/common/stats/recent_lookups_test.cc | 41 ++++++++++--------- test/common/stats/symbol_table_impl_test.cc | 13 +++--- 8 files changed, 80 insertions(+), 75 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index f5ec0666c656..097164fa2141 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -149,7 +149,7 @@ class SymbolTable { virtual void rememberSet(StatNameSet& stat_name_set) PURE; virtual void forgetSet(StatNameSet& stat_name_set) PURE; - using RecentLookupsFn = std::function; + using RecentLookupsFn = std::function; virtual bool getRecentLookups(const RecentLookupsFn&) PURE; private: diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 2d32dd66203b..d32b8cb02c71 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -6,9 +6,6 @@ #include "envoy/common/time.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" - namespace Envoy { namespace Stats { @@ -30,24 +27,12 @@ template class RecentLookups { } using FreeFn = std::function; - using IterFn = std::function; + using IterFn = std::function; - // Calls fn(item, timestmp, count) for each of the remembered - // lookups. Duplicates are collated and provided with their timestamp and - // count. + // Calls fn(item, timestamp) for each of the remembered lookups. void forEach(IterFn fn) { - using TimeCount = std::pair; - absl::flat_hash_map entry_map; for (ItemTime& item_time : queue_) { - TimeCount& time_count = entry_map[item_time.first]; - if ((time_count.second == 0) || (item_time.second > time_count.first)) { - time_count.first = item_time.second; - } - ++time_count.second; - } - for (auto& item_time_count : entry_map) { - TimeCount& time_count = item_time_count.second; - fn(item_time_count.first, time_count.first, time_count.second); + fn(item_time.first, item_time.second); } } diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 415a1067a182..a6cd79b91ea0 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -221,9 +221,20 @@ void SymbolTableImpl::freeSymbolLockHeld(Symbol symbol) { } void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { - Thread::LockGuard lock(lock_); - recent_lookups_ = std::make_unique>(time_source); - recent_lookups_->setFreeFn([this](Symbol symbol) { freeSymbol(symbol); }); + { + Thread::LockGuard lock(lock_); + recent_lookups_ = std::make_unique>(time_source); + recent_lookups_->setFreeFn([this](Symbol symbol) NO_THREAD_SAFETY_ANALYSIS { + freeSymbolLockHeld(symbol); + }); + } + + { + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->trackRecentLookups(time_source); + } + } } bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { @@ -231,7 +242,6 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { StatName stat_name_; Symbol symbol_; // Used if stat_name is empty, as 0 is a valid symbol. SystemTime time_; - size_t count_; std::string name(SymbolTableImpl& symbol_table) const { if (stat_name_.empty()) { @@ -248,11 +258,10 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { - stat_name_set->getRecentLookups( - [&lookup_data](StatName stat_name, SystemTime time, size_t count) { - ASSERT(!stat_name.empty()); - lookup_data.push_back({stat_name, Symbol(0), time, count}); - }); + stat_name_set->getRecentLookups([&lookup_data](StatName stat_name, SystemTime time) { + ASSERT(!stat_name.empty()); + lookup_data.push_back({stat_name, Symbol(0), time}); + }); } } @@ -261,9 +270,9 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { if (recent_lookups_ == nullptr) { return false; } - recent_lookups_->forEach([&lookup_data](Symbol symbol, SystemTime time, size_t count) + recent_lookups_->forEach([&lookup_data](Symbol symbol, SystemTime time) NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({StatName(), symbol, time, count}); + lookup_data.push_back({StatName(), symbol, time}); }); } @@ -275,7 +284,7 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { return a.time_ > b.time_; // Sort latest first. }); for (const LookupData& lookup : lookup_data) { - iter(lookup.name(*this), lookup.time_, lookup.count_); + iter(lookup.name(*this), lookup.time_); } return true; } @@ -285,15 +294,16 @@ void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { // stat_name_set.trackRecentLookups() below, as that will result in a // false-positive in absl::Mutex's detection analysis. It is easy enough to // work around by capturing the TimeSource and then releasing lock_. - TimeSource* time_source; + TimeSource* time_source = nullptr; { Thread::LockGuard lock(lock_); - if (recent_lookups_ == nullptr) { - return; + if (recent_lookups_ != nullptr) { + time_source = &recent_lookups_->timeSource(); } - time_source = &recent_lookups_->timeSource(); } - stat_name_set.trackRecentLookups(*time_source); + if (time_source != nullptr) { + stat_name_set.trackRecentLookups(*time_source); + } { Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.insert(&stat_name_set); diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index c069c64f12d4..e8ab45df72bc 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -345,10 +345,6 @@ class StatName { return HashUtil::xxHash64(absl::string_view(cdata, dataSize())); } - template friend H AbslHashValue(H h, StatName stat_name) { - return H::combine(std::move(h), stat_name.hash()); - } - bool operator==(const StatName& rhs) const { const uint64_t sz = dataSize(); return sz == rhs.dataSize() && memcmp(data(), rhs.data(), sz * sizeof(uint8_t)) == 0; @@ -544,11 +540,22 @@ class StatNameList { SymbolTable::StoragePtr storage_; }; +// Helper class for constructing hash-tables with StatName keys. +struct StatNameCompare { + bool operator()(const StatName& a, const StatName& b) const { return a == b; } +}; + +// Helper class for constructing hash-tables with StatName keys. +struct StatNameHash { + size_t operator()(const StatName& a) const { return a.hash(); } +}; + // Value-templatized hash-map with StatName key. -template using StatNameHashMap = absl::flat_hash_map; +template +using StatNameHashMap = absl::flat_hash_map; // Hash-set of StatNames -using StatNameHashSet = absl::flat_hash_set; +using StatNameHashSet = absl::flat_hash_set; // Helper class for sorting StatNames. struct StatNameLessThan { diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 7cb737aa43c5..ca9064397b8b 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -755,17 +755,13 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo if (params.find("recentlookups") != params.end()) { Stats::SymbolTable& symbol_table = server_.stats().symbolTable(); response_headers.insertContentType().value().setReference( - Http::Headers::get().ContentTypeValues.Html); - response.add(""); - response.add("\n"); + Http::Headers::get().ContentTypeValues.TextUtf8); + response.add("Date Time Lookup\n"); + DateFormatter formatter("%Y-%m-%d %H:%M:%S"); symbol_table.getRecentLookups( - [&response](absl::string_view name, SystemTime time, size_t count) { - DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); - response.add(absl::StrCat("")); + [&response, &formatter](absl::string_view name, SystemTime time) { + response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); }); - response.add("
Last LookupNameCount
", formatter.fromTime(time), "", - name, // TODO(jmarantz): ESCAPE THIS BEFORE SUBMITTING - "", count, "
"); return rc; } diff --git a/source/server/server.cc b/source/server/server.cc index 4994d53ccdf8..ce7fccb292ae 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -75,6 +75,7 @@ InstanceImpl::InstanceImpl(const Options& options, Event::TimeSystem& time_syste : nullptr), grpc_context_(store.symbolTable()), http_context_(store.symbolTable()), process_context_(std::move(process_context)), main_thread_id_(std::this_thread::get_id()) { + store.symbolTable().trackRecentLookups(time_source_); try { if (!options.logPath().empty()) { try { diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index a5aebba5ddcf..facb8eca4b22 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -22,9 +22,9 @@ class RecentLookupsTest : public testing::Test { std::string joinLookups() { std::vector accum; - recent_lookups_.forEach([&accum](std::string item, SystemTime time, size_t count) { + recent_lookups_.forEach([&accum](std::string item, SystemTime time) { DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); - accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", item, ";Count=", count)); + accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", item)); }); std::sort(accum.begin(), accum.end()); return StringUtil::join(accum, " "); @@ -38,7 +38,7 @@ TEST_F(RecentLookupsTest, Empty) { EXPECT_EQ("", joinLookups()); } TEST_F(RecentLookupsTest, One) { recent_lookups_.lookup("Hello"); - EXPECT_EQ("2009-12-22,00:00:00;Item=Hello;Count=1", joinLookups()); + EXPECT_EQ("2009-12-22,00:00:00;Item=Hello", joinLookups()); } TEST_F(RecentLookupsTest, DropOne) { @@ -46,16 +46,16 @@ TEST_F(RecentLookupsTest, DropOne) { recent_lookups_.lookup(absl::StrCat("lookup", i)); time_system_.sleep(std::chrono::seconds(1)); } - EXPECT_EQ("2009-12-22,00:00:01;Item=lookup1;Count=1 " - "2009-12-22,00:00:02;Item=lookup2;Count=1 " - "2009-12-22,00:00:03;Item=lookup3;Count=1 " - "2009-12-22,00:00:04;Item=lookup4;Count=1 " - "2009-12-22,00:00:05;Item=lookup5;Count=1 " - "2009-12-22,00:00:06;Item=lookup6;Count=1 " - "2009-12-22,00:00:07;Item=lookup7;Count=1 " - "2009-12-22,00:00:08;Item=lookup8;Count=1 " - "2009-12-22,00:00:09;Item=lookup9;Count=1 " - "2009-12-22,00:00:10;Item=lookup10;Count=1", + EXPECT_EQ("2009-12-22,00:00:01;Item=lookup1 " + "2009-12-22,00:00:02;Item=lookup2 " + "2009-12-22,00:00:03;Item=lookup3 " + "2009-12-22,00:00:04;Item=lookup4 " + "2009-12-22,00:00:05;Item=lookup5 " + "2009-12-22,00:00:06;Item=lookup6 " + "2009-12-22,00:00:07;Item=lookup7 " + "2009-12-22,00:00:08;Item=lookup8 " + "2009-12-22,00:00:09;Item=lookup9 " + "2009-12-22,00:00:10;Item=lookup10", joinLookups()); } @@ -66,11 +66,16 @@ TEST_F(RecentLookupsTest, RepeatDrop) { recent_lookups_.lookup(absl::StrCat("lookup", i)); time_system_.sleep(std::chrono::seconds(1)); } - EXPECT_EQ("2009-12-22,00:00:13;Item=lookup6;Count=2 " - "2009-12-22,00:00:15;Item=lookup7;Count=2 " - "2009-12-22,00:00:17;Item=lookup8;Count=2 " - "2009-12-22,00:00:19;Item=lookup9;Count=2 " - "2009-12-22,00:00:21;Item=lookup10;Count=2", + EXPECT_EQ("2009-12-22,00:00:12;Item=lookup6 " + "2009-12-22,00:00:13;Item=lookup6 " + "2009-12-22,00:00:14;Item=lookup7 " + "2009-12-22,00:00:15;Item=lookup7 " + "2009-12-22,00:00:16;Item=lookup8 " + "2009-12-22,00:00:17;Item=lookup8 " + "2009-12-22,00:00:18;Item=lookup9 " + "2009-12-22,00:00:19;Item=lookup9 " + "2009-12-22,00:00:20;Item=lookup10 " + "2009-12-22,00:00:21;Item=lookup10", joinLookups()); } diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 39a088631eb8..c54c873395fa 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -581,16 +581,17 @@ TEST_P(StatNameTest, RecentLookups) { encodeDecode("direct.stat"); std::vector accum; - table_->getRecentLookups([&accum](absl::string_view name, SystemTime time, size_t count) { + table_->getRecentLookups([&accum](absl::string_view name, SystemTime time) { DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); - accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", name, ";Count=", count)); + accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", name)); }); std::string recent_lookups_str = StringUtil::join(accum, " "); - EXPECT_EQ("2009-12-22,00:00:01;Item=direct;Count=1 " - "2009-12-22,00:00:01;Item=stat;Count=2 " - "2009-12-22,00:00:00;Item=dynamic;Count=1 " - "2009-12-22,00:00:00;Item=dynamic.stat;Count=1", + EXPECT_EQ("2009-12-22,00:00:01;Item=direct " + "2009-12-22,00:00:01;Item=stat " + "2009-12-22,00:00:00;Item=dynamic " + "2009-12-22,00:00:00;Item=dynamic.stat " + "2009-12-22,00:00:00;Item=stat", recent_lookups_str); } From 86e9204b5a381de728e1c51f445420d36e13de90 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 19:47:57 -0400 Subject: [PATCH 10/50] admin interface kind of works. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 30 ++++++++++----------- source/common/stats/symbol_table_impl.h | 2 +- source/server/http/admin.cc | 7 +++-- test/common/stats/symbol_table_impl_test.cc | 6 ++--- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index a6cd79b91ea0..2caaf2b86cf7 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -126,12 +126,11 @@ void SymbolTableImpl::addTokensToEncoding(const absl::string_view name, Encoding // ref-counts in this. { Thread::LockGuard lock(lock_); + if (recent_lookups_ != nullptr) { + recent_lookups_->lookup(std::string(name)); + } for (auto& token : tokens) { - Symbol symbol = toSymbol(token); - symbols.push_back(symbol); - if (recent_lookups_ != nullptr) { - recent_lookups_->lookup(symbol); - } + symbols.push_back(toSymbol(token)); } } @@ -223,10 +222,7 @@ void SymbolTableImpl::freeSymbolLockHeld(Symbol symbol) { void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { { Thread::LockGuard lock(lock_); - recent_lookups_ = std::make_unique>(time_source); - recent_lookups_->setFreeFn([this](Symbol symbol) NO_THREAD_SAFETY_ANALYSIS { - freeSymbolLockHeld(symbol); - }); + recent_lookups_ = std::make_unique>(time_source); } { @@ -240,12 +236,12 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { struct LookupData { StatName stat_name_; - Symbol symbol_; // Used if stat_name is empty, as 0 is a valid symbol. + std::string name_; // Used if stat_name is empty. SystemTime time_; std::string name(SymbolTableImpl& symbol_table) const { if (stat_name_.empty()) { - return std::string(symbol_table.fromSymbol(symbol_)); + return name_; } return symbol_table.toString(stat_name_); }; @@ -260,7 +256,7 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { for (StatNameSet* stat_name_set : stat_name_sets_) { stat_name_set->getRecentLookups([&lookup_data](StatName stat_name, SystemTime time) { ASSERT(!stat_name.empty()); - lookup_data.push_back({stat_name, Symbol(0), time}); + lookup_data.push_back({stat_name, "", time}); }); } } @@ -270,9 +266,9 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { if (recent_lookups_ == nullptr) { return false; } - recent_lookups_->forEach([&lookup_data](Symbol symbol, SystemTime time) + recent_lookups_->forEach([&lookup_data](const std::string& str, SystemTime time) NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({StatName(), symbol, time}); + lookup_data.push_back({StatName(), str, time}); }); } @@ -283,8 +279,12 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { } return a.time_ > b.time_; // Sort latest first. }); + const LookupData* prev = nullptr; for (const LookupData& lookup : lookup_data) { - iter(lookup.name(*this), lookup.time_); + if (prev == nullptr || prev->name_ != lookup.name_) { + iter(lookup.name(*this), lookup.time_); + prev = &lookup; + } } return true; } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index e8ab45df72bc..874fd436a8dc 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -252,7 +252,7 @@ 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_ GUARDED_BY(lock_); - std::unique_ptr> recent_lookups_ GUARDED_BY(lock_); + std::unique_ptr> recent_lookups_ GUARDED_BY(lock_); absl::flat_hash_set stat_name_sets_ GUARDED_BY(stat_name_set_mutex_); }; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index ca9064397b8b..95a851cf36bd 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -758,10 +758,9 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Headers::get().ContentTypeValues.TextUtf8); response.add("Date Time Lookup\n"); DateFormatter formatter("%Y-%m-%d %H:%M:%S"); - symbol_table.getRecentLookups( - [&response, &formatter](absl::string_view name, SystemTime time) { - response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); - }); + symbol_table.getRecentLookups([&response, &formatter](absl::string_view name, SystemTime time) { + response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); + }); return rc; } diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index c54c873395fa..5e9302df9a3e 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -587,11 +587,9 @@ TEST_P(StatNameTest, RecentLookups) { }); std::string recent_lookups_str = StringUtil::join(accum, " "); - EXPECT_EQ("2009-12-22,00:00:01;Item=direct " - "2009-12-22,00:00:01;Item=stat " - "2009-12-22,00:00:00;Item=dynamic " + EXPECT_EQ("2009-12-22,00:00:01;Item=direct.stat " "2009-12-22,00:00:00;Item=dynamic.stat " - "2009-12-22,00:00:00;Item=stat", + "2009-12-22,00:00:00;Item=dynamic.stat", recent_lookups_str); } From cd83c624eff7cf095c04f7eeb8ebf13d167b5ff1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 19:52:31 -0400 Subject: [PATCH 11/50] collate fix. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 2 +- test/common/stats/symbol_table_impl_test.cc | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 2caaf2b86cf7..677598d2d895 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -281,7 +281,7 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { }); const LookupData* prev = nullptr; for (const LookupData& lookup : lookup_data) { - if (prev == nullptr || prev->name_ != lookup.name_) { + if (prev == nullptr || prev->name(*this) != lookup.name(*this)) { iter(lookup.name(*this), lookup.time_); prev = &lookup; } diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 5e9302df9a3e..2930c3e5ddc9 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -574,9 +574,12 @@ TEST_P(StatNameTest, RecentLookups) { Event::SimulatedTimeSystem time_system; const uint64_t years = 365 * 24 * 3600; time_system.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); + StatNameSet set1(*table_); table_->trackRecentLookups(time_system); - StatNameSet set(*table_); - set.getStatName("dynamic.stat"); + StatNameSet set2(*table_); + set1.getStatName("dynamic.stat1"); + time_system.sleep(std::chrono::seconds(1)); + set2.getStatName("dynamic.stat2"); time_system.sleep(std::chrono::seconds(1)); encodeDecode("direct.stat"); @@ -587,9 +590,9 @@ TEST_P(StatNameTest, RecentLookups) { }); std::string recent_lookups_str = StringUtil::join(accum, " "); - EXPECT_EQ("2009-12-22,00:00:01;Item=direct.stat " - "2009-12-22,00:00:00;Item=dynamic.stat " - "2009-12-22,00:00:00;Item=dynamic.stat", + EXPECT_EQ("2009-12-22,00:00:02;Item=direct.stat " + "2009-12-22,00:00:01;Item=dynamic.stat2 " + "2009-12-22,00:00:00;Item=dynamic.stat1", recent_lookups_str); } From fd3bbbdd21154a55f99bb193ab2723dc3b8d86d9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 20:21:29 -0400 Subject: [PATCH 12/50] report the total lookup count as well. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/recent_lookups.h | 10 ++++---- source/common/stats/symbol_table_impl.cc | 25 ++++++++++---------- source/common/stats/symbol_table_impl.h | 4 ++-- source/server/http/admin.cc | 4 +++- test/common/stats/symbol_table_impl_test.cc | 3 ++- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 097164fa2141..61583cba3360 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -150,7 +150,7 @@ class SymbolTable { virtual void forgetSet(StatNameSet& stat_name_set) PURE; using RecentLookupsFn = std::function; - virtual bool getRecentLookups(const RecentLookupsFn&) PURE; + virtual uint64_t getRecentLookups(const RecentLookupsFn&) PURE; private: friend struct HeapStatData; diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index aede87891fa6..4a30dc08916e 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -128,7 +128,7 @@ class FakeSymbolTableImpl : public SymbolTable { void trackRecentLookups(TimeSource&) override {} void rememberSet(StatNameSet&) override {} void forgetSet(StatNameSet&) override {} - bool getRecentLookups(const RecentLookupsFn&) override { return false; } + uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } private: absl::string_view toStringView(const StatName& stat_name) const { diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index d32b8cb02c71..f57685b2a302 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -17,16 +18,13 @@ template class RecentLookups { // Records a lookup of an object of type T. Only the last 'Capacity' lookups // are remembered. void lookup(T t) { + ++total_; if (queue_.size() >= Capacity) { - if (free_fn_) { - free_fn_(queue_.back().first); - } queue_.pop_back(); } queue_.push_front(ItemTime(t, time_source_.systemTime())); } - using FreeFn = std::function; using IterFn = std::function; // Calls fn(item, timestamp) for each of the remembered lookups. @@ -37,14 +35,14 @@ template class RecentLookups { } void clear() { queue_.clear(); } - void setFreeFn(const FreeFn& free_fn) { free_fn_ = free_fn; } TimeSource& timeSource() { return time_source_; } + uint64_t total() const { return total_; } private: using ItemTime = std::pair; std::deque queue_; TimeSource& time_source_; - FreeFn free_fn_; + uint64_t total_{0}; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 677598d2d895..c8f0d650b7e3 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -233,7 +233,8 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { } } -bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { +uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { + uint64_t total = 0; struct LookupData { StatName stat_name_; std::string name_; // Used if stat_name is empty. @@ -254,7 +255,7 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { - stat_name_set->getRecentLookups([&lookup_data](StatName stat_name, SystemTime time) { + total += stat_name_set->getRecentLookups([&lookup_data](StatName stat_name, SystemTime time) { ASSERT(!stat_name.empty()); lookup_data.push_back({stat_name, "", time}); }); @@ -263,13 +264,13 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { { Thread::LockGuard lock(lock_); - if (recent_lookups_ == nullptr) { - return false; + if (recent_lookups_ != nullptr) { + recent_lookups_->forEach([&lookup_data](const std::string& str, SystemTime time) + NO_THREAD_SAFETY_ANALYSIS { + lookup_data.push_back({StatName(), str, time}); + }); + total += recent_lookups_->total(); } - recent_lookups_->forEach([&lookup_data](const std::string& str, SystemTime time) - NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({StatName(), str, time}); - }); } std::sort(lookup_data.begin(), lookup_data.end(), @@ -286,7 +287,7 @@ bool SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { prev = &lookup; } } - return true; + return total; } void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { @@ -585,13 +586,13 @@ void StatNameSet::trackRecentLookups(TimeSource& time_source) { recent_lookups_ = std::make_unique>(time_source); } -bool StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { +uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { absl::MutexLock lock(&mutex_); if (recent_lookups_ == nullptr) { - return false; + return 0; } recent_lookups_->forEach(iter); - return true; + return recent_lookups_->total(); } } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 874fd436a8dc..70517911992b 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -164,7 +164,7 @@ class SymbolTableImpl : public SymbolTable { void trackRecentLookups(TimeSource& timeSource) override; void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; - bool getRecentLookups(const RecentLookupsFn&) override; + uint64_t getRecentLookups(const RecentLookupsFn&) override; private: friend class StatName; @@ -692,7 +692,7 @@ class StatNameSet { void trackRecentLookups(TimeSource& time_source); SymbolTable& symbolTable() { return symbol_table_; } - bool getRecentLookups(const RecentLookups::IterFn& iter); + uint64_t getRecentLookups(const RecentLookups::IterFn& iter); private: Stats::SymbolTable& symbol_table_; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 95a851cf36bd..a6ef54ad41a9 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -758,9 +758,11 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Headers::get().ContentTypeValues.TextUtf8); response.add("Date Time Lookup\n"); DateFormatter formatter("%Y-%m-%d %H:%M:%S"); - symbol_table.getRecentLookups([&response, &formatter](absl::string_view name, SystemTime time) { + uint64_t total = symbol_table.getRecentLookups( + [&response, &formatter](absl::string_view name, SystemTime time) { response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); }); + response.add(absl::StrCat("\ntotal: ", total, "\n")); return rc; } diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 2930c3e5ddc9..8a959ce2bf08 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -584,10 +584,11 @@ TEST_P(StatNameTest, RecentLookups) { encodeDecode("direct.stat"); std::vector accum; - table_->getRecentLookups([&accum](absl::string_view name, SystemTime time) { + uint64_t total = table_->getRecentLookups([&accum](absl::string_view name, SystemTime time) { DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", name)); }); + EXPECT_EQ(5, total); std::string recent_lookups_str = StringUtil::join(accum, " "); EXPECT_EQ("2009-12-22,00:00:02;Item=direct.stat " From cfa163453d3861d3bea57d7fd6e37fd3e9044f05 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 2 Sep 2019 22:16:24 -0400 Subject: [PATCH 13/50] format and add admin tests. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 6 +++--- source/server/http/admin.cc | 4 ++-- test/integration/BUILD | 1 + test/integration/integration_admin_test.cc | 11 +++++++++++ test/server/http/admin_test.cc | 9 +++++++++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index c8f0d650b7e3..cebe27143bd4 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -266,9 +266,9 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { Thread::LockGuard lock(lock_); if (recent_lookups_ != nullptr) { recent_lookups_->forEach([&lookup_data](const std::string& str, SystemTime time) - NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({StatName(), str, time}); - }); + NO_THREAD_SAFETY_ANALYSIS { + lookup_data.push_back({StatName(), str, time}); + }); total += recent_lookups_->total(); } } diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index a6ef54ad41a9..b05517551df6 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -760,8 +760,8 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo DateFormatter formatter("%Y-%m-%d %H:%M:%S"); uint64_t total = symbol_table.getRecentLookups( [&response, &formatter](absl::string_view name, SystemTime time) { - response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); - }); + response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); + }); response.add(absl::StrCat("\ntotal: ", total, "\n")); return rc; } diff --git a/test/integration/BUILD b/test/integration/BUILD index 70082725f8d9..c555d3c0b4d5 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -311,6 +311,7 @@ envoy_cc_test( "//source/common/stats:stats_matcher_lib", "//source/extensions/filters/http/buffer:config", "//source/extensions/filters/http/health_check:config", + "//test/common/stats:stat_test_utility_lib", "@envoy_api//envoy/admin/v2alpha:config_dump_cc", "@envoy_api//envoy/config/metrics/v2:stats_cc", ], diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 41085b762d9c..95311a0180a9 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -10,6 +10,7 @@ #include "common/profiler/profiler.h" #include "common/stats/stats_matcher_impl.h" +#include "test/common/stats/stat_test_utility.h" #include "test/integration/utility.h" #include "test/test_common/utility.h" @@ -122,6 +123,7 @@ std::string ContentType(const BufferingStreamDecoderPtr& response) { } // namespace TEST_P(IntegrationAdminTest, Admin) { + Stats::TestUtil::SymbolTableCreatorTestPeer::setUseFakeSymbolTables(false); initialize(); BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( @@ -165,6 +167,15 @@ TEST_P(IntegrationAdminTest, Admin) { EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); + response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?recentlookups", + "", downstreamProtocol(), version_); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); + EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); + EXPECT_TRUE(absl::StartsWith(response->body(), "Date Time Lookup\n")) + << response->body(); + EXPECT_LT(30, response->body().size()); + response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?usedonly", "", downstreamProtocol(), version_); EXPECT_TRUE(response->complete()); diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 38b87e72fb7b..1adcce89c357 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -1354,6 +1354,15 @@ TEST_P(AdminInstanceTest, GetRequestJson) { HasSubstr("application/json")); } +TEST_P(AdminInstanceTest, RecentLookups) { + Http::HeaderMapImpl response_headers; + std::string body; + EXPECT_EQ(Http::Code::OK, admin_.request("/stats?recentlookups", "GET", response_headers, body)); + EXPECT_EQ("Date Time Lookup\n\ntotal: 0\n", body); + EXPECT_THAT(std::string(response_headers.ContentType()->value().getStringView()), + HasSubstr("text/plain")); +} + TEST_P(AdminInstanceTest, PostRequest) { Http::HeaderMapImpl response_headers; std::string body; From e8498a27a882e836a38a390939b4c4191f4adec6 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 3 Sep 2019 17:35:48 -0400 Subject: [PATCH 14/50] add function doc to new functions. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 38 +++++++++++++++++++++++++--- source/common/stats/recent_lookups.h | 22 +++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 61583cba3360..89fc5ce780ca 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -145,17 +145,31 @@ class SymbolTable { virtual void callWithStringView(StatName stat_name, const std::function& fn) const PURE; - virtual void trackRecentLookups(TimeSource&) PURE; - virtual void rememberSet(StatNameSet& stat_name_set) PURE; - virtual void forgetSet(StatNameSet& stat_name_set) PURE; + /** + * Enables tracking the most recent symbol-table lookups in a bounded queue, + * to help identify the source of potential mutex contention in highly + * threaded systems. Ideally, once a system has been initialized, there + * should be no more symbol-table lookups. + * + * @param time_source used to help capture the time when a lookup occurs. + */ + virtual void trackRecentLookups(TimeSource& time_source) PURE; using RecentLookupsFn = std::function; - virtual uint64_t getRecentLookups(const RecentLookupsFn&) PURE; + + /** + * Calls the provided function with the name of the most recently looked-up + * symbols, including lookups on any StatNameSets. + * + * @param iter the function to call for every recent item. + */ + virtual uint64_t getRecentLookups(const RecentLookupsFn& iter) PURE; private: friend struct HeapStatData; friend class StatNameStorage; friend class StatNameList; + friend class StatNameSet; // The following methods are private, but are called by friend classes // StatNameStorage and StatNameList, which must be friendly with SymbolTable @@ -192,6 +206,22 @@ class SymbolTable { * */ virtual StoragePtr encode(absl::string_view name) PURE; + + /** + * Enables trackRecentLookups to also track lookups that occur in + * StatNameSets, which has its own mutex. This function is called from + * StatNameSet's constructor. + * + * @param stat_name_set the set. + */ + virtual void rememberSet(StatNameSet& stat_name_set) PURE; + + /** + * Called by StatNameSet's destructor. + * + * @param stat_name_set the set. + */ + virtual void forgetSet(StatNameSet& stat_name_set) PURE; }; using SymbolTablePtr = std::unique_ptr; diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index f57685b2a302..1a4066646bbe 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -15,8 +15,12 @@ template class RecentLookups { public: explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} - // Records a lookup of an object of type T. Only the last 'Capacity' lookups - // are remembered. + /** + * Records a lookup of an object of type T. Only the last 'Capacity' lookups + * are remembered. + * + * @param t the item being looked up. + */ void lookup(T t) { ++total_; if (queue_.size() >= Capacity) { @@ -27,15 +31,25 @@ template class RecentLookups { using IterFn = std::function; - // Calls fn(item, timestamp) for each of the remembered lookups. + /** + * Calls fn(item, timestamp) for each of the remembered lookups. + * + * @param fn The function to call for every recently looked up item. + */ void forEach(IterFn fn) { for (ItemTime& item_time : queue_) { fn(item_time.first, item_time.second); } } - void clear() { queue_.clear(); } + /** + * @return the time-source associated with the object. + */ TimeSource& timeSource() { return time_source_; } + + /** + * @return the total number of lookups since tracking began. + */ uint64_t total() const { return total_; } private: From 491ab32b3be75b54f73c98ac5c6cfedca9bad60e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 3 Sep 2019 17:55:33 -0400 Subject: [PATCH 15/50] cleanup Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 50 +++++++++--------------- source/common/stats/symbol_table_impl.h | 6 +-- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index cebe27143bd4..db5010b9b117 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -162,7 +162,7 @@ std::string SymbolTableImpl::decodeSymbolVec(const SymbolVec& symbols) const { // Hold the lock only while decoding symbols. Thread::LockGuard lock(lock_); for (Symbol symbol : symbols) { - name_tokens.push_back(fromSymbolLockHeld(symbol)); + name_tokens.push_back(fromSymbol(symbol)); } } return absl::StrJoin(name_tokens, "."); @@ -190,32 +190,23 @@ void SymbolTableImpl::free(const StatName& stat_name) { Thread::LockGuard lock(lock_); for (Symbol symbol : symbols) { - freeSymbolLockHeld(symbol); - } -} - -void SymbolTableImpl::freeSymbol(Symbol symbol) { - Thread::LockGuard lock(lock_); - freeSymbolLockHeld(symbol); -} - -void SymbolTableImpl::freeSymbolLockHeld(Symbol symbol) { - auto decode_search = decode_map_.find(symbol); - ASSERT(decode_search != decode_map_.end()); + auto decode_search = decode_map_.find(symbol); + ASSERT(decode_search != decode_map_.end()); - auto encode_search = encode_map_.find(decode_search->second->toStringView()); - ASSERT(encode_search != encode_map_.end()); + auto encode_search = encode_map_.find(decode_search->second->toStringView()); + ASSERT(encode_search != encode_map_.end()); - // If that was the last remaining client usage of the symbol, erase the - // current mappings and add the now-unused symbol to the reuse pool. - // - // The "if (--EXPR.ref_count_)" pattern speeds up BM_CreateRace by 20% in - // symbol_table_speed_test.cc, relative to breaking out the decrement into a - // separate step, likely due to the non-trivial dereferences in EXPR. - if (--encode_search->second.ref_count_ == 0) { - decode_map_.erase(decode_search); - encode_map_.erase(encode_search); - pool_.push(symbol); + // If that was the last remaining client usage of the symbol, erase the + // current mappings and add the now-unused symbol to the reuse pool. + // + // The "if (--EXPR.ref_count_)" pattern speeds up BM_CreateRace by 20% in + // symbol_table_speed_test.cc, relative to breaking out the decrement into a + // separate step, likely due to the non-trivial dereferences in EXPR. + if (--encode_search->second.ref_count_ == 0) { + decode_map_.erase(decode_search); + encode_map_.erase(encode_search); + pool_.push(symbol); + } } } @@ -342,18 +333,13 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { return result; } -absl::string_view SymbolTableImpl::fromSymbolLockHeld(const Symbol symbol) const +absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_) { auto search = decode_map_.find(symbol); RELEASE_ASSERT(search != decode_map_.end(), "no such symbol"); return search->second->toStringView(); } -absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const { - Thread::LockGuard lock(lock_); - return fromSymbolLockHeld(symbol); -} - void SymbolTableImpl::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { if (pool_.empty()) { next_symbol_ = ++monotonic_counter_; @@ -378,7 +364,7 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { Thread::LockGuard lock(lock_); for (uint64_t i = 0, n = std::min(av.size(), bv.size()); i < n; ++i) { if (av[i] != bv[i]) { - bool ret = fromSymbolLockHeld(av[i]) < fromSymbolLockHeld(bv[i]); + bool ret = fromSymbol(av[i]) < fromSymbol(bv[i]); return ret; } } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 70517911992b..5df7e45056f6 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -208,17 +208,13 @@ class SymbolTableImpl : public SymbolTable { * @param symbol the individual symbol to be decoded. * @return absl::string_view the decoded string. */ - absl::string_view fromSymbolLockHeld(Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_); - absl::string_view fromSymbol(Symbol symbol) const; + absl::string_view fromSymbol(Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_); /** * Stages a new symbol for use. To be called after a successful insertion. */ void newSymbol(); - inline void freeSymbol(Symbol symbol); - void freeSymbolLockHeld(Symbol symbol) EXCLUSIVE_LOCKS_REQUIRED(lock_); - /** * Tokenizes name, finds or allocates symbols for each token, and adds them * to encoding. From 45a4feb32163e587600429fc35b5bd0671f11c7c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 3 Sep 2019 18:04:36 -0400 Subject: [PATCH 16/50] Privatize some functions. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.h | 4 ++-- source/common/stats/symbol_table_impl.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 1a4066646bbe..f5080a16563d 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -36,8 +36,8 @@ template class RecentLookups { * * @param fn The function to call for every recently looked up item. */ - void forEach(IterFn fn) { - for (ItemTime& item_time : queue_) { + void forEach(IterFn fn) const { + for (const ItemTime& item_time : queue_) { fn(item_time.first, item_time.second); } } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 5df7e45056f6..6b5a76033c33 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -685,12 +685,12 @@ class StatNameSet { */ StatName add(absl::string_view str) { return pool_.add(str); } - void trackRecentLookups(TimeSource& time_source); - SymbolTable& symbolTable() { return symbol_table_; } +private: + friend class SymbolTableImpl; + void trackRecentLookups(TimeSource& time_source); uint64_t getRecentLookups(const RecentLookups::IterFn& iter); -private: Stats::SymbolTable& symbol_table_; Stats::StatNamePool pool_; absl::Mutex mutex_; From 90327f7a786ce3257907903c4312e88611db9ade Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 6 Sep 2019 16:28:20 -0400 Subject: [PATCH 17/50] add doc. Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 19 +++++++- source/docs/stats.md | 81 ++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index c681deb55951..5f63bbe41900 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -200,7 +200,7 @@ modify different aspects of the server: .. http:post:: /reset_counters Reset all counters to zero. This is useful along with :http:get:`/stats` during debugging. Note - that this does not drop any data sent to statsd. It just effects local output of the + that this does not drop any data sent to statsd. It just affects local output of the :http:get:`/stats` command. .. http:get:: /server_info @@ -354,6 +354,23 @@ modify different aspects of the server: Envoy has updated (counters incremented at least once, gauges changed at least once, and histograms added to at least once) + .. http:get:: /stats?recentlookups + + This endpoint is intended for Envoy developers debugging potential contention issues + in the stats system. + + It emits a table of stat names that were recently accessed as strings by Envoy. In + general, strings should be converted into StatNames, counters, gauges, and histograms + by Envoy code only during startup or when receiving a new configuration via xDS. This + is because when stats are looked up as strings they must take a global symbol table + lock. During startup this is expected, but in response to user requests on high + core-count machines, this can cause performance issues due to mutex contention. + + See `stats.md `_ + for more details. + + Note also that actual mutex contention can be tracked via `/contention`. + .. _operations_admin_interface_runtime: .. http:get:: /runtime diff --git a/source/docs/stats.md b/source/docs/stats.md index 965dcb569d16..28bb9707b523 100644 --- a/source/docs/stats.md +++ b/source/docs/stats.md @@ -12,10 +12,9 @@ binary program restarts. The metrics are tracked as: In order to support restarting the Envoy binary program without losing counter and gauge values, they are passed from parent to child in an RPC protocol. They were previously held in shared memory, which imposed various restrictions. -Unlike the shared memory implementation, the RPC passing *requires special indication -in source/common/stats/stat_merger.cc when simple addition is not appropriate for -combining two instances of a given stat*. - +Unlike the shared memory implementation, the RPC passing *requires a mode-bit specified +when constructing gauges indicating whether it should be accumulated across hot-restarts*. + ## Performance and Thread Local Storage A key tenant of the Envoy architecture is high performance on machines with @@ -80,7 +79,8 @@ followed. Stat names are replicated in several places in various forms. - * Held with the stat values, in `HeapStatData` + * Held with the stat values, in `CounterImpl` and `GaugeImpl`, which are defined in + [allocator_impl.cc](https://github.com/envoyproxy/envoy/blob/master/source/common/stats/allocator_impl.cc) * In [MetricImpl](https://github.com/envoyproxy/envoy/blob/master/source/common/stats/metric_impl.h) in a transformed state, with tags extracted into vectors of name/value strings. * In static strings across the codebase where stats are referenced @@ -90,7 +90,7 @@ Stat names are replicated in several places in various forms. There are stat maps in `ThreadLocalStore` for capturing all stats in a scope, and each per-thread caches. However, they don't duplicate the stat names. -Instead, they reference the `char*` held in the `HeapStatData` itself, and thus +Instead, they reference the `StatName` held in the `CounterImpl` or `GaugeImpl`, and thus are relatively cheap; effectively those maps are all pointer-to-pointer. For this to be safe, cache lookups from locally scoped strings must use `.find` @@ -120,36 +120,49 @@ etc, must explicitly store partial stat-names their class instances, which later can be composed dynamically at runtime in order to fully elaborate counters, gauges, etc, without taking symbol-table locks, via `SymbolTable::join()`. +### `StatNamePool` and `StatNameSet` + +These two helper classes evolved to make it easy to deploy the symbol table API +across the codebase. + +`StatNamePool` provides pooled allocation for any number of +`StatName` objects, and is intended to be held in a data structure alongside the +`const StatName` member variables. Most names should be established during +process initializion or in response to xDS updates. + +`StatNameSet` provides some associative lookups at runtime, using two maps: a +static map and a dynamic map. + ### Current State and Strategy To Deploy Symbol Tables -As of April 1, 2019, there are a fairly large number of files that directly -lookup stats by name, e.g. via `Stats::Scope::counter(const std::string&)` in -the request path. In most cases, this runtime lookup concatenates the scope name -with a string literal or other request-dependent token to form the stat name, so -it is not possible to fully memoize the stats at startup; there must be a -runtime name lookup. - -If a PR is issued that changes the underlying representation of a stat name to -be a symbol table entry then each stat-name will need to be transformed -whenever names are looked up, which would add CPU overhead and lock contention -in the request-path, violating one of the principles of Envoy's [threading -model](https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310). Before -issuing such a PR we need to first iterate through the codebase memoizing the -symbols that are used to form stat-names. - -To resolve this chicken-and-egg challenge of switching to symbol-table stat-name -representation without suffering a temporary loss of performance, we employ a -["fake" symbol table -implementation](https://github.com/envoyproxy/envoy/blob/master/source/common/stats/fake_symbol_table_impl.h). -This implemenation uses elaborated strings as an underlying representation, but -implements the same API as the ["real" -implemention](https://github.com/envoyproxy/envoy/blob/master/source/common/stats/symbol_table_impl.h). -The underlying string representation means that there is minimal runtime -overhead compared to the current state. But once all stat-allocation call-sites -have been converted to use the abstract [SymbolTable -API](https://github.com/envoyproxy/envoy/blob/master/include/envoy/stats/symbol_table.h), -the real implementation can be swapped in, the space savings realized, and the -fake implementation deleted. +As of September 5, 2019, the symbol table API has been integrated into the +production code, using a temporary ["fake" symbol table +implementation](https://github.com/envoyproxy/envoy/blob/master/source/common/stats/fake_symbol_table_impl.h). This +fake has enabled us to incrementally transform the codebase to pre-symbolize +names as much as possible, avoiding contention in the hot-path. + +There are no longer any explicit production calls to create counters +or gauges directly from a string via `Stats::Scope::counter(const +std::string&)`, though they are ubiquitous in tests. There is also a +`check_format` protection against reintroducting production calls to +`counter()`. + +However, there are still several ways to create hot-path contention +looking up stats by name, and there is no bulletproof way to prevent it from +occurring. + * The [stats macros](https://github.com/envoyproxy/envoy/blob/master/include/envoy/stats/stats_macros.h) may be used in a data structure which is constructed in response to requests. + * An explicit symbol-table lookup, via `StatNamePool` or `StatNameSet` can be + made in the hot path. + +It is difficult to search for those scenarios in the source code or prevent them +with a format-check, but we can determine whether symbol-table lookups are +occuring during via an admin endpoint that shows 20 recent lookups by name, at +`ENVOY_HOST:ADMIN_PORT/stats?recentlookups`. This works only when real symbol +tables are enabled, via command-line option `--use-fake-symbol-table 0`. + +Once we are confident we've removed all hot-path symbol-table lookups, ideally +through usage of real symbol tables in production, examining that endpoint, we +can enable real symbol tables by default. ## Tags and Tag Extraction From 7b867b3b7a63aa88cf5a6464a3cf83e16fd97fd6 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 6 Sep 2019 16:29:08 -0400 Subject: [PATCH 18/50] format Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 2 +- source/docs/stats.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 5f63bbe41900..57bc04e4fbe7 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -359,7 +359,7 @@ modify different aspects of the server: This endpoint is intended for Envoy developers debugging potential contention issues in the stats system. - It emits a table of stat names that were recently accessed as strings by Envoy. In + It emits a table of stat names that were recently accessed as strings by Envoy. In general, strings should be converted into StatNames, counters, gauges, and histograms by Envoy code only during startup or when receiving a new configuration via xDS. This is because when stats are looked up as strings they must take a global symbol table diff --git a/source/docs/stats.md b/source/docs/stats.md index 28bb9707b523..c247495e6372 100644 --- a/source/docs/stats.md +++ b/source/docs/stats.md @@ -156,7 +156,7 @@ occurring. It is difficult to search for those scenarios in the source code or prevent them with a format-check, but we can determine whether symbol-table lookups are -occuring during via an admin endpoint that shows 20 recent lookups by name, at +occurring during via an admin endpoint that shows 20 recent lookups by name, at `ENVOY_HOST:ADMIN_PORT/stats?recentlookups`. This works only when real symbol tables are enabled, via command-line option `--use-fake-symbol-table 0`. From fd23fbe80ba0829be3f592ea7dd9deb3368da8f5 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 17 Sep 2019 19:08:51 -0400 Subject: [PATCH 19/50] Add logging and simplify impl slightly by making it untemplated and just committing to strings for simplicity. Signed-off-by: Joshua Marantz --- source/common/stats/BUILD | 6 ++- source/common/stats/recent_lookups.cc | 57 ++++++++++++++++++++++++ source/common/stats/recent_lookups.h | 29 +++++------- source/common/stats/symbol_table_impl.cc | 39 +++++++--------- source/common/stats/symbol_table_impl.h | 6 +-- test/common/stats/BUILD | 1 + test/common/stats/recent_lookups_test.cc | 15 ++++++- 7 files changed, 104 insertions(+), 49 deletions(-) create mode 100644 source/common/stats/recent_lookups.cc diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 9f8c12b8412c..360a510b63ec 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -89,8 +89,12 @@ envoy_cc_library( envoy_cc_library( name = "recent_lookups_lib", + srcs = ["recent_lookups.cc"], hdrs = ["recent_lookups.h"], - deps = ["//include/envoy/common:time_interface"], + deps = [ + "//include/envoy/common:time_interface", + "//source/common/common:logger_lib", + ], ) envoy_cc_library( diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc new file mode 100644 index 000000000000..ecffb6614832 --- /dev/null +++ b/source/common/stats/recent_lookups.cc @@ -0,0 +1,57 @@ +#include "common/stats/recent_lookups.h" + +#include +#include + +#include "envoy/common/time.h" + +#include "common/common/logger.h" + +#include "absl/strings/str_join.h" + +namespace Envoy { +namespace Stats { + +namespace { +constexpr size_t Capacity = 10; +constexpr uint64_t LogIntervalSec = 300; +} // namespace + +void RecentLookups::lookup(absl::string_view str) { + ++total_; + if (queue_.size() >= Capacity) { + queue_.pop_back(); + } + SystemTime now = time_source_.systemTime(); + queue_.push_front(ItemTime(std::string(str), now)); + if (now <= last_log_time_) { // handle black-swan event for non-monotonic time. + return; + } + std::chrono::seconds duration = + std::chrono::duration_cast(now - last_log_time_); + + if (duration >= std::chrono::seconds(LogIntervalSec)) { + std::vector message; + forEach([&message, this](absl::string_view item, SystemTime time) { + if (time > last_log_time_) { + message.push_back(item); + } + }); + ENVOY_LOG_MISC(warn, "Recent lookups for {}", absl::StrJoin(message, ", ")); + last_log_time_ = now; + } +} + +/** + * Calls fn(item, timestamp) for each of the remembered lookups. + * + * @param fn The function to call for every recently looked up item. + */ +void RecentLookups::forEach(IterFn fn) const { + for (const ItemTime& item_time : queue_) { + fn(item_time.first, item_time.second); + } +} + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index f5080a16563d..1692461503cb 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -1,46 +1,36 @@ #pragma once -#include #include #include #include #include "envoy/common/time.h" +#include "absl/strings/string_view.h" + namespace Envoy { namespace Stats { // Remembers the last 'Capacity' items passed to lookup(). -template class RecentLookups { +class RecentLookups { public: explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} /** - * Records a lookup of an object of type T. Only the last 'Capacity' lookups - * are remembered. + * Records a lookup of a string. Only the last 'Capacity' lookups are remembered. * - * @param t the item being looked up. + * @param str the item being looked up. */ - void lookup(T t) { - ++total_; - if (queue_.size() >= Capacity) { - queue_.pop_back(); - } - queue_.push_front(ItemTime(t, time_source_.systemTime())); - } + void lookup(absl::string_view str); - using IterFn = std::function; + using IterFn = std::function; /** * Calls fn(item, timestamp) for each of the remembered lookups. * * @param fn The function to call for every recently looked up item. */ - void forEach(IterFn fn) const { - for (const ItemTime& item_time : queue_) { - fn(item_time.first, item_time.second); - } - } + void forEach(IterFn fn) const; /** * @return the time-source associated with the object. @@ -53,10 +43,11 @@ template class RecentLookups { uint64_t total() const { return total_; } private: - using ItemTime = std::pair; + using ItemTime = std::pair; std::deque queue_; TimeSource& time_source_; uint64_t total_{0}; + SystemTime last_log_time_; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index db5010b9b117..0407ad25750b 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -213,7 +213,7 @@ void SymbolTableImpl::free(const StatName& stat_name) { void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { { Thread::LockGuard lock(lock_); - recent_lookups_ = std::make_unique>(time_source); + recent_lookups_ = std::make_unique(time_source); } { @@ -227,16 +227,8 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { uint64_t total = 0; struct LookupData { - StatName stat_name_; - std::string name_; // Used if stat_name is empty. + std::string name_; SystemTime time_; - - std::string name(SymbolTableImpl& symbol_table) const { - if (stat_name_.empty()) { - return name_; - } - return symbol_table.toString(stat_name_); - }; }; std::vector lookup_data; @@ -246,35 +238,34 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { - total += stat_name_set->getRecentLookups([&lookup_data](StatName stat_name, SystemTime time) { - ASSERT(!stat_name.empty()); - lookup_data.push_back({stat_name, "", time}); - }); + total += + stat_name_set->getRecentLookups([&lookup_data](absl::string_view str, SystemTime time) { + lookup_data.push_back({std::string(str), time}); + }); } } { Thread::LockGuard lock(lock_); if (recent_lookups_ != nullptr) { - recent_lookups_->forEach([&lookup_data](const std::string& str, SystemTime time) + recent_lookups_->forEach([&lookup_data](absl::string_view str, SystemTime time) NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({StatName(), str, time}); + lookup_data.push_back({std::string(str), time}); }); total += recent_lookups_->total(); } } - std::sort(lookup_data.begin(), lookup_data.end(), - [this](const LookupData& a, const LookupData& b) -> bool { + [](const LookupData& a, const LookupData& b) -> bool { if (a.time_ == b.time_) { - return a.name(*this) < b.name(*this); + return a.name_ < b.name_; } return a.time_ > b.time_; // Sort latest first. }); const LookupData* prev = nullptr; for (const LookupData& lookup : lookup_data) { - if (prev == nullptr || prev->name(*this) != lookup.name(*this)) { - iter(lookup.name(*this), lookup.time_); + if (prev == nullptr || prev->name_ != lookup.name_) { + iter(lookup.name_, lookup.time_); prev = &lookup; } } @@ -561,7 +552,7 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { if (stat_name.empty()) { // Note that builtin_stat_names_ already has one for "". stat_name = pool_.add(token); if (recent_lookups_ != nullptr) { - recent_lookups_->lookup(stat_name); + recent_lookups_->lookup(token); } } return stat_name; @@ -569,10 +560,10 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { void StatNameSet::trackRecentLookups(TimeSource& time_source) { absl::MutexLock lock(&mutex_); - recent_lookups_ = std::make_unique>(time_source); + recent_lookups_ = std::make_unique(time_source); } -uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { +uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { absl::MutexLock lock(&mutex_); if (recent_lookups_ == nullptr) { return 0; diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 6a7cdad1f0ce..7a011859509d 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -248,7 +248,7 @@ 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_ GUARDED_BY(lock_); - std::unique_ptr> recent_lookups_ GUARDED_BY(lock_); + std::unique_ptr recent_lookups_ GUARDED_BY(lock_); absl::flat_hash_set stat_name_sets_ GUARDED_BY(stat_name_set_mutex_); }; @@ -696,7 +696,7 @@ class StatNameSet { friend class SymbolTableImpl; void trackRecentLookups(TimeSource& time_source); - uint64_t getRecentLookups(const RecentLookups::IterFn& iter); + uint64_t getRecentLookups(const RecentLookups::IterFn& iter); Stats::SymbolTable& symbol_table_; Stats::StatNamePool pool_; @@ -704,7 +704,7 @@ class StatNameSet { using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); - std::unique_ptr> recent_lookups_ GUARDED_BY(mutex_); + std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); }; } // namespace Stats diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 3ad0a1a790e6..96d46ad3b017 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -45,6 +45,7 @@ envoy_cc_test( deps = [ "//source/common/common:utility_lib", "//source/common/stats:recent_lookups_lib", + "//test/test_common:logging_lib", "//test/test_common:simulated_time_system_lib", ], ) diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index facb8eca4b22..d906017e990a 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -4,6 +4,7 @@ #include "common/common/utility.h" #include "common/stats/recent_lookups.h" +#include "test/test_common/logging.h" #include "test/test_common/simulated_time_system.h" #include "absl/strings/str_cat.h" @@ -22,7 +23,7 @@ class RecentLookupsTest : public testing::Test { std::string joinLookups() { std::vector accum; - recent_lookups_.forEach([&accum](std::string item, SystemTime time) { + recent_lookups_.forEach([&accum](absl::string_view item, SystemTime time) { DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", item)); }); @@ -31,7 +32,7 @@ class RecentLookupsTest : public testing::Test { } Event::SimulatedTimeSystem time_system_; - RecentLookups recent_lookups_; + RecentLookups recent_lookups_; }; TEST_F(RecentLookupsTest, Empty) { EXPECT_EQ("", joinLookups()); } @@ -79,6 +80,16 @@ TEST_F(RecentLookupsTest, RepeatDrop) { joinLookups()); } +TEST_F(RecentLookupsTest, Log) { + EXPECT_LOG_CONTAINS("warn", "Recent lookups for alpha", recent_lookups_.lookup("alpha")); + EXPECT_NO_LOGS(recent_lookups_.lookup("beta")); + time_system_.sleep(std::chrono::seconds(100)); + EXPECT_NO_LOGS(recent_lookups_.lookup("gamma")); + time_system_.sleep(std::chrono::seconds(250)); + const Envoy::ExpectedLogMessages messages{{"warn", "gamma"}, {"warn", "delta"}}; + EXPECT_LOG_CONTAINS_ALL_OF(messages, recent_lookups_.lookup("delta")); +} + } // namespace } // namespace Stats } // namespace Envoy From 1bf77732e2d0eac23be6ee2f9bab53d284c48154 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 15:50:08 -0400 Subject: [PATCH 20/50] added speed test. Signed-off-by: Joshua Marantz --- .../common/stats/recent_lookups_speed_test.cc | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/common/stats/recent_lookups_speed_test.cc diff --git a/test/common/stats/recent_lookups_speed_test.cc b/test/common/stats/recent_lookups_speed_test.cc new file mode 100644 index 000000000000..a1dc5177d5f2 --- /dev/null +++ b/test/common/stats/recent_lookups_speed_test.cc @@ -0,0 +1,41 @@ +// Note: this should be run with --compilation_mode=opt, and would benefit from a +// quiescent system with disabled cstate power management. +// +// NOLINT(namespace-envoy) + +#include "common/stats/recent_lookups.h" +#include "common/runtime/runtime_impl.h" + +#include "absl/strings/str_cat.h" + +#include "benchmark/benchmark.h" + + +static void BM_Lookups(benchmark::State& state) { + Envoy::Runtime::RandomGeneratorImpl random; + const size_t vec_size = 1000; + const size_t lookup_variants = 50; + std::vector lookups; + for (size_t i = 0; i < vec_size; ++i) { + lookups.push_back(absl::StrCat("lookup #", random.random() % lookup_variants)); + } + Envoy::Stats::RecentLookups recent_lookups; + size_t index = 0; + for (auto _ : state) { + recent_lookups.lookup(lookups[++index % vec_size]); + } +} +BENCHMARK(BM_Lookups); + + +int main(int argc, char** argv) { + Envoy::Thread::MutexBasicLockable lock; + Envoy::Logger::Context logger_context(spdlog::level::warn, + Envoy::Logger::Logger::DEFAULT_LOG_FORMAT, lock); + benchmark::Initialize(&argc, argv); + + if (benchmark::ReportUnrecognizedArguments(argc, argv)) { + return 1; + } + benchmark::RunSpecifiedBenchmarks(); +} From c9498932945e45a3aa344b0c631c67cb1aa7bc23 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 15:50:32 -0400 Subject: [PATCH 21/50] switch to lru Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 4 +- source/common/stats/BUILD | 3 +- source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/recent_lookups.cc | 56 ++++++++------- source/common/stats/recent_lookups.h | 37 +++++++--- source/common/stats/symbol_table_impl.cc | 59 ++++++++-------- source/common/stats/symbol_table_impl.h | 9 +-- test/common/stats/BUILD | 13 ++++ test/common/stats/recent_lookups_test.cc | 71 +++++++++++--------- test/common/stats/symbol_table_impl_test.cc | 2 +- 10 files changed, 153 insertions(+), 103 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 89fc5ce780ca..1fa527b672f7 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -153,9 +153,9 @@ class SymbolTable { * * @param time_source used to help capture the time when a lookup occurs. */ - virtual void trackRecentLookups(TimeSource& time_source) PURE; + //virtual void trackRecentLookups(TimeSource& time_source) PURE; - using RecentLookupsFn = std::function; + using RecentLookupsFn = std::function; /** * Calls the provided function with the name of the most recently looked-up diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 360a510b63ec..71ef98628baf 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -92,8 +92,7 @@ envoy_cc_library( srcs = ["recent_lookups.cc"], hdrs = ["recent_lookups.h"], deps = [ - "//include/envoy/common:time_interface", - "//source/common/common:logger_lib", + "//source/common/common:assert_lib", ], ) diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 4a30dc08916e..690d1429bf35 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -125,7 +125,7 @@ class FakeSymbolTableImpl : public SymbolTable { fn(toStringView(stat_name)); } - void trackRecentLookups(TimeSource&) override {} + //void trackRecentLookups(TimeSource&) override {} void rememberSet(StatNameSet&) override {} void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index ecffb6614832..5398ce1c162b 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -3,9 +3,10 @@ #include #include -#include "envoy/common/time.h" +//#include "envoy/common/time.h" -#include "common/common/logger.h" +#include "common/common/assert.h" +//#include "common/common/logger.h" #include "absl/strings/str_join.h" @@ -14,31 +15,36 @@ namespace Stats { namespace { constexpr size_t Capacity = 10; -constexpr uint64_t LogIntervalSec = 300; +//constexpr uint64_t LogIntervalSec = 300; } // namespace void RecentLookups::lookup(absl::string_view str) { ++total_; - if (queue_.size() >= Capacity) { - queue_.pop_back(); - } - SystemTime now = time_source_.systemTime(); - queue_.push_front(ItemTime(std::string(str), now)); - if (now <= last_log_time_) { // handle black-swan event for non-monotonic time. - return; - } - std::chrono::seconds duration = - std::chrono::duration_cast(now - last_log_time_); - - if (duration >= std::chrono::seconds(LogIntervalSec)) { - std::vector message; - forEach([&message, this](absl::string_view item, SystemTime time) { - if (time > last_log_time_) { - message.push_back(item); - } - }); - ENVOY_LOG_MISC(warn, "Recent lookups for {}", absl::StrJoin(message, ", ")); - last_log_time_ = now; + + Map::iterator map_iter = map_.find(str); + if (map_iter != map_.end()) { + // The item is already in the list, but we need to bump its count and move + // it to the front, so we must re-order the list, which will invalidate the + // iterators to i. + List::iterator list_iter = map_iter->second; + ItemCount item_count = std::move(*list_iter); + list_.erase(list_iter); + ++item_count.count_; + list_.push_front(std::move(item_count)); + map_iter->second = list_.begin(); + } else { + ASSERT(list_.size() <= Capacity); + // Evict oldest item if needed. + if (list_.size() >= Capacity) { + ItemCount item_count = std::move(list_.back()); + list_.pop_back(); + map_.erase(item_count.item_); + } + + // The string storage is in the list entry. + list_.push_front(ItemCount{std::string(str), 1}); + List::iterator list_iter = list_.begin(); + map_[list_iter->item_] = list_iter; } } @@ -48,8 +54,8 @@ void RecentLookups::lookup(absl::string_view str) { * @param fn The function to call for every recently looked up item. */ void RecentLookups::forEach(IterFn fn) const { - for (const ItemTime& item_time : queue_) { - fn(item_time.first, item_time.second); + for (const ItemCount& item_count : list_) { + fn(item_count.item_, item_count.count_); } } diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 1692461503cb..de73a0de9d79 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -1,11 +1,14 @@ #pragma once -#include #include +#include #include -#include "envoy/common/time.h" +//#include "envoy/common/time.h" +//#include "common/common/hash.h" + +#include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -14,7 +17,7 @@ namespace Stats { // Remembers the last 'Capacity' items passed to lookup(). class RecentLookups { public: - explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} + //explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} /** * Records a lookup of a string. Only the last 'Capacity' lookups are remembered. @@ -23,7 +26,7 @@ class RecentLookups { */ void lookup(absl::string_view str); - using IterFn = std::function; + using IterFn = std::function; /** * Calls fn(item, timestamp) for each of the remembered lookups. @@ -35,7 +38,7 @@ class RecentLookups { /** * @return the time-source associated with the object. */ - TimeSource& timeSource() { return time_source_; } + //TimeSource& timeSource() { return time_source_; } /** * @return the total number of lookups since tracking began. @@ -43,11 +46,27 @@ class RecentLookups { uint64_t total() const { return total_; } private: - using ItemTime = std::pair; - std::deque queue_; - TimeSource& time_source_; + struct ItemCount { + std::string item_; + int64_t count_; + + /*bool operator<(const ItemCount& that) const { + if (count_ == that.count_) { + return item_ < that.item_; + } + return count_ < that.count_; + } + + template friend H AbslHashValue(H h, const ItemCount& item_count) { + return H::combine(std::move(h), item_count.item_); + }*/ + }; + + using List = std::list; + List list_; + using Map = absl::flat_hash_map; + Map map_; uint64_t total_{0}; - SystemTime last_log_time_; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 0407ad25750b..34f3132006eb 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -126,9 +126,7 @@ void SymbolTableImpl::addTokensToEncoding(const absl::string_view name, Encoding // ref-counts in this. { Thread::LockGuard lock(lock_); - if (recent_lookups_ != nullptr) { - recent_lookups_->lookup(std::string(name)); - } + recent_lookups_.lookup(name); for (auto& token : tokens) { symbols.push_back(toSymbol(token)); } @@ -210,12 +208,12 @@ void SymbolTableImpl::free(const StatName& stat_name) { } } + /* void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { { Thread::LockGuard lock(lock_); recent_lookups_ = std::make_unique(time_source); } - { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { @@ -223,12 +221,13 @@ void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { } } } +*/ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { uint64_t total = 0; struct LookupData { std::string name_; - SystemTime time_; + uint64_t count_; }; std::vector lookup_data; @@ -239,33 +238,33 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { total += - stat_name_set->getRecentLookups([&lookup_data](absl::string_view str, SystemTime time) { - lookup_data.push_back({std::string(str), time}); + stat_name_set->getRecentLookups([&lookup_data](absl::string_view str, uint64_t count) { + lookup_data.push_back({std::string(str), count}); }); } } { Thread::LockGuard lock(lock_); - if (recent_lookups_ != nullptr) { - recent_lookups_->forEach([&lookup_data](absl::string_view str, SystemTime time) + //if (recent_lookups_ != nullptr) { + recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({std::string(str), time}); + lookup_data.push_back({std::string(str), count}); }); - total += recent_lookups_->total(); - } + total += recent_lookups_.total(); + //} } std::sort(lookup_data.begin(), lookup_data.end(), [](const LookupData& a, const LookupData& b) -> bool { - if (a.time_ == b.time_) { + if (a.count_ == b.count_) { return a.name_ < b.name_; } - return a.time_ > b.time_; // Sort latest first. + return a.count_ > b.count_; // Sort largest-counts first. }); const LookupData* prev = nullptr; for (const LookupData& lookup : lookup_data) { if (prev == nullptr || prev->name_ != lookup.name_) { - iter(lookup.name_, lookup.time_); + iter(lookup.name_, lookup.count_); prev = &lookup; } } @@ -277,20 +276,20 @@ void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { // stat_name_set.trackRecentLookups() below, as that will result in a // false-positive in absl::Mutex's detection analysis. It is easy enough to // work around by capturing the TimeSource and then releasing lock_. - TimeSource* time_source = nullptr; + /*TimeSource* time_source = nullptr; { Thread::LockGuard lock(lock_); if (recent_lookups_ != nullptr) { time_source = &recent_lookups_->timeSource(); } - } - if (time_source != nullptr) { - stat_name_set.trackRecentLookups(*time_source); - } - { + }*/ + //if (time_source != nullptr) { + //stat_name_set.trackRecentLookups()*time_source); + //} + //{ Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.insert(&stat_name_set); - } + //} } void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { @@ -551,25 +550,27 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { Stats::StatName& stat_name = dynamic_stat_names_[token]; if (stat_name.empty()) { // Note that builtin_stat_names_ already has one for "". stat_name = pool_.add(token); - if (recent_lookups_ != nullptr) { - recent_lookups_->lookup(token); - } + //if (recent_lookups_ != nullptr) { + recent_lookups_.lookup(token); + //} } return stat_name; } +/* void StatNameSet::trackRecentLookups(TimeSource& time_source) { absl::MutexLock lock(&mutex_); recent_lookups_ = std::make_unique(time_source); } +*/ uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { absl::MutexLock lock(&mutex_); - if (recent_lookups_ == nullptr) { + /*if (recent_lookups_ == nullptr) { return 0; - } - recent_lookups_->forEach(iter); - return recent_lookups_->total(); + }*/ + recent_lookups_.forEach(iter); + return recent_lookups_.total(); } } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 7a011859509d..d5478e7b500f 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -161,7 +161,7 @@ class SymbolTableImpl : public SymbolTable { return bytes; } - void trackRecentLookups(TimeSource& timeSource) override; + //void trackRecentLookups(TimeSource& timeSource) override; void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; @@ -248,7 +248,7 @@ 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_ GUARDED_BY(lock_); - std::unique_ptr recent_lookups_ GUARDED_BY(lock_); + RecentLookups recent_lookups_ GUARDED_BY(lock_); absl::flat_hash_set stat_name_sets_ GUARDED_BY(stat_name_set_mutex_); }; @@ -695,7 +695,7 @@ class StatNameSet { private: friend class SymbolTableImpl; - void trackRecentLookups(TimeSource& time_source); + //void trackRecentLookups(TimeSource& time_source); uint64_t getRecentLookups(const RecentLookups::IterFn& iter); Stats::SymbolTable& symbol_table_; @@ -704,7 +704,8 @@ class StatNameSet { using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); - std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); + //std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); + RecentLookups recent_lookups_ GUARDED_BY(mutex_); }; } // namespace Stats diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 96d46ad3b017..55dc8b9d4396 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -50,6 +50,19 @@ envoy_cc_test( ], ) +envoy_cc_test_binary( + name = "recent_lookups_speed_test", + srcs = ["recent_lookups_speed_test.cc"], + external_deps = [ + "benchmark", + ], + deps = [ + "//source/common/common:utility_lib", + "//source/common/runtime:runtime_lib", + "//source/common/stats:recent_lookups_lib", + ], +) + envoy_cc_test( name = "stat_merger_test", srcs = ["stat_merger_test.cc"], diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index d906017e990a..beb0a90d8317 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -1,7 +1,7 @@ #include #include -#include "common/common/utility.h" +//#include "common/common/utility.h" #include "common/stats/recent_lookups.h" #include "test/test_common/logging.h" @@ -16,18 +16,27 @@ namespace { class RecentLookupsTest : public testing::Test { protected: - RecentLookupsTest() : recent_lookups_(time_system_) { + RecentLookupsTest() { //: recent_lookups_(time_system_) { const uint64_t years = 365 * 24 * 3600; time_system_.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); } std::string joinLookups() { + using ItemCount = std::pair; + std::vector items; + recent_lookups_.forEach([&items](absl::string_view item, uint64_t count) { + items.push_back(ItemCount(std::string(item), count)); + }); + std::sort(items.begin(), items.end(), [](const ItemCount& a, const ItemCount& b) -> bool { + if (a.second == b.second) { + return a.first < b.first; + } + return a.second < b.second; + }); std::vector accum; - recent_lookups_.forEach([&accum](absl::string_view item, SystemTime time) { - DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); - accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", item)); - }); - std::sort(accum.begin(), accum.end()); + for (auto item : items) { + accum.push_back(absl::StrCat(item.second, ": ", item.first)); + } return StringUtil::join(accum, " "); } @@ -39,7 +48,7 @@ TEST_F(RecentLookupsTest, Empty) { EXPECT_EQ("", joinLookups()); } TEST_F(RecentLookupsTest, One) { recent_lookups_.lookup("Hello"); - EXPECT_EQ("2009-12-22,00:00:00;Item=Hello", joinLookups()); + EXPECT_EQ("1: Hello", joinLookups()); } TEST_F(RecentLookupsTest, DropOne) { @@ -47,40 +56,42 @@ TEST_F(RecentLookupsTest, DropOne) { recent_lookups_.lookup(absl::StrCat("lookup", i)); time_system_.sleep(std::chrono::seconds(1)); } - EXPECT_EQ("2009-12-22,00:00:01;Item=lookup1 " - "2009-12-22,00:00:02;Item=lookup2 " - "2009-12-22,00:00:03;Item=lookup3 " - "2009-12-22,00:00:04;Item=lookup4 " - "2009-12-22,00:00:05;Item=lookup5 " - "2009-12-22,00:00:06;Item=lookup6 " - "2009-12-22,00:00:07;Item=lookup7 " - "2009-12-22,00:00:08;Item=lookup8 " - "2009-12-22,00:00:09;Item=lookup9 " - "2009-12-22,00:00:10;Item=lookup10", + EXPECT_EQ("1: lookup1 " + "1: lookup10 " + "1: lookup2 " + "1: lookup3 " + "1: lookup4 " + "1: lookup5 " + "1: lookup6 " + "1: lookup7 " + "1: lookup8 " + "1: lookup9", joinLookups()); } TEST_F(RecentLookupsTest, RepeatDrop) { + recent_lookups_.lookup("drop_early"); for (int i = 0; i < 11; ++i) { recent_lookups_.lookup(absl::StrCat("lookup", i)); time_system_.sleep(std::chrono::seconds(1)); recent_lookups_.lookup(absl::StrCat("lookup", i)); time_system_.sleep(std::chrono::seconds(1)); } - EXPECT_EQ("2009-12-22,00:00:12;Item=lookup6 " - "2009-12-22,00:00:13;Item=lookup6 " - "2009-12-22,00:00:14;Item=lookup7 " - "2009-12-22,00:00:15;Item=lookup7 " - "2009-12-22,00:00:16;Item=lookup8 " - "2009-12-22,00:00:17;Item=lookup8 " - "2009-12-22,00:00:18;Item=lookup9 " - "2009-12-22,00:00:19;Item=lookup9 " - "2009-12-22,00:00:20;Item=lookup10 " - "2009-12-22,00:00:21;Item=lookup10", + recent_lookups_.lookup("add_late"); + EXPECT_EQ("1: add_late " + "2: lookup10 " + "2: lookup2 " + "2: lookup3 " + "2: lookup4 " + "2: lookup5 " + "2: lookup6 " + "2: lookup7 " + "2: lookup8 " + "2: lookup9", joinLookups()); } -TEST_F(RecentLookupsTest, Log) { +/*TEST_F(RecentLookupsTest, Log) { EXPECT_LOG_CONTAINS("warn", "Recent lookups for alpha", recent_lookups_.lookup("alpha")); EXPECT_NO_LOGS(recent_lookups_.lookup("beta")); time_system_.sleep(std::chrono::seconds(100)); @@ -88,7 +99,7 @@ TEST_F(RecentLookupsTest, Log) { time_system_.sleep(std::chrono::seconds(250)); const Envoy::ExpectedLogMessages messages{{"warn", "gamma"}, {"warn", "delta"}}; EXPECT_LOG_CONTAINS_ALL_OF(messages, recent_lookups_.lookup("delta")); -} + }*/ } // namespace } // namespace Stats diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 18a069fd69ff..84ee21e4f668 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -576,7 +576,7 @@ TEST_P(StatNameTest, RecentLookups) { const uint64_t years = 365 * 24 * 3600; time_system.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); StatNameSet set1(*table_); - table_->trackRecentLookups(time_system); + //table_->trackRecentLookups(time_system); StatNameSet set2(*table_); set1.getStatName("dynamic.stat1"); time_system.sleep(std::chrono::seconds(1)); From 14f9eae0b76cf835a2d6d2bd7c3ab6fa5fbb6828 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 16:05:00 -0400 Subject: [PATCH 22/50] cleanup test deque behavior. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.cc | 62 ++++++++++++------------ source/common/stats/recent_lookups.h | 35 +------------ test/common/stats/recent_lookups_test.cc | 6 +-- 3 files changed, 33 insertions(+), 70 deletions(-) diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index 5398ce1c162b..cc4a20e2b6ef 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -3,11 +3,7 @@ #include #include -//#include "envoy/common/time.h" - -#include "common/common/assert.h" -//#include "common/common/logger.h" - +#include "absl/container/flat_hash_map.h" #include "absl/strings/str_join.h" namespace Envoy { @@ -20,32 +16,30 @@ constexpr size_t Capacity = 10; void RecentLookups::lookup(absl::string_view str) { ++total_; - - Map::iterator map_iter = map_.find(str); - if (map_iter != map_.end()) { - // The item is already in the list, but we need to bump its count and move - // it to the front, so we must re-order the list, which will invalidate the - // iterators to i. - List::iterator list_iter = map_iter->second; - ItemCount item_count = std::move(*list_iter); - list_.erase(list_iter); - ++item_count.count_; - list_.push_front(std::move(item_count)); - map_iter->second = list_.begin(); - } else { - ASSERT(list_.size() <= Capacity); - // Evict oldest item if needed. - if (list_.size() >= Capacity) { - ItemCount item_count = std::move(list_.back()); - list_.pop_back(); - map_.erase(item_count.item_); - } - - // The string storage is in the list entry. - list_.push_front(ItemCount{std::string(str), 1}); - List::iterator list_iter = list_.begin(); - map_[list_iter->item_] = list_iter; + if (queue_.size() >= Capacity) { + queue_.pop_back(); } + queue_.push_front(std::string(str)); + /* + SystemTime now = time_source_.systemTime(); + queue_.push_front(ItemTime(std::string(str), now)); + if (now <= last_log_time_) { // handle black-swan event for non-monotonic time. + return; + } + std::chrono::seconds duration = + std::chrono::duration_cast(now - last_log_time_); + + if (duration >= std::chrono::seconds(LogIntervalSec)) { + std::vector message; + forEach([&message, this](absl::string_view item, SystemTime time) { + if (time > last_log_time_) { + message.push_back(item); + } + }); + ENVOY_LOG_MISC(warn, "Recent lookups for {}", absl::StrJoin(message, ", ")); + last_log_time_ = now; + } + */ } /** @@ -54,8 +48,12 @@ void RecentLookups::lookup(absl::string_view str) { * @param fn The function to call for every recently looked up item. */ void RecentLookups::forEach(IterFn fn) const { - for (const ItemCount& item_count : list_) { - fn(item_count.item_, item_count.count_); + absl::flat_hash_map counts; + for (const std::string& item : queue_) { + ++counts[item]; + } + for (auto iter : counts) { + fn(iter.first, iter.second); } } diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index de73a0de9d79..cc08ab8d6cff 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -1,14 +1,9 @@ #pragma once +#include #include -#include #include -//#include "envoy/common/time.h" - -//#include "common/common/hash.h" - -#include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -17,8 +12,6 @@ namespace Stats { // Remembers the last 'Capacity' items passed to lookup(). class RecentLookups { public: - //explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} - /** * Records a lookup of a string. Only the last 'Capacity' lookups are remembered. * @@ -35,37 +28,13 @@ class RecentLookups { */ void forEach(IterFn fn) const; - /** - * @return the time-source associated with the object. - */ - //TimeSource& timeSource() { return time_source_; } - /** * @return the total number of lookups since tracking began. */ uint64_t total() const { return total_; } private: - struct ItemCount { - std::string item_; - int64_t count_; - - /*bool operator<(const ItemCount& that) const { - if (count_ == that.count_) { - return item_ < that.item_; - } - return count_ < that.count_; - } - - template friend H AbslHashValue(H h, const ItemCount& item_count) { - return H::combine(std::move(h), item_count.item_); - }*/ - }; - - using List = std::list; - List list_; - using Map = absl::flat_hash_map; - Map map_; + std::deque queue_; uint64_t total_{0}; }; diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index beb0a90d8317..6eeadce62cb2 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -79,12 +79,8 @@ TEST_F(RecentLookupsTest, RepeatDrop) { } recent_lookups_.lookup("add_late"); EXPECT_EQ("1: add_late " + "1: lookup6 " "2: lookup10 " - "2: lookup2 " - "2: lookup3 " - "2: lookup4 " - "2: lookup5 " - "2: lookup6 " "2: lookup7 " "2: lookup8 " "2: lookup9", From 56b0fec051d97c26f6ab47f139000b7ac0f90e63 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 16:06:44 -0400 Subject: [PATCH 23/50] format Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/recent_lookups.cc | 2 +- source/common/stats/symbol_table_impl.cc | 54 +++++++++---------- source/common/stats/symbol_table_impl.h | 6 +-- .../common/stats/recent_lookups_speed_test.cc | 5 +- test/common/stats/recent_lookups_test.cc | 14 ++--- test/common/stats/symbol_table_impl_test.cc | 2 +- 8 files changed, 42 insertions(+), 45 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 1fa527b672f7..752c616457e5 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -153,7 +153,7 @@ class SymbolTable { * * @param time_source used to help capture the time when a lookup occurs. */ - //virtual void trackRecentLookups(TimeSource& time_source) PURE; + // virtual void trackRecentLookups(TimeSource& time_source) PURE; using RecentLookupsFn = std::function; diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 690d1429bf35..cc5f2e73a85e 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -125,7 +125,7 @@ class FakeSymbolTableImpl : public SymbolTable { fn(toStringView(stat_name)); } - //void trackRecentLookups(TimeSource&) override {} + // void trackRecentLookups(TimeSource&) override {} void rememberSet(StatNameSet&) override {} void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index cc4a20e2b6ef..247bfdbc1449 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -11,7 +11,7 @@ namespace Stats { namespace { constexpr size_t Capacity = 10; -//constexpr uint64_t LogIntervalSec = 300; +// constexpr uint64_t LogIntervalSec = 300; } // namespace void RecentLookups::lookup(absl::string_view str) { diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 34f3132006eb..f98472c1f99b 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -208,19 +208,19 @@ void SymbolTableImpl::free(const StatName& stat_name) { } } - /* +/* void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { - { - Thread::LockGuard lock(lock_); - recent_lookups_ = std::make_unique(time_source); - } - { - Thread::LockGuard lock(stat_name_set_mutex_); - for (StatNameSet* stat_name_set : stat_name_sets_) { - stat_name_set->trackRecentLookups(time_source); - } +{ + Thread::LockGuard lock(lock_); + recent_lookups_ = std::make_unique(time_source); +} +{ + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->trackRecentLookups(time_source); } } +} */ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { @@ -239,20 +239,20 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { for (StatNameSet* stat_name_set : stat_name_sets_) { total += stat_name_set->getRecentLookups([&lookup_data](absl::string_view str, uint64_t count) { - lookup_data.push_back({std::string(str), count}); + lookup_data.push_back({std::string(str), count}); }); } } { Thread::LockGuard lock(lock_); - //if (recent_lookups_ != nullptr) { - recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) - NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({std::string(str), count}); - }); - total += recent_lookups_.total(); - //} + // if (recent_lookups_ != nullptr) { + recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) + NO_THREAD_SAFETY_ANALYSIS { + lookup_data.push_back({std::string(str), count}); + }); + total += recent_lookups_.total(); + //} } std::sort(lookup_data.begin(), lookup_data.end(), [](const LookupData& a, const LookupData& b) -> bool { @@ -283,13 +283,13 @@ void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { time_source = &recent_lookups_->timeSource(); } }*/ - //if (time_source != nullptr) { - //stat_name_set.trackRecentLookups()*time_source); - //} + // if (time_source != nullptr) { + // stat_name_set.trackRecentLookups()*time_source); + //} //{ - Thread::LockGuard lock(stat_name_set_mutex_); - stat_name_sets_.insert(&stat_name_set); - //} + Thread::LockGuard lock(stat_name_set_mutex_); + stat_name_sets_.insert(&stat_name_set); + //} } void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { @@ -550,9 +550,9 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { Stats::StatName& stat_name = dynamic_stat_names_[token]; if (stat_name.empty()) { // Note that builtin_stat_names_ already has one for "". stat_name = pool_.add(token); - //if (recent_lookups_ != nullptr) { - recent_lookups_.lookup(token); - //} + // if (recent_lookups_ != nullptr) { + recent_lookups_.lookup(token); + //} } return stat_name; } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index d5478e7b500f..e9c437487bfa 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -161,7 +161,7 @@ class SymbolTableImpl : public SymbolTable { return bytes; } - //void trackRecentLookups(TimeSource& timeSource) override; + // void trackRecentLookups(TimeSource& timeSource) override; void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; @@ -695,7 +695,7 @@ class StatNameSet { private: friend class SymbolTableImpl; - //void trackRecentLookups(TimeSource& time_source); + // void trackRecentLookups(TimeSource& time_source); uint64_t getRecentLookups(const RecentLookups::IterFn& iter); Stats::SymbolTable& symbol_table_; @@ -704,7 +704,7 @@ class StatNameSet { using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); - //std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); + // std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); RecentLookups recent_lookups_ GUARDED_BY(mutex_); }; diff --git a/test/common/stats/recent_lookups_speed_test.cc b/test/common/stats/recent_lookups_speed_test.cc index a1dc5177d5f2..d31a975b4ac0 100644 --- a/test/common/stats/recent_lookups_speed_test.cc +++ b/test/common/stats/recent_lookups_speed_test.cc @@ -3,14 +3,12 @@ // // NOLINT(namespace-envoy) -#include "common/stats/recent_lookups.h" #include "common/runtime/runtime_impl.h" +#include "common/stats/recent_lookups.h" #include "absl/strings/str_cat.h" - #include "benchmark/benchmark.h" - static void BM_Lookups(benchmark::State& state) { Envoy::Runtime::RandomGeneratorImpl random; const size_t vec_size = 1000; @@ -27,7 +25,6 @@ static void BM_Lookups(benchmark::State& state) { } BENCHMARK(BM_Lookups); - int main(int argc, char** argv) { Envoy::Thread::MutexBasicLockable lock; Envoy::Logger::Context logger_context(spdlog::level::warn, diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index 6eeadce62cb2..de39c0734393 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -25,14 +25,14 @@ class RecentLookupsTest : public testing::Test { using ItemCount = std::pair; std::vector items; recent_lookups_.forEach([&items](absl::string_view item, uint64_t count) { - items.push_back(ItemCount(std::string(item), count)); - }); + items.push_back(ItemCount(std::string(item), count)); + }); std::sort(items.begin(), items.end(), [](const ItemCount& a, const ItemCount& b) -> bool { - if (a.second == b.second) { - return a.first < b.first; - } - return a.second < b.second; - }); + if (a.second == b.second) { + return a.first < b.first; + } + return a.second < b.second; + }); std::vector accum; for (auto item : items) { accum.push_back(absl::StrCat(item.second, ": ", item.first)); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 84ee21e4f668..bf7a47821e0b 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -576,7 +576,7 @@ TEST_P(StatNameTest, RecentLookups) { const uint64_t years = 365 * 24 * 3600; time_system.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); StatNameSet set1(*table_); - //table_->trackRecentLookups(time_system); + // table_->trackRecentLookups(time_system); StatNameSet set2(*table_); set1.getStatName("dynamic.stat1"); time_system.sleep(std::chrono::seconds(1)); From 8cdf5fc83a2fa43213f6e555452c49808e4fe4bc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 17:43:03 -0400 Subject: [PATCH 24/50] format Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/recent_lookups.cc | 2 +- source/common/stats/recent_lookups.h | 4 +- source/common/stats/symbol_table_impl.cc | 54 +++++++++---------- source/common/stats/symbol_table_impl.h | 6 +-- .../common/stats/recent_lookups_speed_test.cc | 5 +- test/common/stats/recent_lookups_test.cc | 14 ++--- test/common/stats/symbol_table_impl_test.cc | 2 +- 9 files changed, 44 insertions(+), 47 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 1fa527b672f7..752c616457e5 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -153,7 +153,7 @@ class SymbolTable { * * @param time_source used to help capture the time when a lookup occurs. */ - //virtual void trackRecentLookups(TimeSource& time_source) PURE; + // virtual void trackRecentLookups(TimeSource& time_source) PURE; using RecentLookupsFn = std::function; diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 690d1429bf35..cc5f2e73a85e 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -125,7 +125,7 @@ class FakeSymbolTableImpl : public SymbolTable { fn(toStringView(stat_name)); } - //void trackRecentLookups(TimeSource&) override {} + // void trackRecentLookups(TimeSource&) override {} void rememberSet(StatNameSet&) override {} void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index 5398ce1c162b..5c15ef23ee2d 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -15,7 +15,7 @@ namespace Stats { namespace { constexpr size_t Capacity = 10; -//constexpr uint64_t LogIntervalSec = 300; +// constexpr uint64_t LogIntervalSec = 300; } // namespace void RecentLookups::lookup(absl::string_view str) { diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index de73a0de9d79..b652ebfaa071 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -17,7 +17,7 @@ namespace Stats { // Remembers the last 'Capacity' items passed to lookup(). class RecentLookups { public: - //explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} + // explicit RecentLookups(TimeSource& time_source) : time_source_(time_source) {} /** * Records a lookup of a string. Only the last 'Capacity' lookups are remembered. @@ -38,7 +38,7 @@ class RecentLookups { /** * @return the time-source associated with the object. */ - //TimeSource& timeSource() { return time_source_; } + // TimeSource& timeSource() { return time_source_; } /** * @return the total number of lookups since tracking began. diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 34f3132006eb..f98472c1f99b 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -208,19 +208,19 @@ void SymbolTableImpl::free(const StatName& stat_name) { } } - /* +/* void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { - { - Thread::LockGuard lock(lock_); - recent_lookups_ = std::make_unique(time_source); - } - { - Thread::LockGuard lock(stat_name_set_mutex_); - for (StatNameSet* stat_name_set : stat_name_sets_) { - stat_name_set->trackRecentLookups(time_source); - } +{ + Thread::LockGuard lock(lock_); + recent_lookups_ = std::make_unique(time_source); +} +{ + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->trackRecentLookups(time_source); } } +} */ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { @@ -239,20 +239,20 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { for (StatNameSet* stat_name_set : stat_name_sets_) { total += stat_name_set->getRecentLookups([&lookup_data](absl::string_view str, uint64_t count) { - lookup_data.push_back({std::string(str), count}); + lookup_data.push_back({std::string(str), count}); }); } } { Thread::LockGuard lock(lock_); - //if (recent_lookups_ != nullptr) { - recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) - NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({std::string(str), count}); - }); - total += recent_lookups_.total(); - //} + // if (recent_lookups_ != nullptr) { + recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) + NO_THREAD_SAFETY_ANALYSIS { + lookup_data.push_back({std::string(str), count}); + }); + total += recent_lookups_.total(); + //} } std::sort(lookup_data.begin(), lookup_data.end(), [](const LookupData& a, const LookupData& b) -> bool { @@ -283,13 +283,13 @@ void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { time_source = &recent_lookups_->timeSource(); } }*/ - //if (time_source != nullptr) { - //stat_name_set.trackRecentLookups()*time_source); - //} + // if (time_source != nullptr) { + // stat_name_set.trackRecentLookups()*time_source); + //} //{ - Thread::LockGuard lock(stat_name_set_mutex_); - stat_name_sets_.insert(&stat_name_set); - //} + Thread::LockGuard lock(stat_name_set_mutex_); + stat_name_sets_.insert(&stat_name_set); + //} } void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { @@ -550,9 +550,9 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { Stats::StatName& stat_name = dynamic_stat_names_[token]; if (stat_name.empty()) { // Note that builtin_stat_names_ already has one for "". stat_name = pool_.add(token); - //if (recent_lookups_ != nullptr) { - recent_lookups_.lookup(token); - //} + // if (recent_lookups_ != nullptr) { + recent_lookups_.lookup(token); + //} } return stat_name; } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index d5478e7b500f..e9c437487bfa 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -161,7 +161,7 @@ class SymbolTableImpl : public SymbolTable { return bytes; } - //void trackRecentLookups(TimeSource& timeSource) override; + // void trackRecentLookups(TimeSource& timeSource) override; void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; @@ -695,7 +695,7 @@ class StatNameSet { private: friend class SymbolTableImpl; - //void trackRecentLookups(TimeSource& time_source); + // void trackRecentLookups(TimeSource& time_source); uint64_t getRecentLookups(const RecentLookups::IterFn& iter); Stats::SymbolTable& symbol_table_; @@ -704,7 +704,7 @@ class StatNameSet { using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); - //std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); + // std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); RecentLookups recent_lookups_ GUARDED_BY(mutex_); }; diff --git a/test/common/stats/recent_lookups_speed_test.cc b/test/common/stats/recent_lookups_speed_test.cc index a1dc5177d5f2..d31a975b4ac0 100644 --- a/test/common/stats/recent_lookups_speed_test.cc +++ b/test/common/stats/recent_lookups_speed_test.cc @@ -3,14 +3,12 @@ // // NOLINT(namespace-envoy) -#include "common/stats/recent_lookups.h" #include "common/runtime/runtime_impl.h" +#include "common/stats/recent_lookups.h" #include "absl/strings/str_cat.h" - #include "benchmark/benchmark.h" - static void BM_Lookups(benchmark::State& state) { Envoy::Runtime::RandomGeneratorImpl random; const size_t vec_size = 1000; @@ -27,7 +25,6 @@ static void BM_Lookups(benchmark::State& state) { } BENCHMARK(BM_Lookups); - int main(int argc, char** argv) { Envoy::Thread::MutexBasicLockable lock; Envoy::Logger::Context logger_context(spdlog::level::warn, diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index beb0a90d8317..446575e89377 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -25,14 +25,14 @@ class RecentLookupsTest : public testing::Test { using ItemCount = std::pair; std::vector items; recent_lookups_.forEach([&items](absl::string_view item, uint64_t count) { - items.push_back(ItemCount(std::string(item), count)); - }); + items.push_back(ItemCount(std::string(item), count)); + }); std::sort(items.begin(), items.end(), [](const ItemCount& a, const ItemCount& b) -> bool { - if (a.second == b.second) { - return a.first < b.first; - } - return a.second < b.second; - }); + if (a.second == b.second) { + return a.first < b.first; + } + return a.second < b.second; + }); std::vector accum; for (auto item : items) { accum.push_back(absl::StrCat(item.second, ": ", item.first)); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 84ee21e4f668..bf7a47821e0b 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -576,7 +576,7 @@ TEST_P(StatNameTest, RecentLookups) { const uint64_t years = 365 * 24 * 3600; time_system.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); StatNameSet set1(*table_); - //table_->trackRecentLookups(time_system); + // table_->trackRecentLookups(time_system); StatNameSet set2(*table_); set1.getStatName("dynamic.stat1"); time_system.sleep(std::chrono::seconds(1)); From b92022035206ef087d6ebf79a4a0ce61f3d5c5bd Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 20:47:21 -0400 Subject: [PATCH 25/50] Update call-sites to expect the count rather than the time. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 13 +---- source/common/stats/recent_lookups.cc | 21 -------- source/common/stats/symbol_table_impl.cc | 53 ++------------------- source/common/stats/symbol_table_impl.h | 5 +- source/server/http/admin.cc | 7 ++- source/server/server.cc | 1 - test/common/stats/recent_lookups_test.cc | 22 +-------- test/common/stats/symbol_table_impl_test.cc | 11 ++--- 8 files changed, 17 insertions(+), 116 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 752c616457e5..0223e5cd6879 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -145,21 +145,12 @@ class SymbolTable { virtual void callWithStringView(StatName stat_name, const std::function& fn) const PURE; - /** - * Enables tracking the most recent symbol-table lookups in a bounded queue, - * to help identify the source of potential mutex contention in highly - * threaded systems. Ideally, once a system has been initialized, there - * should be no more symbol-table lookups. - * - * @param time_source used to help capture the time when a lookup occurs. - */ - // virtual void trackRecentLookups(TimeSource& time_source) PURE; - using RecentLookupsFn = std::function; /** * Calls the provided function with the name of the most recently looked-up - * symbols, including lookups on any StatNameSets. + * symbols, including lookups on any StatNameSets, and with a count of + * the recent lookups on that symbol. * * @param iter the function to call for every recent item. */ diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index 247bfdbc1449..3584885ae7fe 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -11,7 +11,6 @@ namespace Stats { namespace { constexpr size_t Capacity = 10; -// constexpr uint64_t LogIntervalSec = 300; } // namespace void RecentLookups::lookup(absl::string_view str) { @@ -20,26 +19,6 @@ void RecentLookups::lookup(absl::string_view str) { queue_.pop_back(); } queue_.push_front(std::string(str)); - /* - SystemTime now = time_source_.systemTime(); - queue_.push_front(ItemTime(std::string(str), now)); - if (now <= last_log_time_) { // handle black-swan event for non-monotonic time. - return; - } - std::chrono::seconds duration = - std::chrono::duration_cast(now - last_log_time_); - - if (duration >= std::chrono::seconds(LogIntervalSec)) { - std::vector message; - forEach([&message, this](absl::string_view item, SystemTime time) { - if (time > last_log_time_) { - message.push_back(item); - } - }); - ENVOY_LOG_MISC(warn, "Recent lookups for {}", absl::StrJoin(message, ", ")); - last_log_time_ = now; - } - */ } /** diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index f98472c1f99b..7db461bc72b8 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -208,21 +208,6 @@ void SymbolTableImpl::free(const StatName& stat_name) { } } -/* -void SymbolTableImpl::trackRecentLookups(TimeSource& time_source) { -{ - Thread::LockGuard lock(lock_); - recent_lookups_ = std::make_unique(time_source); -} -{ - Thread::LockGuard lock(stat_name_set_mutex_); - for (StatNameSet* stat_name_set : stat_name_sets_) { - stat_name_set->trackRecentLookups(time_source); - } -} -} -*/ - uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { uint64_t total = 0; struct LookupData { @@ -231,9 +216,8 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { }; std::vector lookup_data; - // We don't want to hold stat_name_set_mutex and lock_ at the same time. - // StatNameSet::getRecentLookups does not take SymbolTableImpl::lock_, so we - // collect the lookups via StatName, and decode the collected data below. + // We don't want to hold stat_name_set_mutex while calling the iterator, so + // buffer lookup_data. { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { @@ -244,15 +228,15 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { } } + // We also don't want to hold lock_ while calling the iterate, but we need it + // to access recent_lookups_. { Thread::LockGuard lock(lock_); - // if (recent_lookups_ != nullptr) { recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) NO_THREAD_SAFETY_ANALYSIS { lookup_data.push_back({std::string(str), count}); }); total += recent_lookups_.total(); - //} } std::sort(lookup_data.begin(), lookup_data.end(), [](const LookupData& a, const LookupData& b) -> bool { @@ -261,6 +245,7 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { } return a.count_ > b.count_; // Sort largest-counts first. }); + const LookupData* prev = nullptr; for (const LookupData& lookup : lookup_data) { if (prev == nullptr || prev->name_ != lookup.name_) { @@ -272,24 +257,8 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { } void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { - // We don't want to be holding lock_ when calling - // stat_name_set.trackRecentLookups() below, as that will result in a - // false-positive in absl::Mutex's detection analysis. It is easy enough to - // work around by capturing the TimeSource and then releasing lock_. - /*TimeSource* time_source = nullptr; - { - Thread::LockGuard lock(lock_); - if (recent_lookups_ != nullptr) { - time_source = &recent_lookups_->timeSource(); - } - }*/ - // if (time_source != nullptr) { - // stat_name_set.trackRecentLookups()*time_source); - //} - //{ Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.insert(&stat_name_set); - //} } void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { @@ -550,25 +519,13 @@ Stats::StatName StatNameSet::getStatName(absl::string_view token) { Stats::StatName& stat_name = dynamic_stat_names_[token]; if (stat_name.empty()) { // Note that builtin_stat_names_ already has one for "". stat_name = pool_.add(token); - // if (recent_lookups_ != nullptr) { recent_lookups_.lookup(token); - //} } return stat_name; } -/* -void StatNameSet::trackRecentLookups(TimeSource& time_source) { - absl::MutexLock lock(&mutex_); - recent_lookups_ = std::make_unique(time_source); -} -*/ - uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { absl::MutexLock lock(&mutex_); - /*if (recent_lookups_ == nullptr) { - return 0; - }*/ recent_lookups_.forEach(iter); return recent_lookups_.total(); } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index e9c437487bfa..419ab604f767 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -9,7 +9,6 @@ #include #include "envoy/common/exception.h" -#include "envoy/common/time.h" #include "envoy/stats/symbol_table.h" #include "common/common/assert.h" @@ -161,7 +160,6 @@ class SymbolTableImpl : public SymbolTable { return bytes; } - // void trackRecentLookups(TimeSource& timeSource) override; void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; @@ -180,7 +178,7 @@ class SymbolTableImpl : public SymbolTable { // This must be held during both encode() and free(). mutable Thread::MutexBasicLockable lock_; - // This must be held while updateing stat_name_sets_. + // This must be held while updating stat_name_sets_. mutable Thread::MutexBasicLockable stat_name_set_mutex_; /** @@ -695,7 +693,6 @@ class StatNameSet { private: friend class SymbolTableImpl; - // void trackRecentLookups(TimeSource& time_source); uint64_t getRecentLookups(const RecentLookups::IterFn& iter); Stats::SymbolTable& symbol_table_; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index b05517551df6..5c0bf06117b1 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -757,10 +757,9 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.TextUtf8); response.add("Date Time Lookup\n"); - DateFormatter formatter("%Y-%m-%d %H:%M:%S"); - uint64_t total = symbol_table.getRecentLookups( - [&response, &formatter](absl::string_view name, SystemTime time) { - response.add(absl::StrCat(formatter.fromTime(time), " ", name, "\n")); + uint64_t total = + symbol_table.getRecentLookups([&response](absl::string_view name, uint64_t count) { + response.add(absl::StrCat(count, ": ", name, "\n")); }); response.add(absl::StrCat("\ntotal: ", total, "\n")); return rc; diff --git a/source/server/server.cc b/source/server/server.cc index 8b76d371db12..a92f83dd9620 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -75,7 +75,6 @@ InstanceImpl::InstanceImpl(const Options& options, Event::TimeSystem& time_syste : nullptr), grpc_context_(store.symbolTable()), http_context_(store.symbolTable()), process_context_(std::move(process_context)), main_thread_id_(std::this_thread::get_id()) { - store.symbolTable().trackRecentLookups(time_source_); try { if (!options.logPath().empty()) { try { diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index de39c0734393..ccb38ed9c867 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -1,11 +1,10 @@ #include #include -//#include "common/common/utility.h" +#include "common/common/utility.h" #include "common/stats/recent_lookups.h" #include "test/test_common/logging.h" -#include "test/test_common/simulated_time_system.h" #include "absl/strings/str_cat.h" #include "gtest/gtest.h" @@ -16,11 +15,6 @@ namespace { class RecentLookupsTest : public testing::Test { protected: - RecentLookupsTest() { //: recent_lookups_(time_system_) { - const uint64_t years = 365 * 24 * 3600; - time_system_.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); - } - std::string joinLookups() { using ItemCount = std::pair; std::vector items; @@ -40,7 +34,6 @@ class RecentLookupsTest : public testing::Test { return StringUtil::join(accum, " "); } - Event::SimulatedTimeSystem time_system_; RecentLookups recent_lookups_; }; @@ -54,7 +47,6 @@ TEST_F(RecentLookupsTest, One) { TEST_F(RecentLookupsTest, DropOne) { for (int i = 0; i < 11; ++i) { recent_lookups_.lookup(absl::StrCat("lookup", i)); - time_system_.sleep(std::chrono::seconds(1)); } EXPECT_EQ("1: lookup1 " "1: lookup10 " @@ -73,9 +65,7 @@ TEST_F(RecentLookupsTest, RepeatDrop) { recent_lookups_.lookup("drop_early"); for (int i = 0; i < 11; ++i) { recent_lookups_.lookup(absl::StrCat("lookup", i)); - time_system_.sleep(std::chrono::seconds(1)); recent_lookups_.lookup(absl::StrCat("lookup", i)); - time_system_.sleep(std::chrono::seconds(1)); } recent_lookups_.lookup("add_late"); EXPECT_EQ("1: add_late " @@ -87,16 +77,6 @@ TEST_F(RecentLookupsTest, RepeatDrop) { joinLookups()); } -/*TEST_F(RecentLookupsTest, Log) { - EXPECT_LOG_CONTAINS("warn", "Recent lookups for alpha", recent_lookups_.lookup("alpha")); - EXPECT_NO_LOGS(recent_lookups_.lookup("beta")); - time_system_.sleep(std::chrono::seconds(100)); - EXPECT_NO_LOGS(recent_lookups_.lookup("gamma")); - time_system_.sleep(std::chrono::seconds(250)); - const Envoy::ExpectedLogMessages messages{{"warn", "gamma"}, {"warn", "delta"}}; - EXPECT_LOG_CONTAINS_ALL_OF(messages, recent_lookups_.lookup("delta")); - }*/ - } // namespace } // namespace Stats } // namespace Envoy diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index bf7a47821e0b..3f3e6f29d59f 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -585,16 +585,15 @@ TEST_P(StatNameTest, RecentLookups) { encodeDecode("direct.stat"); std::vector accum; - uint64_t total = table_->getRecentLookups([&accum](absl::string_view name, SystemTime time) { - DateFormatter formatter("%Y-%m-%d,%H:%M:%S"); - accum.emplace_back(absl::StrCat(formatter.fromTime(time), ";Item=", name)); + uint64_t total = table_->getRecentLookups([&accum](absl::string_view name, uint64_t count) { + accum.emplace_back(absl::StrCat(count, ": ", name)); }); EXPECT_EQ(5, total); std::string recent_lookups_str = StringUtil::join(accum, " "); - EXPECT_EQ("2009-12-22,00:00:02;Item=direct.stat " - "2009-12-22,00:00:01;Item=dynamic.stat2 " - "2009-12-22,00:00:00;Item=dynamic.stat1", + EXPECT_EQ("1: direct.stat " + "1: dynamic.stat1 " + "1: dynamic.stat2", recent_lookups_str); } From 2e4f12214deee4d2241472b5a2533e7c52b08122 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 18 Sep 2019 20:59:45 -0400 Subject: [PATCH 26/50] update test to reflect we are showing counts rather than date/time. Signed-off-by: Joshua Marantz --- source/server/http/admin.cc | 4 ++-- test/integration/integration_admin_test.cc | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 5c0bf06117b1..701bb8c5845d 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -756,10 +756,10 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Stats::SymbolTable& symbol_table = server_.stats().symbolTable(); response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.TextUtf8); - response.add("Date Time Lookup\n"); + response.add(" Count Lookup\n"); uint64_t total = symbol_table.getRecentLookups([&response](absl::string_view name, uint64_t count) { - response.add(absl::StrCat(count, ": ", name, "\n")); + response.add(fmt::format("{:8d} {}\n", count, name)); }); response.add(absl::StrCat("\ntotal: ", total, "\n")); return rc; diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 95311a0180a9..2fca122be749 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -172,8 +172,7 @@ TEST_P(IntegrationAdminTest, Admin) { EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - EXPECT_TRUE(absl::StartsWith(response->body(), "Date Time Lookup\n")) - << response->body(); + EXPECT_TRUE(absl::StartsWith(response->body(), " Count Lookup\n")) << response->body(); EXPECT_LT(30, response->body().size()); response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?usedonly", "", From 82c03cd12ca6430b4af7c6094c601755b1b05fcb Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 19 Sep 2019 07:16:59 -0400 Subject: [PATCH 27/50] fix golden result for admin test. Signed-off-by: Joshua Marantz --- test/server/http/admin_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 1adcce89c357..5f309a4e2aa7 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -1358,7 +1358,7 @@ TEST_P(AdminInstanceTest, RecentLookups) { Http::HeaderMapImpl response_headers; std::string body; EXPECT_EQ(Http::Code::OK, admin_.request("/stats?recentlookups", "GET", response_headers, body)); - EXPECT_EQ("Date Time Lookup\n\ntotal: 0\n", body); + EXPECT_EQ(" Count Lookup\n\ntotal: 0\n", body); EXPECT_THAT(std::string(response_headers.ContentType()->value().getStringView()), HasSubstr("text/plain")); } From 60c0673c7e1bd765ee9ca2df36226ad6b3d2cfe3 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 19 Sep 2019 22:28:22 -0400 Subject: [PATCH 28/50] address review comments Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 5 ++--- include/envoy/stats/BUILD | 1 - include/envoy/stats/symbol_table.h | 1 - source/common/stats/recent_lookups.h | 2 +- source/common/stats/symbol_table_impl.cc | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 57bc04e4fbe7..f0a3543ee2ec 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -366,10 +366,9 @@ modify different aspects of the server: lock. During startup this is expected, but in response to user requests on high core-count machines, this can cause performance issues due to mutex contention. - See `stats.md `_ - for more details. + See :repo:`source/docs/stats.md` for more details. - Note also that actual mutex contention can be tracked via `/contention`. + Note also that actual mutex contention can be tracked via :http:get:`/contention`. .. _operations_admin_interface_runtime: diff --git a/include/envoy/stats/BUILD b/include/envoy/stats/BUILD index 4691fdb2b8b0..b6a0389265fa 100644 --- a/include/envoy/stats/BUILD +++ b/include/envoy/stats/BUILD @@ -40,7 +40,6 @@ envoy_cc_library( name = "symbol_table_interface", hdrs = ["symbol_table.h"], deps = [ - "//include/envoy/common:time_interface", "//source/common/common:hash_lib", ], ) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 0223e5cd6879..a6400cfda5c4 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -5,7 +5,6 @@ #include #include "envoy/common/pure.h" -#include "envoy/common/time.h" #include "absl/strings/string_view.h" diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index cc08ab8d6cff..82d80f2170a3 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -22,7 +22,7 @@ class RecentLookups { using IterFn = std::function; /** - * Calls fn(item, timestamp) for each of the remembered lookups. + * Calls fn(item, count) for each of the remembered lookups. * * @param fn The function to call for every recently looked up item. */ diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 93bcd82d0990..3838382830f8 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -228,7 +228,7 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { } } - // We also don't want to hold lock_ while calling the iterate, but we need it + // We also don't want to hold lock_ while calling the iterator, but we need it // to access recent_lookups_. { Thread::LockGuard lock(lock_); From 9b92a3f720b90f188a6bb62eaa16cafe59621e7c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 20 Sep 2019 08:47:14 -0400 Subject: [PATCH 29/50] clang tidy and do a better job of collating the aggregated lookup data. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.cc | 2 +- source/common/stats/recent_lookups.h | 2 +- source/common/stats/symbol_table_impl.cc | 44 +++++++++------------ test/common/stats/BUILD | 1 - test/common/stats/recent_lookups_test.cc | 5 ++- test/common/stats/symbol_table_impl_test.cc | 10 +---- tools/spelling_dictionary.txt | 1 + 7 files changed, 27 insertions(+), 38 deletions(-) diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index 3584885ae7fe..10aa23ecc0fc 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -26,7 +26,7 @@ void RecentLookups::lookup(absl::string_view str) { * * @param fn The function to call for every recently looked up item. */ -void RecentLookups::forEach(IterFn fn) const { +void RecentLookups::forEach(const IterFn& fn) const { absl::flat_hash_map counts; for (const std::string& item : queue_) { ++counts[item]; diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 82d80f2170a3..6d6f64e0421e 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -26,7 +26,7 @@ class RecentLookups { * * @param fn The function to call for every recently looked up item. */ - void forEach(IterFn fn) const; + void forEach(const IterFn& fn) const; /** * @return the total number of lookups since tracking began. diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 3838382830f8..ad6613f8f382 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -210,11 +210,7 @@ void SymbolTableImpl::free(const StatName& stat_name) { uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { uint64_t total = 0; - struct LookupData { - std::string name_; - uint64_t count_; - }; - std::vector lookup_data; + absl::flat_hash_map name_count_map; // We don't want to hold stat_name_set_mutex while calling the iterator, so // buffer lookup_data. @@ -222,8 +218,8 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { Thread::LockGuard lock(stat_name_set_mutex_); for (StatNameSet* stat_name_set : stat_name_sets_) { total += - stat_name_set->getRecentLookups([&lookup_data](absl::string_view str, uint64_t count) { - lookup_data.push_back({std::string(str), count}); + stat_name_set->getRecentLookups([&name_count_map](absl::string_view str, uint64_t count) { + name_count_map[std::string(str)] += count; }); } } @@ -232,26 +228,24 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { // to access recent_lookups_. { Thread::LockGuard lock(lock_); - recent_lookups_.forEach([&lookup_data](absl::string_view str, uint64_t count) - NO_THREAD_SAFETY_ANALYSIS { - lookup_data.push_back({std::string(str), count}); - }); + recent_lookups_.forEach( + [&name_count_map](absl::string_view str, uint64_t count) + NO_THREAD_SAFETY_ANALYSIS { name_count_map[std::string(str)] += count; }); total += recent_lookups_.total(); } - std::sort(lookup_data.begin(), lookup_data.end(), - [](const LookupData& a, const LookupData& b) -> bool { - if (a.count_ == b.count_) { - return a.name_ < b.name_; - } - return a.count_ > b.count_; // Sort largest-counts first. - }); - - const LookupData* prev = nullptr; - for (const LookupData& lookup : lookup_data) { - if (prev == nullptr || prev->name_ != lookup.name_) { - iter(lookup.name_, lookup.count_); - prev = &lookup; - } + + // Now we have the collated name-count map data: we need to vectorize and + // sort. We define the pair with the count first as std::pair::operator< + // prioritizes its first element over its second. + using LookupCount = std::pair; + std::vector lookup_data; + lookup_data.reserve(name_count_map.size()); + for (const auto& iter : name_count_map) { + lookup_data.emplace_back(LookupCount(iter.second, iter.first)); + } + std::sort(lookup_data.begin(), lookup_data.end()); + for (const LookupCount& lookup_count : lookup_data) { + iter(lookup_count.second, lookup_count.first); } return total; } diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 55dc8b9d4396..65ed7bf5fd45 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -117,7 +117,6 @@ envoy_cc_test( "//source/common/stats:symbol_table_lib", "//test/mocks/stats:stats_mocks", "//test/test_common:logging_lib", - "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], ) diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index ccb38ed9c867..9e356af059c6 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -19,7 +19,7 @@ class RecentLookupsTest : public testing::Test { using ItemCount = std::pair; std::vector items; recent_lookups_.forEach([&items](absl::string_view item, uint64_t count) { - items.push_back(ItemCount(std::string(item), count)); + items.emplace_back(ItemCount(std::string(item), count)); }); std::sort(items.begin(), items.end(), [](const ItemCount& a, const ItemCount& b) -> bool { if (a.second == b.second) { @@ -28,7 +28,8 @@ class RecentLookupsTest : public testing::Test { return a.second < b.second; }); std::vector accum; - for (auto item : items) { + accum.reserve(items.size()); + for (const auto& item : items) { accum.push_back(absl::StrCat(item.second, ": ", item.first)); } return StringUtil::join(accum, " "); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index a1818d01c048..4e4fcdf2e7ce 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -8,7 +8,6 @@ #include "test/common/stats/stat_test_utility.h" #include "test/test_common/logging.h" -#include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" #include "absl/hash/hash_testing.h" @@ -574,16 +573,11 @@ TEST_P(StatNameTest, RecentLookups) { return; } - Event::SimulatedTimeSystem time_system; const uint64_t years = 365 * 24 * 3600; - time_system.setSystemTime(SystemTime() + std::chrono::seconds(40 * years)); StatNameSet set1(*table_); - // table_->trackRecentLookups(time_system); StatNameSet set2(*table_); set1.getDynamic("dynamic.stat1"); - time_system.sleep(std::chrono::seconds(1)); set2.getDynamic("dynamic.stat2"); - time_system.sleep(std::chrono::seconds(1)); encodeDecode("direct.stat"); std::vector accum; @@ -594,8 +588,8 @@ TEST_P(StatNameTest, RecentLookups) { std::string recent_lookups_str = StringUtil::join(accum, " "); EXPECT_EQ("1: direct.stat " - "1: dynamic.stat1 " - "1: dynamic.stat2", + "2: dynamic.stat1 " // Combines entries from set and symbol-table. + "2: dynamic.stat2", recent_lookups_str); } diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index 0cd9883bd93b..cb9ad8f1dae5 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -314,6 +314,7 @@ getaddrinfo sendto ssize upcasts +vectorize vip xDSes XFCC From c0ad91d34d842c41fe3e36840f3ca9b7514e5ae7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 20 Sep 2019 09:07:04 -0400 Subject: [PATCH 30/50] remove dead local variable. Signed-off-by: Joshua Marantz --- test/common/stats/symbol_table_impl_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 4e4fcdf2e7ce..ffc0348d30b1 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -573,7 +573,6 @@ TEST_P(StatNameTest, RecentLookups) { return; } - const uint64_t years = 365 * 24 * 3600; StatNameSet set1(*table_); StatNameSet set2(*table_); set1.getDynamic("dynamic.stat1"); From e08c677c60e7a0a109b9d7060b54b2ee925c0787 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 23 Sep 2019 02:24:13 -0400 Subject: [PATCH 31/50] Hook up reset_counters to clear recent_lookups. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 5 +++++ source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/recent_lookups.h | 8 ++++++++ source/common/stats/symbol_table_impl.cc | 18 ++++++++++++++++++ source/common/stats/symbol_table_impl.h | 6 ++++++ source/server/http/admin.cc | 2 +- source/server/http/admin.h | 1 + test/common/stats/recent_lookups_test.cc | 6 ++++++ test/common/stats/symbol_table_impl_test.cc | 6 ++++++ test/integration/integration_admin_test.cc | 13 +++++++++++++ 10 files changed, 65 insertions(+), 2 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index a6400cfda5c4..6b8d1e08bbf3 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -155,6 +155,11 @@ class SymbolTable { */ virtual uint64_t getRecentLookups(const RecentLookupsFn& iter) PURE; + /** + * Clears the recent-lookups structures. + */ + virtual void clearRecentLookups() PURE; + private: friend struct HeapStatData; friend class StatNameStorage; diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index cc5f2e73a85e..e0b7b4474f88 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -125,10 +125,10 @@ class FakeSymbolTableImpl : public SymbolTable { fn(toStringView(stat_name)); } - // void trackRecentLookups(TimeSource&) override {} void rememberSet(StatNameSet&) override {} void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } + void clearRecentLookups() override {} private: absl::string_view toStringView(const StatName& stat_name) const { diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 6d6f64e0421e..7e9909675cd8 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -33,6 +33,14 @@ class RecentLookups { */ uint64_t total() const { return total_; } + /** + * Clears out all contents. + */ + void clear() { + total_ = 0; + queue_.clear(); + } + private: std::deque queue_; uint64_t total_{0}; diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index ad6613f8f382..2f6007b6818b 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -250,6 +250,19 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { return total; } +void SymbolTableImpl::clearRecentLookups() { + { + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->clearRecentLookups(); + } + } + { + Thread::LockGuard lock(lock_); + recent_lookups_.clear(); + } +} + void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { Thread::LockGuard lock(stat_name_set_mutex_); stat_name_sets_.insert(&stat_name_set); @@ -531,5 +544,10 @@ uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { return recent_lookups_.total(); } +void StatNameSet::clearRecentLookups() { + absl::MutexLock lock(&mutex_); + recent_lookups_.clear(); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 6ad8c484e018..c872e5019477 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -163,6 +163,7 @@ class SymbolTableImpl : public SymbolTable { void rememberSet(StatNameSet& stat_name_set) override; void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; + void clearRecentLookups() override; private: friend class StatName; @@ -706,6 +707,11 @@ class StatNameSet { return pool_.add(str); } + /** + * Clears recent lookups. + */ + void clearRecentLookups(); + private: friend class SymbolTableImpl; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 701bb8c5845d..9c3afd4f5531 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -709,7 +709,7 @@ Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) { counter->reset(); } - + server_.stats().symbolTable().clearRecentLookups(); response.add("OK\n"); return Http::Code::OK; } diff --git a/source/server/http/admin.h b/source/server/http/admin.h index f54d72fee7ff..136e4927c052 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -151,6 +151,7 @@ class AdminImpl : public Admin, Http::HeaderMap& response_headers, std::string& body) override; void closeSocket(); void addListenerToHandler(Network::ConnectionHandler* handler) override; + Server::Instance& server() { return server_; } private: /** diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index 9e356af059c6..43be7d543290 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -43,6 +43,8 @@ TEST_F(RecentLookupsTest, Empty) { EXPECT_EQ("", joinLookups()); } TEST_F(RecentLookupsTest, One) { recent_lookups_.lookup("Hello"); EXPECT_EQ("1: Hello", joinLookups()); + recent_lookups_.clear(); + EXPECT_EQ("", joinLookups()); } TEST_F(RecentLookupsTest, DropOne) { @@ -60,6 +62,8 @@ TEST_F(RecentLookupsTest, DropOne) { "1: lookup8 " "1: lookup9", joinLookups()); + recent_lookups_.clear(); + EXPECT_EQ("", joinLookups()); } TEST_F(RecentLookupsTest, RepeatDrop) { @@ -76,6 +80,8 @@ TEST_F(RecentLookupsTest, RepeatDrop) { "2: lookup8 " "2: lookup9", joinLookups()); + recent_lookups_.clear(); + EXPECT_EQ("", joinLookups()); } } // namespace diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index ffc0348d30b1..415554ba31c5 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -590,6 +590,12 @@ TEST_P(StatNameTest, RecentLookups) { "2: dynamic.stat1 " // Combines entries from set and symbol-table. "2: dynamic.stat2", recent_lookups_str); + + table_->clearRecentLookups(); + uint32_t num_calls = 0; + EXPECT_EQ(0, + table_->getRecentLookups([&num_calls](absl::string_view, uint64_t) { ++num_calls; })); + EXPECT_EQ(0, num_calls); } TEST_P(StatNameTest, StatNameEmptyEquivalent) { diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 2fca122be749..f886e6ef7e5c 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -326,6 +326,19 @@ TEST_P(IntegrationAdminTest, Admin) { EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); + // TODO(#8324): "http1.metadata_not_supported_error" should not still be in + // the 'recent lookups' output after reset_counters. + response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?recentlookups", + "", downstreamProtocol(), version_); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); + EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); + EXPECT_EQ(" Count Lookup\n" + " 1 http1.metadata_not_supported_error\n" + "\n" + "total: 1\n", + response->body()); + response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/certs", "", downstreamProtocol(), version_); EXPECT_TRUE(response->complete()); From b38b505178485f0eb733ac936fb47a52931c2c1f Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 23 Sep 2019 10:04:33 -0400 Subject: [PATCH 32/50] cover http2 case as well. Signed-off-by: Joshua Marantz --- test/integration/integration_admin_test.cc | 34 +++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index f886e6ef7e5c..5825f3564eae 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -326,18 +326,38 @@ TEST_P(IntegrationAdminTest, Admin) { EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - // TODO(#8324): "http1.metadata_not_supported_error" should not still be in - // the 'recent lookups' output after reset_counters. response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?recentlookups", "", downstreamProtocol(), version_); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - EXPECT_EQ(" Count Lookup\n" - " 1 http1.metadata_not_supported_error\n" - "\n" - "total: 1\n", - response->body()); + + // TODO(#8324): "http1.metadata_not_supported_error" should not still be in + // the 'recent lookups' output after reset_counters. + switch (GetParam().downstream_protocol) { + case Http::CodecClient::Type::HTTP1: + EXPECT_EQ(" Count Lookup\n" + " 1 http1.metadata_not_supported_error\n" + "\n" + "total: 1\n", + response->body()); + break; + case Http::CodecClient::Type::HTTP2: + EXPECT_EQ(" Count Lookup\n" + " 1 http2.inbound_empty_frames_flood\n" + " 1 http2.inbound_priority_frames_flood\n" + " 1 http2.inbound_window_update_frames_flood\n" + " 1 http2.outbound_control_flood\n" + " 1 http2.outbound_flood\n" + " 1 http2.rx_messaging_error\n" + " 1 http2.rx_reset\n" + " 1 http2.too_many_header_frames\n" + " 1 http2.trailers\n" + " 1 http2.tx_reset\n" + "\n" + "total: 12\n", + response->body()); + } response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/certs", "", downstreamProtocol(), version_); From a8f685ff248300ceb385820d5481c3ae22179657 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 23 Sep 2019 17:52:25 -0400 Subject: [PATCH 33/50] Construct StatNameSet from SymbolTable, to avoid the 'rememberSet' hack. Also supply names. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 19 ++++---- source/common/stats/fake_symbol_table_impl.h | 4 +- source/common/stats/symbol_table_impl.cc | 11 +++-- source/common/stats/symbol_table_impl.h | 10 ++-- .../filters/http/dynamo/dynamo_stats.cc | 48 +++++++++---------- .../filters/http/dynamo/dynamo_stats.h | 6 +-- .../filters/http/fault/fault_filter.cc | 11 +++-- .../filters/http/fault/fault_filter.h | 2 +- .../http/ip_tagging/ip_tagging_filter.cc | 10 ++-- .../http/ip_tagging/ip_tagging_filter.h | 2 +- .../common/redis/redis_command_stats.cc | 28 +++++------ .../common/redis/redis_command_stats.h | 2 +- .../network/mongo_proxy/mongo_stats.cc | 20 ++++---- .../filters/network/mongo_proxy/mongo_stats.h | 6 +-- .../filters/network/zookeeper_proxy/filter.cc | 18 +++---- .../filters/network/zookeeper_proxy/filter.h | 2 +- .../transport_sockets/tls/context_impl.cc | 39 +++++++-------- .../transport_sockets/tls/context_impl.h | 2 +- test/common/stats/symbol_table_impl_test.cc | 30 ++++++------ 19 files changed, 141 insertions(+), 129 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 6b8d1e08bbf3..37dc6694f885 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -24,6 +24,8 @@ using StatNameVec = std::vector; class StatNameList; class StatNameSet; +using StatNameSetPtr = std::unique_ptr; + /** * SymbolTable manages a namespace optimized for stat names, exploiting their * typical composition from "."-separated tokens, with a significant overlap @@ -160,6 +162,14 @@ class SymbolTable { */ virtual void clearRecentLookups() PURE; + /** + * Creates a StatNameSet. + * + * @param name the name of the set. + * @return the set. + */ + virtual StatNameSetPtr makeSet(absl::string_view name) PURE; + private: friend struct HeapStatData; friend class StatNameStorage; @@ -202,15 +212,6 @@ class SymbolTable { */ virtual StoragePtr encode(absl::string_view name) PURE; - /** - * Enables trackRecentLookups to also track lookups that occur in - * StatNameSets, which has its own mutex. This function is called from - * StatNameSet's constructor. - * - * @param stat_name_set the set. - */ - virtual void rememberSet(StatNameSet& stat_name_set) PURE; - /** * Called by StatNameSet's destructor. * diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index e0b7b4474f88..87519f3d3450 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -125,7 +125,9 @@ class FakeSymbolTableImpl : public SymbolTable { fn(toStringView(stat_name)); } - void rememberSet(StatNameSet&) override {} + StatNameSetPtr makeSet(absl::string_view name) override { + return std::make_unique(*this, name); + } void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } void clearRecentLookups() override {} diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 2f6007b6818b..58a045932c2d 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -263,9 +263,11 @@ void SymbolTableImpl::clearRecentLookups() { } } -void SymbolTableImpl::rememberSet(StatNameSet& stat_name_set) { +StatNameSetPtr SymbolTableImpl::makeSet(absl::string_view name) { Thread::LockGuard lock(stat_name_set_mutex_); - stat_name_sets_.insert(&stat_name_set); + auto stat_name_set = std::make_unique(*this, name); + stat_name_sets_.insert(stat_name_set.get()); + return stat_name_set; } void SymbolTableImpl::forgetSet(StatNameSet& stat_name_set) { @@ -496,10 +498,9 @@ void StatNameList::clear(SymbolTable& symbol_table) { storage_.reset(); } -StatNameSet::StatNameSet(SymbolTable& symbol_table) - : symbol_table_(symbol_table), pool_(symbol_table) { +StatNameSet::StatNameSet(SymbolTable& symbol_table, absl::string_view name) + : name_(std::string(name)), symbol_table_(symbol_table), pool_(symbol_table) { builtin_stat_names_[""] = StatName(); - symbol_table.rememberSet(*this); } StatNameSet::~StatNameSet() { symbol_table_.forgetSet(*this); } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index c872e5019477..12c3842f6cd2 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -160,7 +160,7 @@ class SymbolTableImpl : public SymbolTable { return bytes; } - void rememberSet(StatNameSet& stat_name_set) override; + StatNameSetPtr makeSet(absl::string_view name) override; void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; void clearRecentLookups() override; @@ -659,7 +659,8 @@ class StatNameStorageSet { // builtins must *not* be added in the request-path. class StatNameSet { public: - explicit StatNameSet(SymbolTable& symbol_table); + // This object must be instantiated via SymbolTable::makeSet(), thus constructor is private. + ~StatNameSet(); /** @@ -712,18 +713,21 @@ class StatNameSet { */ void clearRecentLookups(); + StatNameSet(SymbolTable& symbol_table, absl::string_view name); + private: + friend class FakeSymbolTableImpl; friend class SymbolTableImpl; uint64_t getRecentLookups(const RecentLookups::IterFn& iter); + std::string name_; Stats::SymbolTable& symbol_table_; Stats::StatNamePool pool_ GUARDED_BY(mutex_); absl::Mutex mutex_; using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); - // std::unique_ptr recent_lookups_ GUARDED_BY(mutex_); RecentLookups recent_lookups_ GUARDED_BY(mutex_); }; diff --git a/source/extensions/filters/http/dynamo/dynamo_stats.cc b/source/extensions/filters/http/dynamo/dynamo_stats.cc index 53e51133394c..29e2ef1d00bc 100644 --- a/source/extensions/filters/http/dynamo/dynamo_stats.cc +++ b/source/extensions/filters/http/dynamo/dynamo_stats.cc @@ -15,35 +15,35 @@ namespace HttpFilters { namespace Dynamo { DynamoStats::DynamoStats(Stats::Scope& scope, const std::string& prefix) - : scope_(scope), stat_name_set_(scope.symbolTable()), - prefix_(stat_name_set_.add(prefix + "dynamodb")), - batch_failure_unprocessed_keys_(stat_name_set_.add("BatchFailureUnprocessedKeys")), - capacity_(stat_name_set_.add("capacity")), - empty_response_body_(stat_name_set_.add("empty_response_body")), - error_(stat_name_set_.add("error")), - invalid_req_body_(stat_name_set_.add("invalid_req_body")), - invalid_resp_body_(stat_name_set_.add("invalid_resp_body")), - multiple_tables_(stat_name_set_.add("multiple_tables")), - no_table_(stat_name_set_.add("no_table")), - operation_missing_(stat_name_set_.add("operation_missing")), - table_(stat_name_set_.add("table")), table_missing_(stat_name_set_.add("table_missing")), - upstream_rq_time_(stat_name_set_.add("upstream_rq_time")), - upstream_rq_total_(stat_name_set_.add("upstream_rq_total")), - unknown_entity_type_(stat_name_set_.add("unknown_entity_type")), - unknown_operation_(stat_name_set_.add("unknown_operation")) { - upstream_rq_total_groups_[0] = stat_name_set_.add("upstream_rq_total_unknown"); - upstream_rq_time_groups_[0] = stat_name_set_.add("upstream_rq_time_unknown"); + : scope_(scope), stat_name_set_(scope.symbolTable().makeSet("Dynamo")), + prefix_(stat_name_set_->add(prefix + "dynamodb")), + batch_failure_unprocessed_keys_(stat_name_set_->add("BatchFailureUnprocessedKeys")), + capacity_(stat_name_set_->add("capacity")), + empty_response_body_(stat_name_set_->add("empty_response_body")), + error_(stat_name_set_->add("error")), + invalid_req_body_(stat_name_set_->add("invalid_req_body")), + invalid_resp_body_(stat_name_set_->add("invalid_resp_body")), + multiple_tables_(stat_name_set_->add("multiple_tables")), + no_table_(stat_name_set_->add("no_table")), + operation_missing_(stat_name_set_->add("operation_missing")), + table_(stat_name_set_->add("table")), table_missing_(stat_name_set_->add("table_missing")), + upstream_rq_time_(stat_name_set_->add("upstream_rq_time")), + upstream_rq_total_(stat_name_set_->add("upstream_rq_total")), + unknown_entity_type_(stat_name_set_->add("unknown_entity_type")), + unknown_operation_(stat_name_set_->add("unknown_operation")) { + upstream_rq_total_groups_[0] = stat_name_set_->add("upstream_rq_total_unknown"); + upstream_rq_time_groups_[0] = stat_name_set_->add("upstream_rq_time_unknown"); for (size_t i = 1; i < DynamoStats::NumGroupEntries; ++i) { - upstream_rq_total_groups_[i] = stat_name_set_.add(fmt::format("upstream_rq_total_{}xx", i)); - upstream_rq_time_groups_[i] = stat_name_set_.add(fmt::format("upstream_rq_time_{}xx", i)); + upstream_rq_total_groups_[i] = stat_name_set_->add(fmt::format("upstream_rq_total_{}xx", i)); + upstream_rq_time_groups_[i] = stat_name_set_->add(fmt::format("upstream_rq_time_{}xx", i)); } RequestParser::forEachStatString( - [this](const std::string& str) { stat_name_set_.rememberBuiltin(str); }); + [this](const std::string& str) { stat_name_set_->rememberBuiltin(str); }); for (uint32_t status_code : {200, 400, 403, 502}) { - stat_name_set_.rememberBuiltin(absl::StrCat("upstream_rq_time_", status_code)); - stat_name_set_.rememberBuiltin(absl::StrCat("upstream_rq_total_", status_code)); + stat_name_set_->rememberBuiltin(absl::StrCat("upstream_rq_time_", status_code)); + stat_name_set_->rememberBuiltin(absl::StrCat("upstream_rq_total_", status_code)); } - stat_name_set_.rememberBuiltins({"operation", "table"}); + stat_name_set_->rememberBuiltins({"operation", "table"}); } Stats::SymbolTable::StoragePtr DynamoStats::addPrefix(const Stats::StatNameVec& names) { diff --git a/source/extensions/filters/http/dynamo/dynamo_stats.h b/source/extensions/filters/http/dynamo/dynamo_stats.h index de5fab8ff523..8b802592877e 100644 --- a/source/extensions/filters/http/dynamo/dynamo_stats.h +++ b/source/extensions/filters/http/dynamo/dynamo_stats.h @@ -37,16 +37,16 @@ class DynamoStats { * TODO(jmarantz): Potential perf issue here with mutex contention for names * that have not been remembered as builtins in the constructor. */ - Stats::StatName getDynamic(const std::string& str) { return stat_name_set_.getDynamic(str); } + Stats::StatName getDynamic(const std::string& str) { return stat_name_set_->getDynamic(str); } Stats::StatName getBuiltin(const std::string& str, Stats::StatName fallback) { - return stat_name_set_.getBuiltin(str, fallback); + return stat_name_set_->getBuiltin(str, fallback); } private: Stats::SymbolTable::StoragePtr addPrefix(const Stats::StatNameVec& names); Stats::Scope& scope_; - Stats::StatNameSet stat_name_set_; + Stats::StatNameSetPtr stat_name_set_; const Stats::StatName prefix_; public: diff --git a/source/extensions/filters/http/fault/fault_filter.cc b/source/extensions/filters/http/fault/fault_filter.cc index 65d1211f6623..3fcc408f4de2 100644 --- a/source/extensions/filters/http/fault/fault_filter.cc +++ b/source/extensions/filters/http/fault/fault_filter.cc @@ -78,15 +78,16 @@ FaultFilterConfig::FaultFilterConfig(const envoy::config::filter::http::fault::v Runtime::Loader& runtime, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) : settings_(fault), runtime_(runtime), stats_(generateStats(stats_prefix, scope)), - scope_(scope), time_source_(time_source), stat_name_set_(scope.symbolTable()), - aborts_injected_(stat_name_set_.add("aborts_injected")), - delays_injected_(stat_name_set_.add("delays_injected")), - stats_prefix_(stat_name_set_.add(absl::StrCat(stats_prefix, "fault"))) {} + scope_(scope), time_source_(time_source), + stat_name_set_(scope.symbolTable().makeSet("Fault")), + aborts_injected_(stat_name_set_->add("aborts_injected")), + delays_injected_(stat_name_set_->add("delays_injected")), + stats_prefix_(stat_name_set_->add(absl::StrCat(stats_prefix, "fault"))) {} void FaultFilterConfig::incCounter(absl::string_view downstream_cluster, Stats::StatName stat_name) { Stats::SymbolTable::StoragePtr storage = scope_.symbolTable().join( - {stats_prefix_, stat_name_set_.getDynamic(downstream_cluster), stat_name}); + {stats_prefix_, stat_name_set_->getDynamic(downstream_cluster), stat_name}); scope_.counterFromStatName(Stats::StatName(storage.get())).inc(); } diff --git a/source/extensions/filters/http/fault/fault_filter.h b/source/extensions/filters/http/fault/fault_filter.h index b0d3a0f1bf30..066e2e3fb869 100644 --- a/source/extensions/filters/http/fault/fault_filter.h +++ b/source/extensions/filters/http/fault/fault_filter.h @@ -133,7 +133,7 @@ class FaultFilterConfig { FaultFilterStats stats_; Stats::Scope& scope_; TimeSource& time_source_; - Stats::StatNameSet stat_name_set_; + Stats::StatNameSetPtr stat_name_set_; const Stats::StatName aborts_injected_; const Stats::StatName delays_injected_; const Stats::StatName stats_prefix_; // Includes ".fault". diff --git a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc index 3d739e8f7933..fe3648c082de 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc @@ -14,10 +14,10 @@ IpTaggingFilterConfig::IpTaggingFilterConfig( const envoy::config::filter::http::ip_tagging::v2::IPTagging& config, const std::string& stat_prefix, Stats::Scope& scope, Runtime::Loader& runtime) : request_type_(requestTypeEnum(config.request_type())), scope_(scope), runtime_(runtime), - stat_name_set_(scope.symbolTable()), - stats_prefix_(stat_name_set_.add(stat_prefix + "ip_tagging")), - hit_(stat_name_set_.add("hit")), no_hit_(stat_name_set_.add("no_hit")), - total_(stat_name_set_.add("total")) { + stat_name_set_(scope.symbolTable().makeSet("IpTagging")), + stats_prefix_(stat_name_set_->add(stat_prefix + "ip_tagging")), + hit_(stat_name_set_->add("hit")), no_hit_(stat_name_set_->add("no_hit")), + total_(stat_name_set_->add("total")) { // Once loading IP tags from a file system is supported, the restriction on the size // of the set should be removed and observability into what tags are loaded needs @@ -52,7 +52,7 @@ IpTaggingFilterConfig::IpTaggingFilterConfig( void IpTaggingFilterConfig::incCounter(Stats::StatName name, absl::string_view tag) { Stats::SymbolTable::StoragePtr storage = - scope_.symbolTable().join({stats_prefix_, stat_name_set_.getDynamic(tag), name}); + scope_.symbolTable().join({stats_prefix_, stat_name_set_->getDynamic(tag), name}); scope_.counterFromStatName(Stats::StatName(storage.get())).inc(); } diff --git a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h index 6cf2b19b0e74..95ab46909682 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h @@ -64,7 +64,7 @@ class IpTaggingFilterConfig { const FilterRequestType request_type_; Stats::Scope& scope_; Runtime::Loader& runtime_; - Stats::StatNameSet stat_name_set_; + Stats::StatNameSetPtr stat_name_set_; const Stats::StatName stats_prefix_; const Stats::StatName hit_; const Stats::StatName no_hit_; diff --git a/source/extensions/filters/network/common/redis/redis_command_stats.cc b/source/extensions/filters/network/common/redis/redis_command_stats.cc index 6eb6967c7644..6178f363c6d2 100644 --- a/source/extensions/filters/network/common/redis/redis_command_stats.cc +++ b/source/extensions/filters/network/common/redis/redis_command_stats.cc @@ -9,24 +9,24 @@ namespace Common { namespace Redis { RedisCommandStats::RedisCommandStats(Stats::SymbolTable& symbol_table, const std::string& prefix) - : symbol_table_(symbol_table), stat_name_set_(symbol_table_), - prefix_(stat_name_set_.add(prefix)), - upstream_rq_time_(stat_name_set_.add("upstream_rq_time")), - latency_(stat_name_set_.add("latency")), total_(stat_name_set_.add("total")), - success_(stat_name_set_.add("success")), error_(stat_name_set_.add("error")), - unused_metric_(stat_name_set_.add("unused")), null_metric_(stat_name_set_.add("null")), - unknown_metric_(stat_name_set_.add("unknown")) { + : symbol_table_(symbol_table), stat_name_set_(symbol_table_.makeSet("Redis")), + prefix_(stat_name_set_->add(prefix)), + upstream_rq_time_(stat_name_set_->add("upstream_rq_time")), + latency_(stat_name_set_->add("latency")), total_(stat_name_set_->add("total")), + success_(stat_name_set_->add("success")), error_(stat_name_set_->add("error")), + unused_metric_(stat_name_set_->add("unused")), null_metric_(stat_name_set_->add("null")), + unknown_metric_(stat_name_set_->add("unknown")) { // Note: Even if this is disabled, we track the upstream_rq_time. // Create StatName for each Redis command. Note that we don't include Auth or Ping. - stat_name_set_.rememberBuiltins( + stat_name_set_->rememberBuiltins( Extensions::NetworkFilters::Common::Redis::SupportedCommands::simpleCommands()); - stat_name_set_.rememberBuiltins( + stat_name_set_->rememberBuiltins( Extensions::NetworkFilters::Common::Redis::SupportedCommands::evalCommands()); - stat_name_set_.rememberBuiltins(Extensions::NetworkFilters::Common::Redis::SupportedCommands:: - hashMultipleSumResultCommands()); - stat_name_set_.rememberBuiltin( + stat_name_set_->rememberBuiltins(Extensions::NetworkFilters::Common::Redis::SupportedCommands:: + hashMultipleSumResultCommands()); + stat_name_set_->rememberBuiltin( Extensions::NetworkFilters::Common::Redis::SupportedCommands::mget()); - stat_name_set_.rememberBuiltin( + stat_name_set_->rememberBuiltin( Extensions::NetworkFilters::Common::Redis::SupportedCommands::mset()); } @@ -69,7 +69,7 @@ Stats::StatName RedisCommandStats::getCommandFromRequest(const RespValue& reques default: std::string to_lower_command(request.asString()); to_lower_table_.toLowerCase(to_lower_command); - return stat_name_set_.getBuiltin(to_lower_command, unknown_metric_); + return stat_name_set_->getBuiltin(to_lower_command, unknown_metric_); } } diff --git a/source/extensions/filters/network/common/redis/redis_command_stats.h b/source/extensions/filters/network/common/redis/redis_command_stats.h index b4b8c87a29db..556cc00e0150 100644 --- a/source/extensions/filters/network/common/redis/redis_command_stats.h +++ b/source/extensions/filters/network/common/redis/redis_command_stats.h @@ -42,7 +42,7 @@ class RedisCommandStats { private: Stats::SymbolTable& symbol_table_; - Stats::StatNameSet stat_name_set_; + Stats::StatNameSetPtr stat_name_set_; const Stats::StatName prefix_; const Stats::StatName upstream_rq_time_; const Stats::StatName latency_; diff --git a/source/extensions/filters/network/mongo_proxy/mongo_stats.cc b/source/extensions/filters/network/mongo_proxy/mongo_stats.cc index f82ca1a41f11..d23de1b6f49b 100644 --- a/source/extensions/filters/network/mongo_proxy/mongo_stats.cc +++ b/source/extensions/filters/network/mongo_proxy/mongo_stats.cc @@ -14,19 +14,21 @@ namespace NetworkFilters { namespace MongoProxy { MongoStats::MongoStats(Stats::Scope& scope, const std::string& prefix) - : scope_(scope), stat_name_set_(scope.symbolTable()), prefix_(stat_name_set_.add(prefix)), - callsite_(stat_name_set_.add("callsite")), cmd_(stat_name_set_.add("cmd")), - collection_(stat_name_set_.add("collection")), multi_get_(stat_name_set_.add("multi_get")), - reply_num_docs_(stat_name_set_.add("reply_num_docs")), - reply_size_(stat_name_set_.add("reply_size")), - reply_time_ms_(stat_name_set_.add("reply_time_ms")), time_ms_(stat_name_set_.add("time_ms")), - query_(stat_name_set_.add("query")), scatter_get_(stat_name_set_.add("scatter_get")), - total_(stat_name_set_.add("total")), unknown_command_(stat_name_set_.add("unknown_command")) { + : scope_(scope), stat_name_set_(scope.symbolTable().makeSet("Mongo")), + prefix_(stat_name_set_->add(prefix)), callsite_(stat_name_set_->add("callsite")), + cmd_(stat_name_set_->add("cmd")), collection_(stat_name_set_->add("collection")), + multi_get_(stat_name_set_->add("multi_get")), + reply_num_docs_(stat_name_set_->add("reply_num_docs")), + reply_size_(stat_name_set_->add("reply_size")), + reply_time_ms_(stat_name_set_->add("reply_time_ms")), + time_ms_(stat_name_set_->add("time_ms")), query_(stat_name_set_->add("query")), + scatter_get_(stat_name_set_->add("scatter_get")), total_(stat_name_set_->add("total")), + unknown_command_(stat_name_set_->add("unknown_command")) { // TODO(jmarantz): is this the right set of mongo commands to use as builtins? // Should we also have builtins for callsites or collections, or do those need // to be dynamic? - stat_name_set_.rememberBuiltins({"insert", "query", "update", "delete"}); + stat_name_set_->rememberBuiltins({"insert", "query", "update", "delete"}); } Stats::SymbolTable::StoragePtr MongoStats::addPrefix(const std::vector& names) { diff --git a/source/extensions/filters/network/mongo_proxy/mongo_stats.h b/source/extensions/filters/network/mongo_proxy/mongo_stats.h index e4cb0426ba00..aeb657f13526 100644 --- a/source/extensions/filters/network/mongo_proxy/mongo_stats.h +++ b/source/extensions/filters/network/mongo_proxy/mongo_stats.h @@ -27,16 +27,16 @@ class MongoStats { * that have not been remembered as builtins in the constructor. */ Stats::StatName getBuiltin(const std::string& str, Stats::StatName fallback) { - return stat_name_set_.getBuiltin(str, fallback); + return stat_name_set_->getBuiltin(str, fallback); } - Stats::StatName getDynamic(const std::string& str) { return stat_name_set_.getDynamic(str); } + Stats::StatName getDynamic(const std::string& str) { return stat_name_set_->getDynamic(str); } private: Stats::SymbolTable::StoragePtr addPrefix(const std::vector& names); Stats::Scope& scope_; - Stats::StatNameSet stat_name_set_; + Stats::StatNameSetPtr stat_name_set_; public: const Stats::StatName prefix_; diff --git a/source/extensions/filters/network/zookeeper_proxy/filter.cc b/source/extensions/filters/network/zookeeper_proxy/filter.cc index 2fee4392cc5b..bae059cf2757 100644 --- a/source/extensions/filters/network/zookeeper_proxy/filter.cc +++ b/source/extensions/filters/network/zookeeper_proxy/filter.cc @@ -19,15 +19,15 @@ namespace ZooKeeperProxy { ZooKeeperFilterConfig::ZooKeeperFilterConfig(const std::string& stat_prefix, const uint32_t max_packet_bytes, Stats::Scope& scope) : scope_(scope), max_packet_bytes_(max_packet_bytes), stats_(generateStats(stat_prefix, scope)), - stat_name_set_(scope.symbolTable()), stat_prefix_(stat_name_set_.add(stat_prefix)), - auth_(stat_name_set_.add("auth")), - connect_latency_(stat_name_set_.add("connect_response_latency")), - unknown_scheme_rq_(stat_name_set_.add("unknown_scheme_rq")), - unknown_opcode_latency_(stat_name_set_.add("unknown_opcode_latency")) { + stat_name_set_(scope.symbolTable().makeSet("Zookeeper")), + stat_prefix_(stat_name_set_->add(stat_prefix)), auth_(stat_name_set_->add("auth")), + connect_latency_(stat_name_set_->add("connect_response_latency")), + unknown_scheme_rq_(stat_name_set_->add("unknown_scheme_rq")), + unknown_opcode_latency_(stat_name_set_->add("unknown_opcode_latency")) { // https://zookeeper.apache.org/doc/r3.5.4-beta/zookeeperProgrammers.html#sc_BuiltinACLSchemes // lists commons schemes: "world", "auth", "digest", "host", "x509", and // "ip". These are used in filter.cc by appending "_rq". - stat_name_set_.rememberBuiltins( + stat_name_set_->rememberBuiltins( {"auth_rq", "digest_rq", "host_rq", "ip_rq", "ping_response_rq", "world_rq", "x509_rq"}); initOpCode(OpCodes::PING, stats_.ping_resp_, "ping_response"); @@ -62,7 +62,7 @@ void ZooKeeperFilterConfig::initOpCode(OpCodes opcode, Stats::Counter& counter, OpCodeInfo& opcode_info = op_code_map_[opcode]; opcode_info.counter_ = &counter; opcode_info.opname_ = std::string(name); - opcode_info.latency_name_ = stat_name_set_.add(absl::StrCat(name, "_latency")); + opcode_info.latency_name_ = stat_name_set_->add(absl::StrCat(name, "_latency")); } ZooKeeperFilter::ZooKeeperFilter(ZooKeeperFilterConfigSharedPtr config, TimeSource& time_source) @@ -153,8 +153,8 @@ void ZooKeeperFilter::onPing() { void ZooKeeperFilter::onAuthRequest(const std::string& scheme) { Stats::SymbolTable::StoragePtr storage = config_->scope_.symbolTable().join( {config_->stat_prefix_, config_->auth_, - config_->stat_name_set_.getBuiltin(absl::StrCat(scheme, "_rq"), - config_->unknown_scheme_rq_)}); + config_->stat_name_set_->getBuiltin(absl::StrCat(scheme, "_rq"), + config_->unknown_scheme_rq_)}); config_->scope_.counterFromStatName(Stats::StatName(storage.get())).inc(); setDynamicMetadata("opname", "auth"); } diff --git a/source/extensions/filters/network/zookeeper_proxy/filter.h b/source/extensions/filters/network/zookeeper_proxy/filter.h index 14d6ca1b9a89..56b9d858c35b 100644 --- a/source/extensions/filters/network/zookeeper_proxy/filter.h +++ b/source/extensions/filters/network/zookeeper_proxy/filter.h @@ -116,7 +116,7 @@ class ZooKeeperFilterConfig { Stats::Scope& scope_; const uint32_t max_packet_bytes_; ZooKeeperProxyStats stats_; - Stats::StatNameSet stat_name_set_; + Stats::StatNameSetPtr stat_name_set_; const Stats::StatName stat_prefix_; const Stats::StatName auth_; const Stats::StatName connect_latency_; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 873bcc7af087..64917787dce4 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -51,15 +51,16 @@ bool cbsContainsU16(CBS& cbs, uint16_t n) { ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source) : scope_(scope), stats_(generateStats(scope)), time_source_(time_source), - tls_max_version_(config.maxProtocolVersion()), stat_name_set_(scope.symbolTable()), - unknown_ssl_cipher_(stat_name_set_.add("unknown_ssl_cipher")), - unknown_ssl_curve_(stat_name_set_.add("unknown_ssl_curve")), - unknown_ssl_algorithm_(stat_name_set_.add("unknown_ssl_algorithm")), - unknown_ssl_version_(stat_name_set_.add("unknown_ssl_version")), - ssl_ciphers_(stat_name_set_.add("ssl.ciphers")), - ssl_versions_(stat_name_set_.add("ssl.versions")), - ssl_curves_(stat_name_set_.add("ssl.curves")), - ssl_sigalgs_(stat_name_set_.add("ssl.sigalgs")) { + tls_max_version_(config.maxProtocolVersion()), + stat_name_set_(scope.symbolTable().makeSet("TransportSockets::Tls")), + unknown_ssl_cipher_(stat_name_set_->add("unknown_ssl_cipher")), + unknown_ssl_curve_(stat_name_set_->add("unknown_ssl_curve")), + unknown_ssl_algorithm_(stat_name_set_->add("unknown_ssl_algorithm")), + unknown_ssl_version_(stat_name_set_->add("unknown_ssl_version")), + ssl_ciphers_(stat_name_set_->add("ssl.ciphers")), + ssl_versions_(stat_name_set_->add("ssl.versions")), + ssl_curves_(stat_name_set_->add("ssl.curves")), + ssl_sigalgs_(stat_name_set_->add("ssl.sigalgs")) { const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(static_cast(1), tls_certificates.size())); @@ -388,23 +389,23 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c // contention. // Ciphers - stat_name_set_.rememberBuiltin("AEAD-AES128-GCM-SHA256"); - stat_name_set_.rememberBuiltin("ECDHE-ECDSA-AES128-GCM-SHA256"); - stat_name_set_.rememberBuiltin("ECDHE-RSA-AES128-GCM-SHA256"); - stat_name_set_.rememberBuiltin("ECDHE-RSA-AES128-SHA"); - stat_name_set_.rememberBuiltin("ECDHE-RSA-CHACHA20-POLY1305"); - stat_name_set_.rememberBuiltin("TLS_AES_128_GCM_SHA256"); + stat_name_set_->rememberBuiltin("AEAD-AES128-GCM-SHA256"); + stat_name_set_->rememberBuiltin("ECDHE-ECDSA-AES128-GCM-SHA256"); + stat_name_set_->rememberBuiltin("ECDHE-RSA-AES128-GCM-SHA256"); + stat_name_set_->rememberBuiltin("ECDHE-RSA-AES128-SHA"); + stat_name_set_->rememberBuiltin("ECDHE-RSA-CHACHA20-POLY1305"); + stat_name_set_->rememberBuiltin("TLS_AES_128_GCM_SHA256"); // Curves from // https://github.com/google/boringssl/blob/f4d8b969200f1ee2dd872ffb85802e6a0976afe7/ssl/ssl_key_share.cc#L384 - stat_name_set_.rememberBuiltins( + stat_name_set_->rememberBuiltins( {"P-224", "P-256", "P-384", "P-521", "X25519", "CECPQ2", "CECPQ2b"}); // Algorithms - stat_name_set_.rememberBuiltins({"ecdsa_secp256r1_sha256", "rsa_pss_rsae_sha256"}); + stat_name_set_->rememberBuiltins({"ecdsa_secp256r1_sha256", "rsa_pss_rsae_sha256"}); // Versions - stat_name_set_.rememberBuiltins({"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}); + stat_name_set_->rememberBuiltins({"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}); } int ServerContextImpl::alpnSelectCallback(const unsigned char** out, unsigned char* outlen, @@ -517,7 +518,7 @@ void ContextImpl::incCounter(const Stats::StatName name, absl::string_view value const Stats::StatName fallback) const { Stats::SymbolTable& symbol_table = scope_.symbolTable(); Stats::SymbolTable::StoragePtr storage = - symbol_table.join({name, stat_name_set_.getBuiltin(value, fallback)}); + symbol_table.join({name, stat_name_set_->getBuiltin(value, fallback)}); scope_.counterFromStatName(Stats::StatName(storage.get())).inc(); #ifdef LOG_BUILTIN_STAT_NAMES diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index af88c9cf0ba3..21857f97443a 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -171,7 +171,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context { std::string cert_chain_file_path_; TimeSource& time_source_; const unsigned tls_max_version_; - mutable Stats::StatNameSet stat_name_set_; + mutable Stats::StatNameSetPtr stat_name_set_; const Stats::StatName unknown_ssl_cipher_; const Stats::StatName unknown_ssl_curve_; const Stats::StatName unknown_ssl_algorithm_; diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 415554ba31c5..a238f66a2a76 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -539,32 +539,32 @@ TEST_P(StatNameTest, SharedStatNameStorageSetSwap) { } TEST_P(StatNameTest, StatNameSet) { - StatNameSet set(*table_); + StatNameSetPtr set(table_->makeSet("set")); // Test that we get a consistent StatName object from a remembered name. - set.rememberBuiltin("remembered"); - const StatName fallback = set.add("fallback"); - const Stats::StatName remembered = set.getBuiltin("remembered", fallback); + set->rememberBuiltin("remembered"); + const StatName fallback = set->add("fallback"); + const Stats::StatName remembered = set->getBuiltin("remembered", fallback); EXPECT_EQ("remembered", table_->toString(remembered)); - EXPECT_EQ(remembered.data(), set.getBuiltin("remembered", fallback).data()); - EXPECT_EQ(fallback.data(), set.getBuiltin("not_remembered", fallback).data()); + EXPECT_EQ(remembered.data(), set->getBuiltin("remembered", fallback).data()); + EXPECT_EQ(fallback.data(), set->getBuiltin("not_remembered", fallback).data()); // Same test for a dynamically allocated name. The only difference between // the behavior with a remembered vs dynamic name is that when looking // up a remembered name, a mutex is not taken. But we have no easy way // to test for that. So we'll at least cover the code. - const Stats::StatName dynamic = set.getDynamic("dynamic"); + const Stats::StatName dynamic = set->getDynamic("dynamic"); EXPECT_EQ("dynamic", table_->toString(dynamic)); - EXPECT_EQ(dynamic.data(), set.getDynamic("dynamic").data()); + EXPECT_EQ(dynamic.data(), set->getDynamic("dynamic").data()); // There's another corner case for the same "dynamic" name from a // different set. Here we will get a different StatName object // out of the second set, though it will share the same underlying // symbol-table symbol. - StatNameSet set2(*table_); - const Stats::StatName dynamic2 = set2.getDynamic("dynamic"); + StatNameSetPtr set2(table_->makeSet("set2")); + const Stats::StatName dynamic2 = set2->getDynamic("dynamic"); EXPECT_EQ("dynamic", table_->toString(dynamic2)); - EXPECT_EQ(dynamic2.data(), set2.getDynamic("dynamic").data()); + EXPECT_EQ(dynamic2.data(), set2->getDynamic("dynamic").data()); EXPECT_NE(dynamic2.data(), dynamic.data()); } @@ -573,10 +573,10 @@ TEST_P(StatNameTest, RecentLookups) { return; } - StatNameSet set1(*table_); - StatNameSet set2(*table_); - set1.getDynamic("dynamic.stat1"); - set2.getDynamic("dynamic.stat2"); + StatNameSetPtr set1(table_->makeSet("set1")); + StatNameSetPtr set2(table_->makeSet("set2")); + set1->getDynamic("dynamic.stat1"); + set2->getDynamic("dynamic.stat2"); encodeDecode("direct.stat"); std::vector accum; From 67b0f4983b106469522a43385d8619ddd0f1aaec Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 23 Sep 2019 20:18:04 -0400 Subject: [PATCH 34/50] switch to lru implementation. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.cc | 39 ++++++++++++++++++------ source/common/stats/recent_lookups.h | 15 +++++++-- test/common/stats/recent_lookups_test.cc | 6 +++- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index 10aa23ecc0fc..1d1e5f7bf821 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -3,6 +3,8 @@ #include #include +#include "common/common/assert.h" + #include "absl/container/flat_hash_map.h" #include "absl/strings/str_join.h" @@ -15,10 +17,33 @@ constexpr size_t Capacity = 10; void RecentLookups::lookup(absl::string_view str) { ++total_; - if (queue_.size() >= Capacity) { - queue_.pop_back(); + Map::iterator map_iter = map_.find(str); + if (map_iter != map_.end()) { + // The item is already in the list, but we need to bump its count and move + // it to the front, so we must re-order the list, which will invalidate the + // iterators to i. + List::iterator list_iter = map_iter->second; + ItemCount item_count = std::move(*list_iter); + list_.erase(list_iter); + ++item_count.count_; + list_.push_front(std::move(item_count)); + map_iter->second = list_.begin(); + } else { + ASSERT(list_.size() <= Capacity); + // Evict oldest item if needed. + if (list_.size() >= Capacity) { + const ItemCount& item_count = list_.back(); + int erased = map_.erase(item_count.item_); + ASSERT(erased == 1); + list_.pop_back(); + } + + // The string storage is in the list entry. + list_.push_front(ItemCount{std::string(str), 1}); + List::iterator list_iter = list_.begin(); + map_[list_iter->item_] = list_iter; } - queue_.push_front(std::string(str)); + ASSERT(list_.size() == map_.size()); } /** @@ -27,12 +52,8 @@ void RecentLookups::lookup(absl::string_view str) { * @param fn The function to call for every recently looked up item. */ void RecentLookups::forEach(const IterFn& fn) const { - absl::flat_hash_map counts; - for (const std::string& item : queue_) { - ++counts[item]; - } - for (auto iter : counts) { - fn(iter.first, iter.second); + for (const ItemCount& item_count : list_) { + fn(item_count.item_, item_count.count_); } } diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 7e9909675cd8..d5f88b6c9a23 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -1,9 +1,10 @@ #pragma once -#include #include +#include #include +#include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -38,11 +39,19 @@ class RecentLookups { */ void clear() { total_ = 0; - queue_.clear(); + map_.clear(); + list_.clear(); } private: - std::deque queue_; + struct ItemCount { + std::string item_; + int64_t count_; + }; + using List = std::list; + List list_; + using Map = absl::flat_hash_map; + Map map_; uint64_t total_{0}; }; diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index 43be7d543290..032cc1101bc2 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -74,8 +74,12 @@ TEST_F(RecentLookupsTest, RepeatDrop) { } recent_lookups_.lookup("add_late"); EXPECT_EQ("1: add_late " - "1: lookup6 " "2: lookup10 " + "2: lookup2 " + "2: lookup3 " + "2: lookup4 " + "2: lookup5 " + "2: lookup6 " "2: lookup7 " "2: lookup8 " "2: lookup9", From 0760e00efc8695b6e6bbcea912957d1a2989a4e7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 24 Sep 2019 19:50:08 -0400 Subject: [PATCH 35/50] Add more admin endpoints to control recent-lookups tracking. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 6 + source/common/stats/fake_symbol_table_impl.h | 2 + source/common/stats/recent_lookups.cc | 30 +++-- source/common/stats/recent_lookups.h | 11 ++ source/common/stats/symbol_table_impl.cc | 26 ++++ source/common/stats/symbol_table_impl.h | 4 + source/server/http/admin.cc | 61 +++++++-- source/server/http/admin.h | 12 ++ test/common/stats/recent_lookups_test.cc | 15 +++ test/common/stats/symbol_table_impl_test.cc | 1 + test/integration/integration_admin_test.cc | 131 +++++++++---------- test/server/http/admin_test.cc | 9 +- 12 files changed, 213 insertions(+), 95 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 37dc6694f885..06564b365d13 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -162,6 +162,12 @@ class SymbolTable { */ virtual void clearRecentLookups() PURE; + /** + * Sets the recent-lookup capacity. + */ + virtual void setRecentLookupCapacity(uint64_t capacity) PURE; + virtual uint64_t recentLookupCapacity() const PURE; + /** * Creates a StatNameSet. * diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 87519f3d3450..77b1093d2b6a 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -131,6 +131,8 @@ class FakeSymbolTableImpl : public SymbolTable { void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } void clearRecentLookups() override {} + void setRecentLookupCapacity(uint64_t) override {} + uint64_t recentLookupCapacity() const override { return 0; } private: absl::string_view toStringView(const StatName& stat_name) const { diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index 1d1e5f7bf821..c57c25791652 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -11,12 +11,11 @@ namespace Envoy { namespace Stats { -namespace { -constexpr size_t Capacity = 10; -} // namespace - void RecentLookups::lookup(absl::string_view str) { ++total_; + if (capacity_ == 0) { + return; + } Map::iterator map_iter = map_.find(str); if (map_iter != map_.end()) { // The item is already in the list, but we need to bump its count and move @@ -29,13 +28,10 @@ void RecentLookups::lookup(absl::string_view str) { list_.push_front(std::move(item_count)); map_iter->second = list_.begin(); } else { - ASSERT(list_.size() <= Capacity); + ASSERT(list_.size() <= capacity_); // Evict oldest item if needed. - if (list_.size() >= Capacity) { - const ItemCount& item_count = list_.back(); - int erased = map_.erase(item_count.item_); - ASSERT(erased == 1); - list_.pop_back(); + if (list_.size() >= capacity_) { + evictOne(); } // The string storage is in the list entry. @@ -57,5 +53,19 @@ void RecentLookups::forEach(const IterFn& fn) const { } } +void RecentLookups::setCapacity(uint64_t capacity) { + capacity_ = capacity; + while (capacity_ < list_.size()) { + evictOne(); + } +} + +void RecentLookups::evictOne() { + const ItemCount& item_count = list_.back(); + int erased = map_.erase(item_count.item_); + ASSERT(erased == 1); + list_.pop_back(); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index d5f88b6c9a23..897b1f226355 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -43,7 +43,17 @@ class RecentLookups { list_.clear(); } + /** + * Controls the maximum number of recent lookups to remember. If set to 0, + * then only lookup counts is tracked. + * @param capacity The number of lookups to remember. + */ + void setCapacity(uint64_t capacity); + uint64_t capacity() const { return capacity_; } + private: + void evictOne(); + struct ItemCount { std::string item_; int64_t count_; @@ -53,6 +63,7 @@ class RecentLookups { using Map = absl::flat_hash_map; Map map_; uint64_t total_{0}; + uint64_t capacity_{0}; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 58a045932c2d..e8648e24466f 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -250,6 +250,20 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { return total; } +void SymbolTableImpl::setRecentLookupCapacity(uint64_t capacity) { + { + Thread::LockGuard lock(stat_name_set_mutex_); + for (StatNameSet* stat_name_set : stat_name_sets_) { + stat_name_set->setRecentLookupCapacity(capacity); + } + } + + { + Thread::LockGuard lock(lock_); + recent_lookups_.setCapacity(capacity); + } +} + void SymbolTableImpl::clearRecentLookups() { { Thread::LockGuard lock(stat_name_set_mutex_); @@ -263,9 +277,16 @@ void SymbolTableImpl::clearRecentLookups() { } } +uint64_t SymbolTableImpl::recentLookupCapacity() const { + Thread::LockGuard lock(lock_); + return recent_lookups_.capacity(); +} + StatNameSetPtr SymbolTableImpl::makeSet(absl::string_view name) { + const uint64_t capacity = recentLookupCapacity(); Thread::LockGuard lock(stat_name_set_mutex_); auto stat_name_set = std::make_unique(*this, name); + stat_name_set->setRecentLookupCapacity(capacity); stat_name_sets_.insert(stat_name_set.get()); return stat_name_set; } @@ -550,5 +571,10 @@ void StatNameSet::clearRecentLookups() { recent_lookups_.clear(); } +void StatNameSet::setRecentLookupCapacity(uint64_t capacity) { + absl::MutexLock lock(&mutex_); + recent_lookups_.setCapacity(capacity); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 12c3842f6cd2..5e564bdb39bf 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -164,6 +164,8 @@ class SymbolTableImpl : public SymbolTable { void forgetSet(StatNameSet& stat_name_set) override; uint64_t getRecentLookups(const RecentLookupsFn&) override; void clearRecentLookups() override; + void setRecentLookupCapacity(uint64_t capacity) override; + uint64_t recentLookupCapacity() const override; private: friend class StatName; @@ -713,6 +715,8 @@ class StatNameSet { */ void clearRecentLookups(); + void setRecentLookupCapacity(uint64_t capacity); + StatNameSet(SymbolTable& symbol_table, absl::string_view name); private: diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 9c3afd4f5531..9f32fb8f1860 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -714,6 +714,46 @@ Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, return Http::Code::OK; } +Http::Code AdminImpl::handlerStatsRecentLookups(absl::string_view, Http::HeaderMap&, + Buffer::Instance& response, AdminStream&) { + Stats::SymbolTable& symbol_table = server_.stats().symbolTable(); + /*response_headers.insertContentType().value().setReference( + Http::Headers::get().ContentTypeValues.TextUtf8);*/ + std::string table; + const uint64_t total = + symbol_table.getRecentLookups([&table](absl::string_view name, uint64_t count) { + table += fmt::format("{:8d} {}\n", count, name); + }); + if (table.empty() && symbol_table.recentLookupCapacity() == 0) { + table = "Lookup tracking is not enabled. Use /stats/recentlookups/enable to enable.\n"; + } else { + response.add(" Count Lookup\n"); + } + response.add(absl::StrCat(table, "\ntotal: ", total, "\n")); + return Http::Code::OK; +} + +Http::Code AdminImpl::handlerStatsRecentLookupsClear(absl::string_view, Http::HeaderMap&, + Buffer::Instance& response, AdminStream&) { + server_.stats().symbolTable().clearRecentLookups(); + response.add("OK\n"); + return Http::Code::OK; +} + +Http::Code AdminImpl::handlerStatsRecentLookupsDisable(absl::string_view, Http::HeaderMap&, + Buffer::Instance& response, AdminStream&) { + server_.stats().symbolTable().setRecentLookupCapacity(0); + response.add("OK\n"); + return Http::Code::OK; +} + +Http::Code AdminImpl::handlerStatsRecentLookupsEnable(absl::string_view, Http::HeaderMap&, + Buffer::Instance& response, AdminStream&) { + server_.stats().symbolTable().setRecentLookupCapacity(100); + response.add("OK\n"); + return Http::Code::OK; +} + Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap& headers, Buffer::Instance& response, AdminStream&) { time_t current_time = time(nullptr); @@ -752,19 +792,6 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Code rc = Http::Code::OK; const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); - if (params.find("recentlookups") != params.end()) { - Stats::SymbolTable& symbol_table = server_.stats().symbolTable(); - response_headers.insertContentType().value().setReference( - Http::Headers::get().ContentTypeValues.TextUtf8); - response.add(" Count Lookup\n"); - uint64_t total = - symbol_table.getRecentLookups([&response](absl::string_view name, uint64_t count) { - response.add(fmt::format("{:8d} {}\n", count, name)); - }); - response.add(absl::StrCat("\ntotal: ", total, "\n")); - return rc; - } - const bool used_only = params.find("usedonly") != params.end(); const absl::optional regex = filterParam(params); @@ -1245,6 +1272,14 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server) {"/stats", "print server stats", MAKE_ADMIN_HANDLER(handlerStats), false, false}, {"/stats/prometheus", "print server stats in prometheus format", MAKE_ADMIN_HANDLER(handlerPrometheusStats), false, false}, + {"/stats/recentlookups", "Show recent stat-name lookups", + MAKE_ADMIN_HANDLER(handlerStatsRecentLookups), false, false}, + {"/stats/recentlookups/clear", "clear list of stat-name lookups", + MAKE_ADMIN_HANDLER(handlerStatsRecentLookupsClear), false, true}, + {"/stats/recentlookups/disable", "disable recording of reset stat-name lookup names", + MAKE_ADMIN_HANDLER(handlerStatsRecentLookupsDisable), false, true}, + {"/stats/recentlookups/enable", "reset all counters to zero", + MAKE_ADMIN_HANDLER(handlerStatsRecentLookupsEnable), false, true}, {"/listeners", "print listener info", MAKE_ADMIN_HANDLER(handlerListenerInfo), false, false}, {"/runtime", "print runtime values", MAKE_ADMIN_HANDLER(handlerRuntime), false, false}, diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 136e4927c052..873f722135cf 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -287,6 +287,18 @@ class AdminImpl : public Admin, Http::Code handlerResetCounters(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + Http::Code handlerStatsRecentLookups(absl::string_view path_and_query, + Http::HeaderMap& response_headers, + Buffer::Instance& response, AdminStream&); + Http::Code handlerStatsRecentLookupsClear(absl::string_view path_and_query, + Http::HeaderMap& response_headers, + Buffer::Instance& response, AdminStream&); + Http::Code handlerStatsRecentLookupsDisable(absl::string_view path_and_query, + Http::HeaderMap& response_headers, + Buffer::Instance& response, AdminStream&); + Http::Code handlerStatsRecentLookupsEnable(absl::string_view path_and_query, + Http::HeaderMap& response_headers, + Buffer::Instance& response, AdminStream&); Http::Code handlerServerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, AdminStream&); Http::Code handlerReady(absl::string_view path_and_query, Http::HeaderMap& response_headers, diff --git a/test/common/stats/recent_lookups_test.cc b/test/common/stats/recent_lookups_test.cc index 032cc1101bc2..e8be8f9d90be 100644 --- a/test/common/stats/recent_lookups_test.cc +++ b/test/common/stats/recent_lookups_test.cc @@ -42,12 +42,26 @@ TEST_F(RecentLookupsTest, Empty) { EXPECT_EQ("", joinLookups()); } TEST_F(RecentLookupsTest, One) { recent_lookups_.lookup("Hello"); + EXPECT_EQ("", joinLookups()); + recent_lookups_.setCapacity(10); + EXPECT_EQ(1, recent_lookups_.total()); + recent_lookups_.lookup("Hello"); + EXPECT_EQ(2, recent_lookups_.total()); EXPECT_EQ("1: Hello", joinLookups()); + recent_lookups_.clear(); EXPECT_EQ("", joinLookups()); + EXPECT_EQ(0, recent_lookups_.total()); + recent_lookups_.lookup("Hello"); + EXPECT_EQ(1, recent_lookups_.total()); + EXPECT_EQ("1: Hello", joinLookups()); + recent_lookups_.setCapacity(0); + EXPECT_EQ("", joinLookups()); + EXPECT_EQ(1, recent_lookups_.total()); } TEST_F(RecentLookupsTest, DropOne) { + recent_lookups_.setCapacity(10); for (int i = 0; i < 11; ++i) { recent_lookups_.lookup(absl::StrCat("lookup", i)); } @@ -67,6 +81,7 @@ TEST_F(RecentLookupsTest, DropOne) { } TEST_F(RecentLookupsTest, RepeatDrop) { + recent_lookups_.setCapacity(10); recent_lookups_.lookup("drop_early"); for (int i = 0; i < 11; ++i) { recent_lookups_.lookup(absl::StrCat("lookup", i)); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index a238f66a2a76..c9936f58fb94 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -574,6 +574,7 @@ TEST_P(StatNameTest, RecentLookups) { } StatNameSetPtr set1(table_->makeSet("set1")); + table_->setRecentLookupCapacity(10); StatNameSetPtr set2(table_->makeSet("set2")); set1->getDynamic("dynamic.stat1"); set2->getDynamic("dynamic.stat2"); diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 5825f3564eae..cb3df8a82293 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -126,107 +126,114 @@ TEST_P(IntegrationAdminTest, Admin) { Stats::TestUtil::SymbolTableCreatorTestPeer::setUseFakeSymbolTables(false); initialize(); - BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( - lookupPort("admin"), "GET", "/notfound", "", downstreamProtocol(), version_); + auto request = [this](absl::string_view request, + absl::string_view method) -> BufferingStreamDecoderPtr { + return IntegrationUtil::makeSingleRequest(lookupPort("admin"), std::string(method), + std::string(request), "", downstreamProtocol(), + version_); + }; + + BufferingStreamDecoderPtr response = request("/notfound", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("404", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); EXPECT_NE(std::string::npos, response->body().find("invalid path. admin commands are:")) << response->body(); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/help", "", - downstreamProtocol(), version_); + response = request("/help", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); EXPECT_NE(std::string::npos, response->body().find("admin commands are:")) << response->body(); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/", "", - downstreamProtocol(), version_); + response = request("/", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/html; charset=UTF-8", ContentType(response)); EXPECT_NE(std::string::npos, response->body().find("Envoy Admin")) << response->body(); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/server_info", "", - downstreamProtocol(), version_); + response = request("/server_info", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/ready", "", - downstreamProtocol(), version_); + response = request("/ready", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats", "", - downstreamProtocol(), version_); + response = request("/stats", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?recentlookups", - "", downstreamProtocol(), version_); + // Our first attempt to get recent lookups will get the error message as they + // are off by default. + response = request("/stats/recentlookups", "GET"); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); + EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); + EXPECT_THAT(response->body(), testing::HasSubstr("Lookup tracking is not enabled")); + + // Now enable recent-lookups tracking and check that we get a count. + request("/stats/recentlookups/enable", "POST"); + response = request("/stats/recentlookups", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); EXPECT_TRUE(absl::StartsWith(response->body(), " Count Lookup\n")) << response->body(); EXPECT_LT(30, response->body().size()); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?usedonly", "", - downstreamProtocol(), version_); + // Now disable recent-lookups tracking and check that we get the error again. + request("/stats/recentlookups/disable", "POST"); + response = request("/stats/recentlookups", "GET"); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); + EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); + EXPECT_THAT(response->body(), testing::HasSubstr("Lookup tracking is not enabled")); + + response = request("/stats?usedonly", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); // Testing a filter with no matches - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?filter=foo", "", - downstreamProtocol(), version_); + response = request("/stats?filter=foo", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); // Testing a filter with matches - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?filter=server", - "", downstreamProtocol(), version_); + response = request("/stats?filter=server", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", - "/stats?filter=server&usedonly", "", - downstreamProtocol(), version_); + response = request("/stats?filter=server&usedonly", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = - IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?format=json&usedonly", - "", downstreamProtocol(), version_); + response = request("/stats?format=json&usedonly", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); validateStatsJson(response->body(), 0); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?format=blah", - "", downstreamProtocol(), version_); + response = request("/stats?format=blah", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("404", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?format=json", - "", downstreamProtocol(), version_); + response = request("/stats?format=json", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("application/json", ContentType(response)); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); validateStatsJson(response->body(), 1); // Filtering stats by a regex with one match should return just that match. - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", - "/stats?format=json&filter=^server\\.version$", "", - downstreamProtocol(), version_); + response = request("/stats?format=json&filter=^server\\.version$", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("application/json", ContentType(response)); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); @@ -235,9 +242,7 @@ TEST_P(IntegrationAdminTest, Admin) { testing::Eq("{\"stats\":[{\"name\":\"server.version\",\"value\":0}]}")); // Filtering stats by a non-full-string regex should also return just that match. - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", - "/stats?format=json&filter=server\\.version", "", - downstreamProtocol(), version_); + response = request("/stats?format=json&filter=server\\.version", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("application/json", ContentType(response)); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); @@ -247,17 +252,14 @@ TEST_P(IntegrationAdminTest, Admin) { // Filtering stats by a regex with no matches (".*not_intended_to_appear.*") should return a // valid, empty, stats array. - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", - "/stats?format=json&filter=not_intended_to_appear", - "", downstreamProtocol(), version_); + response = request("/stats?format=json&filter=not_intended_to_appear", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("application/json", ContentType(response)); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); validateStatsJson(response->body(), 0); EXPECT_THAT(response->body(), testing::Eq("{\"stats\":[]}")); - response = IntegrationUtil::makeSingleRequest( - lookupPort("admin"), "GET", "/stats?format=prometheus", "", downstreamProtocol(), version_); + response = request("/stats?format=prometheus", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_THAT(response->body(), @@ -275,8 +277,7 @@ TEST_P(IntegrationAdminTest, Admin) { response->body(), testing::HasSubstr("envoy_cluster_upstream_cx_active{envoy_cluster_name=\"cluster_0\"} 0\n")); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats/prometheus", "", - downstreamProtocol(), version_); + response = request("/stats/prometheus", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_THAT(response->body(), @@ -294,40 +295,36 @@ TEST_P(IntegrationAdminTest, Admin) { response->body(), testing::HasSubstr("envoy_cluster_upstream_cx_active{envoy_cluster_name=\"cluster_0\"} 0\n")); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/clusters", "", - downstreamProtocol(), version_); + response = request("/clusters", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_THAT(response->body(), testing::HasSubstr("added_via_api")); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/clusters?format=json", - "", downstreamProtocol(), version_); + response = request("/clusters?format=json", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); EXPECT_NO_THROW(Json::Factory::loadFromString(response->body())); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "POST", "/cpuprofiler", "", - downstreamProtocol(), version_); + response = request("/cpuprofiler", "POST"); EXPECT_TRUE(response->complete()); EXPECT_EQ("400", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/hot_restart_version", - "", downstreamProtocol(), version_); + response = request("/hot_restart_version", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "POST", "/reset_counters", "", - downstreamProtocol(), version_); + response = request("/reset_counters", "POST"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?recentlookups", - "", downstreamProtocol(), version_); + request("/stats/recentlookups/enable", "POST"); + request("/stats/recentlookups/clear", "POST"); + response = request("/stats/recentlookups", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); @@ -344,6 +341,8 @@ TEST_P(IntegrationAdminTest, Admin) { break; case Http::CodecClient::Type::HTTP2: EXPECT_EQ(" Count Lookup\n" + " 1 http2.header_overflow\n" + " 1 http2.headers_cb_no_stream\n" " 1 http2.inbound_empty_frames_flood\n" " 1 http2.inbound_priority_frames_flood\n" " 1 http2.inbound_window_update_frames_flood\n" @@ -359,26 +358,21 @@ TEST_P(IntegrationAdminTest, Admin) { response->body()); } - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/certs", "", - downstreamProtocol(), version_); + response = request("/certs", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/runtime", "", - downstreamProtocol(), version_); + response = request("/runtime", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); - response = IntegrationUtil::makeSingleRequest( - lookupPort("admin"), "POST", "/runtime_modify", "foo=bar&foo1=bar1", downstreamProtocol(), - version_, "host", Http::Headers::get().ContentTypeValues.FormUrlEncoded); + response = request("/runtime_modify?foo=bar&foo1=bar1", "POST"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/runtime?format=json", - "", downstreamProtocol(), version_); + response = request("/runtime?format=json", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); @@ -390,8 +384,7 @@ TEST_P(IntegrationAdminTest, Admin) { auto foo1_obj = entries->getObject("foo1"); EXPECT_EQ("bar1", foo1_obj->getString("final_value")); - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/listeners", "", - downstreamProtocol(), version_); + response = request("/listeners", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); @@ -403,8 +396,7 @@ TEST_P(IntegrationAdminTest, Admin) { listener_it->get().socket().localAddress()->asString()))); } - response = IntegrationUtil::makeSingleRequest( - lookupPort("admin"), "GET", "/listeners?format=json", "", downstreamProtocol(), version_); + response = request("/listeners?format=json", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); @@ -424,8 +416,7 @@ TEST_P(IntegrationAdminTest, Admin) { socket_address->getInteger("port_value")); } - response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/config_dump", "", - downstreamProtocol(), version_); + response = request("/config_dump", "GET"); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().Status()->value().getStringView()); EXPECT_EQ("application/json", ContentType(response)); diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 5f309a4e2aa7..34548d93ed6f 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -1357,10 +1357,15 @@ TEST_P(AdminInstanceTest, GetRequestJson) { TEST_P(AdminInstanceTest, RecentLookups) { Http::HeaderMapImpl response_headers; std::string body; - EXPECT_EQ(Http::Code::OK, admin_.request("/stats?recentlookups", "GET", response_headers, body)); - EXPECT_EQ(" Count Lookup\n\ntotal: 0\n", body); + + // Recent lookup tracking is disabled by default. + EXPECT_EQ(Http::Code::OK, admin_.request("/stats/recentlookups", "GET", response_headers, body)); + EXPECT_THAT(body, HasSubstr("Lookup tracking is not enabled")); EXPECT_THAT(std::string(response_headers.ContentType()->value().getStringView()), HasSubstr("text/plain")); + + // We can't test RecentLookups in admin unit tests as it doesn't work with a + // fake symbol table. However we cover this solidly in integration tests. } TEST_P(AdminInstanceTest, PostRequest) { From 09c948c45191aa3524e362524f30e6e166f16700 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 24 Sep 2019 20:10:40 -0400 Subject: [PATCH 36/50] cleanups. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 4 ++++ source/common/stats/fake_symbol_table_impl.h | 3 ++- source/common/stats/recent_lookups.h | 4 ++++ source/common/stats/symbol_table_impl.cc | 3 ++- source/common/stats/symbol_table_impl.h | 17 +++++++++++++++-- source/server/http/admin.cc | 4 +++- 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 06564b365d13..c761b16d6488 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -166,6 +166,10 @@ class SymbolTable { * Sets the recent-lookup capacity. */ virtual void setRecentLookupCapacity(uint64_t capacity) PURE; + + /** + * @return The configured recent-lookup tracking capacity. + */ virtual uint64_t recentLookupCapacity() const PURE; /** diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 77b1093d2b6a..f119cdeb7c16 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -126,7 +126,8 @@ class FakeSymbolTableImpl : public SymbolTable { } StatNameSetPtr makeSet(absl::string_view name) override { - return std::make_unique(*this, name); + // make_unique does not work with private ctor, even though FakeSymbolTableImpl is friended. + return StatNameSetPtr(new StatNameSet(*this, name)); } void forgetSet(StatNameSet&) override {} uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index 897b1f226355..f5e3f41fd16e 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -49,6 +49,10 @@ class RecentLookups { * @param capacity The number of lookups to remember. */ void setCapacity(uint64_t capacity); + + /** + * @return The configured capacity. + */ uint64_t capacity() const { return capacity_; } private: diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index e8648e24466f..9a2ae90d9927 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -285,7 +285,8 @@ uint64_t SymbolTableImpl::recentLookupCapacity() const { StatNameSetPtr SymbolTableImpl::makeSet(absl::string_view name) { const uint64_t capacity = recentLookupCapacity(); Thread::LockGuard lock(stat_name_set_mutex_); - auto stat_name_set = std::make_unique(*this, name); + // make_unique does not work with private ctor, even though FakeSymbolTableImpl is friended. + StatNameSetPtr stat_name_set(new StatNameSet(*this, name)); stat_name_set->setRecentLookupCapacity(capacity); stat_name_sets_.insert(stat_name_set.get()); return stat_name_set; diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 5e564bdb39bf..86a3eeb97771 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -695,11 +695,20 @@ class StatNameSet { * subsequent lookups of the same string to take only the set's lock, and not * the whole symbol-table lock. * + * @return a StatName corresponding to the passed-in token, owned by the set. + * * TODO(jmarantz): Potential perf issue here with contention, both on this * set's mutex and also the SymbolTable mutex which must be taken during * StatNamePool::add(). */ StatName getDynamic(absl::string_view token); + + /** + * Finds a builtin StatName by name. If the builtin has not been registered, + * then the fallback is returned. + * + * @return the StatName or fallback. + */ StatName getBuiltin(absl::string_view token, StatName fallback); /** @@ -715,14 +724,18 @@ class StatNameSet { */ void clearRecentLookups(); + /** + * Sets the number of names recorded in the recent-lookups set. + * + * @param capacity the capacity to configure. + */ void setRecentLookupCapacity(uint64_t capacity); - StatNameSet(SymbolTable& symbol_table, absl::string_view name); - private: friend class FakeSymbolTableImpl; friend class SymbolTableImpl; + StatNameSet(SymbolTable& symbol_table, absl::string_view name); uint64_t getRecentLookups(const RecentLookups::IterFn& iter); std::string name_; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 9f32fb8f1860..d83c8c72c9d4 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -140,6 +140,8 @@ const char AdminHtmlEnd[] = R"( const std::regex PromRegex("[^a-zA-Z0-9_]"); +const uint64_t RecentLookupsCapacity = 100; + void populateFallbackResponseHeaders(Http::Code code, Http::HeaderMap& header_map) { header_map.insertStatus().value(std::to_string(enumToInt(code))); const auto& headers = Http::Headers::get(); @@ -749,7 +751,7 @@ Http::Code AdminImpl::handlerStatsRecentLookupsDisable(absl::string_view, Http:: Http::Code AdminImpl::handlerStatsRecentLookupsEnable(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, AdminStream&) { - server_.stats().symbolTable().setRecentLookupCapacity(100); + server_.stats().symbolTable().setRecentLookupCapacity(RecentLookupsCapacity); response.add("OK\n"); return Http::Code::OK; } From 25cbe85f4c60a1db3ba78a65db3289516001122c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 24 Sep 2019 20:18:07 -0400 Subject: [PATCH 37/50] more comments. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.cc | 12 +++++++----- source/common/stats/recent_lookups.h | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index c57c25791652..f96a47dc6b7e 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -18,14 +18,16 @@ void RecentLookups::lookup(absl::string_view str) { } Map::iterator map_iter = map_.find(str); if (map_iter != map_.end()) { - // The item is already in the list, but we need to bump its count and move - // it to the front, so we must re-order the list, which will invalidate the - // iterators to i. + // The item is already in the list. We need to bump its count and move it to + // the front. Moving the item invalidates the iterator, so we need to update + // the map entry. The map's string_View references the std::string item in + // the list, so we need to be careful to move it out of and back into the + // list, to avoid invalidating the map key. List::iterator list_iter = map_iter->second; - ItemCount item_count = std::move(*list_iter); + ItemCount item_count = std::move(*list_iter); // Preserves map's string_view key. list_.erase(list_iter); ++item_count.count_; - list_.push_front(std::move(item_count)); + list_.push_front(std::move(item_count)); // Preserves map's string_view key. map_iter->second = list_.begin(); } else { ASSERT(list_.size() <= capacity_); diff --git a/source/common/stats/recent_lookups.h b/source/common/stats/recent_lookups.h index f5e3f41fd16e..c47999eb366d 100644 --- a/source/common/stats/recent_lookups.h +++ b/source/common/stats/recent_lookups.h @@ -64,6 +64,9 @@ class RecentLookups { }; using List = std::list; List list_; + + // TODO(jmarantz): we could make this more compact by making this a set of + // list-iterators with heterogeneous hash/compare functors. using Map = absl::flat_hash_map; Map map_; uint64_t total_{0}; From 514fb31bfb756c6fd1bb43ccd5ea94fe9854c1ef Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 24 Sep 2019 20:19:15 -0400 Subject: [PATCH 38/50] spelling. Signed-off-by: Joshua Marantz --- source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/symbol_table_impl.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index f119cdeb7c16..648226546a28 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -126,7 +126,7 @@ class FakeSymbolTableImpl : public SymbolTable { } StatNameSetPtr makeSet(absl::string_view name) override { - // make_unique does not work with private ctor, even though FakeSymbolTableImpl is friended. + // make_unique does not work with private ctor, even though FakeSymbolTableImpl is a friend. return StatNameSetPtr(new StatNameSet(*this, name)); } void forgetSet(StatNameSet&) override {} diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 9a2ae90d9927..c83f9bbae1a2 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -285,7 +285,7 @@ uint64_t SymbolTableImpl::recentLookupCapacity() const { StatNameSetPtr SymbolTableImpl::makeSet(absl::string_view name) { const uint64_t capacity = recentLookupCapacity(); Thread::LockGuard lock(stat_name_set_mutex_); - // make_unique does not work with private ctor, even though FakeSymbolTableImpl is friended. + // make_unique does not work with private ctor, even though FakeSymbolTableImpl is a friend. StatNameSetPtr stat_name_set(new StatNameSet(*this, name)); stat_name_set->setRecentLookupCapacity(capacity); stat_name_sets_.insert(stat_name_set.get()); From 5ec6fce9f006af7db1f4412132ab7f7918588964 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 25 Sep 2019 01:14:23 -0400 Subject: [PATCH 39/50] Cleanup LRU and fix asan error. Signed-off-by: Joshua Marantz --- source/common/stats/recent_lookups.cc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/source/common/stats/recent_lookups.cc b/source/common/stats/recent_lookups.cc index f96a47dc6b7e..439d71f1892f 100644 --- a/source/common/stats/recent_lookups.cc +++ b/source/common/stats/recent_lookups.cc @@ -18,17 +18,13 @@ void RecentLookups::lookup(absl::string_view str) { } Map::iterator map_iter = map_.find(str); if (map_iter != map_.end()) { - // The item is already in the list. We need to bump its count and move it to - // the front. Moving the item invalidates the iterator, so we need to update - // the map entry. The map's string_View references the std::string item in - // the list, so we need to be careful to move it out of and back into the - // list, to avoid invalidating the map key. + // The item is already in the list. Bump its reference-count and move it to + // the front of the list. List::iterator list_iter = map_iter->second; - ItemCount item_count = std::move(*list_iter); // Preserves map's string_view key. - list_.erase(list_iter); - ++item_count.count_; - list_.push_front(std::move(item_count)); // Preserves map's string_view key. - map_iter->second = list_.begin(); + ++list_iter->count_; + if (list_iter != list_.begin()) { + list_.splice(list_.begin(), list_, list_iter); + } } else { ASSERT(list_.size() <= capacity_); // Evict oldest item if needed. From af6d5de4bc4da1387381a7c39b3a6e26722b6847 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 1 Oct 2019 11:30:23 -0400 Subject: [PATCH 40/50] address remaining comments. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/stats/fake_symbol_table_impl.h | 2 +- source/common/stats/symbol_table_impl.cc | 4 ++-- source/common/stats/symbol_table_impl.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index c761b16d6488..d1aded28d630 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -155,7 +155,7 @@ class SymbolTable { * * @param iter the function to call for every recent item. */ - virtual uint64_t getRecentLookups(const RecentLookupsFn& iter) PURE; + virtual uint64_t getRecentLookups(const RecentLookupsFn& iter) const PURE; /** * Clears the recent-lookups structures. diff --git a/source/common/stats/fake_symbol_table_impl.h b/source/common/stats/fake_symbol_table_impl.h index 648226546a28..a8b7464e363c 100644 --- a/source/common/stats/fake_symbol_table_impl.h +++ b/source/common/stats/fake_symbol_table_impl.h @@ -130,7 +130,7 @@ class FakeSymbolTableImpl : public SymbolTable { return StatNameSetPtr(new StatNameSet(*this, name)); } void forgetSet(StatNameSet&) override {} - uint64_t getRecentLookups(const RecentLookupsFn&) override { return 0; } + uint64_t getRecentLookups(const RecentLookupsFn&) const override { return 0; } void clearRecentLookups() override {} void setRecentLookupCapacity(uint64_t) override {} uint64_t recentLookupCapacity() const override { return 0; } diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 7bd3b002a47b..9edc92f573f9 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -208,7 +208,7 @@ void SymbolTableImpl::free(const StatName& stat_name) { } } -uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) { +uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) const { uint64_t total = 0; absl::flat_hash_map name_count_map; @@ -563,7 +563,7 @@ StatName StatNameSet::getDynamic(absl::string_view token) { return stat_name; } -uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) { +uint64_t StatNameSet::getRecentLookups(const RecentLookups::IterFn& iter) const { absl::MutexLock lock(&mutex_); recent_lookups_.forEach(iter); return recent_lookups_.total(); diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index e74dd40f0c1a..75b5a07b936a 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -162,7 +162,7 @@ class SymbolTableImpl : public SymbolTable { StatNameSetPtr makeSet(absl::string_view name) override; void forgetSet(StatNameSet& stat_name_set) override; - uint64_t getRecentLookups(const RecentLookupsFn&) override; + uint64_t getRecentLookups(const RecentLookupsFn&) const override; void clearRecentLookups() override; void setRecentLookupCapacity(uint64_t capacity) override; uint64_t recentLookupCapacity() const override; @@ -736,12 +736,12 @@ class StatNameSet { friend class SymbolTableImpl; StatNameSet(SymbolTable& symbol_table, absl::string_view name); - uint64_t getRecentLookups(const RecentLookups::IterFn& iter); + uint64_t getRecentLookups(const RecentLookups::IterFn& iter) const; const std::string name_; Stats::SymbolTable& symbol_table_; Stats::StatNamePool pool_ GUARDED_BY(mutex_); - absl::Mutex mutex_; + mutable absl::Mutex mutex_; using StringStatNameMap = absl::flat_hash_map; StringStatNameMap builtin_stat_names_; StringStatNameMap dynamic_stat_names_ GUARDED_BY(mutex_); From 6bca221dd894342da8fe695c2b500fb3480614ef Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 13 Oct 2019 23:27:21 -0400 Subject: [PATCH 41/50] fix builda Signed-off-by: Joshua Marantz --- test/integration/BUILD | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/integration/BUILD b/test/integration/BUILD index 50433fcd72ff..2a526b446aeb 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -327,10 +327,8 @@ envoy_cc_test( "//source/extensions/filters/http/buffer:config", "//source/extensions/filters/http/health_check:config", "//test/common/stats:stat_test_utility_lib", - "@envoy_api//envoy/admin/v2alpha:config_dump_cc", "@envoy_api//envoy/admin/v2alpha:pkg_cc_proto", "@envoy_api//envoy/config/metrics/v2:pkg_cc_proto", - "@envoy_api//envoy/config/metrics/v2:stats_cc", ], ) From 14cbbcf6c4e3ba941bf02dd72269f402e73a39ca Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 10:04:16 -0400 Subject: [PATCH 42/50] Record the total recent lookups as a counter. Signed-off-by: Joshua Marantz --- source/server/server.cc | 9 ++++++ source/server/server.h | 5 ++-- test/server/BUILD | 2 ++ test/server/server_test.cc | 56 ++++++++++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/source/server/server.cc b/source/server/server.cc index 8aba537de9b2..ff1fad3c2379 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -198,6 +198,15 @@ void InstanceImpl::flushStatsInternal() { sslContextManager().daysUntilFirstCertExpires()); server_stats_->state_.set( enumToInt(Utility::serverState(initManager().state(), healthCheckFailed()))); + + // Compute the total number of lookups and track it as a stat. + const Stats::SymbolTable& symbol_table = stats_store_.symbolTable(); + const uint64_t total = symbol_table.getRecentLookups([](absl::string_view, uint64_t) {}); + const uint64_t prev_total = server_stats_->stats_recent_lookups_.value(); + if (total > prev_total) { + server_stats_->stats_recent_lookups_.add(total - prev_total); + } + InstanceUtil::flushMetricsToSinks(config_.statsSinks(), stats_store_); // TODO(ramaraochavali): consider adding different flush interval for histograms. if (stat_flush_timer_ != nullptr) { diff --git a/source/server/server.h b/source/server/server.h index 1993e2276f01..f3a3d1cb2edf 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -49,9 +49,10 @@ namespace Server { * All server wide stats. @see stats_macros.h */ #define ALL_SERVER_STATS(COUNTER, GAUGE, HISTOGRAM) \ - COUNTER(static_unknown_fields) \ - COUNTER(dynamic_unknown_fields) \ COUNTER(debug_assertion_failures) \ + COUNTER(dynamic_unknown_fields) \ + COUNTER(static_unknown_fields) \ + COUNTER(stats_recent_lookups) \ GAUGE(concurrency, NeverImport) \ GAUGE(days_until_first_cert_expiring, Accumulate) \ GAUGE(hot_restart_epoch, NeverImport) \ diff --git a/test/server/BUILD b/test/server/BUILD index e07c8826f69d..2b1e2f03de4c 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -318,9 +318,11 @@ envoy_cc_test( "//source/extensions/stat_sinks/statsd:config", "//source/extensions/tracers/zipkin:config", "//source/server:server_lib", + "//test/common/stats:stat_test_utility_lib", "//test/integration:integration_lib", "//test/mocks/server:server_mocks", "//test/mocks/stats:stats_mocks", + "//test/test_common:simulated_time_system_lib", "//test/test_common:test_time_lib", "//test/test_common:utility_lib", ], diff --git a/test/server/server_test.cc b/test/server/server_test.cc index 1abb1ba977b8..cbdce639a460 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -10,10 +10,12 @@ #include "server/process_context_impl.h" #include "server/server.h" +#include "test/common/stats/stat_test_utility.h" #include "test/integration/server.h" #include "test/mocks/server/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/environment.h" +#include "test/test_common/simulated_time_system.h" #include "test/test_common/test_time.h" #include "test/test_common/utility.h" @@ -159,7 +161,7 @@ class ServerInstanceImplTestBase { : std::make_unique("Server"); server_ = std::make_unique( - *init_manager_, options_, test_time_.timeSystem(), + *init_manager_, options_, time_system_, Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("127.0.0.1")), hooks_, restart_, stats_store_, fakelock_, component_factory_, std::make_unique>(), *thread_local_, @@ -178,7 +180,7 @@ class ServerInstanceImplTestBase { thread_local_ = std::make_unique(); init_manager_ = std::make_unique("Server"); server_ = std::make_unique( - *init_manager_, options_, test_time_.timeSystem(), + *init_manager_, options_, time_system_, Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("127.0.0.1")), hooks_, restart_, stats_store_, fakelock_, component_factory_, std::make_unique>(), *thread_local_, @@ -224,7 +226,7 @@ class ServerInstanceImplTestBase { Stats::TestIsolatedStoreImpl stats_store_; Thread::MutexBasicLockable fakelock_; TestComponentFactory component_factory_; - DangerousDeprecatedTestTime test_time_; + Event::GlobalTimeSystem time_system_; ProcessObject* process_object_ = nullptr; std::unique_ptr process_context_; std::unique_ptr init_manager_; @@ -281,7 +283,7 @@ TEST_P(ServerInstanceImplTest, StatsFlushWhenServerIsStillInitializing) { auto server_thread = startTestServer("test/server/stats_sink_bootstrap.yaml", true); // Wait till stats are flushed to custom sink and validate that the actual flush happens. - TestUtility::waitForCounterEq(stats_store_, "stats.flushed", 1, test_time_.timeSystem()); + TestUtility::waitForCounterEq(stats_store_, "stats.flushed", 1, time_system_); EXPECT_EQ(3L, TestUtility::findGauge(stats_store_, "server.state")->value()); EXPECT_EQ(Init::Manager::State::Initializing, server_->initManager().state()); @@ -442,6 +444,50 @@ TEST_P(ServerInstanceImplTest, Stats) { #endif } +class TestWithSimTimeAndRealSymbolTables : public Event::TestUsingSimulatedTime { +protected: + TestWithSimTimeAndRealSymbolTables() { + Stats::TestUtil::SymbolTableCreatorTestPeer::setUseFakeSymbolTables(false); + } +}; + +class ServerStatsTest : public TestWithSimTimeAndRealSymbolTables, public ServerInstanceImplTest { +protected: + void flushStats() { + // Default flush interval is 5 seconds. + simTime().sleep(std::chrono::seconds(6)); + server_->dispatcher().run(Event::Dispatcher::RunType::Block); + } +}; + +TEST_P(ServerStatsTest, FlushStats) { + initialize("test/server/empty_bootstrap.yaml"); + Stats::Counter& recent_lookups = stats_store_.counter("server.stats_recent_lookups"); + EXPECT_EQ(0, recent_lookups.value()); + flushStats(); + uint64_t strobed_recent_lookups = recent_lookups.value(); + EXPECT_LT(100, strobed_recent_lookups); // Recently this was 319 but exact value not important. + Stats::StatNameSetPtr test_set = stats_store_.symbolTable().makeSet("test"); + + // When we remember a StatNameSet builtin, we charge only for the SymbolTable + // lookup, which requires a lock. + test_set->rememberBuiltin("a.b"); + flushStats(); + EXPECT_EQ(1, recent_lookups.value() - strobed_recent_lookups); + strobed_recent_lookups = recent_lookups.value(); + + // When we use a StatNameSet dynamic, we charge for the SymbolTable lookup and + // for the lookup in the StatNameSet as well, as that requires a lock for its + // dynamic hash_map. + test_set->getDynamic("c.d"); + flushStats(); + EXPECT_EQ(2, recent_lookups.value() - strobed_recent_lookups); +} + +INSTANTIATE_TEST_SUITE_P(IpVersions, ServerStatsTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + // Default validation mode TEST_P(ServerInstanceImplTest, ValidationDefault) { options_.service_cluster_name_ = "some_cluster_name"; @@ -757,7 +803,7 @@ TEST_P(ServerInstanceImplTest, NoOptionsPassed) { init_manager_ = std::make_unique("Server"); EXPECT_THROW_WITH_MESSAGE( server_.reset(new InstanceImpl( - *init_manager_, options_, test_time_.timeSystem(), + *init_manager_, options_, time_system_, Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("127.0.0.1")), hooks_, restart_, stats_store_, fakelock_, component_factory_, std::make_unique>(), *thread_local_, From d82b1df2d0a31772ac9741e6dc8dc8cc4f430e66 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 11:30:32 -0400 Subject: [PATCH 43/50] simplify flush-stats impl for recent lookups by switching from counter to gauge. Signed-off-by: Joshua Marantz --- source/server/server.cc | 10 ++-------- source/server/server.h | 2 +- test/server/server_test.cc | 3 ++- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/source/server/server.cc b/source/server/server.cc index ff1fad3c2379..3700c2c30586 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -198,14 +198,8 @@ void InstanceImpl::flushStatsInternal() { sslContextManager().daysUntilFirstCertExpires()); server_stats_->state_.set( enumToInt(Utility::serverState(initManager().state(), healthCheckFailed()))); - - // Compute the total number of lookups and track it as a stat. - const Stats::SymbolTable& symbol_table = stats_store_.symbolTable(); - const uint64_t total = symbol_table.getRecentLookups([](absl::string_view, uint64_t) {}); - const uint64_t prev_total = server_stats_->stats_recent_lookups_.value(); - if (total > prev_total) { - server_stats_->stats_recent_lookups_.add(total - prev_total); - } + server_stats_->stats_recent_lookups_.set( + stats_store_.symbolTable().getRecentLookups([](absl::string_view, uint64_t) {})); InstanceUtil::flushMetricsToSinks(config_.statsSinks(), stats_store_); // TODO(ramaraochavali): consider adding different flush interval for histograms. diff --git a/source/server/server.h b/source/server/server.h index f3a3d1cb2edf..9f3c2ad6ffd8 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -52,7 +52,6 @@ namespace Server { COUNTER(debug_assertion_failures) \ COUNTER(dynamic_unknown_fields) \ COUNTER(static_unknown_fields) \ - COUNTER(stats_recent_lookups) \ GAUGE(concurrency, NeverImport) \ GAUGE(days_until_first_cert_expiring, Accumulate) \ GAUGE(hot_restart_epoch, NeverImport) \ @@ -61,6 +60,7 @@ namespace Server { GAUGE(memory_heap_size, Accumulate) \ GAUGE(parent_connections, Accumulate) \ GAUGE(state, NeverImport) \ + GAUGE(stats_recent_lookups, NeverImport) \ GAUGE(total_connections, Accumulate) \ GAUGE(uptime, Accumulate) \ GAUGE(version, NeverImport) \ diff --git a/test/server/server_test.cc b/test/server/server_test.cc index cbdce639a460..e103a9aff4e1 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -462,7 +462,8 @@ class ServerStatsTest : public TestWithSimTimeAndRealSymbolTables, public Server TEST_P(ServerStatsTest, FlushStats) { initialize("test/server/empty_bootstrap.yaml"); - Stats::Counter& recent_lookups = stats_store_.counter("server.stats_recent_lookups"); + Stats::Gauge& recent_lookups = + stats_store_.gauge("server.stats_recent_lookups", Stats::Gauge::ImportMode::NeverImport); EXPECT_EQ(0, recent_lookups.value()); flushStats(); uint64_t strobed_recent_lookups = recent_lookups.value(); From ff84c2c36d46545ee5a20b89492bd19755bcfc36 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 12:53:58 -0400 Subject: [PATCH 44/50] update doc Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 28 ++++++++++++++++++++++++++-- source/server/http/admin.cc | 2 -- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index f0a3543ee2ec..e726cc67080d 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -354,10 +354,11 @@ modify different aspects of the server: Envoy has updated (counters incremented at least once, gauges changed at least once, and histograms added to at least once) - .. http:get:: /stats?recentlookups + .. http:get:: /stats/recentlookups This endpoint is intended for Envoy developers debugging potential contention issues - in the stats system. + in the stats system. In order to use this API you must first enable it by POSTing to + `/stats/recentlookups/enable`. It emits a table of stat names that were recently accessed as strings by Envoy. In general, strings should be converted into StatNames, counters, gauges, and histograms @@ -370,6 +371,29 @@ modify different aspects of the server: Note also that actual mutex contention can be tracked via :http:get:`/contention`. + .. http:post:: /stats/recentlookups/enable + + Turns on collection of recent lookup of stat-names, thus enabling + `/stats/recentlookups`. + + See :repo:`source/docs/stats.md` for more details. + + .. http:post:: /stats/recentlookups/disable + + Turns off collection of recent lookup of stat-names, thus disabling + `/stats/recentlookups` and clearing any outstanding data. + + See :repo:`source/docs/stats.md` for more details. + + .. http:post:: /stats/recentlookups/clear + + Clearing any outstanding lookups. If called when recent lookup + collection is enabled, this clears all the data, but collection + continues. If called when recent lookup collection is disabled, + there is no effect, as disabling collection clears the data. + + See :repo:`source/docs/stats.md` for more details. + .. _operations_admin_interface_runtime: .. http:get:: /runtime diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 8b509c269679..1b303250dd2c 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -712,8 +712,6 @@ Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, Http::Code AdminImpl::handlerStatsRecentLookups(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, AdminStream&) { Stats::SymbolTable& symbol_table = server_.stats().symbolTable(); - /*response_headers.insertContentType().value().setReference( - Http::Headers::get().ContentTypeValues.TextUtf8);*/ std::string table; const uint64_t total = symbol_table.getRecentLookups([&table](absl::string_view name, uint64_t count) { From e8cb00c88d606c9dc02dc0f02edf1117a94cea50 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 15:20:50 -0400 Subject: [PATCH 45/50] language cleanup. Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index e726cc67080d..edeff6afe5dd 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -356,16 +356,21 @@ modify different aspects of the server: .. http:get:: /stats/recentlookups - This endpoint is intended for Envoy developers debugging potential contention issues - in the stats system. In order to use this API you must first enable it by POSTing to - `/stats/recentlookups/enable`. - - It emits a table of stat names that were recently accessed as strings by Envoy. In - general, strings should be converted into StatNames, counters, gauges, and histograms - by Envoy code only during startup or when receiving a new configuration via xDS. This - is because when stats are looked up as strings they must take a global symbol table - lock. During startup this is expected, but in response to user requests on high - core-count machines, this can cause performance issues due to mutex contention. + This endpoint helps Envoy developers debug potential contention + issues in the stats system. Initially, only the count of StatName + lookups is acumulated, not the specific names that are being looked + up. In order to see specific recent requests, you must enable the + feature by POSTing to `/stats/recentlookups/enable`. There may be + approximately 40-100 nanoseconds of added overhead per lookup. + + When enabled, this endpoint emits a table of stat names that were + recently accessed as strings by Envoy. Ideally, strings should be + converted into StatNames, counters, gauges, and histograms by Envoy + code only during startup or when receiving a new configuration via + xDS. This is because when stats are looked up as strings they must + take a global symbol table lock. During startup this is acceptable, + but in response to user requests on high core-count machines, this + can cause performance issues due to mutex contention. See :repo:`source/docs/stats.md` for more details. @@ -381,14 +386,16 @@ modify different aspects of the server: .. http:post:: /stats/recentlookups/disable Turns off collection of recent lookup of stat-names, thus disabling - `/stats/recentlookups` and clearing any outstanding data. + `/stats/recentlookups`. It also clears the list of lookups. However, + the total count, visible as stat `server.stats_recent_lookups`, is + not cleared, and continues to accumulate. See :repo:`source/docs/stats.md` for more details. .. http:post:: /stats/recentlookups/clear - Clearing any outstanding lookups. If called when recent lookup - collection is enabled, this clears all the data, but collection + Clears all outstanding lookups and counts. If called when recent lookup + collection is enabled, this clears all the, but collection continues. If called when recent lookup collection is disabled, there is no effect, as disabling collection clears the data. From faf1cfae800553d1b3db5800705018c2f00d9264 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 15:46:12 -0400 Subject: [PATCH 46/50] add release note. Signed-off-by: Joshua Marantz --- docs/root/intro/version_history.rst | 2 ++ docs/root/operations/admin.rst | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index a7351555b52b..f9c59682f6c7 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -9,6 +9,8 @@ Version history * access log: reintroduce :ref:`filesystem ` stats and added the `write_failed` counter to track failed log writes * admin: added ability to configure listener :ref:`socket options `. * admin: added config dump support for Secret Discovery Service :ref:`SecretConfigDump `. +* admin: added :http:get:`/stats/recentlookups', :http:get:`/stats/recentlookups/clear', + :http:get:`/stats/recentlookups/disable', and :http:get:`/stats/recentlookups/enable' endpoints. * api: added ::ref:`set_node_on_first_message_only ` option to omit the node identifier from the subsequent discovery requests on the same stream. * buffer filter: the buffer filter populates content-length header if not present, behavior can be disabled using the runtime feature `envoy.reloadable_features.buffer_filter_populate_content_length`. * config: added support for :ref:`delta xDS ` (including ADS) delivery diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index edeff6afe5dd..19c1503ce2c1 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -372,6 +372,8 @@ modify different aspects of the server: but in response to user requests on high core-count machines, this can cause performance issues due to mutex contention. + This option requires Envoy to be started with `use-fake-symbol-table 0`. + See :repo:`source/docs/stats.md` for more details. Note also that actual mutex contention can be tracked via :http:get:`/contention`. From a70b2de314b835c64d10d58e4f30563de59aee76 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 15:55:57 -0400 Subject: [PATCH 47/50] use correct method name. Awesome that we have a docs consistency check for this! Signed-off-by: Joshua Marantz --- docs/root/intro/version_history.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index f9c59682f6c7..385352d4f500 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -9,8 +9,8 @@ Version history * access log: reintroduce :ref:`filesystem ` stats and added the `write_failed` counter to track failed log writes * admin: added ability to configure listener :ref:`socket options `. * admin: added config dump support for Secret Discovery Service :ref:`SecretConfigDump `. -* admin: added :http:get:`/stats/recentlookups', :http:get:`/stats/recentlookups/clear', - :http:get:`/stats/recentlookups/disable', and :http:get:`/stats/recentlookups/enable' endpoints. +* admin: added :http:get:`/stats/recentlookups', :http:post:`/stats/recentlookups/clear', + :http:post:`/stats/recentlookups/disable', and :http:post:`/stats/recentlookups/enable' endpoints. * api: added ::ref:`set_node_on_first_message_only ` option to omit the node identifier from the subsequent discovery requests on the same stream. * buffer filter: the buffer filter populates content-length header if not present, behavior can be disabled using the runtime feature `envoy.reloadable_features.buffer_filter_populate_content_length`. * config: added support for :ref:`delta xDS ` (including ADS) delivery From c18f1842cf2e52aa085f56d169875de0cfde2bfa Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Oct 2019 16:06:40 -0400 Subject: [PATCH 48/50] quotes vs backquotes Signed-off-by: Joshua Marantz --- docs/root/intro/version_history.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index 385352d4f500..9f8386f1d2d1 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -9,8 +9,8 @@ Version history * access log: reintroduce :ref:`filesystem ` stats and added the `write_failed` counter to track failed log writes * admin: added ability to configure listener :ref:`socket options `. * admin: added config dump support for Secret Discovery Service :ref:`SecretConfigDump `. -* admin: added :http:get:`/stats/recentlookups', :http:post:`/stats/recentlookups/clear', - :http:post:`/stats/recentlookups/disable', and :http:post:`/stats/recentlookups/enable' endpoints. +* admin: added :http:get:`/stats/recentlookups`, :http:post:`/stats/recentlookups/clear`, + :http:post:`/stats/recentlookups/disable`, and :http:post:`/stats/recentlookups/enable` endpoints. * api: added ::ref:`set_node_on_first_message_only ` option to omit the node identifier from the subsequent discovery requests on the same stream. * buffer filter: the buffer filter populates content-length header if not present, behavior can be disabled using the runtime feature `envoy.reloadable_features.buffer_filter_populate_content_length`. * config: added support for :ref:`delta xDS ` (including ADS) delivery From f17bcdb17044093d75c8b8359ddb568030ad57ca Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 16 Oct 2019 16:37:09 -0400 Subject: [PATCH 49/50] fix typos and add explicit option syntax. Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 19c1503ce2c1..b04c37bb4d34 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -372,7 +372,7 @@ modify different aspects of the server: but in response to user requests on high core-count machines, this can cause performance issues due to mutex contention. - This option requires Envoy to be started with `use-fake-symbol-table 0`. + This admin endpoint requires Envoy to be started with :option:`use-fake-symbol-table 0`. See :repo:`source/docs/stats.md` for more details. @@ -396,10 +396,9 @@ modify different aspects of the server: .. http:post:: /stats/recentlookups/clear - Clears all outstanding lookups and counts. If called when recent lookup - collection is enabled, this clears all the, but collection - continues. If called when recent lookup collection is disabled, - there is no effect, as disabling collection clears the data. + Clears all outstanding lookups and counts. This clears all recent + lookups data as well as the count, but collection continues if + it is enabled. See :repo:`source/docs/stats.md` for more details. From 298210c5b67bc6d683ec41abd91eed1e0fa39c45 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 16 Oct 2019 17:17:29 -0400 Subject: [PATCH 50/50] un-option the reference to use-fake-symbol-table, which is not doc'd. Signed-off-by: Joshua Marantz --- docs/root/operations/admin.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index b04c37bb4d34..2cec683f2f60 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -372,7 +372,8 @@ modify different aspects of the server: but in response to user requests on high core-count machines, this can cause performance issues due to mutex contention. - This admin endpoint requires Envoy to be started with :option:`use-fake-symbol-table 0`. + This admin endpoint requires Envoy to be started with option + `--use-fake-symbol-table 0`. See :repo:`source/docs/stats.md` for more details.