From acc53250ea1d8a87c2eed46e6abb9c73c21dbfef Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 21 Jan 2022 11:52:31 -0500 Subject: [PATCH 01/74] get basic streaming working for /stats Signed-off-by: Joshua Marantz --- source/server/admin/admin_filter.cc | 20 +++-- source/server/admin/stats_handler.cc | 121 ++++++++++++++++----------- source/server/admin/stats_handler.h | 42 +++++++--- 3 files changed, 117 insertions(+), 66 deletions(-) diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index ecd5fa6e95624..da63489f2a054 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -68,14 +68,18 @@ void AdminFilter::onComplete() { Buffer::OwnedImpl response; auto header_map = Http::ResponseHeaderMapImpl::create(); RELEASE_ASSERT(request_headers_, ""); - Http::Code code = admin_server_callback_func_(path, *header_map, response, *this); - Utility::populateFallbackResponseHeaders(code, *header_map); - decoder_callbacks_->encodeHeaders(std::move(header_map), - end_stream_on_complete_ && response.length() == 0, - StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); - - if (response.length() > 0) { - decoder_callbacks_->encodeData(response, end_stream_on_complete_); + for (bool cont = true, first = true; cont; first = false) { + Http::Code code = admin_server_callback_func_(path, *header_map, response, *this); + cont = code == Http::Code::Continue; + if (first) { + Utility::populateFallbackResponseHeaders(cont ? Http::Code::OK : code, *header_map); + decoder_callbacks_->encodeHeaders(std::move(header_map), + end_stream_on_complete_ && response.length() == 0, + StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); + } + if (response.length() > 0) { + decoder_callbacks_->encodeData(response, !cont && end_stream_on_complete_); + } } } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 78903b3926905..10bac1359b794 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -90,43 +90,65 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, return handlerPrometheusStats(url, response_headers, response, admin_stream); } - std::map all_stats; - for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) { - if (shouldShowMetric(*counter, used_only, regex)) { - all_stats.emplace(counter->name(), counter->value()); + auto iter = context_map_.find(&admin_stream); + if (iter == context_map_.end()) { + auto insertion = context_map_.emplace(&admin_stream, std::make_unique( + server_, used_only, regex, format_value)); + iter = insertion.first; + } + Context& context = *iter->second; + + Http::Code code = Http::Code::NotFound; + if (!format_value.has_value()) { + // Display plain stats if format query param is not there. + code = context.statsAsText(response_headers, response); + } else if (format_value.value() == "json") { + code = context.statsAsJson(response_headers, response, params.find("pretty") != params.end()); + } else { + response.add("usage: /stats?format=json or /stats?format=prometheus \n"); + response.add("\n"); + } + if (code != Http::Code::Continue) { + context_map_.erase(iter); + } + + return code; +} + +StatsHandler::Context::Context(Server::Instance& server, + bool used_only, absl::optional regex, + absl::optional format_value) + : used_only_(used_only), regex_(regex), format_value_(format_value) { + for (const Stats::CounterSharedPtr& counter : server.stats().counters()) { + if (shouldShowMetric(*counter)) { + all_stats_.emplace(counter->name(), counter->value()); } } - for (const Stats::GaugeSharedPtr& gauge : server_.stats().gauges()) { - if (shouldShowMetric(*gauge, used_only, regex)) { + for (const Stats::GaugeSharedPtr& gauge : server.stats().gauges()) { + if (shouldShowMetric(*gauge)) { ASSERT(gauge->importMode() != Stats::Gauge::ImportMode::Uninitialized); - all_stats.emplace(gauge->name(), gauge->value()); + all_stats_.emplace(gauge->name(), gauge->value()); } } + all_stats_iter_ = all_stats_.begin(); - std::map text_readouts; - for (const auto& text_readout : server_.stats().textReadouts()) { - if (shouldShowMetric(*text_readout, used_only, regex)) { - text_readouts.emplace(text_readout->name(), text_readout->value()); + for (const auto& text_readout : server.stats().textReadouts()) { + if (shouldShowMetric(*text_readout)) { + text_readouts_.emplace(text_readout->name(), text_readout->value()); } } + text_readouts_iter_ = text_readouts_.begin(); - if (!format_value.has_value()) { - // Display plain stats if format query param is not there. - statsAsText(all_stats, text_readouts, server_.stats().histograms(), used_only, regex, response); - return Http::Code::OK; - } + histograms_ = server.stats().histograms(); +} - if (format_value.value() == "json") { - response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); - response.add( - statsAsJson(all_stats, text_readouts, server_.stats().histograms(), used_only, regex)); - return Http::Code::OK; +bool StatsHandler::Context::Context::nextChunk() { + if (++chunk_index_ == chunk_size_) { + chunk_index_= 0; + return true; } - - response.add("usage: /stats?format=json or /stats?format=prometheus \n"); - response.add("\n"); - return Http::Code::NotFound; + return false; } Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, @@ -171,22 +193,26 @@ Http::Code StatsHandler::handlerContention(absl::string_view, return Http::Code::OK; } -void StatsHandler::statsAsText(const std::map& all_stats, - const std::map& text_readouts, - const std::vector& histograms, - bool used_only, const absl::optional& regex, - Buffer::Instance& response) { +Http::Code StatsHandler::Context::statsAsText(Http::ResponseHeaderMap& /*response_headers*/, + Buffer::Instance& response) { // Display plain stats if format query param is not there. - for (const auto& text_readout : text_readouts) { + for (; text_readouts_iter_ != text_readouts_.end(); ++text_readouts_iter_) { + if (nextChunk()) { + return Http::Code::Continue; + } response.addFragments( - {text_readout.first, ": \"", Html::Utility::sanitize(text_readout.second), "\"\n"}); + {text_readouts_iter_->first, ": \"", Html::Utility::sanitize(text_readouts_iter_->second), + "\"\n"}); } - for (const auto& stat : all_stats) { - response.addFragments({stat.first, ": ", absl::StrCat(stat.second), "\n"}); + for (; all_stats_iter_ != all_stats_.end(); ++all_stats_iter_) { + if (nextChunk()) { + return Http::Code::Continue; + } + response.addFragments({all_stats_iter_->first, ": ", absl::StrCat(all_stats_iter_->second), "\n"}); } std::map all_histograms; - for (const Stats::ParentHistogramSharedPtr& histogram : histograms) { - if (shouldShowMetric(*histogram, used_only, regex)) { + for (const Stats::ParentHistogramSharedPtr& histogram : histograms_) { + if (shouldShowMetric(*histogram)) { auto insert = all_histograms.emplace(histogram->name(), histogram->quantileSummary()); ASSERT(insert.second); // No duplicates expected. } @@ -194,25 +220,25 @@ void StatsHandler::statsAsText(const std::map& all_stats, for (const auto& histogram : all_histograms) { response.addFragments({histogram.first, ": ", histogram.second, "\n"}); } + return Http::Code::OK; } -std::string -StatsHandler::statsAsJson(const std::map& all_stats, - const std::map& text_readouts, - const std::vector& all_histograms, - const bool used_only, const absl::optional& regex, - const bool pretty_print) { +Http::Code StatsHandler::Context::statsAsJson(Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response, + const bool pretty_print) { + + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); ProtobufWkt::Struct document; std::vector stats_array; - for (const auto& text_readout : text_readouts) { + for (const auto& text_readout : text_readouts_) { ProtobufWkt::Struct stat_obj; auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(text_readout.first); (*stat_obj_fields)["value"] = ValueUtil::stringValue(text_readout.second); stats_array.push_back(ValueUtil::structValue(stat_obj)); } - for (const auto& stat : all_stats) { + for (const auto& stat : all_stats_) { ProtobufWkt::Struct stat_obj; auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(stat.first); @@ -228,8 +254,8 @@ StatsHandler::statsAsJson(const std::map& all_stats, std::vector computed_quantile_array; bool found_used_histogram = false; - for (const Stats::ParentHistogramSharedPtr& histogram : all_histograms) { - if (shouldShowMetric(*histogram, used_only, regex)) { + for (const Stats::ParentHistogramSharedPtr& histogram : histograms_) { + if (shouldShowMetric(*histogram)) { if (!found_used_histogram) { // It is not possible for the supported quantiles to differ across histograms, so it is ok // to send them once. @@ -274,7 +300,8 @@ StatsHandler::statsAsJson(const std::map& all_stats, auto* document_fields = document.mutable_fields(); (*document_fields)["stats"] = ValueUtil::listValue(stats_array); - return MessageUtil::getJsonStringFromMessageOrDie(document, pretty_print, true); + response.add(MessageUtil::getJsonStringFromMessageOrDie(document, pretty_print, true)); + return Http::Code::OK; } } // namespace Server diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 796bd35bc8ff7..378f12e00494d 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -12,6 +12,7 @@ #include "source/common/stats/histogram_impl.h" #include "source/server/admin/handler_ctx.h" +#include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -47,6 +48,35 @@ class StatsHandler : public HandlerContextBase { Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + class Context { + public: + Context(Server::Instance& server, + bool used_only, absl::optional regex, + absl::optional format_value); + + bool nextChunk(); + + template bool shouldShowMetric(const StatType& stat) { + return StatsHandler::shouldShowMetric(stat, used_only_, regex_); + } + + Http::Code statsAsText(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response); + Http::Code statsAsJson(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, + bool pretty); + + const bool used_only_; + absl::optional regex_; + absl::optional format_value_; + std::map all_stats_; + std::map::iterator all_stats_iter_; + std::map text_readouts_; + std::map::iterator text_readouts_iter_; + std::vector histograms_; + static constexpr uint32_t chunk_size_ = 10; + uint32_t chunk_index_{0};; + }; + using ContextPtr = std::unique_ptr; + private: template static bool shouldShowMetric(const StatType& metric, const bool used_only, @@ -57,17 +87,7 @@ class StatsHandler : public HandlerContextBase { friend class StatsHandlerTest; - static std::string statsAsJson(const std::map& all_stats, - const std::map& text_readouts, - const std::vector& all_histograms, - bool used_only, const absl::optional& regex, - bool pretty_print = false); - - void statsAsText(const std::map& all_stats, - const std::map& text_readouts, - const std::vector& all_histograms, - bool used_only, const absl::optional& regex, - Buffer::Instance& response); + absl::flat_hash_map> context_map_; }; } // namespace Server From 75861f138acac25a1de71da3cee9b26f949ee2ae Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 24 Jan 2022 23:58:25 -0500 Subject: [PATCH 02/74] chunk-by-scope checkpoint Signed-off-by: Joshua Marantz --- envoy/stats/scope.h | 20 +- source/common/stats/isolated_store_impl.cc | 23 +- source/common/stats/isolated_store_impl.h | 15 ++ source/common/stats/scope_prefixer.cc | 2 +- source/common/stats/scope_prefixer.h | 32 +++ source/common/stats/symbol_table_impl.cc | 3 +- source/common/stats/thread_local_store.cc | 7 +- source/common/stats/thread_local_store.h | 18 ++ source/server/admin/stats_handler.cc | 251 ++++++++++++++++-- source/server/admin/stats_handler.h | 31 ++- test/common/stats/isolated_store_impl_test.cc | 8 +- test/common/stats/utility_test.cc | 1 + test/mocks/stats/mocks.h | 8 + 13 files changed, 383 insertions(+), 36 deletions(-) diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index 7271883314ad9..18468c44ee1bd 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -6,6 +6,10 @@ #include "envoy/common/pure.h" #include "envoy/stats/histogram.h" +#define SCOPE_REFCOUNT 1 +#if SCOPE_REFCOUNT +#include "envoy/stats/refcount_ptr.h" +#endif #include "envoy/stats/symbol_table.h" #include "envoy/stats/tag.h" @@ -25,8 +29,14 @@ using CounterOptConstRef = absl::optional> using GaugeOptConstRef = absl::optional>; using HistogramOptConstRef = absl::optional>; using TextReadoutOptConstRef = absl::optional>; -using ScopePtr = std::unique_ptr; +#if SCOPE_REFCOUNT +using ConstScopeSharedPtr = RefcountPtr; +using ScopeSharedPtr = RefcountPtr; +#else +using ConstScopeSharedPtr = std::shared_ptr; using ScopeSharedPtr = std::shared_ptr; +#endif +using ScopePtr = ScopeSharedPtr; // TODO(jmarantz): global s/ShaedPtr/ScopeSharedPtr/ & remove alias template using IterateFn = std::function&)>; @@ -34,7 +44,13 @@ template using IterateFn = std::function +#endif +{ public: virtual ~Scope() = default; diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 47cf0b3fc5c07..2638baf82eb60 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -9,6 +9,8 @@ #include "source/common/stats/scope_prefixer.h" #include "source/common/stats/utility.h" +#include "absl/strings/str_cat.h" + namespace Envoy { namespace Stats { @@ -36,13 +38,30 @@ IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) null_counter_(new NullCounterImpl(symbol_table)), null_gauge_(new NullGaugeImpl(symbol_table)) {} +IsolatedStoreImpl::~IsolatedStoreImpl() { +#if SCOPE_REFCOUNT + ENVOY_LOG_MISC(error, "ref_count={}", ref_count_); + ASSERT(ref_count_ == 0); +#endif +} + +#if SCOPE_REFCOUNT +ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { + return ScopeSharedPtr(new ScopePrefixer(name, *this)); +} + +ScopePtr IsolatedStoreImpl::scopeFromStatName(StatName name) { + return ScopeSharedPtr(new ScopePrefixer(name, *this)); +} +#else ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { - return std::make_unique(name, *this); + return std::make_shared(name, *this); } ScopePtr IsolatedStoreImpl::scopeFromStatName(StatName name) { - return std::make_unique(name, *this); + return std::make_shared(name, *this); } +#endif } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 99b06eb85dd4f..e75bd5a4738ad 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -132,6 +132,7 @@ class IsolatedStoreImpl : public StoreImpl { public: IsolatedStoreImpl(); explicit IsolatedStoreImpl(SymbolTable& symbol_table); + ~IsolatedStoreImpl(); // Stats::Scope Counter& counterFromStatNameWithTags(const StatName& name, @@ -249,8 +250,19 @@ class IsolatedStoreImpl : public StoreImpl { forEachTextReadout(f_size, f_stat); } +#if SCOPE_REFCOUNT + // RefcountInterface + void incRefCount() override { ++ref_count_; } + bool decRefCount() override { + return --ref_count_ == 0; + } + uint32_t use_count() const override { return ref_count_; } +#endif + private: IsolatedStoreImpl(std::unique_ptr&& symbol_table); + IsolatedStoreImpl(const IsolatedStoreImpl&) = delete; + IsolatedStoreImpl& operator=(const IsolatedStoreImpl&) = delete; SymbolTablePtr symbol_table_storage_; AllocatorImpl alloc_; @@ -260,6 +272,9 @@ class IsolatedStoreImpl : public StoreImpl { IsolatedStatsCache text_readouts_; RefcountPtr null_counter_; RefcountPtr null_gauge_; +#if SCOPE_REFCOUNT + std::atomic ref_count_{0}; +#endif }; } // namespace Stats diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index cbe4c3b1c45e6..83be5ac51db12 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -18,7 +18,7 @@ ScopePrefixer::~ScopePrefixer() { prefix_.free(symbolTable()); } ScopePtr ScopePrefixer::scopeFromStatName(StatName name) { SymbolTable::StoragePtr joined = symbolTable().join({prefix_.statName(), name}); - return std::make_unique(StatName(joined.get()), scope_); + return ScopeSharedPtr(new ScopePrefixer(StatName(joined.get()), scope_)); } ScopePtr ScopePrefixer::createScope(const std::string& name) { diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index 8d4f6ee19cc6d..07bb8578e8c0f 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -11,10 +11,32 @@ namespace Stats { // prior to creation. class ScopePrefixer : public Scope { public: +#if 0 + ScopePrefixer(absl::string_view prefix, const ScopeSharedPtr& scope); + ScopePrefixer(StatName prefix, const ScopeSharedPtr& scope); +#else ScopePrefixer(absl::string_view prefix, Scope& scope); ScopePrefixer(StatName prefix, Scope& scope); +#endif ~ScopePrefixer() override; +#if SCOPE_REFCOUNT + // RefcountInterface +#if 1 + void incRefCount() override { + ++ref_count_; + } + bool decRefCount() override { + return --ref_count_ == 0; + } + uint32_t use_count() const override { return ref_count_; } +#else + void incRefCount() override { scope_->incRefCount(); } + bool decRefCount() override { return scope_->decRefCount(); } + uint32_t use_count() const override { return scope_->use_count(); } +#endif +#endif + // Scope ScopePtr createScope(const std::string& name) override; ScopePtr scopeFromStatName(StatName name) override; @@ -63,6 +85,9 @@ class ScopePrefixer : public Scope { StatName prefix() const override { return prefix_.statName(); } private: + ScopePrefixer(const ScopePrefixer&) = delete; + ScopePrefixer& operator=(const ScopePrefixer&) = delete; + template bool iterHelper(const IterateFn& fn) const { // We determine here what's in the scope by looking at name // prefixes. Strictly speaking this is not correct, as a stat name can be in @@ -85,8 +110,15 @@ class ScopePrefixer : public Scope { return scope_.iterate(filter_scope); } +#if 0 + ScopeSharedPtr scope_; +#else Scope& scope_; +#endif StatNameStorage prefix_; +#if SCOPE_REFCOUNT + std::atomic ref_count_{0}; +#endif }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 0b15b66ba4547..a6ecbe64f581f 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -228,6 +228,7 @@ SymbolTableImpl::~SymbolTableImpl() { // Note: this could potentially be short-circuited if we decide a fast exit // is needed in production. But it would be good to ensure clean up during // tests. + debugPrint(); ASSERT(numSymbols() == 0); } @@ -470,7 +471,7 @@ void SymbolTableImpl::debugPrint() const { for (Symbol symbol : symbols) { const InlineString& token = *decode_map_.find(symbol)->second; const SharedSymbol& shared_symbol = encode_map_.find(token.toStringView())->second; - ENVOY_LOG_MISC(info, "{}: '{}' ({})", symbol, token.toStringView(), shared_symbol.ref_count_); + ENVOY_LOG_MISC(error, "{}: '{}' ({})", symbol, token.toStringView(), shared_symbol.ref_count_); } } #endif diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 6dc693ab1a7c3..81c20bae69bed 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -46,6 +46,7 @@ ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(scopes_to_cleanup_.empty()); ASSERT(central_cache_entries_to_cleanup_.empty()); ASSERT(histograms_to_cleanup_.empty()); + ASSERT(ref_count_ == 0); } void ThreadLocalStoreImpl::setHistogramSettings(HistogramSettingsConstPtr&& histogram_settings) { @@ -150,7 +151,11 @@ ScopePtr ThreadLocalStoreImpl::createScope(const std::string& name) { } ScopePtr ThreadLocalStoreImpl::scopeFromStatName(StatName name) { - auto new_scope = std::make_unique(*this, name); +#if SCOPE_REFCOUNT + RefcountPtr new_scope(new ScopeImpl(*this, name)); +#else + auto new_scope = std::make_shared(*this, name); +#endif Thread::LockGuard lock(lock_); scopes_.emplace(new_scope.get()); return new_scope; diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index cfd64f97d3575..969af8737fa71 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -282,6 +282,13 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo bool decHistogramRefCount(ParentHistogramImpl& histogram, std::atomic& ref_count); void releaseHistogramCrossThread(uint64_t histogram_id); +#if SCOPE_REFCOUNT + // RefcountInterface + void incRefCount() override { ++ref_count_; } + bool decRefCount() override { return --ref_count_ == 0; } + uint32_t use_count() const override { return ref_count_; } +#endif + private: friend class ThreadLocalStoreTestingPeer; @@ -336,6 +343,12 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ScopeImpl(ThreadLocalStoreImpl& parent, StatName prefix); ~ScopeImpl() override; +#if SCOPE_REFCOUNT + // RefcountInterface + void incRefCount() override { ++ref_count_; } + bool decRefCount() override { return --ref_count_ == 0; } + uint32_t use_count() const override { return ref_count_; } +#endif // Stats::Scope Counter& counterFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags) override; @@ -455,6 +468,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ThreadLocalStoreImpl& parent_; StatNameStorage prefix_; mutable CentralCacheEntrySharedPtr central_cache_; +#if SCOPE_REFCOUNT + std::atomic ref_count_{0}; +#endif }; struct TlsCache : public ThreadLocal::ThreadLocalObject { @@ -566,6 +582,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo // (e.g. when a scope is deleted), it is likely more efficient to batch their // cleanup, which would otherwise entail a post() per histogram per thread. std::vector histograms_to_cleanup_ ABSL_GUARDED_BY(hist_mutex_); + + std::atomic ref_count_{0}; }; using ThreadLocalStoreImplPtr = std::unique_ptr; diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 10bac1359b794..ea663ea57c848 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -86,28 +86,29 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, } const absl::optional format_value = Utility::formatParam(params); - if (format_value.has_value() && format_value.value() == "prometheus") { - return handlerPrometheusStats(url, response_headers, response, admin_stream); + bool json = false; + if (format_value.has_value()) { + if (format_value.value() == "prometheus") { + return handlerPrometheusStats(url, response_headers, response, admin_stream); + } + if (format_value.value() == "json") { + json = true; + } else { + response.add("usage: /stats?format=json or /stats?format=prometheus \n"); + response.add("\n"); + return Http::Code::BadRequest; + } } auto iter = context_map_.find(&admin_stream); if (iter == context_map_.end()) { auto insertion = context_map_.emplace(&admin_stream, std::make_unique( - server_, used_only, regex, format_value)); + server_, used_only, regex, json, response_headers, response)); iter = insertion.first; } Context& context = *iter->second; - Http::Code code = Http::Code::NotFound; - if (!format_value.has_value()) { - // Display plain stats if format query param is not there. - code = context.statsAsText(response_headers, response); - } else if (format_value.value() == "json") { - code = context.statsAsJson(response_headers, response, params.find("pretty") != params.end()); - } else { - response.add("usage: /stats?format=json or /stats?format=prometheus \n"); - response.add("\n"); - } + Http::Code code = context.writeChunk(response); if (code != Http::Code::Continue) { context_map_.erase(iter); } @@ -115,10 +116,213 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, return code; } +class StatsHandler::Render { +public: + virtual ~Render() = default; + virtual void generate(const std::string& name, uint64_t value) PURE; + virtual void generate(const std::string& name, const std::string& value) PURE; + virtual void generate(const std::string& name, + const Stats::ParentHistogramSharedPtr& histogram) PURE; + virtual void render() PURE; +}; + +Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { + for (uint32_t start = response.length(); + stats_and_scopes_index_ < stats_and_scopes_.size() && (response.length() - start) < chunk_size_; + ++stats_and_scopes_index_) { + const StatOrScope& stat_or_scope = stats_and_scopes_[stats_and_scopes_index_]; + switch (stat_or_scope.index()) { + case 0: + //renderScope(absl::get(stat_or_scope)); + break; + case 1: { + auto text_readout = absl::get(stat_or_scope); + render_->generate(text_readout->name(), text_readout->value()); + break; + } + case 2: { + auto counter = absl::get(stat_or_scope); + render_->generate(counter->name(), counter->value()); + break; + } + case 3: { + auto gauge = absl::get(stat_or_scope); + render_->generate(gauge->name(), gauge->value()); + break; + } + case 4: { + auto histogram = absl::get(stat_or_scope); + render_->generate(histogram->name(), histogram); + } + } + } + + if (stats_and_scopes_index_ < stats_and_scopes_.size()) { + return Http::Code::Continue; + } + return Http::Code::OK; + +} + +class StatsHandler::TextRender : public StatsHandler::Render { +public: + TextRender(Buffer::Instance& response) : response_(response) {} + + void generate(const std::string& name, uint64_t value) override { + response_.addFragments({name, ": ", absl::StrCat(value), "\n"}); + } + + void generate(const std::string& name, const std::string& value) override { + response_.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); + } + + void generate(const std::string& name, + const Stats::ParentHistogramSharedPtr& histogram) override { + response_.addFragments({name, ": ", histogram->quantileSummary(), "\n"}); + } + + void render() override {} + +protected: + Buffer::Instance& response_; +}; + +class StatsHandler::JsonRender : public StatsHandler::Render { +public: + JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) + : response_(response) { + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + } + + void generate(const std::string& name, uint64_t value) override { + add(name, ValueUtil::numberValue(value)); + } + void generate(const std::string& name, const std::string& value) override { + add(name, ValueUtil::stringValue(value)); + } + void generate(const std::string& name, + const Stats::ParentHistogramSharedPtr& histogram) override { + if (!found_used_histogram_) { + auto* histograms_obj_fields = histograms_obj_.mutable_fields(); + + // It is not possible for the supported quantiles to differ across histograms, so it is ok + // to send them once. + Stats::HistogramStatisticsImpl empty_statistics; + std::vector supported_quantile_array; + for (double quantile : empty_statistics.supportedQuantiles()) { + supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); + } + (*histograms_obj_fields)["supported_quantiles"] = + ValueUtil::listValue(supported_quantile_array); + found_used_histogram_ = true; + } + + ProtobufWkt::Struct computed_quantile; + auto* computed_quantile_fields = computed_quantile.mutable_fields(); + (*computed_quantile_fields)["name"] = ValueUtil::stringValue(name); + + std::vector computed_quantile_value_array; + for (size_t i = 0; i < histogram->intervalStatistics().supportedQuantiles().size(); ++i) { + ProtobufWkt::Struct computed_quantile_value; + auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); + const auto& interval = histogram->intervalStatistics().computedQuantiles()[i]; + const auto& cumulative = histogram->cumulativeStatistics().computedQuantiles()[i]; + (*computed_quantile_value_fields)["interval"] = + std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); + (*computed_quantile_value_fields)["cumulative"] = + std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); + + computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); + } + (*computed_quantile_fields)["values"] = ValueUtil::listValue(computed_quantile_value_array); + computed_quantile_array_.push_back(ValueUtil::structValue(computed_quantile)); + } + + void render() override { + if (found_used_histogram_) { + auto* histograms_obj_fields = histograms_obj_.mutable_fields(); + (*histograms_obj_fields)["computed_quantiles"] = + ValueUtil::listValue(computed_quantile_array_); + auto* histograms_obj_container_fields = histograms_obj_container_.mutable_fields(); + (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); + stats_array_.push_back(ValueUtil::structValue(histograms_obj_container_)); + } + + auto* document_fields = document_.mutable_fields(); + (*document_fields)["stats"] = ValueUtil::listValue(stats_array_); + response_.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, true)); + } + +private: + template void add(const std::string& name, const Value& value) { + ProtobufWkt::Struct stat_obj; + auto* stat_obj_fields = stat_obj.mutable_fields(); + (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); + (*stat_obj_fields)["value"] = value; + stats_array_.push_back(ValueUtil::structValue(stat_obj)); + } + + Buffer::Instance& response_; + ProtobufWkt::Struct document_; + std::vector stats_array_; + std::vector scope_array_; + ProtobufWkt::Struct histograms_obj_; + ProtobufWkt::Struct histograms_obj_container_; + std::vector computed_quantile_array_; + bool found_used_histogram_{false}; +}; + StatsHandler::Context::Context(Server::Instance& server, bool used_only, absl::optional regex, - absl::optional format_value) - : used_only_(used_only), regex_(regex), format_value_(format_value) { + bool json, + Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response) + : used_only_(used_only), regex_(regex), stats_(server.stats()) { + if (json) { + render_ = std::make_unique(response_headers, response); + } else { + render_ = std::make_unique(response); + } + + // Populate the top-level scopes and the stats underneath any scopes with an empty name. + // We will have to de-dup, but we can do that after sorting. + stats_.forEachScope(nullptr, [this](const Stats::Scope& scope) { + if (scope.prefix().empty()) { + Stats::IterateFn fn = [this](const Stats::CounterSharedPtr& counter) -> bool { + stats_and_scopes_.emplace_back(counter); + return true; + }; + scope.iterate(fn); + } else { + stats_and_scopes_.emplace_back(Stats::ScopeSharedPtr(const_cast(&scope))); + } + }); + + struct CompareStatsAndScopes { + CompareStatsAndScopes(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table) {} + bool operator()(const StatOrScope& a, const StatOrScope& b) const { + return symbol_table_.lessThan(name(a), name(b)); + } + + Stats::StatName name(const StatOrScope& stat_or_scope) const { + switch (stat_or_scope.index()) { + case 0: return absl::get(stat_or_scope)->prefix(); + case 1: return absl::get(stat_or_scope)->statName(); + case 2: return absl::get(stat_or_scope)->statName(); + case 3: return absl::get(stat_or_scope)->statName(); + case 4: return absl::get(stat_or_scope)->statName(); + } + return Stats::StatName(); + } + + Stats::SymbolTable& symbol_table_; + }; + + + std::sort(stats_and_scopes_.begin(), stats_and_scopes_.end(), CompareStatsAndScopes(stats_.symbolTable())); + std::unique(stats_and_scopes_.begin(), stats_and_scopes_.end()); + + /* for (const Stats::CounterSharedPtr& counter : server.stats().counters()) { if (shouldShowMetric(*counter)) { all_stats_.emplace(counter->name(), counter->value()); @@ -141,6 +345,10 @@ StatsHandler::Context::Context(Server::Instance& server, text_readouts_iter_ = text_readouts_.begin(); histograms_ = server.stats().histograms(); + */ +} + +StatsHandler::Context::~Context() { } bool StatsHandler::Context::Context::nextChunk() { @@ -194,7 +402,8 @@ Http::Code StatsHandler::handlerContention(absl::string_view, } Http::Code StatsHandler::Context::statsAsText(Http::ResponseHeaderMap& /*response_headers*/, - Buffer::Instance& response) { + Buffer::Instance& /*response*/) { + /* // Display plain stats if format query param is not there. for (; text_readouts_iter_ != text_readouts_.end(); ++text_readouts_iter_) { if (nextChunk()) { @@ -221,12 +430,15 @@ Http::Code StatsHandler::Context::statsAsText(Http::ResponseHeaderMap& /*respons response.addFragments({histogram.first, ": ", histogram.second, "\n"}); } return Http::Code::OK; + */ + return Http::Code::OK; } -Http::Code StatsHandler::Context::statsAsJson(Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response, - const bool pretty_print) { +Http::Code StatsHandler::Context::statsAsJson(Http::ResponseHeaderMap& /*response_headers*/, + Buffer::Instance& /*response*/, + const bool /*pretty_print*/) { +#if 0 response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); ProtobufWkt::Struct document; @@ -301,6 +513,7 @@ Http::Code StatsHandler::Context::statsAsJson(Http::ResponseHeaderMap& response_ (*document_fields)["stats"] = ValueUtil::listValue(stats_array); response.add(MessageUtil::getJsonStringFromMessageOrDie(document, pretty_print, true)); +#endif return Http::Code::OK; } diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 378f12e00494d..ab4db149e3e26 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -14,6 +14,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" +#include "absl/types/variant.h" namespace Envoy { namespace Server { @@ -48,13 +49,20 @@ class StatsHandler : public HandlerContextBase { Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + class JsonRender; + class Render; + class TextRender; + class Context { public: Context(Server::Instance& server, bool used_only, absl::optional regex, - absl::optional format_value); + bool json, Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response); + ~Context(); bool nextChunk(); + Http::Code writeChunk(Buffer::Instance& response); template bool shouldShowMetric(const StatType& stat) { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); @@ -67,13 +75,22 @@ class StatsHandler : public HandlerContextBase { const bool used_only_; absl::optional regex_; absl::optional format_value_; - std::map all_stats_; - std::map::iterator all_stats_iter_; - std::map text_readouts_; - std::map::iterator text_readouts_iter_; - std::vector histograms_; + + std::unique_ptr render_; + + using StatOrScope = absl::variant< + Stats::ScopeSharedPtr, + Stats::TextReadoutSharedPtr, + Stats::CounterSharedPtr, + Stats::GaugeSharedPtr, + Stats::ParentHistogramSharedPtr>; + using StatOrScopeVec = std::vector; + static constexpr uint32_t chunk_size_ = 10; - uint32_t chunk_index_{0};; + Stats::Store& stats_; + StatOrScopeVec stats_and_scopes_; + uint32_t stats_and_scopes_index_{0}; + uint32_t chunk_index_{0}; }; using ContextPtr = std::unique_ptr; diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index f3a3e0a31dcd2..d121ca6ac23a8 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -16,22 +16,23 @@ namespace Stats { class StatsIsolatedStoreImplTest : public testing::Test { protected: StatsIsolatedStoreImplTest() - : store_(std::make_unique(symbol_table_)), pool_(symbol_table_) {} + : pool_(symbol_table_), store_(std::make_unique(symbol_table_)) {} ~StatsIsolatedStoreImplTest() override { - pool_.clear(); store_.reset(); + pool_.clear(); EXPECT_EQ(0, symbol_table_.numSymbols()); } StatName makeStatName(absl::string_view name) { return pool_.add(name); } SymbolTableImpl symbol_table_; - std::unique_ptr store_; StatNamePool pool_; + std::unique_ptr store_; }; TEST_F(StatsIsolatedStoreImplTest, All) { ScopePtr scope1 = store_->createScope("scope1."); + EXPECT_EQ(1, scope1.use_count()); Counter& c1 = store_->counterFromString("c1"); Counter& c2 = scope1->counterFromString("c2"); EXPECT_EQ("c1", c1.name()); @@ -122,6 +123,7 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(store_->findCounter(nonexistent_name.statName()), absl::nullopt); EXPECT_EQ(store_->findGauge(nonexistent_name.statName()), absl::nullopt); EXPECT_EQ(store_->findHistogram(nonexistent_name.statName()), absl::nullopt); + scope1.reset(); } TEST_F(StatsIsolatedStoreImplTest, PrefixIsStatName) { diff --git a/test/common/stats/utility_test.cc b/test/common/stats/utility_test.cc index 040b63bb34bbc..a0f733eae8618 100644 --- a/test/common/stats/utility_test.cc +++ b/test/common/stats/utility_test.cc @@ -186,6 +186,7 @@ TEST_P(StatsUtilityTest, Counters) { Counter& ctags = Utility::counterFromElements(*scope, {DynamicName("x"), token, DynamicName("y")}, tags_); EXPECT_EQ("scope.x.token.y.tag1.value1.tag2.value2", ctags.name()); + scope.reset(); } TEST_P(StatsUtilityTest, Gauges) { diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index fd3d245395510..2c4d7ebb946b8 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -278,6 +278,13 @@ class MockStore : public TestUtil::TestStore { MockStore(); ~MockStore() override; +#if SCOPE_REFCOUNT + // RefcountInterface + void incRefCount() override { ++ref_count_; } + bool decRefCount() override { return --ref_count_ == 0; } + uint32_t use_count() const override { return ref_count_; } +#endif + ScopePtr createScope(const std::string& name) override { return ScopePtr{createScope_(name)}; } ScopePtr scopeFromStatName(StatName name) override { return createScope(symbolTable().toString(name)); @@ -328,6 +335,7 @@ class MockStore : public TestUtil::TestStore { testing::NiceMock counter_; testing::NiceMock gauge_; std::vector> histograms_; + std::atomic ref_count_{0}; }; /** From 74fcba9e15ee3e99280b02cf01eeaee390e57e0a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 26 Jan 2022 00:06:33 -0500 Subject: [PATCH 03/74] checkpoint with basic chunked text working, but not json. Signed-off-by: Joshua Marantz --- envoy/stats/scope.h | 8 +- source/common/http/http1/codec_impl.cc | 1 + source/server/admin/admin_filter.cc | 2 +- source/server/admin/stats_handler.cc | 408 ++++++++++-------------- source/server/admin/stats_handler.h | 38 ++- test/server/admin/stats_handler_test.cc | 43 ++- 6 files changed, 225 insertions(+), 275 deletions(-) diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index 18468c44ee1bd..6b35acfcbf6d1 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -6,7 +6,7 @@ #include "envoy/common/pure.h" #include "envoy/stats/histogram.h" -#define SCOPE_REFCOUNT 1 +#define SCOPE_REFCOUNT 0 #if SCOPE_REFCOUNT #include "envoy/stats/refcount_ptr.h" #endif @@ -54,6 +54,12 @@ class Scope : public public: virtual ~Scope() = default; +#if SCOPE_REFCOUNT + ScopeSharedPtr makeShared() { return ScopeSharedPtr(this); } +#else + ScopeSharedPtr makeShared() { return shared_from_this(); } +#endif + /** * Allocate a new scope. NOTE: The implementation should correctly handle overlapping scopes * that point to the same reference counted backing stats. This allows a new scope to be diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index ab52c97061c9b..1d3353398638e 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -237,6 +237,7 @@ void StreamEncoderImpl::encodeData(Buffer::Instance& data, bool end_stream) { } connection_.buffer().move(data); + ASSERT(data.length() == 0); if (chunk_encoding_) { connection_.buffer().add(CRLF); diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index da63489f2a054..4effbd9e9edf8 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -77,7 +77,7 @@ void AdminFilter::onComplete() { end_stream_on_complete_ && response.length() == 0, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); } - if (response.length() > 0) { + if (response.length() > 0 || !cont) { decoder_callbacks_->encodeData(response, !cont && end_stream_on_complete_); } } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index ea663ea57c848..2b8c931d08983 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -117,91 +117,50 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, } class StatsHandler::Render { -public: + public: virtual ~Render() = default; - virtual void generate(const std::string& name, uint64_t value) PURE; - virtual void generate(const std::string& name, const std::string& value) PURE; - virtual void generate(const std::string& name, - const Stats::ParentHistogramSharedPtr& histogram) PURE; - virtual void render() PURE; + virtual void generate(Buffer::Instance& response, const std::string& name, uint64_t value) PURE; + virtual void generate(Buffer::Instance& response, const std::string& name, const std::string& value) PURE; + virtual void generate(Buffer::Instance& response, const std::string& name, + const Stats::ParentHistogram& histogram) PURE; + virtual void render(Buffer::Instance& response) PURE; }; -Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { - for (uint32_t start = response.length(); - stats_and_scopes_index_ < stats_and_scopes_.size() && (response.length() - start) < chunk_size_; - ++stats_and_scopes_index_) { - const StatOrScope& stat_or_scope = stats_and_scopes_[stats_and_scopes_index_]; - switch (stat_or_scope.index()) { - case 0: - //renderScope(absl::get(stat_or_scope)); - break; - case 1: { - auto text_readout = absl::get(stat_or_scope); - render_->generate(text_readout->name(), text_readout->value()); - break; - } - case 2: { - auto counter = absl::get(stat_or_scope); - render_->generate(counter->name(), counter->value()); - break; - } - case 3: { - auto gauge = absl::get(stat_or_scope); - render_->generate(gauge->name(), gauge->value()); - break; - } - case 4: { - auto histogram = absl::get(stat_or_scope); - render_->generate(histogram->name(), histogram); - } - } - } - - if (stats_and_scopes_index_ < stats_and_scopes_.size()) { - return Http::Code::Continue; - } - return Http::Code::OK; - -} - class StatsHandler::TextRender : public StatsHandler::Render { -public: - TextRender(Buffer::Instance& response) : response_(response) {} - - void generate(const std::string& name, uint64_t value) override { - response_.addFragments({name, ": ", absl::StrCat(value), "\n"}); + public: + void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { + response.addFragments({name, ": ", absl::StrCat(value), "\n"}); } - void generate(const std::string& name, const std::string& value) override { - response_.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); + void generate(Buffer::Instance& response, const std::string& name, const std::string& value) + override { + response.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); } - void generate(const std::string& name, - const Stats::ParentHistogramSharedPtr& histogram) override { - response_.addFragments({name, ": ", histogram->quantileSummary(), "\n"}); + void generate(Buffer::Instance& response, const std::string& name, + const Stats::ParentHistogram& histogram) override { + response.addFragments({name, ": ", histogram.quantileSummary(), "\n"}); } - void render() override {} - -protected: - Buffer::Instance& response_; + void render(Buffer::Instance&) override {} }; +#if 0 class StatsHandler::JsonRender : public StatsHandler::Render { -public: - JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) - : response_(response) { + public: + JsonRender(Http::ResponseHeaderMap& response_headers) { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); } - void generate(const std::string& name, uint64_t value) override { + void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { add(name, ValueUtil::numberValue(value)); } - void generate(const std::string& name, const std::string& value) override { + void generate(Buffer::Instance& response, const std::string& name, + const std::string& value) override { add(name, ValueUtil::stringValue(value)); } - void generate(const std::string& name, - const Stats::ParentHistogramSharedPtr& histogram) override { + void generate(Buffer::Instance& response, const std::string& name, + const Stats::ParentHistogram& histogram) override { if (!found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); @@ -222,11 +181,11 @@ class StatsHandler::JsonRender : public StatsHandler::Render { (*computed_quantile_fields)["name"] = ValueUtil::stringValue(name); std::vector computed_quantile_value_array; - for (size_t i = 0; i < histogram->intervalStatistics().supportedQuantiles().size(); ++i) { + for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { ProtobufWkt::Struct computed_quantile_value; auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); - const auto& interval = histogram->intervalStatistics().computedQuantiles()[i]; - const auto& cumulative = histogram->cumulativeStatistics().computedQuantiles()[i]; + const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; + const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; (*computed_quantile_value_fields)["interval"] = std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); (*computed_quantile_value_fields)["cumulative"] = @@ -238,7 +197,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { computed_quantile_array_.push_back(ValueUtil::structValue(computed_quantile)); } - void render() override { + void render(Buffer::Instance& response) override { if (found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); (*histograms_obj_fields)["computed_quantiles"] = @@ -250,10 +209,10 @@ class StatsHandler::JsonRender : public StatsHandler::Render { auto* document_fields = document_.mutable_fields(); (*document_fields)["stats"] = ValueUtil::listValue(stats_array_); - response_.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, true)); + response.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, true)); } -private: + private: template void add(const std::string& name, const Value& value) { ProtobufWkt::Struct stat_obj; auto* stat_obj_fields = stat_obj.mutable_fields(); @@ -262,7 +221,6 @@ class StatsHandler::JsonRender : public StatsHandler::Render { stats_array_.push_back(ValueUtil::structValue(stat_obj)); } - Buffer::Instance& response_; ProtobufWkt::Struct document_; std::vector stats_array_; std::vector scope_array_; @@ -271,94 +229,173 @@ class StatsHandler::JsonRender : public StatsHandler::Render { std::vector computed_quantile_array_; bool found_used_histogram_{false}; }; +#endif StatsHandler::Context::Context(Server::Instance& server, bool used_only, absl::optional regex, - bool json, - Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response) + bool /*json*/, + Http::ResponseHeaderMap& /*response_headers*/, + Buffer::Instance& /*response*/) : used_only_(used_only), regex_(regex), stats_(server.stats()) { - if (json) { - render_ = std::make_unique(response_headers, response); - } else { - render_ = std::make_unique(response); - } + //if (json) { + // render_ = std::make_unique(response_headers, response); + //} else { + render_ = std::make_unique(); + //} // Populate the top-level scopes and the stats underneath any scopes with an empty name. // We will have to de-dup, but we can do that after sorting. - stats_.forEachScope(nullptr, [this](const Stats::Scope& scope) { - if (scope.prefix().empty()) { - Stats::IterateFn fn = [this](const Stats::CounterSharedPtr& counter) -> bool { - stats_and_scopes_.emplace_back(counter); - return true; - }; - scope.iterate(fn); - } else { - stats_and_scopes_.emplace_back(Stats::ScopeSharedPtr(const_cast(&scope))); - } + // + // First capture all the scopes and hold onto them with a SharedPtr so they + // can't be deleted after the initial iteration. + stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, [this](const Stats::Scope& scope) { + scopes_.emplace_back(const_cast(scope).makeShared()); }); - struct CompareStatsAndScopes { - CompareStatsAndScopes(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table) {} - bool operator()(const StatOrScope& a, const StatOrScope& b) const { - return symbol_table_.lessThan(name(a), name(b)); + startPhase(); +} + +void StatsHandler::Context::startPhase() { + ASSERT(stat_map_.empty()); + for (const Stats::ScopeSharedPtr& scope : scopes_) { + StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; + if (variant.index() == absl::variant_npos) { + variant = ScopeVec(); } + absl::get(variant).emplace_back(scope); + } + + // Populate stat_map with all the counters found in all the scopes with an + // empty prefix. + auto iter = stat_map_.find(""); + if (iter != stat_map_.end()) { + StatOrScopes variant = iter->second; + stat_map_.erase(iter); + auto& scope_vec = absl::get(variant); + populateStatsForCurrentPhase(scope_vec); + } +} + +void StatsHandler::Context::populateStatsForCurrentPhase(const ScopeVec& scope_vec) { + switch (phase_) { + case Phase::TextReadouts: + populateStatsFromScopes(scope_vec); + break; + case Phase::CountersAndGauges: + populateStatsFromScopes(scope_vec); + populateStatsFromScopes(scope_vec); + break; + case Phase::Histograms: + populateStatsFromScopes(scope_vec); + break; + } +} + +template void StatsHandler::Context::populateStatsFromScopes(const ScopeVec& scope_vec) { + for (const Stats::ScopeSharedPtr& scope : scope_vec) { + Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { + stat_map_[stat->name()] = stat; + return true; + }; + scope->iterate(fn); + } +} - Stats::StatName name(const StatOrScope& stat_or_scope) const { - switch (stat_or_scope.index()) { - case 0: return absl::get(stat_or_scope)->prefix(); - case 1: return absl::get(stat_or_scope)->statName(); - case 2: return absl::get(stat_or_scope)->statName(); - case 3: return absl::get(stat_or_scope)->statName(); - case 4: return absl::get(stat_or_scope)->statName(); +template void StatsHandler::Context::renderStat( + Buffer::Instance& response, StatOrScopes& variant) { + auto stat = absl::get(variant); + render_->generate(response, stat->name(), stat->value()); +} + +Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { + for (uint32_t i = 0; i < num_stats_per_chunk_ && !stat_map_.empty(); ++i) { + auto iter = stat_map_.begin(); + StatOrScopes variant = iter->second; + stat_map_.erase(iter); + switch (variant.index()) { + case 0: populateStatsForCurrentPhase(absl::get(variant)); --i; break; + case 1: renderStat(response, variant); break; + case 2: renderStat(response, variant); break; + case 3: renderStat(response, variant); break; + case 4: { + auto histogram = absl::get(variant); + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, parent_histogram->name(), *parent_histogram); + } } - return Stats::StatName(); } + } + if (stat_map_.empty()) { + switch (phase_) { + case Phase::TextReadouts: + phase_ = Phase::CountersAndGauges; + startPhase(); + break; + case Phase::CountersAndGauges: + phase_ = Phase::Histograms; + startPhase(); + break; + case Phase::Histograms: + return Http::Code::OK; + } + } + return Http::Code::Continue; +} - Stats::SymbolTable& symbol_table_; - }; +#if 0 +struct CompareStatsAndScopes { + CompareStatsAndScopes(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table) {} + bool operator()(const StatOrScope& a, const StatOrScope& b) const { + return symbol_table_.lessThan(name(a), name(b)); + } + Stats::StatName name(const StatOrScope& stat_or_scope) const { + switch (stat_or_scope.index()) { + case 0: return absl::get(stat_or_scope)->prefix(); + case 1: return absl::get(stat_or_scope)->statName(); + case 2: return absl::get(stat_or_scope)->statName(); + case 3: return absl::get(stat_or_scope)->statName(); + case 4: return absl::get(stat_or_scope)->statName(); + } + return Stats::StatName(); + } - std::sort(stats_and_scopes_.begin(), stats_and_scopes_.end(), CompareStatsAndScopes(stats_.symbolTable())); - std::unique(stats_and_scopes_.begin(), stats_and_scopes_.end()); + Stats::SymbolTable& symbol_table_; +}; + +std::sort(stats_and_scopes_.begin(), stats_and_scopes_.end(), CompareStatsAndScopes(stats_.symbolTable())); +std::unique(stats_and_scopes_.begin(), stats_and_scopes_.end()); - /* +/* for (const Stats::CounterSharedPtr& counter : server.stats().counters()) { - if (shouldShowMetric(*counter)) { - all_stats_.emplace(counter->name(), counter->value()); - } + if (shouldShowMetric(*counter)) { + all_stats_.emplace(counter->name(), counter->value()); + } } for (const Stats::GaugeSharedPtr& gauge : server.stats().gauges()) { - if (shouldShowMetric(*gauge)) { - ASSERT(gauge->importMode() != Stats::Gauge::ImportMode::Uninitialized); - all_stats_.emplace(gauge->name(), gauge->value()); - } + if (shouldShowMetric(*gauge)) { + ASSERT(gauge->importMode() != Stats::Gauge::ImportMode::Uninitialized); + all_stats_.emplace(gauge->name(), gauge->value()); + } } all_stats_iter_ = all_stats_.begin(); for (const auto& text_readout : server.stats().textReadouts()) { - if (shouldShowMetric(*text_readout)) { - text_readouts_.emplace(text_readout->name(), text_readout->value()); - } + if (shouldShowMetric(*text_readout)) { + text_readouts_.emplace(text_readout->name(), text_readout->value()); + } } text_readouts_iter_ = text_readouts_.begin(); histograms_ = server.stats().histograms(); - */ -} +*/ +#endif StatsHandler::Context::~Context() { } -bool StatsHandler::Context::Context::nextChunk() { - if (++chunk_index_ == chunk_size_) { - chunk_index_= 0; - return true; - } - return false; -} - Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap&, Buffer::Instance& response, AdminStream&) { @@ -381,7 +418,6 @@ Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query return Http::Code::OK; } -// TODO(ambuc) Export this as a server (?) stat for monitoring. Http::Code StatsHandler::handlerContention(absl::string_view, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&) { @@ -401,121 +437,5 @@ Http::Code StatsHandler::handlerContention(absl::string_view, return Http::Code::OK; } -Http::Code StatsHandler::Context::statsAsText(Http::ResponseHeaderMap& /*response_headers*/, - Buffer::Instance& /*response*/) { - /* - // Display plain stats if format query param is not there. - for (; text_readouts_iter_ != text_readouts_.end(); ++text_readouts_iter_) { - if (nextChunk()) { - return Http::Code::Continue; - } - response.addFragments( - {text_readouts_iter_->first, ": \"", Html::Utility::sanitize(text_readouts_iter_->second), - "\"\n"}); - } - for (; all_stats_iter_ != all_stats_.end(); ++all_stats_iter_) { - if (nextChunk()) { - return Http::Code::Continue; - } - response.addFragments({all_stats_iter_->first, ": ", absl::StrCat(all_stats_iter_->second), "\n"}); - } - std::map all_histograms; - for (const Stats::ParentHistogramSharedPtr& histogram : histograms_) { - if (shouldShowMetric(*histogram)) { - auto insert = all_histograms.emplace(histogram->name(), histogram->quantileSummary()); - ASSERT(insert.second); // No duplicates expected. - } - } - for (const auto& histogram : all_histograms) { - response.addFragments({histogram.first, ": ", histogram.second, "\n"}); - } - return Http::Code::OK; - */ - return Http::Code::OK; -} - -Http::Code StatsHandler::Context::statsAsJson(Http::ResponseHeaderMap& /*response_headers*/, - Buffer::Instance& /*response*/, - const bool /*pretty_print*/) { - -#if 0 - response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); - - ProtobufWkt::Struct document; - std::vector stats_array; - for (const auto& text_readout : text_readouts_) { - ProtobufWkt::Struct stat_obj; - auto* stat_obj_fields = stat_obj.mutable_fields(); - (*stat_obj_fields)["name"] = ValueUtil::stringValue(text_readout.first); - (*stat_obj_fields)["value"] = ValueUtil::stringValue(text_readout.second); - stats_array.push_back(ValueUtil::structValue(stat_obj)); - } - for (const auto& stat : all_stats_) { - ProtobufWkt::Struct stat_obj; - auto* stat_obj_fields = stat_obj.mutable_fields(); - (*stat_obj_fields)["name"] = ValueUtil::stringValue(stat.first); - (*stat_obj_fields)["value"] = ValueUtil::numberValue(stat.second); - stats_array.push_back(ValueUtil::structValue(stat_obj)); - } - - ProtobufWkt::Struct histograms_obj; - auto* histograms_obj_fields = histograms_obj.mutable_fields(); - - ProtobufWkt::Struct histograms_obj_container; - auto* histograms_obj_container_fields = histograms_obj_container.mutable_fields(); - std::vector computed_quantile_array; - - bool found_used_histogram = false; - for (const Stats::ParentHistogramSharedPtr& histogram : histograms_) { - if (shouldShowMetric(*histogram)) { - if (!found_used_histogram) { - // It is not possible for the supported quantiles to differ across histograms, so it is ok - // to send them once. - Stats::HistogramStatisticsImpl empty_statistics; - std::vector supported_quantile_array; - for (double quantile : empty_statistics.supportedQuantiles()) { - supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); - } - (*histograms_obj_fields)["supported_quantiles"] = - ValueUtil::listValue(supported_quantile_array); - found_used_histogram = true; - } - - ProtobufWkt::Struct computed_quantile; - auto* computed_quantile_fields = computed_quantile.mutable_fields(); - (*computed_quantile_fields)["name"] = ValueUtil::stringValue(histogram->name()); - - std::vector computed_quantile_value_array; - for (size_t i = 0; i < histogram->intervalStatistics().supportedQuantiles().size(); ++i) { - ProtobufWkt::Struct computed_quantile_value; - auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); - const auto& interval = histogram->intervalStatistics().computedQuantiles()[i]; - const auto& cumulative = histogram->cumulativeStatistics().computedQuantiles()[i]; - (*computed_quantile_value_fields)["interval"] = - std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); - (*computed_quantile_value_fields)["cumulative"] = - std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); - - computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); - } - (*computed_quantile_fields)["values"] = ValueUtil::listValue(computed_quantile_value_array); - computed_quantile_array.push_back(ValueUtil::structValue(computed_quantile)); - } - } - - if (found_used_histogram) { - (*histograms_obj_fields)["computed_quantiles"] = ValueUtil::listValue(computed_quantile_array); - (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj); - stats_array.push_back(ValueUtil::structValue(histograms_obj_container)); - } - - auto* document_fields = document.mutable_fields(); - (*document_fields)["stats"] = ValueUtil::listValue(stats_array); - - response.add(MessageUtil::getJsonStringFromMessageOrDie(document, pretty_print, true)); -#endif - return Http::Code::OK; -} - } // namespace Server } // namespace Envoy diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index ab4db149e3e26..c4c00b5fc91a7 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -54,6 +54,19 @@ class StatsHandler : public HandlerContextBase { class TextRender; class Context { + using ScopeVec = std::vector; + using StatOrScopes = absl::variant< + ScopeVec, + Stats::TextReadoutSharedPtr, + Stats::CounterSharedPtr, + Stats::GaugeSharedPtr, + Stats::HistogramSharedPtr>; + enum class Phase { + TextReadouts, + CountersAndGauges, + Histograms, + }; + public: Context(Server::Instance& server, bool used_only, absl::optional regex, @@ -61,16 +74,17 @@ class StatsHandler : public HandlerContextBase { Buffer::Instance& response); ~Context(); - bool nextChunk(); + void startPhase(); Http::Code writeChunk(Buffer::Instance& response); template bool shouldShowMetric(const StatType& stat) { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); } - Http::Code statsAsText(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response); - Http::Code statsAsJson(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, - bool pretty); + void populateStatsForCurrentPhase(const ScopeVec& scope_vec); + template void populateStatsFromScopes(const ScopeVec& scope); + template void renderStat( + Buffer::Instance& response, StatOrScopes& variant); const bool used_only_; absl::optional regex_; @@ -78,19 +92,15 @@ class StatsHandler : public HandlerContextBase { std::unique_ptr render_; - using StatOrScope = absl::variant< - Stats::ScopeSharedPtr, - Stats::TextReadoutSharedPtr, - Stats::CounterSharedPtr, - Stats::GaugeSharedPtr, - Stats::ParentHistogramSharedPtr>; - using StatOrScopeVec = std::vector; - - static constexpr uint32_t chunk_size_ = 10; + static constexpr uint32_t num_stats_per_chunk_ = 1000; Stats::Store& stats_; - StatOrScopeVec stats_and_scopes_; + ScopeVec scopes_; + //StatOrScopeVec stats_and_scopes_; + using StatMap = std::map; + StatMap stat_map_; uint32_t stats_and_scopes_index_{0}; uint32_t chunk_index_{0}; + Phase phase_{Phase::TextReadouts}; }; using ContextPtr = std::unique_ptr; diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 4724d96b8597d..1c511bd367793 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -27,6 +27,7 @@ class StatsHandlerTest { store_->addSink(sink_); } +#if 0 static std::string statsAsJsonHandler(std::map& all_stats, std::map& all_text_readouts, @@ -35,6 +36,20 @@ class StatsHandlerTest { return StatsHandler::statsAsJson(all_stats, all_text_readouts, all_histograms, used_only, regex, true /*pretty_print*/); } +#endif + + Http::Code handlerStats(Server::Instance& instance, absl::string_view url, std::string& out) { + StatsHandler handler(instance); + Http::TestResponseHeaderMapImpl response_headers; + Http::Code code = Http::Code::Continue; + MockAdminStream admin_stream; + while (code == Http::Code::Continue) { + Buffer::OwnedImpl data; + code = handler.handlerStats(url, response_headers, data, admin_stream); + out += data.toString(); + } + return code; + } Stats::StatName makeStat(absl::string_view name) { return pool_.add(name); } Stats::CustomStatNamespaces& customNamespaces() { return custom_namespaces_; } @@ -65,32 +80,27 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, AdminStatsTest, TEST_P(AdminStatsTest, HandlerStatsInvalidFormat) { const std::string url = "/stats?format=blergh"; - Http::TestResponseHeaderMapImpl response_headers; - Buffer::OwnedImpl data; + std::string data; MockAdminStream admin_stream; Configuration::MockStatsConfig stats_config; EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); MockInstance instance; EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); - StatsHandler handler(instance); - Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream); - EXPECT_EQ(Http::Code::NotFound, code); - EXPECT_EQ("usage: /stats?format=json or /stats?format=prometheus \n\n", data.toString()); + Http::Code code = handlerStats(instance, url, data); + EXPECT_EQ(Http::Code::BadRequest, code); + EXPECT_EQ("usage: /stats?format=json or /stats?format=prometheus \n\n", data); } TEST_P(AdminStatsTest, HandlerStatsPlainText) { const std::string url = "/stats"; - Http::TestResponseHeaderMapImpl response_headers, used_response_headers; - Buffer::OwnedImpl data, used_data; - MockAdminStream admin_stream; + std::string data, used_data; Configuration::MockStatsConfig stats_config; EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); MockInstance instance; store_->initializeThreading(main_thread_dispatcher_, tls_); EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); - StatsHandler handler(instance); Stats::Counter& c1 = store_->counterFromString("c1"); Stats::Counter& c2 = store_->counterFromString("c2"); @@ -112,8 +122,6 @@ TEST_P(AdminStatsTest, HandlerStatsPlainText) { store_->mergeHistograms([]() -> void {}); - Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream); - EXPECT_EQ(Http::Code::OK, code); constexpr char expected[] = "t: \"hello world\"\n" "c1: 10\n" @@ -124,11 +132,14 @@ TEST_P(AdminStatsTest, HandlerStatsPlainText) { "h2: P0(100.0,100.0) P25(102.5,102.5) P50(105.0,105.0) P75(107.5,107.5) " "P90(109.0,109.0) P95(109.5,109.5) P99(109.9,109.9) P99.5(109.95,109.95) " "P99.9(109.99,109.99) P100(110.0,110.0)\n"; - EXPECT_EQ(expected, data.toString()); - code = handler.handlerStats(url + "?usedonly", used_response_headers, used_data, admin_stream); + Http::Code code = handlerStats(instance, url, data); + EXPECT_EQ(Http::Code::OK, code); + EXPECT_EQ(expected, data); + + code = handlerStats(instance, url + "?usedonly", used_data); EXPECT_EQ(Http::Code::OK, code); - EXPECT_EQ(expected, used_data.toString()); + EXPECT_EQ(expected, used_data); shutdownThreading(); } @@ -250,6 +261,7 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { shutdownThreading(); } +#if 0 TEST_P(AdminStatsTest, StatsAsJson) { InSequence s; store_->initializeThreading(main_thread_dispatcher_, tls_); @@ -709,6 +721,7 @@ TEST_P(AdminStatsTest, UsedOnlyStatsAsJsonFilterString) { EXPECT_THAT(expected_json, JsonStringEq(actual_json)); shutdownThreading(); } +#endif INSTANTIATE_TEST_SUITE_P(IpVersions, AdminInstanceTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), From faccfd79d3f3bd5b8f23ac64caf98b78c5274b60 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 26 Jan 2022 14:31:48 -0500 Subject: [PATCH 04/74] get chunking working on live server, 100k clusters. Signed-off-by: Joshua Marantz --- source/server/admin/admin_filter.cc | 1 + source/server/admin/stats_handler.cc | 41 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 4effbd9e9edf8..f454e3e43b275 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -78,6 +78,7 @@ void AdminFilter::onComplete() { StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); } if (response.length() > 0 || !cont) { + //ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); decoder_callbacks_->encodeData(response, !cont && end_stream_on_complete_); } } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 2b8c931d08983..35d0a2ffe86fd 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -124,6 +124,7 @@ class StatsHandler::Render { virtual void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) PURE; virtual void render(Buffer::Instance& response) PURE; + virtual bool nextChunk(Buffer::Instance& response) PURE; }; class StatsHandler::TextRender : public StatsHandler::Render { @@ -143,6 +144,9 @@ class StatsHandler::TextRender : public StatsHandler::Render { } void render(Buffer::Instance&) override {} + bool nextChunk(Buffer::Instance& response) override { + return response.length() > 2000000; + } }; #if 0 @@ -269,7 +273,7 @@ void StatsHandler::Context::startPhase() { // empty prefix. auto iter = stat_map_.find(""); if (iter != stat_map_.end()) { - StatOrScopes variant = iter->second; + StatOrScopes variant = std::move(iter->second); stat_map_.erase(iter); auto& scope_vec = absl::get(variant); populateStatsForCurrentPhase(scope_vec); @@ -308,12 +312,27 @@ template void StatsHandler::Context::renderStat( } Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { - for (uint32_t i = 0; i < num_stats_per_chunk_ && !stat_map_.empty(); ++i) { + while (!render_->nextChunk(response)) { + while (stat_map_.empty()) { + switch (phase_) { + case Phase::TextReadouts: + phase_ = Phase::CountersAndGauges; + startPhase(); + break; + case Phase::CountersAndGauges: + phase_ = Phase::Histograms; + startPhase(); + break; + case Phase::Histograms: + return Http::Code::OK; + } + } + auto iter = stat_map_.begin(); - StatOrScopes variant = iter->second; + StatOrScopes variant = std::move(iter->second); stat_map_.erase(iter); switch (variant.index()) { - case 0: populateStatsForCurrentPhase(absl::get(variant)); --i; break; + case 0: populateStatsForCurrentPhase(absl::get(variant)); break; case 1: renderStat(response, variant); break; case 2: renderStat(response, variant); break; case 3: renderStat(response, variant); break; @@ -326,20 +345,6 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { } } } - if (stat_map_.empty()) { - switch (phase_) { - case Phase::TextReadouts: - phase_ = Phase::CountersAndGauges; - startPhase(); - break; - case Phase::CountersAndGauges: - phase_ = Phase::Histograms; - startPhase(); - break; - case Phase::Histograms: - return Http::Code::OK; - } - } return Http::Code::Continue; } From 582951d455de65de261baf726a1ef00a72d725f9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Jan 2022 08:34:43 -0500 Subject: [PATCH 05/74] stats_handler_test working (except what had to be commented out). Signed-off-by: Joshua Marantz --- envoy/stats/scope.h | 4 + envoy/stats/store.h | 2 +- source/common/stats/isolated_store_impl.cc | 7 +- source/common/stats/isolated_store_impl.h | 8 +- source/common/stats/scope_prefixer.cc | 6 +- source/common/stats/thread_local_store.cc | 6 +- source/common/stats/thread_local_store.h | 2 +- source/server/admin/stats_handler.cc | 81 ++++++++++++++------ test/common/stats/thread_local_store_test.cc | 4 +- test/integration/server.h | 2 +- test/mocks/stats/mocks.h | 2 +- test/server/admin/stats_handler_test.cc | 3 +- 12 files changed, 86 insertions(+), 41 deletions(-) diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index 6b35acfcbf6d1..6afd4a14611bb 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -40,6 +40,8 @@ using ScopePtr = ScopeSharedPtr; // TODO(jmarantz): global s/ShaedPtr/ScopeShare template using IterateFn = std::function&)>; +#define SCOPE_SHARED_FROM_THIS 1 + /** * A named scope for stats. Scopes are a grouping of stats that can be acted on as a unit if needed * (for example to free/delete all of them). @@ -48,8 +50,10 @@ class Scope : public #if SCOPE_REFCOUNT RefcountInterface #else +#if SCOPE_SHARED_FROM_THIS std::enable_shared_from_this #endif +#endif { public: virtual ~Scope() = default; diff --git a/envoy/stats/store.h b/envoy/stats/store.h index f18dc16d37793..eb1ca3bc519a2 100644 --- a/envoy/stats/store.h +++ b/envoy/stats/store.h @@ -67,7 +67,7 @@ class Store : public Scope { virtual void forEachCounter(SizeFn f_size, StatFn f_stat) const PURE; virtual void forEachGauge(SizeFn f_size, StatFn f_stat) const PURE; virtual void forEachTextReadout(SizeFn f_size, StatFn f_stat) const PURE; - virtual void forEachScope(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachScope(SizeFn f_size, StatFn f_stat) const PURE; /** * Iterate over all stats that need to be flushed to sinks. Note, that implementations can diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 2638baf82eb60..00d846e7f58e7 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -36,7 +36,8 @@ IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) return alloc_.makeTextReadout(name, name, StatNameTagVector{}); }), null_counter_(new NullCounterImpl(symbol_table)), - null_gauge_(new NullGaugeImpl(symbol_table)) {} + null_gauge_(new NullGaugeImpl(symbol_table)), + default_scope_(std::make_shared("", *this)) {} IsolatedStoreImpl::~IsolatedStoreImpl() { #if SCOPE_REFCOUNT @@ -47,11 +48,11 @@ IsolatedStoreImpl::~IsolatedStoreImpl() { #if SCOPE_REFCOUNT ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { - return ScopeSharedPtr(new ScopePrefixer(name, *this)); + return (new ScopePrefixer(name, *this))->makeShared(); } ScopePtr IsolatedStoreImpl::scopeFromStatName(StatName name) { - return ScopeSharedPtr(new ScopePrefixer(name, *this)); + return (new ScopePrefixer(name, *this))->makeShared(); } #else ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index e75bd5a4738ad..8764d4344fc16 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -228,12 +228,11 @@ class IsolatedStoreImpl : public StoreImpl { text_readouts_.forEachStat(f_size, f_stat); } - void forEachScope(SizeFn f_size, StatFn f_stat) const override { + void forEachScope(SizeFn f_size, StatFn f_stat) const override { if (f_size != nullptr) { f_size(1); } - const Scope& scope = *this; - f_stat(scope); + f_stat(default_scope_); } Stats::StatName prefix() const override { return StatName(); } @@ -272,8 +271,9 @@ class IsolatedStoreImpl : public StoreImpl { IsolatedStatsCache text_readouts_; RefcountPtr null_counter_; RefcountPtr null_gauge_; + ScopeSharedPtr default_scope_; #if SCOPE_REFCOUNT - std::atomic ref_count_{0}; + std::atomic ref_count_{1}; #endif }; diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 83be5ac51db12..1fcac9aa7547d 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -18,7 +18,11 @@ ScopePrefixer::~ScopePrefixer() { prefix_.free(symbolTable()); } ScopePtr ScopePrefixer::scopeFromStatName(StatName name) { SymbolTable::StoragePtr joined = symbolTable().join({prefix_.statName(), name}); - return ScopeSharedPtr(new ScopePrefixer(StatName(joined.get()), scope_)); +#if SCOPE_REFCOUNT + return (new ScopePrefixer(StatName(joined.get()), scope_))->makeShared(); +#else + return std::make_shared(StatName(joined.get()), scope_); +#endif } ScopePtr ScopePrefixer::createScope(const std::string& name) { diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 81c20bae69bed..7474da30eb412 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -973,14 +973,14 @@ void ThreadLocalStoreImpl::forEachTextReadout(SizeFn f_size, StatFn } void ThreadLocalStoreImpl::forEachScope(std::function f_size, - StatFn f_scope) const { + StatFn f_scope) const { Thread::LockGuard lock(lock_); if (f_size != nullptr) { f_size(scopes_.size() + 1 /* for default_scope_ */); } - f_scope(*default_scope_); + f_scope(default_scope_); for (ScopeImpl* scope : scopes_) { - f_scope(*scope); + f_scope(scope->makeShared()); } } diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 969af8737fa71..b3289f9830bfb 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -247,7 +247,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void forEachCounter(SizeFn f_size, StatFn f_stat) const override; void forEachGauge(SizeFn f_size, StatFn f_stat) const override; void forEachTextReadout(SizeFn f_size, StatFn f_stat) const override; - void forEachScope(SizeFn f_size, StatFn f_stat) const override; + void forEachScope(SizeFn f_size, StatFn f_stat) const override; // Stats::StoreRoot void addSink(Sink& sink) override { timer_sinks_.push_back(sink); } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 35d0a2ffe86fd..efc45c95bdc5d 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -12,6 +12,8 @@ #include "source/server/admin/prometheus_stats.h" #include "source/server/admin/utils.h" +constexpr uint64_t ChunkSize = 2 * 1000 * 1000; + namespace Envoy { namespace Server { @@ -145,25 +147,25 @@ class StatsHandler::TextRender : public StatsHandler::Render { void render(Buffer::Instance&) override {} bool nextChunk(Buffer::Instance& response) override { - return response.length() > 2000000; + return response.length() > ChunkSize; } }; -#if 0 class StatsHandler::JsonRender : public StatsHandler::Render { public: - JsonRender(Http::ResponseHeaderMap& response_headers) { + JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + response.add("{\"stats\":["); } void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { - add(name, ValueUtil::numberValue(value)); + add(response, name, ValueUtil::numberValue(value)); } void generate(Buffer::Instance& response, const std::string& name, const std::string& value) override { - add(name, ValueUtil::stringValue(value)); + add(response, name, ValueUtil::stringValue(value)); } - void generate(Buffer::Instance& response, const std::string& name, + void generate(Buffer::Instance&, const std::string& name, const Stats::ParentHistogram& histogram) override { if (!found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); @@ -202,29 +204,61 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } void render(Buffer::Instance& response) override { + //emitStats(response); if (found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); (*histograms_obj_fields)["computed_quantiles"] = ValueUtil::listValue(computed_quantile_array_); auto* histograms_obj_container_fields = histograms_obj_container_.mutable_fields(); (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); - stats_array_.push_back(ValueUtil::structValue(histograms_obj_container_)); + auto str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::structValue(histograms_obj_container_), + false /* pretty */, true); + //stats_array_.push_back(); + //ENVOY_LOG_MISC(error, "histograms: {}", str); + //response.addFragments({" ", str, "\n"}); + response.add(str); } - auto* document_fields = document_.mutable_fields(); - (*document_fields)["stats"] = ValueUtil::listValue(stats_array_); - response.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, true)); + //auto* document_fields = document_.mutable_fields(); + //(*document_fields)["stats"] = ValueUtil::listValue(stats_array_); + //response.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, true)); + response.add("]}"); + } + + bool nextChunk(Buffer::Instance& response) override { + return response.length() > ChunkSize; + /* + if (++chunk_count_ == 10000) { + emitStats(response); + return true; + } + return false; + */ } private: - template void add(const std::string& name, const Value& value) { + template void add(Buffer::Instance& response, const std::string& name, const Value& value) { ProtobufWkt::Struct stat_obj; auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); (*stat_obj_fields)["value"] = value; - stats_array_.push_back(ValueUtil::structValue(stat_obj)); + //stats_array_.push_back(ValueUtil::structValue(stat_obj)); + auto str = MessageUtil::getJsonStringFromMessageOrDie(stat_obj, false /* pretty */, true); + ENVOY_LOG_MISC(error, "emitting: {}", str); + //response.addFragments({" ", str, ",\n"}); + response.addFragments({str, ","}); + } + + void emitStats(Buffer::Instance& response) { + auto str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::listValue(stats_array_), + false /* pretty */, true); + ENVOY_LOG_MISC(error, "emitting: {}", str); + response.add(str); + chunk_count_ = 0; + stats_array_.clear(); } + uint32_t chunk_count_{0}; ProtobufWkt::Struct document_; std::vector stats_array_; std::vector scope_array_; @@ -233,27 +267,27 @@ class StatsHandler::JsonRender : public StatsHandler::Render { std::vector computed_quantile_array_; bool found_used_histogram_{false}; }; -#endif StatsHandler::Context::Context(Server::Instance& server, bool used_only, absl::optional regex, - bool /*json*/, - Http::ResponseHeaderMap& /*response_headers*/, - Buffer::Instance& /*response*/) + bool json, + Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response) : used_only_(used_only), regex_(regex), stats_(server.stats()) { - //if (json) { - // render_ = std::make_unique(response_headers, response); - //} else { - render_ = std::make_unique(); - //} + if (json) { + render_ = std::make_unique(response_headers, response); + } else { + render_ = std::make_unique(); + } // Populate the top-level scopes and the stats underneath any scopes with an empty name. // We will have to de-dup, but we can do that after sorting. // // First capture all the scopes and hold onto them with a SharedPtr so they // can't be deleted after the initial iteration. - stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, [this](const Stats::Scope& scope) { - scopes_.emplace_back(const_cast(scope).makeShared()); + stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, [this]( + const Stats::ScopeSharedPtr& scope) { + scopes_.emplace_back(scope); }); startPhase(); @@ -324,6 +358,7 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { startPhase(); break; case Phase::Histograms: + render_->render(response); return Http::Code::OK; } } diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index e5f17a6e50cbe..50382fc315830 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -479,8 +479,8 @@ TEST_F(StatsThreadLocalStoreTest, ForEach) { auto collect_scopes = [this]() -> std::vector { std::vector names; store_->forEachScope([](size_t) {}, - [&names](const Scope& scope) { - names.push_back(scope.constSymbolTable().toString(scope.prefix())); + [&names](const ScopeSharedPtr& scope) { + names.push_back(scope->constSymbolTable().toString(scope->prefix())); }); return names; }; diff --git a/test/integration/server.h b/test/integration/server.h index ab369c281026c..c6a6c2ed749fc 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -296,7 +296,7 @@ class TestIsolatedStoreImpl : public StoreRoot { store_.forEachTextReadout(f_size, f_stat); } void forEachScope(std::function f_size, - StatFn f_scope) const override { + StatFn f_scope) const override { Thread::LockGuard lock(lock_); store_.forEachScope(f_size, f_scope); } diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 2c4d7ebb946b8..b807a41af9a09 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -335,7 +335,7 @@ class MockStore : public TestUtil::TestStore { testing::NiceMock counter_; testing::NiceMock gauge_; std::vector> histograms_; - std::atomic ref_count_{0}; + std::atomic ref_count_{1}; }; /** diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 1c511bd367793..6288790622a08 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -256,12 +256,13 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { ] })EOF"; + ENVOY_LOG_MISC(error, "json: {}", data.toString()); EXPECT_THAT(expected_json_old, JsonStringEq(data.toString())); shutdownThreading(); } -#if 0 +#if 1 TEST_P(AdminStatsTest, StatsAsJson) { InSequence s; store_->initializeThreading(main_thread_dispatcher_, tls_); From 5878ecd81c0a7a0bf5913afc9cac6ef05b529ee9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Jan 2022 08:42:55 -0500 Subject: [PATCH 06/74] format Signed-off-by: Joshua Marantz --- envoy/stats/scope.h | 5 +- source/common/stats/isolated_store_impl.h | 4 +- source/common/stats/scope_prefixer.h | 8 +- source/server/admin/admin_filter.cc | 2 +- source/server/admin/stats_handler.cc | 167 ++++++++++------------ source/server/admin/stats_handler.h | 27 ++-- test/server/admin/stats_handler_test.cc | 2 +- 7 files changed, 92 insertions(+), 123 deletions(-) diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index 6afd4a14611bb..00de89dd1ac37 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -6,6 +6,7 @@ #include "envoy/common/pure.h" #include "envoy/stats/histogram.h" + #define SCOPE_REFCOUNT 0 #if SCOPE_REFCOUNT #include "envoy/stats/refcount_ptr.h" @@ -48,10 +49,10 @@ template using IterateFn = std::function + std::enable_shared_from_this #endif #endif { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 8764d4344fc16..4f2f41935ed8e 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -252,9 +252,7 @@ class IsolatedStoreImpl : public StoreImpl { #if SCOPE_REFCOUNT // RefcountInterface void incRefCount() override { ++ref_count_; } - bool decRefCount() override { - return --ref_count_ == 0; - } + bool decRefCount() override { return --ref_count_ == 0; } uint32_t use_count() const override { return ref_count_; } #endif diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index 07bb8578e8c0f..fae39d9d6c7c3 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -23,12 +23,8 @@ class ScopePrefixer : public Scope { #if SCOPE_REFCOUNT // RefcountInterface #if 1 - void incRefCount() override { - ++ref_count_; - } - bool decRefCount() override { - return --ref_count_ == 0; - } + void incRefCount() override { ++ref_count_; } + bool decRefCount() override { return --ref_count_ == 0; } uint32_t use_count() const override { return ref_count_; } #else void incRefCount() override { scope_->incRefCount(); } diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index f454e3e43b275..4e3120fd14dd1 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -78,7 +78,7 @@ void AdminFilter::onComplete() { StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); } if (response.length() > 0 || !cont) { - //ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); + // ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); decoder_callbacks_->encodeData(response, !cont && end_stream_on_complete_); } } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index efc45c95bdc5d..2dc8eb9930d47 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -104,8 +104,9 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, auto iter = context_map_.find(&admin_stream); if (iter == context_map_.end()) { - auto insertion = context_map_.emplace(&admin_stream, std::make_unique( - server_, used_only, regex, json, response_headers, response)); + auto insertion = context_map_.emplace( + &admin_stream, + std::make_unique(server_, used_only, regex, json, response_headers, response)); iter = insertion.first; } Context& context = *iter->second; @@ -119,10 +120,11 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, } class StatsHandler::Render { - public: +public: virtual ~Render() = default; virtual void generate(Buffer::Instance& response, const std::string& name, uint64_t value) PURE; - virtual void generate(Buffer::Instance& response, const std::string& name, const std::string& value) PURE; + virtual void generate(Buffer::Instance& response, const std::string& name, + const std::string& value) PURE; virtual void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) PURE; virtual void render(Buffer::Instance& response) PURE; @@ -130,13 +132,13 @@ class StatsHandler::Render { }; class StatsHandler::TextRender : public StatsHandler::Render { - public: +public: void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { response.addFragments({name, ": ", absl::StrCat(value), "\n"}); } - void generate(Buffer::Instance& response, const std::string& name, const std::string& value) - override { + void generate(Buffer::Instance& response, const std::string& name, + const std::string& value) override { response.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); } @@ -146,13 +148,11 @@ class StatsHandler::TextRender : public StatsHandler::Render { } void render(Buffer::Instance&) override {} - bool nextChunk(Buffer::Instance& response) override { - return response.length() > ChunkSize; - } + bool nextChunk(Buffer::Instance& response) override { return response.length() > ChunkSize; } }; class StatsHandler::JsonRender : public StatsHandler::Render { - public: +public: JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); response.add("{\"stats\":["); @@ -204,24 +204,25 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } void render(Buffer::Instance& response) override { - //emitStats(response); + // emitStats(response); if (found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); (*histograms_obj_fields)["computed_quantiles"] = ValueUtil::listValue(computed_quantile_array_); auto* histograms_obj_container_fields = histograms_obj_container_.mutable_fields(); (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); - auto str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::structValue(histograms_obj_container_), - false /* pretty */, true); - //stats_array_.push_back(); - //ENVOY_LOG_MISC(error, "histograms: {}", str); - //response.addFragments({" ", str, "\n"}); + auto str = MessageUtil::getJsonStringFromMessageOrDie( + ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); + // stats_array_.push_back(); + // ENVOY_LOG_MISC(error, "histograms: {}", str); + // response.addFragments({" ", str, "\n"}); response.add(str); } - //auto* document_fields = document_.mutable_fields(); + // auto* document_fields = document_.mutable_fields(); //(*document_fields)["stats"] = ValueUtil::listValue(stats_array_); - //response.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, true)); + // response.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, + // true)); response.add("]}"); } @@ -236,16 +237,17 @@ class StatsHandler::JsonRender : public StatsHandler::Render { */ } - private: - template void add(Buffer::Instance& response, const std::string& name, const Value& value) { +private: + template + void add(Buffer::Instance& response, const std::string& name, const Value& value) { ProtobufWkt::Struct stat_obj; auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); (*stat_obj_fields)["value"] = value; - //stats_array_.push_back(ValueUtil::structValue(stat_obj)); + // stats_array_.push_back(ValueUtil::structValue(stat_obj)); auto str = MessageUtil::getJsonStringFromMessageOrDie(stat_obj, false /* pretty */, true); ENVOY_LOG_MISC(error, "emitting: {}", str); - //response.addFragments({" ", str, ",\n"}); + // response.addFragments({" ", str, ",\n"}); response.addFragments({str, ","}); } @@ -268,9 +270,8 @@ class StatsHandler::JsonRender : public StatsHandler::Render { bool found_used_histogram_{false}; }; -StatsHandler::Context::Context(Server::Instance& server, - bool used_only, absl::optional regex, - bool json, +StatsHandler::Context::Context(Server::Instance& server, bool used_only, + absl::optional regex, bool json, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) : used_only_(used_only), regex_(regex), stats_(server.stats()) { @@ -285,10 +286,8 @@ StatsHandler::Context::Context(Server::Instance& server, // // First capture all the scopes and hold onto them with a SharedPtr so they // can't be deleted after the initial iteration. - stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, [this]( - const Stats::ScopeSharedPtr& scope) { - scopes_.emplace_back(scope); - }); + stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, + [this](const Stats::ScopeSharedPtr& scope) { scopes_.emplace_back(scope); }); startPhase(); } @@ -316,20 +315,21 @@ void StatsHandler::Context::startPhase() { void StatsHandler::Context::populateStatsForCurrentPhase(const ScopeVec& scope_vec) { switch (phase_) { - case Phase::TextReadouts: - populateStatsFromScopes(scope_vec); - break; - case Phase::CountersAndGauges: - populateStatsFromScopes(scope_vec); - populateStatsFromScopes(scope_vec); - break; - case Phase::Histograms: - populateStatsFromScopes(scope_vec); - break; + case Phase::TextReadouts: + populateStatsFromScopes(scope_vec); + break; + case Phase::CountersAndGauges: + populateStatsFromScopes(scope_vec); + populateStatsFromScopes(scope_vec); + break; + case Phase::Histograms: + populateStatsFromScopes(scope_vec); + break; } } -template void StatsHandler::Context::populateStatsFromScopes(const ScopeVec& scope_vec) { +template +void StatsHandler::Context::populateStatsFromScopes(const ScopeVec& scope_vec) { for (const Stats::ScopeSharedPtr& scope : scope_vec) { Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { stat_map_[stat->name()] = stat; @@ -339,8 +339,8 @@ template void StatsHandler::Context::populateStatsFromScopes(con } } -template void StatsHandler::Context::renderStat( - Buffer::Instance& response, StatOrScopes& variant) { +template +void StatsHandler::Context::renderStat(Buffer::Instance& response, StatOrScopes& variant) { auto stat = absl::get(variant); render_->generate(response, stat->name(), stat->value()); } @@ -349,17 +349,17 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { while (!render_->nextChunk(response)) { while (stat_map_.empty()) { switch (phase_) { - case Phase::TextReadouts: - phase_ = Phase::CountersAndGauges; - startPhase(); - break; - case Phase::CountersAndGauges: - phase_ = Phase::Histograms; - startPhase(); - break; - case Phase::Histograms: - render_->render(response); - return Http::Code::OK; + case Phase::TextReadouts: + phase_ = Phase::CountersAndGauges; + startPhase(); + break; + case Phase::CountersAndGauges: + phase_ = Phase::Histograms; + startPhase(); + break; + case Phase::Histograms: + render_->render(response); + return Http::Code::OK; } } @@ -367,18 +367,26 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { StatOrScopes variant = std::move(iter->second); stat_map_.erase(iter); switch (variant.index()) { - case 0: populateStatsForCurrentPhase(absl::get(variant)); break; - case 1: renderStat(response, variant); break; - case 2: renderStat(response, variant); break; - case 3: renderStat(response, variant); break; - case 4: { - auto histogram = absl::get(variant); - auto parent_histogram = dynamic_cast(histogram.get()); - if (parent_histogram != nullptr) { - render_->generate(response, parent_histogram->name(), *parent_histogram); - } + case 0: + populateStatsForCurrentPhase(absl::get(variant)); + break; + case 1: + renderStat(response, variant); + break; + case 2: + renderStat(response, variant); + break; + case 3: + renderStat(response, variant); + break; + case 4: { + auto histogram = absl::get(variant); + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, parent_histogram->name(), *parent_histogram); } } + } } return Http::Code::Continue; } @@ -403,38 +411,9 @@ struct CompareStatsAndScopes { Stats::SymbolTable& symbol_table_; }; - -std::sort(stats_and_scopes_.begin(), stats_and_scopes_.end(), CompareStatsAndScopes(stats_.symbolTable())); -std::unique(stats_and_scopes_.begin(), stats_and_scopes_.end()); - -/* - for (const Stats::CounterSharedPtr& counter : server.stats().counters()) { - if (shouldShowMetric(*counter)) { - all_stats_.emplace(counter->name(), counter->value()); - } - } - - for (const Stats::GaugeSharedPtr& gauge : server.stats().gauges()) { - if (shouldShowMetric(*gauge)) { - ASSERT(gauge->importMode() != Stats::Gauge::ImportMode::Uninitialized); - all_stats_.emplace(gauge->name(), gauge->value()); - } - } - all_stats_iter_ = all_stats_.begin(); - - for (const auto& text_readout : server.stats().textReadouts()) { - if (shouldShowMetric(*text_readout)) { - text_readouts_.emplace(text_readout->name(), text_readout->value()); - } - } - text_readouts_iter_ = text_readouts_.begin(); - - histograms_ = server.stats().histograms(); -*/ #endif -StatsHandler::Context::~Context() { -} +StatsHandler::Context::~Context() {} Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap&, diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index c4c00b5fc91a7..01879a95774e9 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -55,36 +55,31 @@ class StatsHandler : public HandlerContextBase { class Context { using ScopeVec = std::vector; - using StatOrScopes = absl::variant< - ScopeVec, - Stats::TextReadoutSharedPtr, - Stats::CounterSharedPtr, - Stats::GaugeSharedPtr, - Stats::HistogramSharedPtr>; + using StatOrScopes = + absl::variant; enum class Phase { TextReadouts, CountersAndGauges, Histograms, }; - public: - Context(Server::Instance& server, - bool used_only, absl::optional regex, - bool json, Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response); + public: + Context(Server::Instance& server, bool used_only, absl::optional regex, bool json, + Http::ResponseHeaderMap& response_headers, Buffer::Instance& response); ~Context(); void startPhase(); Http::Code writeChunk(Buffer::Instance& response); - template bool shouldShowMetric(const StatType& stat) { + template bool shouldShowMetric(const StatType& stat) { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); } void populateStatsForCurrentPhase(const ScopeVec& scope_vec); - template void populateStatsFromScopes(const ScopeVec& scope); - template void renderStat( - Buffer::Instance& response, StatOrScopes& variant); + template void populateStatsFromScopes(const ScopeVec& scope); + template + void renderStat(Buffer::Instance& response, StatOrScopes& variant); const bool used_only_; absl::optional regex_; @@ -95,7 +90,7 @@ class StatsHandler : public HandlerContextBase { static constexpr uint32_t num_stats_per_chunk_ = 1000; Stats::Store& stats_; ScopeVec scopes_; - //StatOrScopeVec stats_and_scopes_; + // StatOrScopeVec stats_and_scopes_; using StatMap = std::map; StatMap stat_map_; uint32_t stats_and_scopes_index_{0}; diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 6288790622a08..dcadfe6c002b2 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -262,7 +262,7 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { shutdownThreading(); } -#if 1 +#if 0 TEST_P(AdminStatsTest, StatsAsJson) { InSequence s; store_->initializeThreading(main_thread_dispatcher_, tls_); From 87d89aa848fa67f15ef7d8cd1b441de6550efb81 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Jan 2022 11:06:09 -0500 Subject: [PATCH 07/74] typo Signed-off-by: Joshua Marantz --- test/integration/server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/server.h b/test/integration/server.h index c6a6c2ed749fc..b7b04c0b52f3d 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -296,7 +296,7 @@ class TestIsolatedStoreImpl : public StoreRoot { store_.forEachTextReadout(f_size, f_stat); } void forEachScope(std::function f_size, - StatFn f_scope) const override { + StatFn f_scope) const override { Thread::LockGuard lock(lock_); store_.forEachScope(f_size, f_scope); } From 29f80b463670c9ba6e05288e6deef0098cc928c8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Jan 2022 15:45:26 -0500 Subject: [PATCH 08/74] fix tests Signed-off-by: Joshua Marantz --- source/server/admin/admin_filter.cc | 8 ++-- source/server/admin/stats_handler.cc | 56 ++++++++++++++++------ source/server/admin/stats_handler.h | 4 +- test/integration/integration_admin_test.cc | 2 +- 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 4e3120fd14dd1..75219140d7487 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -71,15 +71,15 @@ void AdminFilter::onComplete() { for (bool cont = true, first = true; cont; first = false) { Http::Code code = admin_server_callback_func_(path, *header_map, response, *this); cont = code == Http::Code::Continue; + bool end_stream = end_stream_on_complete_ && !cont; if (first) { Utility::populateFallbackResponseHeaders(cont ? Http::Code::OK : code, *header_map); - decoder_callbacks_->encodeHeaders(std::move(header_map), - end_stream_on_complete_ && response.length() == 0, + decoder_callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); } - if (response.length() > 0 || !cont) { + if (response.length() > 0) { // ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); - decoder_callbacks_->encodeData(response, !cont && end_stream_on_complete_); + decoder_callbacks_->encodeData(response, end_stream); } } } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 2dc8eb9930d47..5498141a40e64 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -216,7 +216,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // stats_array_.push_back(); // ENVOY_LOG_MISC(error, "histograms: {}", str); // response.addFragments({" ", str, "\n"}); - response.add(str); + addStatJson(response, str); } // auto* document_fields = document_.mutable_fields(); @@ -246,20 +246,29 @@ class StatsHandler::JsonRender : public StatsHandler::Render { (*stat_obj_fields)["value"] = value; // stats_array_.push_back(ValueUtil::structValue(stat_obj)); auto str = MessageUtil::getJsonStringFromMessageOrDie(stat_obj, false /* pretty */, true); - ENVOY_LOG_MISC(error, "emitting: {}", str); + // ENVOY_LOG_MISC(error, "emitting: {}", str); // response.addFragments({" ", str, ",\n"}); - response.addFragments({str, ","}); + addStatJson(response, str); } void emitStats(Buffer::Instance& response) { auto str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::listValue(stats_array_), false /* pretty */, true); - ENVOY_LOG_MISC(error, "emitting: {}", str); + // ENVOY_LOG_MISC(error, "emitting: {}", str); response.add(str); chunk_count_ = 0; stats_array_.clear(); } + void addStatJson(Buffer::Instance& response, const std::string& json) { + if (first_) { + response.add(json); + first_ = false; + } else { + response.addFragments({",", json}); + } + } + uint32_t chunk_count_{0}; ProtobufWkt::Struct document_; std::vector stats_array_; @@ -268,6 +277,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { ProtobufWkt::Struct histograms_obj_container_; std::vector computed_quantile_array_; bool found_used_histogram_{false}; + bool first_{true}; }; StatsHandler::Context::Context(Server::Instance& server, bool used_only, @@ -340,9 +350,24 @@ void StatsHandler::Context::populateStatsFromScopes(const ScopeVec& scope_vec) { } template -void StatsHandler::Context::renderStat(Buffer::Instance& response, StatOrScopes& variant) { +void StatsHandler::Context::renderStat(const std::string& name, Buffer::Instance& response, + StatOrScopes& variant) { auto stat = absl::get(variant); - render_->generate(response, stat->name(), stat->value()); + if (skip(stat, name)) { + return; + } + render_->generate(response, name, stat->value()); +} + +template +bool StatsHandler::Context::skip(const SharedStatType& stat, const std::string& name) { + if (used_only_ && !stat->used()) { + return true; + } + if (regex_.has_value() && !std::regex_search(name, regex_.value())) { + return true; + } + return false; } Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { @@ -364,29 +389,32 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { } auto iter = stat_map_.begin(); - StatOrScopes variant = std::move(iter->second); - stat_map_.erase(iter); + const std::string& name = iter->first; + StatOrScopes& variant = iter->second; switch (variant.index()) { case 0: populateStatsForCurrentPhase(absl::get(variant)); break; case 1: - renderStat(response, variant); + renderStat(name, response, variant); break; case 2: - renderStat(response, variant); + renderStat(name, response, variant); break; case 3: - renderStat(response, variant); + renderStat(name, response, variant); break; case 4: { auto histogram = absl::get(variant); - auto parent_histogram = dynamic_cast(histogram.get()); - if (parent_histogram != nullptr) { - render_->generate(response, parent_histogram->name(), *parent_histogram); + if (!skip(histogram, name)) { + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, name, *parent_histogram); + } } } } + stat_map_.erase(iter); } return Http::Code::Continue; } diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 01879a95774e9..b62c244a0ad07 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -79,8 +79,8 @@ class StatsHandler : public HandlerContextBase { void populateStatsForCurrentPhase(const ScopeVec& scope_vec); template void populateStatsFromScopes(const ScopeVec& scope); template - void renderStat(Buffer::Instance& response, StatOrScopes& variant); - + void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); + template bool skip(const SharedStatType& stat, const std::string& name); const bool used_only_; absl::optional regex_; absl::optional format_value_; diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index b9ec98612876f..4fd1be8016aef 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -139,7 +139,7 @@ TEST_P(IntegrationAdminTest, Admin) { EXPECT_EQ("application/json", ContentType(response)); validateStatsJson(response->body(), 0); - EXPECT_EQ("404", request("admin", "GET", "/stats?format=blah", response)); + EXPECT_EQ("400", request("admin", "GET", "/stats?format=blah", response)); EXPECT_EQ("text/plain; charset=UTF-8", ContentType(response)); EXPECT_EQ("200", request("admin", "GET", "/stats?format=json", response)); From e2fa8e200de6a35aaa5b8c89acb25aba3bffcdec Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Jan 2022 19:21:32 -0500 Subject: [PATCH 09/74] fix clang-tidy issues Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 6 ++++-- source/common/stats/isolated_store_impl.h | 2 +- source/common/stats/symbol_table_impl.cc | 3 +-- source/server/admin/admin_filter.cc | 1 + source/server/admin/stats_handler.cc | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 00d846e7f58e7..94261117a7371 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -39,12 +39,14 @@ IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) null_gauge_(new NullGaugeImpl(symbol_table)), default_scope_(std::make_shared("", *this)) {} -IsolatedStoreImpl::~IsolatedStoreImpl() { #if SCOPE_REFCOUNT +IsolatedStoreImpl::~IsolatedStoreImpl() { ENVOY_LOG_MISC(error, "ref_count={}", ref_count_); ASSERT(ref_count_ == 0); -#endif } +#else +IsolatedStoreImpl::~IsolatedStoreImpl() = default; +#endif #if SCOPE_REFCOUNT ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 4f2f41935ed8e..7528eef3ae227 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -132,7 +132,7 @@ class IsolatedStoreImpl : public StoreImpl { public: IsolatedStoreImpl(); explicit IsolatedStoreImpl(SymbolTable& symbol_table); - ~IsolatedStoreImpl(); + ~IsolatedStoreImpl() override; // Stats::Scope Counter& counterFromStatNameWithTags(const StatName& name, diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index a6ecbe64f581f..0b15b66ba4547 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -228,7 +228,6 @@ SymbolTableImpl::~SymbolTableImpl() { // Note: this could potentially be short-circuited if we decide a fast exit // is needed in production. But it would be good to ensure clean up during // tests. - debugPrint(); ASSERT(numSymbols() == 0); } @@ -471,7 +470,7 @@ void SymbolTableImpl::debugPrint() const { for (Symbol symbol : symbols) { const InlineString& token = *decode_map_.find(symbol)->second; const SharedSymbol& shared_symbol = encode_map_.find(token.toStringView())->second; - ENVOY_LOG_MISC(error, "{}: '{}' ({})", symbol, token.toStringView(), shared_symbol.ref_count_); + ENVOY_LOG_MISC(info, "{}: '{}' ({})", symbol, token.toStringView(), shared_symbol.ref_count_); } } #endif diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 75219140d7487..320fb4163dc6c 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -76,6 +76,7 @@ void AdminFilter::onComplete() { Utility::populateFallbackResponseHeaders(cont ? Http::Code::OK : code, *header_map); decoder_callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); + first = false; } if (response.length() > 0) { // ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 5498141a40e64..12c3a3e56d4b8 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -441,7 +441,7 @@ struct CompareStatsAndScopes { }; #endif -StatsHandler::Context::~Context() {} +StatsHandler::Context::~Context() = default; Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap&, From 7820e0083c78c2a317f9a36a3e0c701c44d30b15 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Jan 2022 23:04:52 -0500 Subject: [PATCH 10/74] clang-tidy suppression. Signed-off-by: Joshua Marantz --- source/server/admin/admin_filter.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 320fb4163dc6c..e2fa3685cec87 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -69,6 +69,11 @@ void AdminFilter::onComplete() { auto header_map = Http::ResponseHeaderMapImpl::create(); RELEASE_ASSERT(request_headers_, ""); for (bool cont = true, first = true; cont; first = false) { + // TODO(jmarantz): admin_server-callback_func_ is only going to access + // header_map on the first iteration. This clang-tidy suppression can be + // cleaned up once the handler mechanism provides a separate "nextChunk" + // interface that does not take a header_map. + // NOLINTNEXTLINE(bugprone-use-after-move) Http::Code code = admin_server_callback_func_(path, *header_map, response, *this); cont = code == Http::Code::Continue; bool end_stream = end_stream_on_complete_ && !cont; @@ -76,7 +81,6 @@ void AdminFilter::onComplete() { Utility::populateFallbackResponseHeaders(cont ? Http::Code::OK : code, *header_map); decoder_callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); - first = false; } if (response.length() > 0) { // ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); From 44d2d0a5c0921bee3f1242967946bb6a63619342 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 1 Feb 2022 15:38:56 -0500 Subject: [PATCH 11/74] Hacks to eliminate irrelevant overhead Signed-off-by: Joshua Marantz --- source/common/api/posix/os_sys_calls_impl.cc | 1 + source/server/admin/stats_handler.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/source/common/api/posix/os_sys_calls_impl.cc b/source/common/api/posix/os_sys_calls_impl.cc index 65fa8ecad506a..375ef6e14193d 100644 --- a/source/common/api/posix/os_sys_calls_impl.cc +++ b/source/common/api/posix/os_sys_calls_impl.cc @@ -318,6 +318,7 @@ bool OsSysCallsImpl::supportsGetifaddrs() const { } SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { + return {0, 0}; if (alternate_getifaddrs_.has_value()) { return alternate_getifaddrs_.value()(interfaces); } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 12c3a3e56d4b8..f0a8571183ed6 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -75,9 +75,11 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, Http::Code StatsHandler::handlerStats(absl::string_view url, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { +#if 0 if (server_.statsConfig().flushOnAdmin()) { server_.flushStats(); } +#endif const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url); From b8ca697df056588050578a8cd78de02773d061ee Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 11:53:23 -0500 Subject: [PATCH 12/74] compile fixes Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.h | 2 +- source/common/stats/thread_local_store.cc | 6 +++--- source/server/admin/stats_handler.cc | 9 +++++---- source/server/admin/stats_handler.h | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index ffce0f476dbb3..f73eab97fa52e 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -237,7 +237,7 @@ class IsolatedStoreImpl : public StoreImpl { if (f_size != nullptr) { f_size(1); } - f_stat(default_scope_); + f_stat(*default_scope_); } Stats::StatName prefix() const override { return StatName(); } diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 1cb7e6fbfcebe..f570fe5e43d35 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -977,14 +977,14 @@ void ThreadLocalStoreImpl::forEachHistogram(SizeFn f_size, StatFn f_size, - StatFn f_scope) const { + StatFn f_scope) const { Thread::LockGuard lock(lock_); if (f_size != nullptr) { f_size(scopes_.size() + 1 /* for default_scope_ */); } - f_scope(default_scope_); + f_scope(*default_scope_); for (ScopeImpl* scope : scopes_) { - f_scope(scope->makeShared()); + f_scope(*scope); } } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index f0a8571183ed6..cde560bb5b4ba 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -299,14 +299,15 @@ StatsHandler::Context::Context(Server::Instance& server, bool used_only, // First capture all the scopes and hold onto them with a SharedPtr so they // can't be deleted after the initial iteration. stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, - [this](const Stats::ScopeSharedPtr& scope) { scopes_.emplace_back(scope); }); + [this](const Stats::Scope& scope) { + scopes_.emplace_back(scope.shared_from_this()); }); startPhase(); } void StatsHandler::Context::startPhase() { ASSERT(stat_map_.empty()); - for (const Stats::ScopeSharedPtr& scope : scopes_) { + for (const Stats::ConstScopeSharedPtr& scope : scopes_) { StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; if (variant.index() == absl::variant_npos) { variant = ScopeVec(); @@ -342,7 +343,7 @@ void StatsHandler::Context::populateStatsForCurrentPhase(const ScopeVec& scope_v template void StatsHandler::Context::populateStatsFromScopes(const ScopeVec& scope_vec) { - for (const Stats::ScopeSharedPtr& scope : scope_vec) { + for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { stat_map_[stat->name()] = stat; return true; @@ -430,7 +431,7 @@ struct CompareStatsAndScopes { Stats::StatName name(const StatOrScope& stat_or_scope) const { switch (stat_or_scope.index()) { - case 0: return absl::get(stat_or_scope)->prefix(); + case 0: return absl::get(stat_or_scope)->prefix(); case 1: return absl::get(stat_or_scope)->statName(); case 2: return absl::get(stat_or_scope)->statName(); case 3: return absl::get(stat_or_scope)->statName(); diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index b62c244a0ad07..b0b4ce1565505 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -54,7 +54,7 @@ class StatsHandler : public HandlerContextBase { class TextRender; class Context { - using ScopeVec = std::vector; + using ScopeVec = std::vector; using StatOrScopes = absl::variant; From 5afc02ac2a17241e48c5e6e07d2c206dc4dbbbce Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 11:56:15 -0500 Subject: [PATCH 13/74] format Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index cde560bb5b4ba..dedef7810c8ea 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -298,9 +298,9 @@ StatsHandler::Context::Context(Server::Instance& server, bool used_only, // // First capture all the scopes and hold onto them with a SharedPtr so they // can't be deleted after the initial iteration. - stats_.forEachScope([this](size_t s) { scopes_.reserve(s); }, - [this](const Stats::Scope& scope) { - scopes_.emplace_back(scope.shared_from_this()); }); + stats_.forEachScope( + [this](size_t s) { scopes_.reserve(s); }, + [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.shared_from_this()); }); startPhase(); } From 1b7695a9e67f9bc5e61ef0ac7e6f430a496cffc0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 12:25:26 -0500 Subject: [PATCH 14/74] remove ifdef-blocks for use of RefcountPtr for scopes. Signed-off-by: Joshua Marantz --- envoy/stats/scope.h | 43 ++++++++------------ source/common/http/http1/codec_impl.cc | 1 - source/common/stats/isolated_store_impl.cc | 17 -------- source/common/stats/isolated_store_impl.h | 12 ------ source/common/stats/scope_prefixer.cc | 4 -- source/common/stats/scope_prefixer.h | 28 ------------- source/common/stats/thread_local_store.cc | 5 --- source/common/stats/thread_local_store.h | 18 -------- source/server/admin/stats_handler.cc | 2 +- test/common/stats/thread_local_store_test.cc | 4 +- test/common/stats/utility_test.cc | 1 - test/integration/server.h | 2 +- test/mocks/stats/mocks.h | 8 ---- 13 files changed, 21 insertions(+), 124 deletions(-) diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index 00de89dd1ac37..80e78a1cdc96c 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -6,11 +6,6 @@ #include "envoy/common/pure.h" #include "envoy/stats/histogram.h" - -#define SCOPE_REFCOUNT 0 -#if SCOPE_REFCOUNT -#include "envoy/stats/refcount_ptr.h" -#endif #include "envoy/stats/symbol_table.h" #include "envoy/stats/tag.h" @@ -30,40 +25,36 @@ using CounterOptConstRef = absl::optional> using GaugeOptConstRef = absl::optional>; using HistogramOptConstRef = absl::optional>; using TextReadoutOptConstRef = absl::optional>; -#if SCOPE_REFCOUNT -using ConstScopeSharedPtr = RefcountPtr; -using ScopeSharedPtr = RefcountPtr; -#else using ConstScopeSharedPtr = std::shared_ptr; using ScopeSharedPtr = std::shared_ptr; -#endif -using ScopePtr = ScopeSharedPtr; // TODO(jmarantz): global s/ShaedPtr/ScopeSharedPtr/ & remove alias +using ScopePtr = ScopeSharedPtr; // TODO(jmarantz): global s/ScopePtr/ScopeSharedPtr/ & remove alias template using IterateFn = std::function&)>; -#define SCOPE_SHARED_FROM_THIS 1 - /** * A named scope for stats. Scopes are a grouping of stats that can be acted on as a unit if needed * (for example to free/delete all of them). + * + * We enable use of shared pointers for Scopes to make it possible for the admin + * stats handler to safely capture all the scope references and remain robust to + * other threads deleting those scopes while rendering an admin stats page. + * + * We use std::shared_ptr rather than Stats::RefcountPtr, which we use for other + * stats, because: + * * existing uses of shared_ptr exist in the Wasm extension and would need + * to be rewritten to allow for RefcountPtr. + * * the main advantage of RefcountPtr is it's smaller per instance by (IIRC) 16 + * bytes, but there are not typically enough scopes that the extra per-scope + * overhead would matter. + * * It's a little less coding to use enable_shared_from_this compared to adding + * a ref_count to the scope object, for each of its implementations. */ -class Scope : public -#if SCOPE_REFCOUNT - RefcountInterface -#else -#if SCOPE_SHARED_FROM_THIS - std::enable_shared_from_this -#endif -#endif -{ +class Scope : public std::enable_shared_from_this { public: virtual ~Scope() = default; -#if SCOPE_REFCOUNT - ScopeSharedPtr makeShared() { return ScopeSharedPtr(this); } -#else ScopeSharedPtr makeShared() { return shared_from_this(); } -#endif + ConstScopeSharedPtr makeConstShared() const { return shared_from_this(); } /** * Allocate a new scope. NOTE: The implementation should correctly handle overlapping scopes diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index 93ee0128fe52b..4db568438c3b8 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -237,7 +237,6 @@ void StreamEncoderImpl::encodeData(Buffer::Instance& data, bool end_stream) { } connection_.buffer().move(data); - ASSERT(data.length() == 0); if (chunk_encoding_) { connection_.buffer().add(CRLF); diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 94261117a7371..4e2c0a2a7ff89 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -39,24 +39,8 @@ IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) null_gauge_(new NullGaugeImpl(symbol_table)), default_scope_(std::make_shared("", *this)) {} -#if SCOPE_REFCOUNT -IsolatedStoreImpl::~IsolatedStoreImpl() { - ENVOY_LOG_MISC(error, "ref_count={}", ref_count_); - ASSERT(ref_count_ == 0); -} -#else IsolatedStoreImpl::~IsolatedStoreImpl() = default; -#endif - -#if SCOPE_REFCOUNT -ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { - return (new ScopePrefixer(name, *this))->makeShared(); -} -ScopePtr IsolatedStoreImpl::scopeFromStatName(StatName name) { - return (new ScopePrefixer(name, *this))->makeShared(); -} -#else ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { return std::make_shared(name, *this); } @@ -64,7 +48,6 @@ ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { ScopePtr IsolatedStoreImpl::scopeFromStatName(StatName name) { return std::make_shared(name, *this); } -#endif } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index f73eab97fa52e..b3da0eddca393 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -254,17 +254,8 @@ class IsolatedStoreImpl : public StoreImpl { forEachTextReadout(f_size, f_stat); } -#if SCOPE_REFCOUNT - // RefcountInterface - void incRefCount() override { ++ref_count_; } - bool decRefCount() override { return --ref_count_ == 0; } - uint32_t use_count() const override { return ref_count_; } -#endif - private: IsolatedStoreImpl(std::unique_ptr&& symbol_table); - IsolatedStoreImpl(const IsolatedStoreImpl&) = delete; - IsolatedStoreImpl& operator=(const IsolatedStoreImpl&) = delete; SymbolTablePtr symbol_table_storage_; AllocatorImpl alloc_; @@ -275,9 +266,6 @@ class IsolatedStoreImpl : public StoreImpl { RefcountPtr null_counter_; RefcountPtr null_gauge_; ScopeSharedPtr default_scope_; -#if SCOPE_REFCOUNT - std::atomic ref_count_{1}; -#endif }; } // namespace Stats diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 1fcac9aa7547d..6cc2dba2740e2 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -18,11 +18,7 @@ ScopePrefixer::~ScopePrefixer() { prefix_.free(symbolTable()); } ScopePtr ScopePrefixer::scopeFromStatName(StatName name) { SymbolTable::StoragePtr joined = symbolTable().join({prefix_.statName(), name}); -#if SCOPE_REFCOUNT - return (new ScopePrefixer(StatName(joined.get()), scope_))->makeShared(); -#else return std::make_shared(StatName(joined.get()), scope_); -#endif } ScopePtr ScopePrefixer::createScope(const std::string& name) { diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index fae39d9d6c7c3..8d4f6ee19cc6d 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -11,28 +11,10 @@ namespace Stats { // prior to creation. class ScopePrefixer : public Scope { public: -#if 0 - ScopePrefixer(absl::string_view prefix, const ScopeSharedPtr& scope); - ScopePrefixer(StatName prefix, const ScopeSharedPtr& scope); -#else ScopePrefixer(absl::string_view prefix, Scope& scope); ScopePrefixer(StatName prefix, Scope& scope); -#endif ~ScopePrefixer() override; -#if SCOPE_REFCOUNT - // RefcountInterface -#if 1 - void incRefCount() override { ++ref_count_; } - bool decRefCount() override { return --ref_count_ == 0; } - uint32_t use_count() const override { return ref_count_; } -#else - void incRefCount() override { scope_->incRefCount(); } - bool decRefCount() override { return scope_->decRefCount(); } - uint32_t use_count() const override { return scope_->use_count(); } -#endif -#endif - // Scope ScopePtr createScope(const std::string& name) override; ScopePtr scopeFromStatName(StatName name) override; @@ -81,9 +63,6 @@ class ScopePrefixer : public Scope { StatName prefix() const override { return prefix_.statName(); } private: - ScopePrefixer(const ScopePrefixer&) = delete; - ScopePrefixer& operator=(const ScopePrefixer&) = delete; - template bool iterHelper(const IterateFn& fn) const { // We determine here what's in the scope by looking at name // prefixes. Strictly speaking this is not correct, as a stat name can be in @@ -106,15 +85,8 @@ class ScopePrefixer : public Scope { return scope_.iterate(filter_scope); } -#if 0 - ScopeSharedPtr scope_; -#else Scope& scope_; -#endif StatNameStorage prefix_; -#if SCOPE_REFCOUNT - std::atomic ref_count_{0}; -#endif }; } // namespace Stats diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index f570fe5e43d35..dd8d2e1185ca1 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -46,7 +46,6 @@ ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(scopes_to_cleanup_.empty()); ASSERT(central_cache_entries_to_cleanup_.empty()); ASSERT(histograms_to_cleanup_.empty()); - ASSERT(ref_count_ == 0); } void ThreadLocalStoreImpl::setHistogramSettings(HistogramSettingsConstPtr&& histogram_settings) { @@ -151,11 +150,7 @@ ScopePtr ThreadLocalStoreImpl::createScope(const std::string& name) { } ScopePtr ThreadLocalStoreImpl::scopeFromStatName(StatName name) { -#if SCOPE_REFCOUNT - RefcountPtr new_scope(new ScopeImpl(*this, name)); -#else auto new_scope = std::make_shared(*this, name); -#endif Thread::LockGuard lock(lock_); scopes_.emplace(new_scope.get()); return new_scope; diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index a7e6349ce988e..e7e973cd6acc7 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -283,13 +283,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo bool decHistogramRefCount(ParentHistogramImpl& histogram, std::atomic& ref_count); void releaseHistogramCrossThread(uint64_t histogram_id); -#if SCOPE_REFCOUNT - // RefcountInterface - void incRefCount() override { ++ref_count_; } - bool decRefCount() override { return --ref_count_ == 0; } - uint32_t use_count() const override { return ref_count_; } -#endif - private: friend class ThreadLocalStoreTestingPeer; @@ -344,12 +337,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ScopeImpl(ThreadLocalStoreImpl& parent, StatName prefix); ~ScopeImpl() override; -#if SCOPE_REFCOUNT - // RefcountInterface - void incRefCount() override { ++ref_count_; } - bool decRefCount() override { return --ref_count_ == 0; } - uint32_t use_count() const override { return ref_count_; } -#endif // Stats::Scope Counter& counterFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags) override; @@ -469,9 +456,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ThreadLocalStoreImpl& parent_; StatNameStorage prefix_; mutable CentralCacheEntrySharedPtr central_cache_; -#if SCOPE_REFCOUNT - std::atomic ref_count_{0}; -#endif }; struct TlsCache : public ThreadLocal::ThreadLocalObject { @@ -583,8 +567,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo // (e.g. when a scope is deleted), it is likely more efficient to batch their // cleanup, which would otherwise entail a post() per histogram per thread. std::vector histograms_to_cleanup_ ABSL_GUARDED_BY(hist_mutex_); - - std::atomic ref_count_{0}; }; using ThreadLocalStoreImplPtr = std::unique_ptr; diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index dedef7810c8ea..b2463970fa308 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -300,7 +300,7 @@ StatsHandler::Context::Context(Server::Instance& server, bool used_only, // can't be deleted after the initial iteration. stats_.forEachScope( [this](size_t s) { scopes_.reserve(s); }, - [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.shared_from_this()); }); + [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.makeConstShared()); }); startPhase(); } diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index ad2b8354118b4..edb0faa12d40f 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -479,8 +479,8 @@ TEST_F(StatsThreadLocalStoreTest, ForEach) { auto collect_scopes = [this]() -> std::vector { std::vector names; store_->forEachScope([](size_t) {}, - [&names](const ScopeSharedPtr& scope) { - names.push_back(scope->constSymbolTable().toString(scope->prefix())); + [&names](const Scope& scope) { + names.push_back(scope.constSymbolTable().toString(scope.prefix())); }); return names; }; diff --git a/test/common/stats/utility_test.cc b/test/common/stats/utility_test.cc index a0f733eae8618..040b63bb34bbc 100644 --- a/test/common/stats/utility_test.cc +++ b/test/common/stats/utility_test.cc @@ -186,7 +186,6 @@ TEST_P(StatsUtilityTest, Counters) { Counter& ctags = Utility::counterFromElements(*scope, {DynamicName("x"), token, DynamicName("y")}, tags_); EXPECT_EQ("scope.x.token.y.tag1.value1.tag2.value2", ctags.name()); - scope.reset(); } TEST_P(StatsUtilityTest, Gauges) { diff --git a/test/integration/server.h b/test/integration/server.h index 69c150067e2cc..6c228624a3206 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -300,7 +300,7 @@ class TestIsolatedStoreImpl : public StoreRoot { store_.forEachHistogram(f_size, f_stat); } void forEachScope(std::function f_size, - StatFn f_scope) const override { + StatFn f_scope) const override { Thread::LockGuard lock(lock_); store_.forEachScope(f_size, f_scope); } diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 83d3aa6ad352e..42837995662a5 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -278,13 +278,6 @@ class MockStore : public TestUtil::TestStore { MockStore(); ~MockStore() override; -#if SCOPE_REFCOUNT - // RefcountInterface - void incRefCount() override { ++ref_count_; } - bool decRefCount() override { return --ref_count_ == 0; } - uint32_t use_count() const override { return ref_count_; } -#endif - ScopePtr createScope(const std::string& name) override { return ScopePtr{createScope_(name)}; } ScopePtr scopeFromStatName(StatName name) override { return createScope(symbolTable().toString(name)); @@ -336,7 +329,6 @@ class MockStore : public TestUtil::TestStore { testing::NiceMock counter_; testing::NiceMock gauge_; std::vector> histograms_; - std::atomic ref_count_{1}; }; /** From 0f58de7dea867a65af43be338b511eb927defe12 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 12:52:46 -0500 Subject: [PATCH 15/74] cleanup hacks used for perf testing Signed-off-by: Joshua Marantz --- source/common/api/posix/os_sys_calls_impl.cc | 1 - source/common/stats/isolated_store_impl.cc | 2 -- source/server/admin/stats_handler.cc | 2 -- 3 files changed, 5 deletions(-) diff --git a/source/common/api/posix/os_sys_calls_impl.cc b/source/common/api/posix/os_sys_calls_impl.cc index 375ef6e14193d..65fa8ecad506a 100644 --- a/source/common/api/posix/os_sys_calls_impl.cc +++ b/source/common/api/posix/os_sys_calls_impl.cc @@ -318,7 +318,6 @@ bool OsSysCallsImpl::supportsGetifaddrs() const { } SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { - return {0, 0}; if (alternate_getifaddrs_.has_value()) { return alternate_getifaddrs_.value()(interfaces); } diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 4e2c0a2a7ff89..cb156813c0899 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -9,8 +9,6 @@ #include "source/common/stats/scope_prefixer.h" #include "source/common/stats/utility.h" -#include "absl/strings/str_cat.h" - namespace Envoy { namespace Stats { diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index b2463970fa308..ba1e930e7b008 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -75,11 +75,9 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, Http::Code StatsHandler::handlerStats(absl::string_view url, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { -#if 0 if (server_.statsConfig().flushOnAdmin()) { server_.flushStats(); } -#endif const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url); From 0613f53defa7162be5e6f31f66edd6da4fa79258 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 13:21:53 -0500 Subject: [PATCH 16/74] big rename Signed-off-by: Joshua Marantz --- contrib/sip_proxy/filters/network/source/tra/stat_names.h | 2 +- envoy/server/proactive_resource_monitor.h | 2 +- envoy/stats/stats_macros.h | 2 +- envoy/stats/symbol_table.h | 2 +- source/common/common/regex.h | 2 +- source/common/grpc/context_impl.h | 2 +- source/common/grpc/stat_names.h | 2 +- source/common/http/codes.h | 2 +- source/common/http/conn_manager_config.h | 2 +- source/common/http/user_agent.cc | 2 +- source/common/http/user_agent.h | 2 +- source/common/local_info/local_info_impl.h | 2 +- source/common/memory/heap_shrinker.cc | 2 +- source/common/quic/quic_stat_names.h | 4 ++-- source/common/router/config_impl.h | 2 +- source/common/router/router.h | 2 +- source/common/stats/BUILD | 4 ++-- source/common/stats/allocator_impl.cc | 2 +- source/common/stats/isolated_store_impl.h | 2 +- source/common/stats/metric_impl.cc | 2 +- source/common/stats/metric_impl.h | 2 +- source/common/stats/scope_prefixer.cc | 2 +- source/common/stats/scope_prefixer.h | 2 +- source/common/stats/stat_merger.h | 2 +- source/common/stats/stats_matcher_impl.h | 2 +- source/common/stats/store_impl.h | 2 +- source/common/stats/{symbol_table_impl.cc => symbol_table.cc} | 2 +- source/common/stats/{symbol_table_impl.h => symbol_table.h} | 0 source/common/stats/tag_utility.cc | 2 +- source/common/stats/tag_utility.h | 2 +- source/common/stats/thread_local_store.h | 2 +- source/common/stats/utility.h | 2 +- source/exe/main_common.h | 2 +- source/extensions/common/wasm/stats_handler.h | 2 +- source/extensions/common/wasm/wasm.h | 2 +- source/extensions/filters/common/ratelimit/stat_names.h | 2 +- source/extensions/filters/http/dynamo/dynamo_stats.cc | 2 +- source/extensions/filters/http/dynamo/dynamo_stats.h | 2 +- source/extensions/filters/http/fault/fault_filter.h | 2 +- .../extensions/filters/http/grpc_stats/grpc_stats_filter.cc | 2 +- source/extensions/filters/http/ip_tagging/ip_tagging_filter.h | 2 +- .../filters/network/common/redis/redis_command_stats.h | 2 +- source/extensions/filters/network/mongo_proxy/mongo_stats.cc | 2 +- source/extensions/filters/network/mongo_proxy/mongo_stats.h | 2 +- .../network/thrift_proxy/filters/ratelimit/ratelimit.h | 2 +- source/extensions/filters/network/zookeeper_proxy/filter.h | 2 +- source/extensions/stat_sinks/common/statsd/statsd.cc | 2 +- source/extensions/stat_sinks/hystrix/hystrix.h | 2 +- source/extensions/tracers/lightstep/lightstep_tracer_impl.h | 2 +- .../transport_sockets/tls/cert_validator/cert_validator.h | 2 +- .../transport_sockets/tls/cert_validator/default_validator.cc | 2 +- .../transport_sockets/tls/cert_validator/default_validator.h | 2 +- .../tls/cert_validator/spiffe/spiffe_validator.cc | 2 +- .../tls/cert_validator/spiffe/spiffe_validator.h | 2 +- source/extensions/transport_sockets/tls/context_impl.h | 2 +- source/extensions/watchdog/profile_action/profile_action.cc | 2 +- source/server/guarddog_impl.cc | 2 +- source/server/hot_restarting_parent.cc | 2 +- source/server/overload_manager_impl.cc | 2 +- test/common/grpc/context_impl_test.cc | 2 +- test/common/grpc/grpc_client_integration_test_harness.h | 2 +- test/common/http/codes_speed_test.cc | 2 +- test/common/stats/BUILD | 4 ++-- test/common/stats/symbol_table_fuzz_test.cc | 2 +- test/common/stats/symbol_table_speed_test.cc | 2 +- .../stats/{symbol_table_impl_test.cc => symbol_table_test.cc} | 2 +- test/common/stats/thread_local_store_speed_test.cc | 2 +- test/common/stats/thread_local_store_test.cc | 2 +- .../http/grpc_http1_bridge/http1_bridge_filter_test.cc | 2 +- test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc | 2 +- .../tracers/lightstep/lightstep_tracer_impl_test.cc | 2 +- test/integration/filters/eds_ready_filter.cc | 2 +- test/mocks/router/mocks.h | 2 +- test/mocks/server/instance.h | 2 +- test/mocks/stats/mocks.cc | 2 +- test/mocks/stats/mocks.h | 2 +- test/mocks/upstream/host.h | 2 +- test/test_common/utility.h | 2 +- test/tools/router_check/router.h | 2 +- 79 files changed, 81 insertions(+), 81 deletions(-) rename source/common/stats/{symbol_table_impl.cc => symbol_table.cc} (99%) rename source/common/stats/{symbol_table_impl.h => symbol_table.h} (100%) rename test/common/stats/{symbol_table_impl_test.cc => symbol_table_test.cc} (99%) diff --git a/contrib/sip_proxy/filters/network/source/tra/stat_names.h b/contrib/sip_proxy/filters/network/source/tra/stat_names.h index dd0940fd394ed..4e73a107beeb6 100644 --- a/contrib/sip_proxy/filters/network/source/tra/stat_names.h +++ b/contrib/sip_proxy/filters/network/source/tra/stat_names.h @@ -1,6 +1,6 @@ #pragma once -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Extensions { diff --git a/envoy/server/proactive_resource_monitor.h b/envoy/server/proactive_resource_monitor.h index ee37ccf094fd5..f4d69aa832b57 100644 --- a/envoy/server/proactive_resource_monitor.h +++ b/envoy/server/proactive_resource_monitor.h @@ -7,7 +7,7 @@ #include "envoy/stats/stats.h" #include "source/common/common/assert.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Server { diff --git a/envoy/stats/stats_macros.h b/envoy/stats/stats_macros.h index 0cc6ee927472d..6ffca3ea0d118 100644 --- a/envoy/stats/stats_macros.h +++ b/envoy/stats/stats_macros.h @@ -5,7 +5,7 @@ #include "envoy/stats/histogram.h" #include "envoy/stats/stats.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "absl/strings/match.h" diff --git a/envoy/stats/symbol_table.h b/envoy/stats/symbol_table.h index 1d642b3fe8a08..7ae1006951293 100644 --- a/envoy/stats/symbol_table.h +++ b/envoy/stats/symbol_table.h @@ -18,7 +18,7 @@ namespace Stats { * the interface without abstract methods, because (a) the underlying class * representation is common to both implementations of SymbolTable, and (b) * we do not want or need the overhead of a vptr per StatName. The common - * declaration for StatName is in source/common/stats/symbol_table_impl.h + * declaration for StatName is in source/common/stats/symbol_table.h */ class StatName; using StatNameVec = absl::InlinedVector; diff --git a/source/common/common/regex.h b/source/common/common/regex.h index e2aee56170abe..b3bacb319ea0a 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -9,7 +9,7 @@ #include "source/common/common/assert.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "re2/re2.h" #include "xds/type/matcher/v3/regex.pb.h" diff --git a/source/common/grpc/context_impl.h b/source/common/grpc/context_impl.h index 8cc073a46be4e..4729f68b14552 100644 --- a/source/common/grpc/context_impl.h +++ b/source/common/grpc/context_impl.h @@ -8,7 +8,7 @@ #include "source/common/common/hash.h" #include "source/common/grpc/stat_names.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "absl/types/optional.h" diff --git a/source/common/grpc/stat_names.h b/source/common/grpc/stat_names.h index 2c65f597aeea2..13d81c78893ca 100644 --- a/source/common/grpc/stat_names.h +++ b/source/common/grpc/stat_names.h @@ -2,7 +2,7 @@ #include "envoy/grpc/status.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/container/flat_hash_map.h" diff --git a/source/common/http/codes.h b/source/common/http/codes.h index d3f78ee16c03f..be8ef2776b08b 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -9,7 +9,7 @@ #include "envoy/stats/scope.h" #include "source/common/common/thread.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Http { diff --git a/source/common/http/conn_manager_config.h b/source/common/http/conn_manager_config.h index 45ceb107d5bf5..d0d9294ac63f7 100644 --- a/source/common/http/conn_manager_config.h +++ b/source/common/http/conn_manager_config.h @@ -13,7 +13,7 @@ #include "source/common/http/date_provider.h" #include "source/common/local_reply/local_reply.h" #include "source/common/network/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Http { diff --git a/source/common/http/user_agent.cc b/source/common/http/user_agent.cc index cf5e6746f7783..5a68f8fd7569c 100644 --- a/source/common/http/user_agent.cc +++ b/source/common/http/user_agent.cc @@ -9,7 +9,7 @@ #include "envoy/stats/timespan.h" #include "source/common/http/headers.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/common/http/user_agent.h b/source/common/http/user_agent.h index 8cb482b556c36..f4bd4454e9263 100644 --- a/source/common/http/user_agent.h +++ b/source/common/http/user_agent.h @@ -10,7 +10,7 @@ #include "envoy/stats/stats_macros.h" #include "envoy/stats/timespan.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Http { diff --git a/source/common/local_info/local_info_impl.h b/source/common/local_info/local_info_impl.h index 8f5b3e5fba560..4e1ae360126b7 100644 --- a/source/common/local_info/local_info_impl.h +++ b/source/common/local_info/local_info_impl.h @@ -6,7 +6,7 @@ #include "envoy/local_info/local_info.h" #include "source/common/config/context_provider_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace LocalInfo { diff --git a/source/common/memory/heap_shrinker.cc b/source/common/memory/heap_shrinker.cc index 4cc3ea7c51319..0fe1cac2c20bf 100644 --- a/source/common/memory/heap_shrinker.cc +++ b/source/common/memory/heap_shrinker.cc @@ -1,7 +1,7 @@ #include "source/common/memory/heap_shrinker.h" #include "source/common/memory/utils.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/strings/str_cat.h" diff --git a/source/common/quic/quic_stat_names.h b/source/common/quic/quic_stat_names.h index 19abf6921f846..7385b2ff7905e 100644 --- a/source/common/quic/quic_stat_names.h +++ b/source/common/quic/quic_stat_names.h @@ -5,7 +5,7 @@ #include "envoy/stats/scope.h" #include "source/common/common/thread.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "quiche/quic/core/quic_error_codes.h" #include "quiche/quic/core/quic_types.h" @@ -50,7 +50,7 @@ class QuicStatNames { #else -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Quic { diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 3a2c683fd44e1..d65b2fe04d1c4 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -31,7 +31,7 @@ #include "source/common/router/metadatamatchcriteria_impl.h" #include "source/common/router/router_ratelimit.h" #include "source/common/router/tls_context_match_criteria_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/container/node_hash_map.h" #include "absl/types/optional.h" diff --git a/source/common/router/router.h b/source/common/router/router.h index 1ae8f0197bc3c..9c9ae021574c2 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -34,7 +34,7 @@ #include "source/common/router/config_impl.h" #include "source/common/router/context_impl.h" #include "source/common/router/upstream_request.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stream_info/stream_info_impl.h" #include "source/common/upstream/load_balancer_impl.h" diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 122d5d66ca4a2..9ee94c3e10080 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -188,8 +188,8 @@ envoy_cc_library( envoy_cc_library( name = "symbol_table_lib", - srcs = ["symbol_table_impl.cc"], - hdrs = ["symbol_table_impl.h"], + srcs = ["symbol_table.cc"], + hdrs = ["symbol_table.h"], external_deps = ["abseil_base"], deps = [ ":recent_lookups_lib", diff --git a/source/common/stats/allocator_impl.cc b/source/common/stats/allocator_impl.cc index 7caf6c1f2e353..16ce001fa7a8c 100644 --- a/source/common/stats/allocator_impl.cc +++ b/source/common/stats/allocator_impl.cc @@ -15,7 +15,7 @@ #include "source/common/common/utility.h" #include "source/common/stats/metric_impl.h" #include "source/common/stats/stat_merger.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/container/flat_hash_set.h" diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index b3da0eddca393..354d55cdfd307 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -13,7 +13,7 @@ #include "source/common/stats/null_counter.h" #include "source/common/stats/null_gauge.h" #include "source/common/stats/store_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/tag_utility.h" #include "source/common/stats/utility.h" diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 7a43ce886e3e5..7aa757481385c 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -2,7 +2,7 @@ #include "envoy/stats/tag.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index e188f1abe7942..28d23c6aceb47 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -8,7 +8,7 @@ #include "envoy/stats/tag.h" #include "source/common/common/assert.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 6cc2dba2740e2..6338b41cc05ef 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -2,7 +2,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index 8d4f6ee19cc6d..bb3d1620489cf 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -2,7 +2,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/stat_merger.h b/source/common/stats/stat_merger.h index 3a9fb6c3093fd..04bf7abdb977c 100644 --- a/source/common/stats/stat_merger.h +++ b/source/common/stats/stat_merger.h @@ -3,7 +3,7 @@ #include "envoy/stats/store.h" #include "source/common/protobuf/protobuf.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/container/flat_hash_map.h" diff --git a/source/common/stats/stats_matcher_impl.h b/source/common/stats/stats_matcher_impl.h index a0d3a085cc3fb..dcd84b087794e 100644 --- a/source/common/stats/stats_matcher_impl.h +++ b/source/common/stats/stats_matcher_impl.h @@ -8,7 +8,7 @@ #include "source/common/common/matchers.h" #include "source/common/protobuf/protobuf.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/strings/string_view.h" diff --git a/source/common/stats/store_impl.h b/source/common/stats/store_impl.h index e4de8fe918b20..9716985f83003 100644 --- a/source/common/stats/store_impl.h +++ b/source/common/stats/store_impl.h @@ -3,7 +3,7 @@ #include "envoy/stats/stats.h" #include "envoy/stats/store.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table.cc similarity index 99% rename from source/common/stats/symbol_table_impl.cc rename to source/common/stats/symbol_table.cc index 4184b10274763..a18d94187f879 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table.cc @@ -1,4 +1,4 @@ -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include #include diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table.h similarity index 100% rename from source/common/stats/symbol_table_impl.h rename to source/common/stats/symbol_table.h diff --git a/source/common/stats/tag_utility.cc b/source/common/stats/tag_utility.cc index 550bdf81180bf..0ff1fa43597d6 100644 --- a/source/common/stats/tag_utility.cc +++ b/source/common/stats/tag_utility.cc @@ -3,7 +3,7 @@ #include #include "source/common/config/well_known_names.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/tag_utility.h b/source/common/stats/tag_utility.h index d0961cf0f3e82..54c36f5c15673 100644 --- a/source/common/stats/tag_utility.h +++ b/source/common/stats/tag_utility.h @@ -3,7 +3,7 @@ #include "envoy/stats/symbol_table.h" #include "envoy/stats/tag.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index e7e973cd6acc7..535ac80e07ab4 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -17,7 +17,7 @@ #include "source/common/stats/null_counter.h" #include "source/common/stats/null_gauge.h" #include "source/common/stats/null_text_readout.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "absl/container/flat_hash_map.h" diff --git a/source/common/stats/utility.h b/source/common/stats/utility.h index 5ada272e3bb45..01d204ed4e6d2 100644 --- a/source/common/stats/utility.h +++ b/source/common/stats/utility.h @@ -6,7 +6,7 @@ #include "envoy/stats/stats.h" #include "source/common/common/thread.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/container/inlined_vector.h" #include "absl/strings/string_view.h" diff --git a/source/exe/main_common.h b/source/exe/main_common.h index a393c841e8b3a..7c684fab2ee3c 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -7,7 +7,7 @@ #include "source/common/common/thread.h" #include "source/common/event/real_time_system.h" #include "source/common/grpc/google_grpc_context.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" #include "source/exe/process_wide.h" diff --git a/source/extensions/common/wasm/stats_handler.h b/source/extensions/common/wasm/stats_handler.h index ad03f3c118209..01e3e4ae30aaa 100644 --- a/source/extensions/common/wasm/stats_handler.h +++ b/source/extensions/common/wasm/stats_handler.h @@ -8,7 +8,7 @@ #include "envoy/upstream/cluster_manager.h" #include "source/common/common/logger.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/common/wasm/wasm.h b/source/extensions/common/wasm/wasm.h index cc0de5d908211..04d5338407c1b 100644 --- a/source/extensions/common/wasm/wasm.h +++ b/source/extensions/common/wasm/wasm.h @@ -17,7 +17,7 @@ #include "source/common/common/assert.h" #include "source/common/common/logger.h" #include "source/common/config/datasource.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/version/version.h" #include "source/extensions/common/wasm/context.h" #include "source/extensions/common/wasm/plugin.h" diff --git a/source/extensions/filters/common/ratelimit/stat_names.h b/source/extensions/filters/common/ratelimit/stat_names.h index 45161fe43d44e..65a8b0614d5df 100644 --- a/source/extensions/filters/common/ratelimit/stat_names.h +++ b/source/extensions/filters/common/ratelimit/stat_names.h @@ -1,6 +1,6 @@ #pragma once -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/http/dynamo/dynamo_stats.cc b/source/extensions/filters/http/dynamo/dynamo_stats.cc index ba0e5d9355389..148e9a2ec2afb 100644 --- a/source/extensions/filters/http/dynamo/dynamo_stats.cc +++ b/source/extensions/filters/http/dynamo/dynamo_stats.cc @@ -5,7 +5,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/http/dynamo/dynamo_request_parser.h" namespace Envoy { diff --git a/source/extensions/filters/http/dynamo/dynamo_stats.h b/source/extensions/filters/http/dynamo/dynamo_stats.h index 78bd72c728035..772b44c80f01a 100644 --- a/source/extensions/filters/http/dynamo/dynamo_stats.h +++ b/source/extensions/filters/http/dynamo/dynamo_stats.h @@ -5,7 +5,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/extensions/filters/http/fault/fault_filter.h b/source/extensions/filters/http/fault/fault_filter.h index 1d8fea719ca48..2fe00acb05f87 100644 --- a/source/extensions/filters/http/fault/fault_filter.h +++ b/source/extensions/filters/http/fault/fault_filter.h @@ -16,7 +16,7 @@ #include "source/common/buffer/watermark_buffer.h" #include "source/common/common/token_bucket_impl.h" #include "source/common/http/header_utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/common/fault/fault_config.h" #include "source/extensions/filters/http/common/stream_rate_limiter.h" diff --git a/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc b/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc index 3e604ddf1f4f4..2e0fe1bd76d73 100644 --- a/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc +++ b/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc @@ -8,7 +8,7 @@ #include "source/common/grpc/common.h" #include "source/common/grpc/context_impl.h" #include "source/common/runtime/runtime_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stream_info/utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" 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 3753122a3dff5..a7db4da855284 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h @@ -14,7 +14,7 @@ #include "source/common/network/cidr_range.h" #include "source/common/network/lc_trie.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Extensions { 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 fd80c5ac850c5..f4ce6b5192917 100644 --- a/source/extensions/filters/network/common/redis/redis_command_stats.h +++ b/source/extensions/filters/network/common/redis/redis_command_stats.h @@ -6,7 +6,7 @@ #include "envoy/stats/scope.h" #include "envoy/stats/timespan.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/network/common/redis/codec.h" namespace Envoy { diff --git a/source/extensions/filters/network/mongo_proxy/mongo_stats.cc b/source/extensions/filters/network/mongo_proxy/mongo_stats.cc index fd362d35af42b..95172aa762e3e 100644 --- a/source/extensions/filters/network/mongo_proxy/mongo_stats.cc +++ b/source/extensions/filters/network/mongo_proxy/mongo_stats.cc @@ -6,7 +6,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/network/mongo_proxy/mongo_stats.h b/source/extensions/filters/network/mongo_proxy/mongo_stats.h index 5dee147145997..127451e5cc29a 100644 --- a/source/extensions/filters/network/mongo_proxy/mongo_stats.h +++ b/source/extensions/filters/network/mongo_proxy/mongo_stats.h @@ -6,7 +6,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h index d796044555e54..75fb2260e2cae 100644 --- a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h +++ b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h @@ -9,7 +9,7 @@ #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/common/ratelimit/ratelimit.h" #include "source/extensions/filters/common/ratelimit/stat_names.h" #include "source/extensions/filters/network/thrift_proxy/filters/pass_through_filter.h" diff --git a/source/extensions/filters/network/zookeeper_proxy/filter.h b/source/extensions/filters/network/zookeeper_proxy/filter.h index 46c4f0ea8da31..33c0af256d8fb 100644 --- a/source/extensions/filters/network/zookeeper_proxy/filter.h +++ b/source/extensions/filters/network/zookeeper_proxy/filter.h @@ -12,7 +12,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/network/zookeeper_proxy/decoder.h" namespace Envoy { diff --git a/source/extensions/stat_sinks/common/statsd/statsd.cc b/source/extensions/stat_sinks/common/statsd/statsd.cc index a69733b87b589..e661e326b2c4d 100644 --- a/source/extensions/stat_sinks/common/statsd/statsd.cc +++ b/source/extensions/stat_sinks/common/statsd/statsd.cc @@ -19,7 +19,7 @@ #include "source/common/config/utility.h" #include "source/common/network/socket_interface.h" #include "source/common/network/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/strings/str_join.h" diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index 97c5b2b2e7f54..ab2502d762eea 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -9,7 +9,7 @@ #include "envoy/stats/histogram.h" #include "envoy/stats/sink.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/tracers/lightstep/lightstep_tracer_impl.h b/source/extensions/tracers/lightstep/lightstep_tracer_impl.h index 6c7c131245a7f..ad482e3510560 100644 --- a/source/extensions/tracers/lightstep/lightstep_tracer_impl.h +++ b/source/extensions/tracers/lightstep/lightstep_tracer_impl.h @@ -15,7 +15,7 @@ #include "source/common/http/message_impl.h" #include "source/common/json/json_loader.h" #include "source/common/protobuf/protobuf.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/upstream/cluster_update_tracker.h" #include "source/extensions/tracers/common/ot/opentracing_driver_impl.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h b/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h index efec7cb3aa4e3..5e0f589b4890c 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h @@ -14,7 +14,7 @@ #include "envoy/ssl/ssl_socket_extended_info.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/stats.h" #include "openssl/ssl.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc index 93bbe8b17a041..3b28200b73abc 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc @@ -22,7 +22,7 @@ #include "source/common/network/address_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/runtime/runtime_features.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/cert_validator/factory.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h index 64f52bc38728f..2cea2782f0349 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h @@ -17,7 +17,7 @@ #include "source/common/common/logger.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "source/extensions/transport_sockets/tls/stats.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index c3b8d338fcae3..00bd8f86eedca 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -10,7 +10,7 @@ #include "source/common/config/datasource.h" #include "source/common/config/utility.h" #include "source/common/protobuf/message_validator_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/cert_validator/factory.h" #include "source/extensions/transport_sockets/tls/cert_validator/utility.h" #include "source/extensions/transport_sockets/tls/stats.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h index 669d77aec7d05..8fe7a213e0de0 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -15,7 +15,7 @@ #include "source/common/common/c_smart_ptr.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "source/extensions/transport_sockets/tls/stats.h" diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index 0107a88ec6fac..8b607018d88cc 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -15,7 +15,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/context_manager_impl.h" #include "source/extensions/transport_sockets/tls/ocsp/ocsp.h" diff --git a/source/extensions/watchdog/profile_action/profile_action.cc b/source/extensions/watchdog/profile_action/profile_action.cc index 3f0e556a3cdeb..f010bcd007922 100644 --- a/source/extensions/watchdog/profile_action/profile_action.cc +++ b/source/extensions/watchdog/profile_action/profile_action.cc @@ -6,7 +6,7 @@ #include "source/common/profiler/profiler.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "absl/strings/str_format.h" diff --git a/source/server/guarddog_impl.cc b/source/server/guarddog_impl.cc index cfc8256d54200..13373b080cb53 100644 --- a/source/server/guarddog_impl.cc +++ b/source/server/guarddog_impl.cc @@ -21,7 +21,7 @@ #include "source/common/common/logger.h" #include "source/common/config/utility.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/server/watchdog_impl.h" #include "absl/synchronization/mutex.h" diff --git a/source/server/hot_restarting_parent.cc b/source/server/hot_restarting_parent.cc index fab51c4fc7b53..b18af67b97be6 100644 --- a/source/server/hot_restarting_parent.cc +++ b/source/server/hot_restarting_parent.cc @@ -5,7 +5,7 @@ #include "source/common/memory/stats.h" #include "source/common/network/utility.h" #include "source/common/stats/stat_merger.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "source/server/listener_impl.h" diff --git a/source/server/overload_manager_impl.cc b/source/server/overload_manager_impl.cc index 4a274c145e1ba..34a9c2b45df18 100644 --- a/source/server/overload_manager_impl.cc +++ b/source/server/overload_manager_impl.cc @@ -11,7 +11,7 @@ #include "source/common/config/utility.h" #include "source/common/event/scaled_range_timer_manager_impl.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/server/resource_monitor_config_impl.h" #include "absl/container/node_hash_map.h" diff --git a/test/common/grpc/context_impl_test.cc b/test/common/grpc/context_impl_test.cc index f95307ef42787..acc6bc1ada4c4 100644 --- a/test/common/grpc/context_impl_test.cc +++ b/test/common/grpc/context_impl_test.cc @@ -5,7 +5,7 @@ #include "source/common/http/headers.h" #include "source/common/http/message_impl.h" #include "source/common/http/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "test/mocks/upstream/cluster_info.h" diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 99808db5ab8b1..f03caff22fecc 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -23,7 +23,7 @@ #include "source/common/network/connection_impl.h" #include "source/common/network/raw_buffer_socket.h" #include "source/common/router/context_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/context_config_impl.h" #include "source/extensions/transport_sockets/tls/ssl_socket.h" diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index fc1d5f4ffac3d..aba98a08dd658 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -10,7 +10,7 @@ #include "source/common/http/codes.h" #include "source/common/stats/isolated_store_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "benchmark/benchmark.h" diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 22e16577b110f..26513bd84f145 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -143,8 +143,8 @@ envoy_cc_test( ) envoy_cc_test( - name = "symbol_table_impl_test", - srcs = ["symbol_table_impl_test.cc"], + name = "symbol_table_test", + srcs = ["symbol_table_test.cc"], external_deps = ["abseil_hash_testing"], deps = [ ":stat_test_utility_lib", diff --git a/test/common/stats/symbol_table_fuzz_test.cc b/test/common/stats/symbol_table_fuzz_test.cc index cab1bda492c27..48c65300c948e 100644 --- a/test/common/stats/symbol_table_fuzz_test.cc +++ b/test/common/stats/symbol_table_fuzz_test.cc @@ -2,7 +2,7 @@ #include "source/common/common/assert.h" #include "source/common/common/base64.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "test/common/stats/stat_test_utility.h" #include "test/fuzz/fuzz_runner.h" diff --git a/test/common/stats/symbol_table_speed_test.cc b/test/common/stats/symbol_table_speed_test.cc index e62ff55da1082..49e835bac4d6e 100644 --- a/test/common/stats/symbol_table_speed_test.cc +++ b/test/common/stats/symbol_table_speed_test.cc @@ -7,7 +7,7 @@ #include "source/common/common/logger.h" #include "source/common/common/thread.h" #include "source/common/stats/isolated_store_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/utility.h" #include "test/common/stats/make_elements_helper.h" diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_test.cc similarity index 99% rename from test/common/stats/symbol_table_impl_test.cc rename to test/common/stats/symbol_table_test.cc index f5af9675bc06d..f12f47b001545 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_test.cc @@ -3,7 +3,7 @@ #include "source/common/common/macros.h" #include "source/common/common/mutex_tracer_impl.h" #include "source/common/memory/stats.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "test/common/stats/stat_test_utility.h" #include "test/test_common/logging.h" diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 620af46c698ab..5ea5e73b6e483 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -8,7 +8,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/stats/allocator_impl.h" #include "source/common/stats/stats_matcher_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/tag_producer_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index edb0faa12d40f..963c689b0a804 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -12,7 +12,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/memory/stats.h" #include "source/common/stats/stats_matcher_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/tag_producer_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" diff --git a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc index 45045ed8a0c47..a1b82e84016d1 100644 --- a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc +++ b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc @@ -4,7 +4,7 @@ #include "source/common/grpc/codec.h" #include "source/common/grpc/common.h" #include "source/common/http/header_map_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/http/grpc_http1_bridge/config.h" #include "source/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter.h" diff --git a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc index 561484f56d0d4..5d22c3ae37e5f 100644 --- a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc +++ b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc @@ -8,7 +8,7 @@ #include "source/common/http/codes.h" #include "source/common/http/header_map_impl.h" #include "source/common/http/headers.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/http/grpc_web/grpc_web_filter.h" #include "test/mocks/http/mocks.h" diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 5fce7e07e4d58..162e8534be470 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -11,7 +11,7 @@ #include "source/common/http/headers.h" #include "source/common/http/message_impl.h" #include "source/common/runtime/runtime_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/tracing/http_tracer_impl.h" #include "source/extensions/tracers/lightstep/lightstep_tracer_impl.h" diff --git a/test/integration/filters/eds_ready_filter.cc b/test/integration/filters/eds_ready_filter.cc index 4c78e2e5fa43d..10fa6c2136b04 100644 --- a/test/integration/filters/eds_ready_filter.cc +++ b/test/integration/filters/eds_ready_filter.cc @@ -6,7 +6,7 @@ #include "envoy/registry/registry.h" #include "envoy/server/filter_config.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "test/extensions/filters/http/common/empty_http_filter_config.h" diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index 80cc62719e1ed..ef9a74b61311f 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -30,7 +30,7 @@ #include "envoy/type/v3/percent.pb.h" #include "envoy/upstream/cluster_manager.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/global.h" diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index c208614218726..b9967e58c3e17 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -6,7 +6,7 @@ #include "source/common/http/context_impl.h" #include "source/common/quic/quic_stat_names.h" #include "source/common/router/context_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/extensions/transport_sockets/tls/context_manager_impl.h" #include "test/mocks/access_log/mocks.h" diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 6ed277eed2fd1..9de8bd4f6f8bd 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -2,7 +2,7 @@ #include -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 42837995662a5..6bd20eec8bf9c 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -18,7 +18,7 @@ #include "source/common/stats/histogram_impl.h" #include "source/common/stats/isolated_store_impl.h" #include "source/common/stats/store_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stats/timespan_impl.h" #include "test/common/stats/stat_test_utility.h" diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index 21eea638773fc..edf02aab76f8d 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -9,7 +9,7 @@ #include "envoy/data/cluster/v3/outlier_detection_event.pb.h" #include "envoy/upstream/upstream.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "test/mocks/network/transport_socket.h" #include "test/mocks/upstream/cluster_info.h" diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 545fcbe17edb5..2b657d2d2b01b 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -24,7 +24,7 @@ #include "source/common/http/header_map_impl.h" #include "source/common/protobuf/message_validator_impl.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "test/test_common/file_system_for_test.h" #include "test/test_common/logging.h" diff --git a/test/tools/router_check/router.h b/test/tools/router_check/router.h index 3624efcb9cc4f..1ab9ae97bf0c2 100644 --- a/test/tools/router_check/router.h +++ b/test/tools/router_check/router.h @@ -12,7 +12,7 @@ #include "source/common/http/headers.h" #include "source/common/json/json_loader.h" #include "source/common/router/config_impl.h" -#include "source/common/stats/symbol_table_impl.h" +#include "source/common/stats/symbol_table.h" #include "source/common/stream_info/stream_info_impl.h" #include "test/mocks/server/instance.h" From cc6243f13c8d5abf197744bcdf8747f3be55f4c0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 13:54:39 -0500 Subject: [PATCH 17/74] Revert "big rename" This reverts commit 0613f53defa7162be5e6f31f66edd6da4fa79258. Signed-off-by: Joshua Marantz --- contrib/sip_proxy/filters/network/source/tra/stat_names.h | 2 +- envoy/server/proactive_resource_monitor.h | 2 +- envoy/stats/stats_macros.h | 2 +- envoy/stats/symbol_table.h | 2 +- source/common/common/regex.h | 2 +- source/common/grpc/context_impl.h | 2 +- source/common/grpc/stat_names.h | 2 +- source/common/http/codes.h | 2 +- source/common/http/conn_manager_config.h | 2 +- source/common/http/user_agent.cc | 2 +- source/common/http/user_agent.h | 2 +- source/common/local_info/local_info_impl.h | 2 +- source/common/memory/heap_shrinker.cc | 2 +- source/common/quic/quic_stat_names.h | 4 ++-- source/common/router/config_impl.h | 2 +- source/common/router/router.h | 2 +- source/common/stats/BUILD | 4 ++-- source/common/stats/allocator_impl.cc | 2 +- source/common/stats/isolated_store_impl.h | 2 +- source/common/stats/metric_impl.cc | 2 +- source/common/stats/metric_impl.h | 2 +- source/common/stats/scope_prefixer.cc | 2 +- source/common/stats/scope_prefixer.h | 2 +- source/common/stats/stat_merger.h | 2 +- source/common/stats/stats_matcher_impl.h | 2 +- source/common/stats/store_impl.h | 2 +- source/common/stats/{symbol_table.cc => symbol_table_impl.cc} | 2 +- source/common/stats/{symbol_table.h => symbol_table_impl.h} | 0 source/common/stats/tag_utility.cc | 2 +- source/common/stats/tag_utility.h | 2 +- source/common/stats/thread_local_store.h | 2 +- source/common/stats/utility.h | 2 +- source/exe/main_common.h | 2 +- source/extensions/common/wasm/stats_handler.h | 2 +- source/extensions/common/wasm/wasm.h | 2 +- source/extensions/filters/common/ratelimit/stat_names.h | 2 +- source/extensions/filters/http/dynamo/dynamo_stats.cc | 2 +- source/extensions/filters/http/dynamo/dynamo_stats.h | 2 +- source/extensions/filters/http/fault/fault_filter.h | 2 +- .../extensions/filters/http/grpc_stats/grpc_stats_filter.cc | 2 +- source/extensions/filters/http/ip_tagging/ip_tagging_filter.h | 2 +- .../filters/network/common/redis/redis_command_stats.h | 2 +- source/extensions/filters/network/mongo_proxy/mongo_stats.cc | 2 +- source/extensions/filters/network/mongo_proxy/mongo_stats.h | 2 +- .../network/thrift_proxy/filters/ratelimit/ratelimit.h | 2 +- source/extensions/filters/network/zookeeper_proxy/filter.h | 2 +- source/extensions/stat_sinks/common/statsd/statsd.cc | 2 +- source/extensions/stat_sinks/hystrix/hystrix.h | 2 +- source/extensions/tracers/lightstep/lightstep_tracer_impl.h | 2 +- .../transport_sockets/tls/cert_validator/cert_validator.h | 2 +- .../transport_sockets/tls/cert_validator/default_validator.cc | 2 +- .../transport_sockets/tls/cert_validator/default_validator.h | 2 +- .../tls/cert_validator/spiffe/spiffe_validator.cc | 2 +- .../tls/cert_validator/spiffe/spiffe_validator.h | 2 +- source/extensions/transport_sockets/tls/context_impl.h | 2 +- source/extensions/watchdog/profile_action/profile_action.cc | 2 +- source/server/guarddog_impl.cc | 2 +- source/server/hot_restarting_parent.cc | 2 +- source/server/overload_manager_impl.cc | 2 +- test/common/grpc/context_impl_test.cc | 2 +- test/common/grpc/grpc_client_integration_test_harness.h | 2 +- test/common/http/codes_speed_test.cc | 2 +- test/common/stats/BUILD | 4 ++-- test/common/stats/symbol_table_fuzz_test.cc | 2 +- .../stats/{symbol_table_test.cc => symbol_table_impl_test.cc} | 2 +- test/common/stats/symbol_table_speed_test.cc | 2 +- test/common/stats/thread_local_store_speed_test.cc | 2 +- test/common/stats/thread_local_store_test.cc | 2 +- .../http/grpc_http1_bridge/http1_bridge_filter_test.cc | 2 +- test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc | 2 +- .../tracers/lightstep/lightstep_tracer_impl_test.cc | 2 +- test/integration/filters/eds_ready_filter.cc | 2 +- test/mocks/router/mocks.h | 2 +- test/mocks/server/instance.h | 2 +- test/mocks/stats/mocks.cc | 2 +- test/mocks/stats/mocks.h | 2 +- test/mocks/upstream/host.h | 2 +- test/test_common/utility.h | 2 +- test/tools/router_check/router.h | 2 +- 79 files changed, 81 insertions(+), 81 deletions(-) rename source/common/stats/{symbol_table.cc => symbol_table_impl.cc} (99%) rename source/common/stats/{symbol_table.h => symbol_table_impl.h} (100%) rename test/common/stats/{symbol_table_test.cc => symbol_table_impl_test.cc} (99%) diff --git a/contrib/sip_proxy/filters/network/source/tra/stat_names.h b/contrib/sip_proxy/filters/network/source/tra/stat_names.h index 4e73a107beeb6..dd0940fd394ed 100644 --- a/contrib/sip_proxy/filters/network/source/tra/stat_names.h +++ b/contrib/sip_proxy/filters/network/source/tra/stat_names.h @@ -1,6 +1,6 @@ #pragma once -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Extensions { diff --git a/envoy/server/proactive_resource_monitor.h b/envoy/server/proactive_resource_monitor.h index f4d69aa832b57..ee37ccf094fd5 100644 --- a/envoy/server/proactive_resource_monitor.h +++ b/envoy/server/proactive_resource_monitor.h @@ -7,7 +7,7 @@ #include "envoy/stats/stats.h" #include "source/common/common/assert.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Server { diff --git a/envoy/stats/stats_macros.h b/envoy/stats/stats_macros.h index 6ffca3ea0d118..0cc6ee927472d 100644 --- a/envoy/stats/stats_macros.h +++ b/envoy/stats/stats_macros.h @@ -5,7 +5,7 @@ #include "envoy/stats/histogram.h" #include "envoy/stats/stats.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "absl/strings/match.h" diff --git a/envoy/stats/symbol_table.h b/envoy/stats/symbol_table.h index 7ae1006951293..1d642b3fe8a08 100644 --- a/envoy/stats/symbol_table.h +++ b/envoy/stats/symbol_table.h @@ -18,7 +18,7 @@ namespace Stats { * the interface without abstract methods, because (a) the underlying class * representation is common to both implementations of SymbolTable, and (b) * we do not want or need the overhead of a vptr per StatName. The common - * declaration for StatName is in source/common/stats/symbol_table.h + * declaration for StatName is in source/common/stats/symbol_table_impl.h */ class StatName; using StatNameVec = absl::InlinedVector; diff --git a/source/common/common/regex.h b/source/common/common/regex.h index b3bacb319ea0a..e2aee56170abe 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -9,7 +9,7 @@ #include "source/common/common/assert.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "re2/re2.h" #include "xds/type/matcher/v3/regex.pb.h" diff --git a/source/common/grpc/context_impl.h b/source/common/grpc/context_impl.h index 4729f68b14552..8cc073a46be4e 100644 --- a/source/common/grpc/context_impl.h +++ b/source/common/grpc/context_impl.h @@ -8,7 +8,7 @@ #include "source/common/common/hash.h" #include "source/common/grpc/stat_names.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "absl/types/optional.h" diff --git a/source/common/grpc/stat_names.h b/source/common/grpc/stat_names.h index 13d81c78893ca..2c65f597aeea2 100644 --- a/source/common/grpc/stat_names.h +++ b/source/common/grpc/stat_names.h @@ -2,7 +2,7 @@ #include "envoy/grpc/status.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/container/flat_hash_map.h" diff --git a/source/common/http/codes.h b/source/common/http/codes.h index be8ef2776b08b..d3f78ee16c03f 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -9,7 +9,7 @@ #include "envoy/stats/scope.h" #include "source/common/common/thread.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Http { diff --git a/source/common/http/conn_manager_config.h b/source/common/http/conn_manager_config.h index d0d9294ac63f7..45ceb107d5bf5 100644 --- a/source/common/http/conn_manager_config.h +++ b/source/common/http/conn_manager_config.h @@ -13,7 +13,7 @@ #include "source/common/http/date_provider.h" #include "source/common/local_reply/local_reply.h" #include "source/common/network/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Http { diff --git a/source/common/http/user_agent.cc b/source/common/http/user_agent.cc index 5a68f8fd7569c..cf5e6746f7783 100644 --- a/source/common/http/user_agent.cc +++ b/source/common/http/user_agent.cc @@ -9,7 +9,7 @@ #include "envoy/stats/timespan.h" #include "source/common/http/headers.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/common/http/user_agent.h b/source/common/http/user_agent.h index f4bd4454e9263..8cb482b556c36 100644 --- a/source/common/http/user_agent.h +++ b/source/common/http/user_agent.h @@ -10,7 +10,7 @@ #include "envoy/stats/stats_macros.h" #include "envoy/stats/timespan.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Http { diff --git a/source/common/local_info/local_info_impl.h b/source/common/local_info/local_info_impl.h index 4e1ae360126b7..8f5b3e5fba560 100644 --- a/source/common/local_info/local_info_impl.h +++ b/source/common/local_info/local_info_impl.h @@ -6,7 +6,7 @@ #include "envoy/local_info/local_info.h" #include "source/common/config/context_provider_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace LocalInfo { diff --git a/source/common/memory/heap_shrinker.cc b/source/common/memory/heap_shrinker.cc index 0fe1cac2c20bf..4cc3ea7c51319 100644 --- a/source/common/memory/heap_shrinker.cc +++ b/source/common/memory/heap_shrinker.cc @@ -1,7 +1,7 @@ #include "source/common/memory/heap_shrinker.h" #include "source/common/memory/utils.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/strings/str_cat.h" diff --git a/source/common/quic/quic_stat_names.h b/source/common/quic/quic_stat_names.h index 7385b2ff7905e..19abf6921f846 100644 --- a/source/common/quic/quic_stat_names.h +++ b/source/common/quic/quic_stat_names.h @@ -5,7 +5,7 @@ #include "envoy/stats/scope.h" #include "source/common/common/thread.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "quiche/quic/core/quic_error_codes.h" #include "quiche/quic/core/quic_types.h" @@ -50,7 +50,7 @@ class QuicStatNames { #else -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Quic { diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index d65b2fe04d1c4..3a2c683fd44e1 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -31,7 +31,7 @@ #include "source/common/router/metadatamatchcriteria_impl.h" #include "source/common/router/router_ratelimit.h" #include "source/common/router/tls_context_match_criteria_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/container/node_hash_map.h" #include "absl/types/optional.h" diff --git a/source/common/router/router.h b/source/common/router/router.h index 9c9ae021574c2..1ae8f0197bc3c 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -34,7 +34,7 @@ #include "source/common/router/config_impl.h" #include "source/common/router/context_impl.h" #include "source/common/router/upstream_request.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stream_info/stream_info_impl.h" #include "source/common/upstream/load_balancer_impl.h" diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 9ee94c3e10080..122d5d66ca4a2 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -188,8 +188,8 @@ envoy_cc_library( envoy_cc_library( name = "symbol_table_lib", - srcs = ["symbol_table.cc"], - hdrs = ["symbol_table.h"], + srcs = ["symbol_table_impl.cc"], + hdrs = ["symbol_table_impl.h"], external_deps = ["abseil_base"], deps = [ ":recent_lookups_lib", diff --git a/source/common/stats/allocator_impl.cc b/source/common/stats/allocator_impl.cc index 16ce001fa7a8c..7caf6c1f2e353 100644 --- a/source/common/stats/allocator_impl.cc +++ b/source/common/stats/allocator_impl.cc @@ -15,7 +15,7 @@ #include "source/common/common/utility.h" #include "source/common/stats/metric_impl.h" #include "source/common/stats/stat_merger.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/container/flat_hash_set.h" diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 354d55cdfd307..b3da0eddca393 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -13,7 +13,7 @@ #include "source/common/stats/null_counter.h" #include "source/common/stats/null_gauge.h" #include "source/common/stats/store_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/tag_utility.h" #include "source/common/stats/utility.h" diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 7aa757481385c..7a43ce886e3e5 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -2,7 +2,7 @@ #include "envoy/stats/tag.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index 28d23c6aceb47..e188f1abe7942 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -8,7 +8,7 @@ #include "envoy/stats/tag.h" #include "source/common/common/assert.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 6338b41cc05ef..6cc2dba2740e2 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -2,7 +2,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index bb3d1620489cf..8d4f6ee19cc6d 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -2,7 +2,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/stat_merger.h b/source/common/stats/stat_merger.h index 04bf7abdb977c..3a9fb6c3093fd 100644 --- a/source/common/stats/stat_merger.h +++ b/source/common/stats/stat_merger.h @@ -3,7 +3,7 @@ #include "envoy/stats/store.h" #include "source/common/protobuf/protobuf.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/container/flat_hash_map.h" diff --git a/source/common/stats/stats_matcher_impl.h b/source/common/stats/stats_matcher_impl.h index dcd84b087794e..a0d3a085cc3fb 100644 --- a/source/common/stats/stats_matcher_impl.h +++ b/source/common/stats/stats_matcher_impl.h @@ -8,7 +8,7 @@ #include "source/common/common/matchers.h" #include "source/common/protobuf/protobuf.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/strings/string_view.h" diff --git a/source/common/stats/store_impl.h b/source/common/stats/store_impl.h index 9716985f83003..e4de8fe918b20 100644 --- a/source/common/stats/store_impl.h +++ b/source/common/stats/store_impl.h @@ -3,7 +3,7 @@ #include "envoy/stats/stats.h" #include "envoy/stats/store.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/symbol_table.cc b/source/common/stats/symbol_table_impl.cc similarity index 99% rename from source/common/stats/symbol_table.cc rename to source/common/stats/symbol_table_impl.cc index a18d94187f879..4184b10274763 100644 --- a/source/common/stats/symbol_table.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -1,4 +1,4 @@ -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include #include diff --git a/source/common/stats/symbol_table.h b/source/common/stats/symbol_table_impl.h similarity index 100% rename from source/common/stats/symbol_table.h rename to source/common/stats/symbol_table_impl.h diff --git a/source/common/stats/tag_utility.cc b/source/common/stats/tag_utility.cc index 0ff1fa43597d6..550bdf81180bf 100644 --- a/source/common/stats/tag_utility.cc +++ b/source/common/stats/tag_utility.cc @@ -3,7 +3,7 @@ #include #include "source/common/config/well_known_names.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/tag_utility.h b/source/common/stats/tag_utility.h index 54c36f5c15673..d0961cf0f3e82 100644 --- a/source/common/stats/tag_utility.h +++ b/source/common/stats/tag_utility.h @@ -3,7 +3,7 @@ #include "envoy/stats/symbol_table.h" #include "envoy/stats/tag.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 535ac80e07ab4..e7e973cd6acc7 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -17,7 +17,7 @@ #include "source/common/stats/null_counter.h" #include "source/common/stats/null_gauge.h" #include "source/common/stats/null_text_readout.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "absl/container/flat_hash_map.h" diff --git a/source/common/stats/utility.h b/source/common/stats/utility.h index 01d204ed4e6d2..5ada272e3bb45 100644 --- a/source/common/stats/utility.h +++ b/source/common/stats/utility.h @@ -6,7 +6,7 @@ #include "envoy/stats/stats.h" #include "source/common/common/thread.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/container/inlined_vector.h" #include "absl/strings/string_view.h" diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 7c684fab2ee3c..a393c841e8b3a 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -7,7 +7,7 @@ #include "source/common/common/thread.h" #include "source/common/event/real_time_system.h" #include "source/common/grpc/google_grpc_context.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" #include "source/exe/process_wide.h" diff --git a/source/extensions/common/wasm/stats_handler.h b/source/extensions/common/wasm/stats_handler.h index 01e3e4ae30aaa..ad03f3c118209 100644 --- a/source/extensions/common/wasm/stats_handler.h +++ b/source/extensions/common/wasm/stats_handler.h @@ -8,7 +8,7 @@ #include "envoy/upstream/cluster_manager.h" #include "source/common/common/logger.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/common/wasm/wasm.h b/source/extensions/common/wasm/wasm.h index 04d5338407c1b..cc0de5d908211 100644 --- a/source/extensions/common/wasm/wasm.h +++ b/source/extensions/common/wasm/wasm.h @@ -17,7 +17,7 @@ #include "source/common/common/assert.h" #include "source/common/common/logger.h" #include "source/common/config/datasource.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/version/version.h" #include "source/extensions/common/wasm/context.h" #include "source/extensions/common/wasm/plugin.h" diff --git a/source/extensions/filters/common/ratelimit/stat_names.h b/source/extensions/filters/common/ratelimit/stat_names.h index 65a8b0614d5df..45161fe43d44e 100644 --- a/source/extensions/filters/common/ratelimit/stat_names.h +++ b/source/extensions/filters/common/ratelimit/stat_names.h @@ -1,6 +1,6 @@ #pragma once -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/http/dynamo/dynamo_stats.cc b/source/extensions/filters/http/dynamo/dynamo_stats.cc index 148e9a2ec2afb..ba0e5d9355389 100644 --- a/source/extensions/filters/http/dynamo/dynamo_stats.cc +++ b/source/extensions/filters/http/dynamo/dynamo_stats.cc @@ -5,7 +5,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/http/dynamo/dynamo_request_parser.h" namespace Envoy { diff --git a/source/extensions/filters/http/dynamo/dynamo_stats.h b/source/extensions/filters/http/dynamo/dynamo_stats.h index 772b44c80f01a..78bd72c728035 100644 --- a/source/extensions/filters/http/dynamo/dynamo_stats.h +++ b/source/extensions/filters/http/dynamo/dynamo_stats.h @@ -5,7 +5,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/extensions/filters/http/fault/fault_filter.h b/source/extensions/filters/http/fault/fault_filter.h index 2fe00acb05f87..1d8fea719ca48 100644 --- a/source/extensions/filters/http/fault/fault_filter.h +++ b/source/extensions/filters/http/fault/fault_filter.h @@ -16,7 +16,7 @@ #include "source/common/buffer/watermark_buffer.h" #include "source/common/common/token_bucket_impl.h" #include "source/common/http/header_utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/common/fault/fault_config.h" #include "source/extensions/filters/http/common/stream_rate_limiter.h" diff --git a/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc b/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc index 2e0fe1bd76d73..3e604ddf1f4f4 100644 --- a/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc +++ b/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc @@ -8,7 +8,7 @@ #include "source/common/grpc/common.h" #include "source/common/grpc/context_impl.h" #include "source/common/runtime/runtime_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stream_info/utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" 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 a7db4da855284..3753122a3dff5 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h @@ -14,7 +14,7 @@ #include "source/common/network/cidr_range.h" #include "source/common/network/lc_trie.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Extensions { 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 f4ce6b5192917..fd80c5ac850c5 100644 --- a/source/extensions/filters/network/common/redis/redis_command_stats.h +++ b/source/extensions/filters/network/common/redis/redis_command_stats.h @@ -6,7 +6,7 @@ #include "envoy/stats/scope.h" #include "envoy/stats/timespan.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/network/common/redis/codec.h" namespace Envoy { diff --git a/source/extensions/filters/network/mongo_proxy/mongo_stats.cc b/source/extensions/filters/network/mongo_proxy/mongo_stats.cc index 95172aa762e3e..fd362d35af42b 100644 --- a/source/extensions/filters/network/mongo_proxy/mongo_stats.cc +++ b/source/extensions/filters/network/mongo_proxy/mongo_stats.cc @@ -6,7 +6,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/network/mongo_proxy/mongo_stats.h b/source/extensions/filters/network/mongo_proxy/mongo_stats.h index 127451e5cc29a..5dee147145997 100644 --- a/source/extensions/filters/network/mongo_proxy/mongo_stats.h +++ b/source/extensions/filters/network/mongo_proxy/mongo_stats.h @@ -6,7 +6,7 @@ #include "envoy/stats/scope.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" namespace Envoy { diff --git a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h index 75fb2260e2cae..d796044555e54 100644 --- a/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h +++ b/source/extensions/filters/network/thrift_proxy/filters/ratelimit/ratelimit.h @@ -9,7 +9,7 @@ #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/common/ratelimit/ratelimit.h" #include "source/extensions/filters/common/ratelimit/stat_names.h" #include "source/extensions/filters/network/thrift_proxy/filters/pass_through_filter.h" diff --git a/source/extensions/filters/network/zookeeper_proxy/filter.h b/source/extensions/filters/network/zookeeper_proxy/filter.h index 33c0af256d8fb..46c4f0ea8da31 100644 --- a/source/extensions/filters/network/zookeeper_proxy/filter.h +++ b/source/extensions/filters/network/zookeeper_proxy/filter.h @@ -12,7 +12,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/network/zookeeper_proxy/decoder.h" namespace Envoy { diff --git a/source/extensions/stat_sinks/common/statsd/statsd.cc b/source/extensions/stat_sinks/common/statsd/statsd.cc index e661e326b2c4d..a69733b87b589 100644 --- a/source/extensions/stat_sinks/common/statsd/statsd.cc +++ b/source/extensions/stat_sinks/common/statsd/statsd.cc @@ -19,7 +19,7 @@ #include "source/common/config/utility.h" #include "source/common/network/socket_interface.h" #include "source/common/network/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/strings/str_join.h" diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index ab2502d762eea..97c5b2b2e7f54 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -9,7 +9,7 @@ #include "envoy/stats/histogram.h" #include "envoy/stats/sink.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/tracers/lightstep/lightstep_tracer_impl.h b/source/extensions/tracers/lightstep/lightstep_tracer_impl.h index ad482e3510560..6c7c131245a7f 100644 --- a/source/extensions/tracers/lightstep/lightstep_tracer_impl.h +++ b/source/extensions/tracers/lightstep/lightstep_tracer_impl.h @@ -15,7 +15,7 @@ #include "source/common/http/message_impl.h" #include "source/common/json/json_loader.h" #include "source/common/protobuf/protobuf.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/upstream/cluster_update_tracker.h" #include "source/extensions/tracers/common/ot/opentracing_driver_impl.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h b/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h index 5e0f589b4890c..efec7cb3aa4e3 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h @@ -14,7 +14,7 @@ #include "envoy/ssl/ssl_socket_extended_info.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/stats.h" #include "openssl/ssl.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc index 3b28200b73abc..93bbe8b17a041 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc @@ -22,7 +22,7 @@ #include "source/common/network/address_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/runtime/runtime_features.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/cert_validator/factory.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h index 2cea2782f0349..64f52bc38728f 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h @@ -17,7 +17,7 @@ #include "source/common/common/logger.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "source/extensions/transport_sockets/tls/stats.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index 00bd8f86eedca..c3b8d338fcae3 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -10,7 +10,7 @@ #include "source/common/config/datasource.h" #include "source/common/config/utility.h" #include "source/common/protobuf/message_validator_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/cert_validator/factory.h" #include "source/extensions/transport_sockets/tls/cert_validator/utility.h" #include "source/extensions/transport_sockets/tls/stats.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h index 8fe7a213e0de0..669d77aec7d05 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -15,7 +15,7 @@ #include "source/common/common/c_smart_ptr.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "source/extensions/transport_sockets/tls/stats.h" diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index 8b607018d88cc..0107a88ec6fac 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -15,7 +15,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/matchers.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" #include "source/extensions/transport_sockets/tls/context_manager_impl.h" #include "source/extensions/transport_sockets/tls/ocsp/ocsp.h" diff --git a/source/extensions/watchdog/profile_action/profile_action.cc b/source/extensions/watchdog/profile_action/profile_action.cc index f010bcd007922..3f0e556a3cdeb 100644 --- a/source/extensions/watchdog/profile_action/profile_action.cc +++ b/source/extensions/watchdog/profile_action/profile_action.cc @@ -6,7 +6,7 @@ #include "source/common/profiler/profiler.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/strings/str_format.h" diff --git a/source/server/guarddog_impl.cc b/source/server/guarddog_impl.cc index 13373b080cb53..cfc8256d54200 100644 --- a/source/server/guarddog_impl.cc +++ b/source/server/guarddog_impl.cc @@ -21,7 +21,7 @@ #include "source/common/common/logger.h" #include "source/common/config/utility.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/server/watchdog_impl.h" #include "absl/synchronization/mutex.h" diff --git a/source/server/hot_restarting_parent.cc b/source/server/hot_restarting_parent.cc index b18af67b97be6..fab51c4fc7b53 100644 --- a/source/server/hot_restarting_parent.cc +++ b/source/server/hot_restarting_parent.cc @@ -5,7 +5,7 @@ #include "source/common/memory/stats.h" #include "source/common/network/utility.h" #include "source/common/stats/stat_merger.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "source/server/listener_impl.h" diff --git a/source/server/overload_manager_impl.cc b/source/server/overload_manager_impl.cc index 34a9c2b45df18..4a274c145e1ba 100644 --- a/source/server/overload_manager_impl.cc +++ b/source/server/overload_manager_impl.cc @@ -11,7 +11,7 @@ #include "source/common/config/utility.h" #include "source/common/event/scaled_range_timer_manager_impl.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/server/resource_monitor_config_impl.h" #include "absl/container/node_hash_map.h" diff --git a/test/common/grpc/context_impl_test.cc b/test/common/grpc/context_impl_test.cc index acc6bc1ada4c4..f95307ef42787 100644 --- a/test/common/grpc/context_impl_test.cc +++ b/test/common/grpc/context_impl_test.cc @@ -5,7 +5,7 @@ #include "source/common/http/headers.h" #include "source/common/http/message_impl.h" #include "source/common/http/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "test/mocks/upstream/cluster_info.h" diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index f03caff22fecc..99808db5ab8b1 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -23,7 +23,7 @@ #include "source/common/network/connection_impl.h" #include "source/common/network/raw_buffer_socket.h" #include "source/common/router/context_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/context_config_impl.h" #include "source/extensions/transport_sockets/tls/ssl_socket.h" diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index aba98a08dd658..fc1d5f4ffac3d 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -10,7 +10,7 @@ #include "source/common/http/codes.h" #include "source/common/stats/isolated_store_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "benchmark/benchmark.h" diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 26513bd84f145..22e16577b110f 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -143,8 +143,8 @@ envoy_cc_test( ) envoy_cc_test( - name = "symbol_table_test", - srcs = ["symbol_table_test.cc"], + name = "symbol_table_impl_test", + srcs = ["symbol_table_impl_test.cc"], external_deps = ["abseil_hash_testing"], deps = [ ":stat_test_utility_lib", diff --git a/test/common/stats/symbol_table_fuzz_test.cc b/test/common/stats/symbol_table_fuzz_test.cc index 48c65300c948e..cab1bda492c27 100644 --- a/test/common/stats/symbol_table_fuzz_test.cc +++ b/test/common/stats/symbol_table_fuzz_test.cc @@ -2,7 +2,7 @@ #include "source/common/common/assert.h" #include "source/common/common/base64.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "test/common/stats/stat_test_utility.h" #include "test/fuzz/fuzz_runner.h" diff --git a/test/common/stats/symbol_table_test.cc b/test/common/stats/symbol_table_impl_test.cc similarity index 99% rename from test/common/stats/symbol_table_test.cc rename to test/common/stats/symbol_table_impl_test.cc index f12f47b001545..f5af9675bc06d 100644 --- a/test/common/stats/symbol_table_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -3,7 +3,7 @@ #include "source/common/common/macros.h" #include "source/common/common/mutex_tracer_impl.h" #include "source/common/memory/stats.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "test/common/stats/stat_test_utility.h" #include "test/test_common/logging.h" diff --git a/test/common/stats/symbol_table_speed_test.cc b/test/common/stats/symbol_table_speed_test.cc index 49e835bac4d6e..e62ff55da1082 100644 --- a/test/common/stats/symbol_table_speed_test.cc +++ b/test/common/stats/symbol_table_speed_test.cc @@ -7,7 +7,7 @@ #include "source/common/common/logger.h" #include "source/common/common/thread.h" #include "source/common/stats/isolated_store_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/utility.h" #include "test/common/stats/make_elements_helper.h" diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 5ea5e73b6e483..620af46c698ab 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -8,7 +8,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/stats/allocator_impl.h" #include "source/common/stats/stats_matcher_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/tag_producer_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 963c689b0a804..edb0faa12d40f 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -12,7 +12,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/memory/stats.h" #include "source/common/stats/stats_matcher_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/tag_producer_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" diff --git a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc index a1b82e84016d1..45045ed8a0c47 100644 --- a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc +++ b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc @@ -4,7 +4,7 @@ #include "source/common/grpc/codec.h" #include "source/common/grpc/common.h" #include "source/common/http/header_map_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/http/grpc_http1_bridge/config.h" #include "source/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter.h" diff --git a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc index 5d22c3ae37e5f..561484f56d0d4 100644 --- a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc +++ b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc @@ -8,7 +8,7 @@ #include "source/common/http/codes.h" #include "source/common/http/header_map_impl.h" #include "source/common/http/headers.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/http/grpc_web/grpc_web_filter.h" #include "test/mocks/http/mocks.h" diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 162e8534be470..5fce7e07e4d58 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -11,7 +11,7 @@ #include "source/common/http/headers.h" #include "source/common/http/message_impl.h" #include "source/common/runtime/runtime_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/tracing/http_tracer_impl.h" #include "source/extensions/tracers/lightstep/lightstep_tracer_impl.h" diff --git a/test/integration/filters/eds_ready_filter.cc b/test/integration/filters/eds_ready_filter.cc index 10fa6c2136b04..4c78e2e5fa43d 100644 --- a/test/integration/filters/eds_ready_filter.cc +++ b/test/integration/filters/eds_ready_filter.cc @@ -6,7 +6,7 @@ #include "envoy/registry/registry.h" #include "envoy/server/filter_config.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "test/extensions/filters/http/common/empty_http_filter_config.h" diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index ef9a74b61311f..80cc62719e1ed 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -30,7 +30,7 @@ #include "envoy/type/v3/percent.pb.h" #include "envoy/upstream/cluster_manager.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/global.h" diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index b9967e58c3e17..c208614218726 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -6,7 +6,7 @@ #include "source/common/http/context_impl.h" #include "source/common/quic/quic_stat_names.h" #include "source/common/router/context_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/context_manager_impl.h" #include "test/mocks/access_log/mocks.h" diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 9de8bd4f6f8bd..6ed277eed2fd1 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -2,7 +2,7 @@ #include -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 6bd20eec8bf9c..42837995662a5 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -18,7 +18,7 @@ #include "source/common/stats/histogram_impl.h" #include "source/common/stats/isolated_store_impl.h" #include "source/common/stats/store_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/timespan_impl.h" #include "test/common/stats/stat_test_utility.h" diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index edf02aab76f8d..21eea638773fc 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -9,7 +9,7 @@ #include "envoy/data/cluster/v3/outlier_detection_event.pb.h" #include "envoy/upstream/upstream.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "test/mocks/network/transport_socket.h" #include "test/mocks/upstream/cluster_info.h" diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 2b657d2d2b01b..545fcbe17edb5 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -24,7 +24,7 @@ #include "source/common/http/header_map_impl.h" #include "source/common/protobuf/message_validator_impl.h" #include "source/common/protobuf/utility.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "test/test_common/file_system_for_test.h" #include "test/test_common/logging.h" diff --git a/test/tools/router_check/router.h b/test/tools/router_check/router.h index 1ab9ae97bf0c2..3624efcb9cc4f 100644 --- a/test/tools/router_check/router.h +++ b/test/tools/router_check/router.h @@ -12,7 +12,7 @@ #include "source/common/http/headers.h" #include "source/common/json/json_loader.h" #include "source/common/router/config_impl.h" -#include "source/common/stats/symbol_table.h" +#include "source/common/stats/symbol_table_impl.h" #include "source/common/stream_info/stream_info_impl.h" #include "test/mocks/server/instance.h" From 82d32b9c19a20363778687bcec4fd08ad1a0319f Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 17:17:48 -0500 Subject: [PATCH 18/74] back out accidental change. Signed-off-by: Joshua Marantz --- envoy/stats/symbol_table.h | 243 +-------------------------- source/server/admin/admin_filter.cc | 3 +- source/server/admin/stats_handler.cc | 2 + 3 files changed, 12 insertions(+), 236 deletions(-) diff --git a/envoy/stats/symbol_table.h b/envoy/stats/symbol_table.h index 1d642b3fe8a08..39ac815d0e32c 100644 --- a/envoy/stats/symbol_table.h +++ b/envoy/stats/symbol_table.h @@ -1,249 +1,22 @@ #pragma once -#include -#include -#include -#include - -#include "envoy/common/pure.h" - -#include "absl/container/inlined_vector.h" -#include "absl/strings/string_view.h" - namespace Envoy { namespace Stats { -/** - * Runtime representation of an encoded stat name. This is predeclared only in - * the interface without abstract methods, because (a) the underlying class - * representation is common to both implementations of SymbolTable, and (b) - * we do not want or need the overhead of a vptr per StatName. The common - * declaration for StatName is in source/common/stats/symbol_table_impl.h - */ -class StatName; -using StatNameVec = absl::InlinedVector; - -class StatNameList; -class StatNameSet; - -using StatNameSetPtr = std::unique_ptr; +// Forward declarations for the symbol table classes. See +// source/common/stats/symbol_table_impl.h" for the class definitions. +// +// TODO(jmarantz): remove this file and put the forward declarations into stats.h. /** - * Holds a range of indexes indicating which parts of a stat-name are - * dynamic. This is used to transfer stats from hot-restart parent to child, - * retaining the same name structure. + * Runtime representation of an encoded stat name. */ -using DynamicSpan = std::pair; -using DynamicSpans = std::vector; +class StatName; /** - * SymbolTable manages a namespace optimized for stat names, exploiting their - * typical composition from "."-separated tokens, with a significant overlap - * between the tokens. The interface is designed to balance optimal storage - * at scale with hiding details from users. We seek to provide the most abstract - * interface possible that avoids adding per-stat overhead or taking locks in - * the hot path. + * Holds a set of symbols used to compose hierarhical names. */ -class SymbolTable { -public: - /** - * Efficient byte-encoded storage of an array of tokens. The most common - * tokens are typically < 127, and are represented directly. tokens >= 128 - * spill into the next byte, allowing for tokens of arbitrary numeric value to - * be stored. As long as the most common tokens are low-valued, the - * representation is space-efficient. This scheme is similar to UTF-8. The - * token ordering is dependent on the order in which stat-names are encoded - * into the SymbolTable, which will not be optimal, but in practice appears - * to be pretty good. - * - * This is exposed in the interface for the benefit of join(), which is - * used in the hot-path to append two stat-names into a temp without taking - * locks. This is used then in thread-local cache lookup, so that once warm, - * no locks are taken when looking up stats. - */ - using Storage = uint8_t[]; - using StoragePtr = std::unique_ptr; - - virtual ~SymbolTable() = default; - - /** - * @return uint64_t the number of symbols in the symbol table. - */ - virtual uint64_t numSymbols() const PURE; - - /** - * 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, - * since this should never happen, and we don't want to continue running - * with a corrupt stats set. - * - * @param stat_name the stat name. - * @return std::string stringified stat_name. - */ - virtual std::string toString(const StatName& stat_name) const PURE; - - /** - * Determines whether one StatName lexically precedes another. Note that - * the lexical order may not exactly match the lexical order of the - * elaborated strings. For example, stat-name of "-.-" would lexically - * sort after "---" but when encoded as a StatName would come lexically - * earlier. In practice this is unlikely to matter as those are not - * reasonable names for Envoy stats. - * - * Note that this operation has to be performed with the context of the - * SymbolTable so that the individual Symbol objects can be converted - * into strings for lexical comparison. - * - * @param a the first stat name - * @param b the second stat name - * @return bool true if a lexically precedes b. - */ - virtual bool lessThan(const StatName& a, const StatName& b) const PURE; - virtual bool lessThanLockHeld(const StatName& a, const StatName& b) const PURE; - - /** - * Joins two or more StatNames. For example if we have StatNames for {"a.b", - * "c.d", "e.f"} then the joined stat-name matches "a.b.c.d.e.f". The - * advantage of using this representation is that it avoids having to - * decode/encode into the elaborated form, and does not require locking the - * SymbolTable. - * - * Note that this method does not bump reference counts on the referenced - * Symbols in the SymbolTable, so it's only valid as long for the lifetime of - * the joined StatNames. - * - * This is intended for use doing cached name lookups of scoped stats, where - * the scope prefix and the names to combine it with are already in StatName - * form. Using this class, they can be combined without accessing the - * SymbolTable or, in particular, taking its lock. - * - * @param stat_names the names to join. - * @return Storage allocated for the joined name. - */ - virtual StoragePtr join(const StatNameVec& stat_names) const PURE; - - /** - * Populates a StatNameList from a list of encodings. This is not done at - * construction time to enable StatNameList to be instantiated directly in - * a class that doesn't have a live SymbolTable when it is constructed. - * - * @param names A pointer to the first name in an array, allocated by the caller. - * @param num_names The number of names. - * @param list The StatNameList representing the stat names. - */ - virtual void populateList(const StatName* names, uint32_t num_names, StatNameList& list) PURE; - -#ifndef ENVOY_CONFIG_COVERAGE - virtual void debugPrint() const PURE; -#endif - - using RecentLookupsFn = std::function; - - /** - * Calls the provided function with the name of the most recently looked-up - * 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. - */ - virtual uint64_t getRecentLookups(const RecentLookupsFn& iter) const PURE; - - /** - * Clears the recent-lookups structures. - */ - virtual void clearRecentLookups() PURE; - - /** - * 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; - - /** - * Creates a StatNameSet. - * - * @param name the name of the set. - * @return the set. - */ - virtual StatNameSetPtr makeSet(absl::string_view name) PURE; - - /** - * Identifies the dynamic components of a stat_name into an array of integer - * pairs, indicating the begin/end of spans of tokens in the stat-name that - * are created from StatNameDynamicStore or StatNameDynamicPool. - * - * This can be used to reconstruct the same exact StatNames in - * StatNames::mergeStats(), to enable stat continuity across hot-restart. - * - * @param stat_name the input stat name. - * @return the array of pairs indicating the bounds. - */ - virtual DynamicSpans getDynamicSpans(StatName stat_name) const PURE; - - /** - * Calls a function with the symbol table's lock held. This is needed - * for sortByStatName to avoid taking a lock on each comparison. - * - * TODO(jmarantz): This indirection can likely be removed once SymbolTable - * is changed from an interface to a concrete implementation. The interface - * was only longer needed during construction to allow for a fake symbol - * table implementation to be used by default and controlled by flag. - * - * @param fn a function to be called once the lock ahs been acquired. - */ - virtual void withLockHeld(std::function fn) const PURE; - -private: - friend struct HeapStatData; - friend class StatNameDynamicStorage; - 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 - // in order to manage the reference-counted symbols they own. - - /** - * Since SymbolTable does manual reference counting, a client of SymbolTable - * must manually call free(symbol_vec) when it is freeing the backing store - * for a StatName. This way, the symbol table will grow and shrink - * dynamically, instead of being write-only. - * - * @param stat_name the stat name. - */ - virtual void free(const StatName& stat_name) PURE; - - /** - * StatName backing-store can be managed by callers in a variety of ways - * to minimize overhead. But any persistent reference to a StatName needs - * to hold onto its own reference-counts for all symbols. This method - * helps callers ensure the symbol-storage is maintained for the lifetime - * of a reference. - * - * @param stat_name the stat name. - */ - virtual void incRefCount(const StatName& stat_name) PURE; - - /** - * Encodes 'name' into the symbol table. Bumps reference counts for referenced - * symbols. The caller must manage the storage, and is responsible for calling - * SymbolTable::free() to release the reference counts. - * - * @param name The name to encode. - * @return The encoded name, transferring ownership to the caller. - * - */ - virtual StoragePtr encode(absl::string_view name) PURE; - - virtual StoragePtr makeDynamicStorage(absl::string_view name) PURE; -}; - -using SymbolTablePtr = std::unique_ptr; +class SymbolTable; } // namespace Stats } // namespace Envoy diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index e2fa3685cec87..1dc694c151a51 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -68,7 +68,7 @@ void AdminFilter::onComplete() { Buffer::OwnedImpl response; auto header_map = Http::ResponseHeaderMapImpl::create(); RELEASE_ASSERT(request_headers_, ""); - for (bool cont = true, first = true; cont; first = false) { + for (bool cont = true, first = true; cont;) { // TODO(jmarantz): admin_server-callback_func_ is only going to access // header_map on the first iteration. This clang-tidy suppression can be // cleaned up once the handler mechanism provides a separate "nextChunk" @@ -81,6 +81,7 @@ void AdminFilter::onComplete() { Utility::populateFallbackResponseHeaders(cont ? Http::Code::OK : code, *header_map); decoder_callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); + first = false; } if (response.length() > 0) { // ENVOY_LOG_MISC(error, "Chunking out {} bytes cont={}", response.length(), cont); diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index ba1e930e7b008..de813f940b977 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -76,6 +76,8 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { if (server_.statsConfig().flushOnAdmin()) { + ENVOY_LOG_MISC(error, "flushing on admin"); + abort(); server_.flushStats(); } From aa0d5a019476b95fa31462f31cd004bcf41a3014 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 17:50:47 -0500 Subject: [PATCH 19/74] back out more accidental commits. Signed-off-by: Joshua Marantz --- source/common/stats/allocator_impl.h | 1 - source/common/stats/symbol_table_impl.cc | 87 +++--- source/common/stats/symbol_table_impl.h | 313 ++++++++++++++----- source/server/admin/stats_handler.cc | 2 - test/common/stats/symbol_table_impl_test.cc | 4 +- test/common/stats/symbol_table_speed_test.cc | 3 +- 6 files changed, 277 insertions(+), 133 deletions(-) diff --git a/source/common/stats/allocator_impl.h b/source/common/stats/allocator_impl.h index 86a1e1aa664cf..5348cb46bad6d 100644 --- a/source/common/stats/allocator_impl.h +++ b/source/common/stats/allocator_impl.h @@ -6,7 +6,6 @@ #include "envoy/stats/allocator.h" #include "envoy/stats/sink.h" #include "envoy/stats/stats.h" -#include "envoy/stats/symbol_table.h" #include "source/common/common/thread_synchronizer.h" #include "source/common/stats/metric_impl.h" diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 4184b10274763..a2b2d00fd3a60 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -35,7 +35,7 @@ size_t StatName::dataSize() const { if (size_and_data_ == nullptr) { return 0; } - return SymbolTableImpl::Encoding::decodeNumber(size_and_data_).first; + return SymbolTable::Encoding::decodeNumber(size_and_data_).first; } #ifndef ENVOY_CONFIG_COVERAGE @@ -51,7 +51,7 @@ void StatName::debugPrint() { for (size_t i = 0; i < nbytes; ++i) { std::cerr << " " << static_cast(data()[i]); } - const SymbolVec encoding = SymbolTableImpl::Encoding::decodeSymbols(*this); + const SymbolVec encoding = SymbolTable::Encoding::decodeSymbols(*this); std::cerr << ", numSymbols=" << encoding.size() << ":"; for (Symbol symbol : encoding) { std::cerr << " " << symbol; @@ -61,13 +61,13 @@ void StatName::debugPrint() { } #endif -SymbolTableImpl::Encoding::~Encoding() { +SymbolTable::Encoding::~Encoding() { // Verifies that moveToMemBlock() was called on this encoding. Failure // to call moveToMemBlock() will result in leaks symbols. ASSERT(mem_block_.capacity() == 0); } -size_t SymbolTableImpl::Encoding::encodingSizeBytes(uint64_t number) { +size_t SymbolTable::Encoding::encodingSizeBytes(uint64_t number) { size_t num_bytes = 0; do { ++num_bytes; @@ -76,8 +76,7 @@ size_t SymbolTableImpl::Encoding::encodingSizeBytes(uint64_t number) { return num_bytes; } -void SymbolTableImpl::Encoding::appendEncoding(uint64_t number, - MemBlockBuilder& mem_block) { +void SymbolTable::Encoding::appendEncoding(uint64_t number, MemBlockBuilder& mem_block) { // UTF-8-like encoding where a value 127 or less gets written as a single // byte. For higher values we write the low-order 7 bits with a 1 in // the high-order bit. Then we right-shift 7 bits and keep adding more bytes @@ -95,7 +94,7 @@ void SymbolTableImpl::Encoding::appendEncoding(uint64_t number, } while (number != 0); } -void SymbolTableImpl::Encoding::addSymbols(const std::vector& symbols) { +void SymbolTable::Encoding::addSymbols(const std::vector& symbols) { ASSERT(data_bytes_required_ == 0); for (Symbol symbol : symbols) { data_bytes_required_ += encodingSizeBytes(symbol); @@ -106,7 +105,7 @@ void SymbolTableImpl::Encoding::addSymbols(const std::vector& symbols) { } } -std::pair SymbolTableImpl::Encoding::decodeNumber(const uint8_t* encoding) { +std::pair SymbolTable::Encoding::decodeNumber(const uint8_t* encoding) { uint64_t number = 0; uint64_t uc = SpilloverMask; const uint8_t* start = encoding; @@ -117,7 +116,7 @@ std::pair SymbolTableImpl::Encoding::decodeNumber(const uint8_ return std::make_pair(number, encoding - start); } -SymbolVec SymbolTableImpl::Encoding::decodeSymbols(StatName stat_name) { +SymbolVec SymbolTable::Encoding::decodeSymbols(StatName stat_name) { SymbolVec symbol_vec; symbol_vec.reserve(stat_name.dataSize()); decodeTokens( @@ -137,7 +136,7 @@ SymbolVec SymbolTableImpl::Encoding::decodeSymbols(StatName stat_name) { // The array-version of decodeSymbols is still a good approach for converting a // StatName to a string as the intermediate array of string_view is needed to // allocate the right size for the joined string. -class SymbolTableImpl::Encoding::TokenIter { +class SymbolTable::Encoding::TokenIter { public: // The type of token reached. enum class TokenType { StringView, Symbol, End }; @@ -208,7 +207,7 @@ class SymbolTableImpl::Encoding::TokenIter { #endif }; -void SymbolTableImpl::Encoding::decodeTokens( +void SymbolTable::Encoding::decodeTokens( StatName stat_name, const std::function& symbol_token_fn, const std::function& string_view_token_fn) { TokenIter iter(stat_name); @@ -224,7 +223,7 @@ void SymbolTableImpl::Encoding::decodeTokens( } bool StatName::startsWith(StatName prefix) const { - using TokenIter = SymbolTableImpl::Encoding::TokenIter; + using TokenIter = SymbolTable::Encoding::TokenIter; TokenIter prefix_iter(prefix); TokenIter this_iter(*this); while (true) { @@ -250,7 +249,7 @@ bool StatName::startsWith(StatName prefix) const { return true; // not reached } -std::vector SymbolTableImpl::decodeStrings(StatName stat_name) const { +std::vector SymbolTable::decodeStrings(StatName stat_name) const { std::vector strings; Thread::LockGuard lock(lock_); Encoding::decodeTokens( @@ -261,14 +260,14 @@ std::vector SymbolTableImpl::decodeStrings(StatName stat_name return strings; } -void SymbolTableImpl::Encoding::moveToMemBlock(MemBlockBuilder& mem_block) { +void SymbolTable::Encoding::moveToMemBlock(MemBlockBuilder& mem_block) { appendEncoding(data_bytes_required_, mem_block); mem_block.appendBlock(mem_block_); mem_block_.reset(); // Logically transfer ownership, enabling empty assert on destruct. } -void SymbolTableImpl::Encoding::appendToMemBlock(StatName stat_name, - MemBlockBuilder& mem_block) { +void SymbolTable::Encoding::appendToMemBlock(StatName stat_name, + MemBlockBuilder& mem_block) { const uint8_t* data = stat_name.dataIncludingSize(); if (data == nullptr) { mem_block.appendOne(0); @@ -277,11 +276,11 @@ void SymbolTableImpl::Encoding::appendToMemBlock(StatName stat_name, } } -SymbolTableImpl::SymbolTableImpl() +SymbolTable::SymbolTable() // Have to be explicitly initialized, if we want to use the ABSL_GUARDED_BY macro. : next_symbol_(FirstValidSymbol), monotonic_counter_(FirstValidSymbol) {} -SymbolTableImpl::~SymbolTableImpl() { +SymbolTable::~SymbolTable() { // To avoid leaks into the symbol table, we expect all StatNames to be freed. // Note: this could potentially be short-circuited if we decide a fast exit // is needed in production. But it would be good to ensure clean up during @@ -292,7 +291,7 @@ SymbolTableImpl::~SymbolTableImpl() { // TODO(ambuc): There is a possible performance optimization here for avoiding // the encoding of IPs / numbers if they appear in stat names. We don't want to // waste time symbolizing an integer as an integer, if we can help it. -void SymbolTableImpl::addTokensToEncoding(const absl::string_view name, Encoding& encoding) { +void SymbolTable::addTokensToEncoding(const absl::string_view name, Encoding& encoding) { if (name.empty()) { return; } @@ -321,17 +320,17 @@ void SymbolTableImpl::addTokensToEncoding(const absl::string_view name, Encoding encoding.addSymbols(symbols); } -uint64_t SymbolTableImpl::numSymbols() const { +uint64_t SymbolTable::numSymbols() const { Thread::LockGuard lock(lock_); ASSERT(encode_map_.size() == decode_map_.size()); return encode_map_.size(); } -std::string SymbolTableImpl::toString(const StatName& stat_name) const { +std::string SymbolTable::toString(const StatName& stat_name) const { return absl::StrJoin(decodeStrings(stat_name), "."); } -void SymbolTableImpl::incRefCount(const StatName& stat_name) { +void SymbolTable::incRefCount(const StatName& stat_name) { // Before taking the lock, decode the array of symbols from the SymbolTable::Storage. const SymbolVec symbols = Encoding::decodeSymbols(stat_name); @@ -353,7 +352,7 @@ void SymbolTableImpl::incRefCount(const StatName& stat_name) { } } -void SymbolTableImpl::free(const StatName& stat_name) { +void SymbolTable::free(const StatName& stat_name) { // Before taking the lock, decode the array of symbols from the SymbolTable::Storage. const SymbolVec symbols = Encoding::decodeSymbols(stat_name); @@ -379,7 +378,7 @@ void SymbolTableImpl::free(const StatName& stat_name) { } } -uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) const { +uint64_t SymbolTable::getRecentLookups(const RecentLookupsFn& iter) const { uint64_t total = 0; absl::flat_hash_map name_count_map; @@ -409,7 +408,7 @@ uint64_t SymbolTableImpl::getRecentLookups(const RecentLookupsFn& iter) const { return total; } -DynamicSpans SymbolTableImpl::getDynamicSpans(StatName stat_name) const { +DynamicSpans SymbolTable::getDynamicSpans(StatName stat_name) const { DynamicSpans dynamic_spans; uint32_t index = 0; @@ -435,28 +434,28 @@ DynamicSpans SymbolTableImpl::getDynamicSpans(StatName stat_name) const { return dynamic_spans; } -void SymbolTableImpl::setRecentLookupCapacity(uint64_t capacity) { +void SymbolTable::setRecentLookupCapacity(uint64_t capacity) { Thread::LockGuard lock(lock_); recent_lookups_.setCapacity(capacity); } -void SymbolTableImpl::clearRecentLookups() { +void SymbolTable::clearRecentLookups() { Thread::LockGuard lock(lock_); recent_lookups_.clear(); } -uint64_t SymbolTableImpl::recentLookupCapacity() const { +uint64_t SymbolTable::recentLookupCapacity() const { Thread::LockGuard lock(lock_); return recent_lookups_.capacity(); } -StatNameSetPtr SymbolTableImpl::makeSet(absl::string_view name) { - // make_unique does not work with private ctor, even though SymbolTableImpl is a friend. +StatNameSetPtr SymbolTable::makeSet(absl::string_view name) { + // make_unique does not work with private ctor, even though SymbolTable is a friend. StatNameSetPtr stat_name_set(new StatNameSet(*this, name)); return stat_name_set; } -Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { +Symbol SymbolTable::toSymbol(absl::string_view sv) { Symbol result; auto encode_find = encode_map_.find(sv); // If the string segment doesn't already exist, @@ -482,14 +481,14 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { return result; } -absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const +absl::string_view SymbolTable::fromSymbol(const Symbol symbol) const ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_) { auto search = decode_map_.find(symbol); RELEASE_ASSERT(search != decode_map_.end(), "no such symbol"); return search->second->toStringView(); } -void SymbolTableImpl::newSymbol() ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_) { +void SymbolTable::newSymbol() ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_) { if (pool_.empty()) { next_symbol_ = ++monotonic_counter_; } else { @@ -500,7 +499,7 @@ void SymbolTableImpl::newSymbol() ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_) { ASSERT(monotonic_counter_ != 0); } -bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { +bool SymbolTable::lessThan(const StatName& a, const StatName& b) const { // Proactively take the table lock in anticipation that we'll need to // convert at least one symbol to a string_view, and it's easier not to // bother to lazily take the lock. @@ -508,7 +507,7 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { return lessThanLockHeld(a, b); } -bool SymbolTableImpl::lessThanLockHeld(const StatName& a, const StatName& b) const +bool SymbolTable::lessThanLockHeld(const StatName& a, const StatName& b) const ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_) { Encoding::TokenIter a_iter(a), b_iter(b); while (true) { @@ -538,7 +537,7 @@ bool SymbolTableImpl::lessThanLockHeld(const StatName& a, const StatName& b) con } #ifndef ENVOY_CONFIG_COVERAGE -void SymbolTableImpl::debugPrint() const { +void SymbolTable::debugPrint() const { Thread::LockGuard lock(lock_); std::vector symbols; for (const auto& p : decode_map_) { @@ -553,7 +552,7 @@ void SymbolTableImpl::debugPrint() const { } #endif -SymbolTable::StoragePtr SymbolTableImpl::encode(absl::string_view name) { +SymbolTable::StoragePtr SymbolTable::encode(absl::string_view name) { name = StringUtil::removeTrailingCharacters(name, '.'); Encoding encoding; addTokensToEncoding(name, encoding); @@ -573,7 +572,7 @@ StatNameStorage::StatNameStorage(StatName src, SymbolTable& table) { table.incRefCount(statName()); } -SymbolTable::StoragePtr SymbolTableImpl::makeDynamicStorage(absl::string_view name) { +SymbolTable::StoragePtr SymbolTable::makeDynamicStorage(absl::string_view name) { name = StringUtil::removeTrailingCharacters(name, '.'); // For all StatName objects, we first have the total number of bytes in the @@ -587,16 +586,16 @@ SymbolTable::StoragePtr SymbolTableImpl::makeDynamicStorage(absl::string_view na // payload_bytes is the total number of bytes needed to represent the // characters in name, plus their encoded size, plus the literal indicator. - const size_t payload_bytes = SymbolTableImpl::Encoding::totalSizeBytes(name.size()) + 1; + const size_t payload_bytes = Encoding::totalSizeBytes(name.size()) + 1; // total_bytes includes the payload_bytes, plus the LiteralStringIndicator, and // the length of those. - const size_t total_bytes = SymbolTableImpl::Encoding::totalSizeBytes(payload_bytes); + const size_t total_bytes = Encoding::totalSizeBytes(payload_bytes); MemBlockBuilder mem_block(total_bytes); - SymbolTableImpl::Encoding::appendEncoding(payload_bytes, mem_block); + Encoding::appendEncoding(payload_bytes, mem_block); mem_block.appendOne(LiteralStringIndicator); - SymbolTableImpl::Encoding::appendEncoding(name.size(), mem_block); + Encoding::appendEncoding(name.size(), mem_block); mem_block.appendData(absl::MakeSpan(reinterpret_cast(name.data()), name.size())); ASSERT(mem_block.capacityRemaining() == 0); return mem_block.release(); @@ -664,7 +663,7 @@ void StatNameStorageSet::free(SymbolTable& symbol_table) { } } -SymbolTable::StoragePtr SymbolTableImpl::join(const StatNameVec& stat_names) const { +SymbolTable::StoragePtr SymbolTable::join(const StatNameVec& stat_names) const { size_t num_bytes = 0; for (StatName stat_name : stat_names) { if (!stat_name.empty()) { @@ -680,7 +679,7 @@ SymbolTable::StoragePtr SymbolTableImpl::join(const StatNameVec& stat_names) con return mem_block.release(); } -void SymbolTableImpl::populateList(const StatName* names, uint32_t num_names, StatNameList& list) { +void SymbolTable::populateList(const StatName* names, uint32_t num_names, StatNameList& list) { RELEASE_ASSERT(num_names < 256, "Maximum number elements in a StatNameList exceeded"); // First encode all the names. diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 6e6bef24175b5..71baef0b74acf 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -1,15 +1,11 @@ #pragma once #include -#include #include #include #include #include -#include "envoy/common/exception.h" -#include "envoy/stats/symbol_table.h" - #include "source/common/common/assert.h" #include "source/common/common/hash.h" #include "source/common/common/lock_guard.h" @@ -21,12 +17,27 @@ #include "absl/container/fixed_array.h" #include "absl/container/flat_hash_map.h" +#include "absl/container/inlined_vector.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" namespace Envoy { namespace Stats { +class StatName; +using StatNameVec = absl::InlinedVector; +class StatNameList; +class StatNameSet; +using StatNameSetPtr = std::unique_ptr; + +/** + * Holds a range of indexes indicating which parts of a stat-name are + * dynamic. This is used to transfer stats from hot-restart parent to child, + * retaining the same name structure. + */ +using DynamicSpan = std::pair; +using DynamicSpans = std::vector; + /** A Symbol represents a string-token with a small index. */ using Symbol = uint32_t; @@ -63,8 +74,26 @@ using SymbolVec = std::vector; * same string is re-encoded, it may or may not encode to the same underlying * symbol. */ -class SymbolTableImpl : public SymbolTable { +class SymbolTable final { public: + /** + * Efficient byte-encoded storage of an array of tokens. The most common + * tokens are typically < 127, and are represented directly. tokens >= 128 + * spill into the next byte, allowing for tokens of arbitrary numeric value to + * be stored. As long as the most common tokens are low-valued, the + * representation is space-efficient. This scheme is similar to UTF-8. The + * token ordering is dependent on the order in which stat-names are encoded + * into the SymbolTable, which will not be optimal, but in practice appears + * to be pretty good. + * + * This is exposed in the interface for the benefit of join(), which is + * used in the hot-path to append two stat-names into a temp without taking + * locks. This is used then in thread-local cache lookup, so that once warm, + * no locks are taken when looking up stats. + */ + using Storage = uint8_t[]; + using StoragePtr = std::unique_ptr; + /** * Intermediate representation for a stat-name. This helps store multiple * names in a single packed allocation. First we encode each desired name, @@ -169,52 +198,203 @@ class SymbolTableImpl : public SymbolTable { private: friend class StatName; - friend class SymbolTableImpl; + friend class SymbolTable; class TokenIter; size_t data_bytes_required_{0}; MemBlockBuilder mem_block_; }; - SymbolTableImpl(); - ~SymbolTableImpl() override; + SymbolTable(); + ~SymbolTable(); + + /** + * 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, + * since this should never happen, and we don't want to continue running + * with a corrupt stats set. + * + * @param stat_name the stat name. + * @return std::string stringified stat_name. + */ + std::string toString(const StatName& stat_name) const; + + /** + * @return uint64_t the number of symbols in the symbol table. + */ + uint64_t numSymbols() const; - // SymbolTable - std::string toString(const StatName& stat_name) const override; - uint64_t numSymbols() const override; - bool lessThan(const StatName& a, const StatName& b) const override; - void free(const StatName& stat_name) override; - void incRefCount(const StatName& stat_name) override; - StoragePtr join(const StatNameVec& stat_names) const override; - void populateList(const StatName* names, uint32_t num_names, StatNameList& list) override; - StoragePtr encode(absl::string_view name) override; - StoragePtr makeDynamicStorage(absl::string_view name) override; + /** + * Determines whether one StatName lexically precedes another. Note that + * the lexical order may not exactly match the lexical order of the + * elaborated strings. For example, stat-name of "-.-" would lexically + * sort after "---" but when encoded as a StatName would come lexically + * earlier. In practice this is unlikely to matter as those are not + * reasonable names for Envoy stats. + * + * Note that this operation has to be performed with the context of the + * SymbolTable so that the individual Symbol objects can be converted + * into strings for lexical comparison. + * + * @param a the first stat name + * @param b the second stat name + * @return bool true if a lexically precedes b. + */ + bool lessThan(const StatName& a, const StatName& b) const; + + /** + * Joins two or more StatNames. For example if we have StatNames for {"a.b", + * "c.d", "e.f"} then the joined stat-name matches "a.b.c.d.e.f". The + * advantage of using this representation is that it avoids having to + * decode/encode into the elaborated form, and does not require locking the + * SymbolTable. + * + * Note that this method does not bump reference counts on the referenced + * Symbols in the SymbolTable, so it's only valid as long for the lifetime of + * the joined StatNames. + * + * This is intended for use doing cached name lookups of scoped stats, where + * the scope prefix and the names to combine it with are already in StatName + * form. Using this class, they can be combined without accessing the + * SymbolTable or, in particular, taking its lock. + * + * @param stat_names the names to join. + * @return Storage allocated for the joined name. + */ + StoragePtr join(const StatNameVec& stat_names) const; + + /** + * Populates a StatNameList from a list of encodings. This is not done at + * construction time to enable StatNameList to be instantiated directly in + * a class that doesn't have a live SymbolTable when it is constructed. + * + * @param names A pointer to the first name in an array, allocated by the caller. + * @param num_names The number of names. + * @param list The StatNameList representing the stat names. + */ + void populateList(const StatName* names, uint32_t num_names, StatNameList& list); #ifndef ENVOY_CONFIG_COVERAGE - void debugPrint() const override; + void debugPrint() const; #endif - StatNameSetPtr makeSet(absl::string_view name) override; - uint64_t getRecentLookups(const RecentLookupsFn&) const override; - void clearRecentLookups() override; - void setRecentLookupCapacity(uint64_t capacity) override; - uint64_t recentLookupCapacity() const override; - DynamicSpans getDynamicSpans(StatName stat_name) const override; - void withLockHeld(std::function fn) const override { - Thread::LockGuard lock(lock_); - fn(); - } + /** + * Creates a StatNameSet. + * + * @param name the name of the set. + * @return the set. + */ + StatNameSetPtr makeSet(absl::string_view name); + + using RecentLookupsFn = std::function; /** - * See doc for lessThan(). This variant requires the lock be taken - * before calling. It is used to help sort() speed. + * Calls the provided function with the name of the most recently looked-up + * 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. + */ + uint64_t getRecentLookups(const RecentLookupsFn&) const; + + /** + * Clears the recent-lookups structures. */ - bool lessThanLockHeld(const StatName& a, const StatName& b) const override; + void clearRecentLookups(); + + /** + * Sets the recent-lookup capacity. + */ + void setRecentLookupCapacity(uint64_t capacity); + + /** + * @return The configured recent-lookup tracking capacity. + */ + uint64_t recentLookupCapacity() const; + + /** + * Identifies the dynamic components of a stat_name into an array of integer + * pairs, indicating the begin/end of spans of tokens in the stat-name that + * are created from StatNameDynamicStore or StatNameDynamicPool. + * + * This can be used to reconstruct the same exact StatNames in + * StatNames::mergeStats(), to enable stat continuity across hot-restart. + * + * @param stat_name the input stat name. + * @return the array of pairs indicating the bounds. + */ + DynamicSpans getDynamicSpans(StatName stat_name) const; + + bool lessThanLockHeld(const StatName& a, const StatName& b) const; + + template struct StatNameCompare { + StatNameCompare(const SymbolTable& symbol_table, GetStatName getter) + : symbol_table_(symbol_table), getter_(getter) {} + + bool operator()(const Obj& a, const Obj& b) const; + + const SymbolTable& symbol_table_; + GetStatName getter_; + }; + + /** + * Sorts a range by StatName. This API is more efficient than + * calling std::sort directly as it takes a single lock for the + * entire sort, rather than locking on each comparison. + * + * @param begin the beginning of the range to sort + * @param end the end of the range to sort + * @param get_stat_name a functor that takes an Obj and returns a StatName. + */ + template + void sortByStatNames(Iter begin, Iter end, GetStatName get_stat_name) const { + // Grab the lock once before sorting begins, so we don't have to re-take + // it on every comparison. + Thread::LockGuard lock(lock_); + StatNameCompare compare(*this, get_stat_name); + std::sort(begin, end, compare); + } private: friend class StatName; friend class StatNameTest; friend class StatNameDeathTest; + friend class StatNameDynamicStorage; + friend class StatNameList; + friend class StatNameStorage; + + /** + * Encodes 'name' into the symbol table. Bumps reference counts for referenced + * symbols. The caller must manage the storage, and is responsible for calling + * SymbolTable::free() to release the reference counts. + * + * @param name The name to encode. + * @return The encoded name, transferring ownership to the caller. + * + */ + StoragePtr encode(absl::string_view name); + StoragePtr makeDynamicStorage(absl::string_view name); + + /** + * Since SymbolTable does manual reference counting, a client of SymbolTable + * must manually call free(symbol_vec) when it is freeing the backing store + * for a StatName. This way, the symbol table will grow and shrink + * dynamically, instead of being write-only. + * + * @param stat_name the stat name. + */ + void free(const StatName& stat_name); + + /** + * StatName backing-store can be managed by callers in a variety of ways + * to minimize overhead. But any persistent reference to a StatName needs + * to hold onto its own reference-counts for all symbols. This method + * helps callers ensure the symbol-storage is maintained for the lifetime + * of a reference. + * + * @param stat_name the stat name. + */ + void incRefCount(const StatName& stat_name); struct SharedSymbol { SharedSymbol(Symbol symbol) : symbol_(symbol), ref_count_(1) {} @@ -422,7 +602,7 @@ class StatName { * @return size_t the number of bytes in the symbol array, including the * overhead for the size itself. */ - size_t size() const { return SymbolTableImpl::Encoding::totalSizeBytes(dataSize()); } + size_t size() const { return SymbolTable::Encoding::totalSizeBytes(dataSize()); } /** * Copies the entire StatName representation into a MemBlockBuilder, including @@ -459,7 +639,7 @@ class StatName { if (size_and_data_ == nullptr) { return nullptr; } - return size_and_data_ + SymbolTableImpl::Encoding::encodingSizeBytes(dataSize()); + return size_and_data_ + SymbolTable::Encoding::encodingSizeBytes(dataSize()); } const uint8_t* dataIncludingSize() const { return size_and_data_; } @@ -536,9 +716,9 @@ class StatNameManagedStorage : public StatNameStorage { */ class StatNameDynamicStorage : public StatNameStorageBase { public: - // Basic constructor based on a name. Note the table is used for - // a virtual-function call to encode the name, but no locks are taken - // in either implementation of the SymbolTable api. + // Basic constructor based on a name. Note the table is used for a call to + // encode the name, but no locks are taken in either implementation of the + // SymbolTable api. StatNameDynamicStorage(absl::string_view name, SymbolTable& table) : StatNameStorageBase(table.makeDynamicStorage(name)) {} // Move constructor. @@ -675,7 +855,7 @@ class StatNameList { void clear(SymbolTable& symbol_table); private: - friend class SymbolTableImpl; + friend class SymbolTable; /** * Moves the specified storage into the list. The storage format is an @@ -688,8 +868,8 @@ class StatNameList { * ... * * - * For SymbolTableImpl, each symbol is 1 or more bytes, in a variable-length - * encoding. See SymbolTableImpl::Encoding::addSymbol for details. + * For SymbolTable, each symbol is 1 or more bytes, in a variable-length + * encoding. See SymbolTable::Encoding::addSymbol for details. */ void moveStorageIntoList(SymbolTable::StoragePtr&& storage) { storage_ = std::move(storage); } @@ -861,7 +1041,7 @@ class StatNameSet { } private: - friend class SymbolTableImpl; + friend class SymbolTable; StatNameSet(SymbolTable& symbol_table, absl::string_view name); @@ -872,49 +1052,18 @@ class StatNameSet { StringStatNameMap builtin_stat_names_; }; -/** - * Sorts a range by StatName. This API is more efficient than - * calling std::sort directly as it takes a single lock for the - * entire sort, rather than locking on each comparison. - * - * This is a free function rather than a method of SymbolTable because - * SymbolTable is an abstract class and it's hard to make a virtual template - * function. - * - * @param symbol_table the symbol table that owns the StatNames. - * @param begin the beginning of the range to sort - * @param end the end of the range to sort - * @param get_stat_name a functor that takes an Obj and returns a StatName. - */ -template -void sortByStatNames(const SymbolTable& symbol_table, Iter begin, Iter end, - GetStatName get_stat_name) { - - struct Compare { - Compare(const SymbolTable& symbol_table, GetStatName getter) - : symbol_table_(symbol_table), getter_(getter) {} +template +bool SymbolTable::StatNameCompare::operator()(const Obj& a, const Obj& b) const { + StatName a_stat_name = getter_(a); + StatName b_stat_name = getter_(b); + return symbol_table_.lessThanLockHeld(a_stat_name, b_stat_name); +} - bool operator()(const Obj& a, const Obj& b) const { - StatName a_stat_name = getter_(a); - StatName b_stat_name = getter_(b); - return symbol_table_.lessThanLockHeld(a_stat_name, b_stat_name); - } +using SymbolTablePtr = std::unique_ptr; - const SymbolTable& symbol_table_; - GetStatName getter_; - }; - - // Grab the lock once before sorting begins, so we don't have to re-take - // it on every comparison. - // - // TODO(jmarantz): Once SymbolTable is changed to a concrete class from an - // interface, we'll be able to change this free function into a method, - // take the lock directly, and remove the withLockHeld method. - symbol_table.withLockHeld([begin, end, get_stat_name, &symbol_table]() { - Compare compare(symbol_table, get_stat_name); - std::sort(begin, end, compare); - }); -} +// TODO(jmarantz): rename all remaining ~47 occurrences of SymbolTableImpl in +// the codebase to SymbolTable, and drop this alias. +using SymbolTableImpl = SymbolTable; } // namespace Stats } // namespace Envoy diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index de813f940b977..ba1e930e7b008 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -76,8 +76,6 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { if (server_.statsConfig().flushOnAdmin()) { - ENVOY_LOG_MISC(error, "flushing on admin"); - abort(); server_.flushStats(); } diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index f5af9675bc06d..ee76b37a3c373 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -435,10 +435,10 @@ TEST_F(StatNameTest, Sort) { const StatNameVec sorted_names{makeStat("a.b"), makeStat("a.c"), makeStat("a.c"), makeStat("d.a"), makeStat("d.a.a"), makeStat("d.e")}; EXPECT_NE(names, sorted_names); - struct Compare { + struct GetStatName { StatName operator()(const StatName& stat_name) const { return stat_name; } }; - sortByStatNames(table_, names.begin(), names.end(), Compare()); + table_.sortByStatNames(names.begin(), names.end(), GetStatName()); EXPECT_EQ(names, sorted_names); } diff --git a/test/common/stats/symbol_table_speed_test.cc b/test/common/stats/symbol_table_speed_test.cc index e62ff55da1082..40cc5d11cb55b 100644 --- a/test/common/stats/symbol_table_speed_test.cc +++ b/test/common/stats/symbol_table_speed_test.cc @@ -153,8 +153,7 @@ static void bmSortByStatNames(benchmark::State& state) { for (auto _ : state) { UNREFERENCED_PARAMETER(_); std::vector sort = names; - Envoy::Stats::sortByStatNames(symbol_table, sort.begin(), sort.end(), - getter); + symbol_table.sortByStatNames(sort.begin(), sort.end(), getter); } } BENCHMARK(bmSortByStatNames); From b4c8141a1e7d73dfe9f58f21fde0eb0a64f8e832 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Feb 2022 17:56:15 -0500 Subject: [PATCH 20/74] remove superfluous allocater_impl change Signed-off-by: Joshua Marantz --- source/common/stats/allocator_impl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/common/stats/allocator_impl.h b/source/common/stats/allocator_impl.h index 5348cb46bad6d..86a1e1aa664cf 100644 --- a/source/common/stats/allocator_impl.h +++ b/source/common/stats/allocator_impl.h @@ -6,6 +6,7 @@ #include "envoy/stats/allocator.h" #include "envoy/stats/sink.h" #include "envoy/stats/stats.h" +#include "envoy/stats/symbol_table.h" #include "source/common/common/thread_synchronizer.h" #include "source/common/stats/metric_impl.h" From 4b421af339dd396dfe9ed03db4164320df8d95d0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 3 Feb 2022 20:37:43 -0500 Subject: [PATCH 21/74] revert un-needed test tweak. Signed-off-by: Joshua Marantz --- test/common/stats/isolated_store_impl_test.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index d121ca6ac23a8..f3a3e0a31dcd2 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -16,23 +16,22 @@ namespace Stats { class StatsIsolatedStoreImplTest : public testing::Test { protected: StatsIsolatedStoreImplTest() - : pool_(symbol_table_), store_(std::make_unique(symbol_table_)) {} + : store_(std::make_unique(symbol_table_)), pool_(symbol_table_) {} ~StatsIsolatedStoreImplTest() override { - store_.reset(); pool_.clear(); + store_.reset(); EXPECT_EQ(0, symbol_table_.numSymbols()); } StatName makeStatName(absl::string_view name) { return pool_.add(name); } SymbolTableImpl symbol_table_; - StatNamePool pool_; std::unique_ptr store_; + StatNamePool pool_; }; TEST_F(StatsIsolatedStoreImplTest, All) { ScopePtr scope1 = store_->createScope("scope1."); - EXPECT_EQ(1, scope1.use_count()); Counter& c1 = store_->counterFromString("c1"); Counter& c2 = scope1->counterFromString("c2"); EXPECT_EQ("c1", c1.name()); @@ -123,7 +122,6 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(store_->findCounter(nonexistent_name.statName()), absl::nullopt); EXPECT_EQ(store_->findGauge(nonexistent_name.statName()), absl::nullopt); EXPECT_EQ(store_->findHistogram(nonexistent_name.statName()), absl::nullopt); - scope1.reset(); } TEST_F(StatsIsolatedStoreImplTest, PrefixIsStatName) { From f2cbfc3b856ff5f89fa1b1c2377fd7f8a53f9fb9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 12 Feb 2022 00:12:45 -0500 Subject: [PATCH 22/74] Connect chunk-friendly implementation of /stats into the admin chunk API. Signed-off-by: Joshua Marantz --- envoy/server/admin.h | 11 +++++ source/server/admin/admin.cc | 32 ++++++++---- source/server/admin/admin.h | 8 --- source/server/admin/stats_handler.cc | 66 ++++++++++++++++++++----- source/server/admin/stats_handler.h | 19 +++++-- test/server/admin/stats_handler_test.cc | 25 +++++----- 6 files changed, 114 insertions(+), 47 deletions(-) diff --git a/envoy/server/admin.h b/envoy/server/admin.h index a3f21b29d3503..8d45421ab0ec8 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -207,6 +207,17 @@ class Admin { * @return the number of worker threads to run in the server. */ virtual uint32_t concurrency() const PURE; + + /** + * Makes a chunked handler for static text. The version that takes the + * Buffer::Instance& transfers the content from the passed-in buffer. + * + * @param resposne_text the text to populate response with + * @param code the Http::Code for the response + * @return the handler + */ + static HandlerPtr makeStaticTextHandler(absl::string_view response_text, Http::Code code); + static HandlerPtr makeStaticTextHandler(Buffer::Instance& response_text, Http::Code code); }; } // namespace Server diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index cee3d35d1cb03..3b980b26d5057 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -199,8 +199,11 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, MAKE_ADMIN_HANDLER(server_info_handler_.handlerServerInfo), false, false), makeHandler("/ready", "print server state, return 200 if LIVE, otherwise return 503", MAKE_ADMIN_HANDLER(server_info_handler_.handlerReady), false, false), - makeHandler("/stats", "print server stats", - MAKE_ADMIN_HANDLER(stats_handler_.handlerStats), false, false), + {"/stats", "print server stats", + [this](absl::string_view path, AdminStream& admin_stream) -> Admin::HandlerPtr { + return stats_handler_.makeContext(path, admin_stream); + }, + false, false}, makeHandler("/stats/prometheus", "print server stats in prometheus format", MAKE_ADMIN_HANDLER(stats_handler_.handlerPrometheusStats), false, false), makeHandler("/stats/recentlookups", "Show recent stat-name lookups", @@ -259,17 +262,21 @@ namespace { // Implements a chunked handler for static text. class StaticTextHandler : public Admin::Handler { public: - StaticTextHandler(absl::string_view response_text, Http::Code code) - : response_text_(std::string(response_text)), code_(code) {} + StaticTextHandler(absl::string_view response_text, Http::Code code) : code_(code) { + response_text_.add(response_text); + } + StaticTextHandler(Buffer::Instance& response_text, Http::Code code) : code_(code) { + response_text_.move(response_text); + } Http::Code start(Http::ResponseHeaderMap&) override { return code_; } bool nextChunk(Buffer::Instance& response) override { - response.add(response_text_); + response.move(response_text_); return false; } private: - const std::string response_text_; + Buffer::OwnedImpl response_text_; const Http::Code code_; }; @@ -307,7 +314,11 @@ class HandlerGasket : public Admin::Handler { } // namespace -Admin::HandlerPtr AdminImpl::makeStaticTextHandler(absl::string_view response, Http::Code code) { +Admin::HandlerPtr Admin::makeStaticTextHandler(absl::string_view response, Http::Code code) { + return std::make_unique(response, code); +} + +Admin::HandlerPtr Admin::makeStaticTextHandler(Buffer::Instance& response, Http::Code code) { return std::make_unique(response, code); } @@ -338,8 +349,9 @@ Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, if (method != Http::Headers::get().MethodValues.Post) { ENVOY_LOG(error, "admin path \"{}\" mutates state, method={} rather than POST", handler.prefix_, method); - return makeStaticTextHandler(fmt::format("Method {} not allowed, POST required.", method), - Http::Code::MethodNotAllowed); + return Admin::makeStaticTextHandler( + fmt::format("Method {} not allowed, POST required.", method), + Http::Code::MethodNotAllowed); } } @@ -352,7 +364,7 @@ Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, Buffer::OwnedImpl error_response; error_response.add("invalid path. "); getHelp(error_response); - return makeStaticTextHandler(error_response.toString(), Http::Code::NotFound); + return Admin::makeStaticTextHandler(error_response, Http::Code::NotFound); } std::vector AdminImpl::sortedHandlers() const { diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index cc7b1c1552281..a47d34b78706a 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -218,14 +218,6 @@ class AdminImpl : public Admin, return proxy_status_config_.get(); } - /** - * Makes a chunked handler for static text. - * @param resposne_text the text to populate response with - * @param code the Http::Code for the response - * @return the handler - */ - static HandlerPtr makeStaticTextHandler(absl::string_view response_text, Http::Code code); - private: /** * Individual admin handler including prefix, help text, and callback. diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index ba1e930e7b008..6a4ada1609cbe 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -72,6 +72,39 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, return Http::Code::OK; } +Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& /*admin_stream*/) { + if (server_.statsConfig().flushOnAdmin()) { + server_.flushStats(); + } + + const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(path); + + const bool used_only = params.find("usedonly") != params.end(); + absl::optional regex; + Buffer::OwnedImpl response; + if (!Utility::filterParam(params, response, regex)) { + return Admin::makeStaticTextHandler(response, Http::Code::BadRequest); + } + + const absl::optional format_value = Utility::formatParam(params); + bool json = false; + if (format_value.has_value()) { + if (format_value.value() == "prometheus") { + Buffer::OwnedImpl response; + Http::Code code = prometheusStats(path, response); + return Admin::makeStaticTextHandler(response, code); + } else if (format_value.value() == "json") { + json = true; + } else { + return Admin::makeStaticTextHandler( + "usage: /stats?format=json or /stats?format=prometheus \n\n", Http::Code::BadRequest); + } + } + + return std::make_unique(server_, used_only, json, regex); +} + +#if 0 Http::Code StatsHandler::handlerStats(absl::string_view url, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { @@ -111,13 +144,14 @@ Http::Code StatsHandler::handlerStats(absl::string_view url, } Context& context = *iter->second; - Http::Code code = context.writeChunk(response); + Http::Code code = context.eChunk(response); if (code != Http::Code::Continue) { context_map_.erase(iter); } return code; } +#endif class StatsHandler::Render { public: @@ -280,13 +314,13 @@ class StatsHandler::JsonRender : public StatsHandler::Render { bool first_{true}; }; -StatsHandler::Context::Context(Server::Instance& server, bool used_only, - absl::optional regex, bool json, - Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response) - : used_only_(used_only), regex_(regex), stats_(server.stats()) { - if (json) { - render_ = std::make_unique(response_headers, response); +StatsHandler::Context::Context(Server::Instance& server, bool used_only, bool json, + absl::optional regex) + : used_only_(used_only), json_(json), regex_(regex), stats_(server.stats()) {} + +Http::Code StatsHandler::Context::start(Http::ResponseHeaderMap& response_headers) { + if (json_) { + render_ = std::make_unique(response_headers, response_); } else { render_ = std::make_unique(); } @@ -301,6 +335,7 @@ StatsHandler::Context::Context(Server::Instance& server, bool used_only, [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.makeConstShared()); }); startPhase(); + return Http::Code::OK; } void StatsHandler::Context::startPhase() { @@ -371,7 +406,11 @@ bool StatsHandler::Context::skip(const SharedStatType& stat, const std::string& return false; } -Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { +bool StatsHandler::Context::nextChunk(Buffer::Instance& response) { + if (response_.length() > 0) { + ASSERT(response.length() == 0); + response.move(response_); + } while (!render_->nextChunk(response)) { while (stat_map_.empty()) { switch (phase_) { @@ -385,7 +424,7 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { break; case Phase::Histograms: render_->render(response); - return Http::Code::OK; + return false; } } @@ -417,7 +456,7 @@ Http::Code StatsHandler::Context::writeChunk(Buffer::Instance& response) { } stat_map_.erase(iter); } - return Http::Code::Continue; + return true; } #if 0 @@ -447,6 +486,11 @@ StatsHandler::Context::~Context() = default; Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap&, Buffer::Instance& response, AdminStream&) { + return prometheusStats(path_and_query, response); +} + +Http::Code StatsHandler::prometheusStats(absl::string_view path_and_query, + Buffer::Instance& response) { const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(path_and_query); const bool used_only = params.find("usedonly") != params.end(); diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index b0b4ce1565505..b60ada69a4f52 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -3,12 +3,12 @@ #include #include -#include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/http/header_map.h" #include "envoy/server/admin.h" #include "envoy/server/instance.h" +#include "source/common/buffer/buffer_impl.h" #include "source/common/stats/histogram_impl.h" #include "source/server/admin/handler_ctx.h" @@ -39,21 +39,26 @@ class StatsHandler : public HandlerContextBase { Http::Code handlerStatsRecentLookupsEnable(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); +#if 0 Http::Code handlerStats(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); +#endif Http::Code handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + Http::Code prometheusStats(absl::string_view path_and_query, Buffer::Instance& response); Http::Code handlerContention(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + Admin::HandlerPtr makeContext(absl::string_view path, AdminStream& admin_stream); + class JsonRender; class Render; class TextRender; - class Context { + class Context : public Admin::Handler { using ScopeVec = std::vector; using StatOrScopes = absl::variant regex, bool json, - Http::ResponseHeaderMap& response_headers, Buffer::Instance& response); + Context(Server::Instance& server, bool used_only, bool json, absl::optional regex); ~Context(); + // Admin::Handler + Http::Code start(Http::ResponseHeaderMap& response_headers) override; + bool nextChunk(Buffer::Instance& response) override; + void startPhase(); - Http::Code writeChunk(Buffer::Instance& response); template bool shouldShowMetric(const StatType& stat) { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); @@ -82,6 +89,7 @@ class StatsHandler : public HandlerContextBase { void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); template bool skip(const SharedStatType& stat, const std::string& name); const bool used_only_; + const bool json_; absl::optional regex_; absl::optional format_value_; @@ -96,6 +104,7 @@ class StatsHandler : public HandlerContextBase { uint32_t stats_and_scopes_index_{0}; uint32_t chunk_index_{0}; Phase phase_{Phase::TextReadouts}; + Buffer::OwnedImpl response_; }; using ContextPtr = std::unique_ptr; diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index dcadfe6c002b2..34d9749eb271b 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -41,13 +41,13 @@ class StatsHandlerTest { Http::Code handlerStats(Server::Instance& instance, absl::string_view url, std::string& out) { StatsHandler handler(instance); Http::TestResponseHeaderMapImpl response_headers; - Http::Code code = Http::Code::Continue; MockAdminStream admin_stream; - while (code == Http::Code::Continue) { - Buffer::OwnedImpl data; - code = handler.handlerStats(url, response_headers, data, admin_stream); - out += data.toString(); + Admin::HandlerPtr context = handler.makeContext(url, admin_stream); + Http::Code code = context->start(response_headers); + Buffer::OwnedImpl data; + while (context->nextChunk(data)) { } + out = data.toString(); return code; } @@ -147,15 +147,12 @@ TEST_P(AdminStatsTest, HandlerStatsPlainText) { TEST_P(AdminStatsTest, HandlerStatsJson) { const std::string url = "/stats?format=json"; Http::TestResponseHeaderMapImpl response_headers; - Buffer::OwnedImpl data; - MockAdminStream admin_stream; Configuration::MockStatsConfig stats_config; EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); MockInstance instance; store_->initializeThreading(main_thread_dispatcher_, tls_); EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); - StatsHandler handler(instance); Stats::Counter& c1 = store_->counterFromString("c1"); Stats::Counter& c2 = store_->counterFromString("c2"); @@ -173,7 +170,8 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { store_->mergeHistograms([]() -> void {}); - Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream); + std::string data; + Http::Code code = handlerStats(instance, url, data); EXPECT_EQ(Http::Code::OK, code); const std::string expected_json_old = R"EOF({ @@ -256,8 +254,8 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { ] })EOF"; - ENVOY_LOG_MISC(error, "json: {}", data.toString()); - EXPECT_THAT(expected_json_old, JsonStringEq(data.toString())); + ENVOY_LOG_MISC(error, "json: {}", data); + EXPECT_THAT(expected_json_old, JsonStringEq(data)); shutdownThreading(); } @@ -830,6 +828,7 @@ class StatsHandlerPrometheusTest : public StatsHandlerTest { } }; +#if 0 class StatsHandlerPrometheusDefaultTest : public StatsHandlerPrometheusTest, public testing::TestWithParam {}; @@ -843,7 +842,6 @@ TEST_P(StatsHandlerPrometheusDefaultTest, StatsHandlerPrometheusDefaultTest) { createTestStats(); std::shared_ptr instance = setupMockedInstance(); - StatsHandler handler(*instance); const std::string expected_response = R"EOF(# TYPE envoy_cluster_upstream_cx_total counter envoy_cluster_upstream_cx_total{cluster="c1"} 10 @@ -855,7 +853,7 @@ envoy_cluster_upstream_cx_active{cluster="c2"} 12 )EOF"; - Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream); + Http::Code code = handlerStats(*instance, url, response_headers, data, admin_stream); EXPECT_EQ(Http::Code::OK, code); EXPECT_THAT(expected_response, data.toString()); @@ -900,6 +898,7 @@ envoy_control_plane_identifier{cluster="c1",text_value="cp-1"} 0 shutdownThreading(); } +#endif } // namespace Server } // namespace Envoy From 766381a807eb2d40536ccb6b2dce2168ad67d658 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 12 Feb 2022 10:27:49 -0500 Subject: [PATCH 23/74] Move StatsHandler::Context into .cc file. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 374 +++++++++++++++------------ source/server/admin/stats_handler.h | 52 +--- 2 files changed, 206 insertions(+), 220 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 6a4ada1609cbe..79511179d90e0 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -72,87 +72,6 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, return Http::Code::OK; } -Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& /*admin_stream*/) { - if (server_.statsConfig().flushOnAdmin()) { - server_.flushStats(); - } - - const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(path); - - const bool used_only = params.find("usedonly") != params.end(); - absl::optional regex; - Buffer::OwnedImpl response; - if (!Utility::filterParam(params, response, regex)) { - return Admin::makeStaticTextHandler(response, Http::Code::BadRequest); - } - - const absl::optional format_value = Utility::formatParam(params); - bool json = false; - if (format_value.has_value()) { - if (format_value.value() == "prometheus") { - Buffer::OwnedImpl response; - Http::Code code = prometheusStats(path, response); - return Admin::makeStaticTextHandler(response, code); - } else if (format_value.value() == "json") { - json = true; - } else { - return Admin::makeStaticTextHandler( - "usage: /stats?format=json or /stats?format=prometheus \n\n", Http::Code::BadRequest); - } - } - - return std::make_unique(server_, used_only, json, regex); -} - -#if 0 -Http::Code StatsHandler::handlerStats(absl::string_view url, - Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response, AdminStream& admin_stream) { - if (server_.statsConfig().flushOnAdmin()) { - server_.flushStats(); - } - - const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url); - - const bool used_only = params.find("usedonly") != params.end(); - absl::optional regex; - if (!Utility::filterParam(params, response, regex)) { - return Http::Code::BadRequest; - } - - const absl::optional format_value = Utility::formatParam(params); - bool json = false; - if (format_value.has_value()) { - if (format_value.value() == "prometheus") { - return handlerPrometheusStats(url, response_headers, response, admin_stream); - } - if (format_value.value() == "json") { - json = true; - } else { - response.add("usage: /stats?format=json or /stats?format=prometheus \n"); - response.add("\n"); - return Http::Code::BadRequest; - } - } - - auto iter = context_map_.find(&admin_stream); - if (iter == context_map_.end()) { - auto insertion = context_map_.emplace( - &admin_stream, - std::make_unique(server_, used_only, regex, json, response_headers, response)); - iter = insertion.first; - } - Context& context = *iter->second; - - Http::Code code = context.eChunk(response); - if (code != Http::Code::Continue) { - context_map_.erase(iter); - } - - return code; -} -#endif - class StatsHandler::Render { public: virtual ~Render() = default; @@ -314,50 +233,222 @@ class StatsHandler::JsonRender : public StatsHandler::Render { bool first_{true}; }; -StatsHandler::Context::Context(Server::Instance& server, bool used_only, bool json, - absl::optional regex) - : used_only_(used_only), json_(json), regex_(regex), stats_(server.stats()) {} +class StatsHandler::Context : public Admin::Handler { + using ScopeVec = std::vector; + using StatOrScopes = + absl::variant; + enum class Phase { + TextReadouts, + CountersAndGauges, + Histograms, + }; + + public: + Context(Server::Instance& server, bool used_only, bool json, absl::optional regex) + : used_only_(used_only), json_(json), regex_(regex), stats_(server.stats()) {} + + // Admin::Handler + Http::Code start(Http::ResponseHeaderMap& response_headers) override { + if (json_) { + render_ = std::make_unique(response_headers, response_); + } else { + render_ = std::make_unique(); + } -Http::Code StatsHandler::Context::start(Http::ResponseHeaderMap& response_headers) { - if (json_) { - render_ = std::make_unique(response_headers, response_); - } else { - render_ = std::make_unique(); + // Populate the top-level scopes and the stats underneath any scopes with an empty name. + // We will have to de-dup, but we can do that after sorting. + // + // First capture all the scopes and hold onto them with a SharedPtr so they + // can't be deleted after the initial iteration. + stats_.forEachScope( + [this](size_t s) { scopes_.reserve(s); }, + [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.makeConstShared()); }); + + startPhase(); + return Http::Code::OK; } - // Populate the top-level scopes and the stats underneath any scopes with an empty name. - // We will have to de-dup, but we can do that after sorting. - // - // First capture all the scopes and hold onto them with a SharedPtr so they - // can't be deleted after the initial iteration. - stats_.forEachScope( - [this](size_t s) { scopes_.reserve(s); }, - [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.makeConstShared()); }); + bool nextChunk(Buffer::Instance& response) override { + if (response_.length() > 0) { + ASSERT(response.length() == 0); + response.move(response_); + } + while (!render_->nextChunk(response)) { + while (stat_map_.empty()) { + switch (phase_) { + case Phase::TextReadouts: + phase_ = Phase::CountersAndGauges; + startPhase(); + break; + case Phase::CountersAndGauges: + phase_ = Phase::Histograms; + startPhase(); + break; + case Phase::Histograms: + render_->render(response); + return false; + } + } - startPhase(); - return Http::Code::OK; + auto iter = stat_map_.begin(); + const std::string& name = iter->first; + StatOrScopes& variant = iter->second; + switch (variant.index()) { + case 0: + populateStatsForCurrentPhase(absl::get(variant)); + break; + case 1: + renderStat(name, response, variant); + break; + case 2: + renderStat(name, response, variant); + break; + case 3: + renderStat(name, response, variant); + break; + case 4: { + auto histogram = absl::get(variant); + if (!skip(histogram, name)) { + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, name, *parent_histogram); + } + } + } + } + stat_map_.erase(iter); + } + return true; + } + void startPhase() { + ASSERT(stat_map_.empty()); + for (const Stats::ConstScopeSharedPtr& scope : scopes_) { + StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; + if (variant.index() == absl::variant_npos) { + variant = ScopeVec(); + } + absl::get(variant).emplace_back(scope); + } + + // Populate stat_map with all the counters found in all the scopes with an + // empty prefix. + auto iter = stat_map_.find(""); + if (iter != stat_map_.end()) { + StatOrScopes variant = std::move(iter->second); + stat_map_.erase(iter); + auto& scope_vec = absl::get(variant); + populateStatsForCurrentPhase(scope_vec); + } + } + + template bool shouldShowMetric(const StatType& stat) { + return StatsHandler::shouldShowMetric(stat, used_only_, regex_); + } + + void populateStatsForCurrentPhase(const ScopeVec& scope_vec); + template void populateStatsFromScopes(const ScopeVec& scope); + template + void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); + template bool skip(const SharedStatType& stat, const std::string& name); + const bool used_only_; + const bool json_; + absl::optional regex_; + absl::optional format_value_; + + std::unique_ptr render_; + + static constexpr uint32_t num_stats_per_chunk_ = 1000; + Stats::Store& stats_; + ScopeVec scopes_; + using StatMap = std::map; + StatMap stat_map_; + uint32_t stats_and_scopes_index_{0}; + uint32_t chunk_index_{0}; + Phase phase_{Phase::TextReadouts}; + Buffer::OwnedImpl response_; +}; + +Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& /*admin_stream*/) { + if (server_.statsConfig().flushOnAdmin()) { + server_.flushStats(); + } + + const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(path); + + const bool used_only = params.find("usedonly") != params.end(); + absl::optional regex; + Buffer::OwnedImpl response; + if (!Utility::filterParam(params, response, regex)) { + return Admin::makeStaticTextHandler(response, Http::Code::BadRequest); + } + + const absl::optional format_value = Utility::formatParam(params); + bool json = false; + if (format_value.has_value()) { + if (format_value.value() == "prometheus") { + Buffer::OwnedImpl response; + Http::Code code = prometheusStats(path, response); + return Admin::makeStaticTextHandler(response, code); + } else if (format_value.value() == "json") { + json = true; + } else { + return Admin::makeStaticTextHandler( + "usage: /stats?format=json or /stats?format=prometheus \n\n", Http::Code::BadRequest); + } + } + + return std::make_unique(server_, used_only, json, regex); } -void StatsHandler::Context::startPhase() { - ASSERT(stat_map_.empty()); - for (const Stats::ConstScopeSharedPtr& scope : scopes_) { - StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; - if (variant.index() == absl::variant_npos) { - variant = ScopeVec(); +#if 0 +Http::Code StatsHandler::handlerStats(absl::string_view url, + Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response, AdminStream& admin_stream) { + if (server_.statsConfig().flushOnAdmin()) { + server_.flushStats(); + } + + const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url); + + const bool used_only = params.find("usedonly") != params.end(); + absl::optional regex; + if (!Utility::filterParam(params, response, regex)) { + return Http::Code::BadRequest; + } + + const absl::optional format_value = Utility::formatParam(params); + bool json = false; + if (format_value.has_value()) { + if (format_value.value() == "prometheus") { + return handlerPrometheusStats(url, response_headers, response, admin_stream); + } + if (format_value.value() == "json") { + json = true; + } else { + response.add("usage: /stats?format=json or /stats?format=prometheus \n"); + response.add("\n"); + return Http::Code::BadRequest; } - absl::get(variant).emplace_back(scope); } - // Populate stat_map with all the counters found in all the scopes with an - // empty prefix. - auto iter = stat_map_.find(""); - if (iter != stat_map_.end()) { - StatOrScopes variant = std::move(iter->second); - stat_map_.erase(iter); - auto& scope_vec = absl::get(variant); - populateStatsForCurrentPhase(scope_vec); + auto iter = context_map_.find(&admin_stream); + if (iter == context_map_.end()) { + auto insertion = context_map_.emplace( + &admin_stream, + std::make_unique(server_, used_only, regex, json, response_headers, response)); + iter = insertion.first; + } + Context& context = *iter->second; + + Http::Code code = context.eChunk(response); + if (code != Http::Code::Continue) { + context_map_.erase(iter); } + + return code; } +#endif void StatsHandler::Context::populateStatsForCurrentPhase(const ScopeVec& scope_vec) { switch (phase_) { @@ -406,59 +497,6 @@ bool StatsHandler::Context::skip(const SharedStatType& stat, const std::string& return false; } -bool StatsHandler::Context::nextChunk(Buffer::Instance& response) { - if (response_.length() > 0) { - ASSERT(response.length() == 0); - response.move(response_); - } - while (!render_->nextChunk(response)) { - while (stat_map_.empty()) { - switch (phase_) { - case Phase::TextReadouts: - phase_ = Phase::CountersAndGauges; - startPhase(); - break; - case Phase::CountersAndGauges: - phase_ = Phase::Histograms; - startPhase(); - break; - case Phase::Histograms: - render_->render(response); - return false; - } - } - - auto iter = stat_map_.begin(); - const std::string& name = iter->first; - StatOrScopes& variant = iter->second; - switch (variant.index()) { - case 0: - populateStatsForCurrentPhase(absl::get(variant)); - break; - case 1: - renderStat(name, response, variant); - break; - case 2: - renderStat(name, response, variant); - break; - case 3: - renderStat(name, response, variant); - break; - case 4: { - auto histogram = absl::get(variant); - if (!skip(histogram, name)) { - auto parent_histogram = dynamic_cast(histogram.get()); - if (parent_histogram != nullptr) { - render_->generate(response, name, *parent_histogram); - } - } - } - } - stat_map_.erase(iter); - } - return true; -} - #if 0 struct CompareStatsAndScopes { CompareStatsAndScopes(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table) {} @@ -481,8 +519,6 @@ struct CompareStatsAndScopes { }; #endif -StatsHandler::Context::~Context() = default; - Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap&, Buffer::Instance& response, AdminStream&) { diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index b60ada69a4f52..25494dda6f24b 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -57,55 +57,7 @@ class StatsHandler : public HandlerContextBase { class JsonRender; class Render; class TextRender; - - class Context : public Admin::Handler { - using ScopeVec = std::vector; - using StatOrScopes = - absl::variant; - enum class Phase { - TextReadouts, - CountersAndGauges, - Histograms, - }; - - public: - Context(Server::Instance& server, bool used_only, bool json, absl::optional regex); - ~Context(); - - // Admin::Handler - Http::Code start(Http::ResponseHeaderMap& response_headers) override; - bool nextChunk(Buffer::Instance& response) override; - - void startPhase(); - - template bool shouldShowMetric(const StatType& stat) { - return StatsHandler::shouldShowMetric(stat, used_only_, regex_); - } - - void populateStatsForCurrentPhase(const ScopeVec& scope_vec); - template void populateStatsFromScopes(const ScopeVec& scope); - template - void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); - template bool skip(const SharedStatType& stat, const std::string& name); - const bool used_only_; - const bool json_; - absl::optional regex_; - absl::optional format_value_; - - std::unique_ptr render_; - - static constexpr uint32_t num_stats_per_chunk_ = 1000; - Stats::Store& stats_; - ScopeVec scopes_; - // StatOrScopeVec stats_and_scopes_; - using StatMap = std::map; - StatMap stat_map_; - uint32_t stats_and_scopes_index_{0}; - uint32_t chunk_index_{0}; - Phase phase_{Phase::TextReadouts}; - Buffer::OwnedImpl response_; - }; + class Context; using ContextPtr = std::unique_ptr; private: @@ -117,8 +69,6 @@ class StatsHandler : public HandlerContextBase { } friend class StatsHandlerTest; - - absl::flat_hash_map> context_map_; }; } // namespace Server From 29e32addc554767d0ea71d1f83e199d3fc37b07b Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 12 Feb 2022 10:43:58 -0500 Subject: [PATCH 24/74] cleanup Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 210 ++++++++++----------------- source/server/admin/stats_handler.h | 1 - 2 files changed, 76 insertions(+), 135 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 79511179d90e0..6ea63c72d4b48 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -235,16 +235,15 @@ class StatsHandler::JsonRender : public StatsHandler::Render { class StatsHandler::Context : public Admin::Handler { using ScopeVec = std::vector; - using StatOrScopes = - absl::variant; + using StatOrScopes = absl::variant; enum class Phase { TextReadouts, CountersAndGauges, Histograms, }; - public: +public: Context(Server::Instance& server, bool used_only, bool json, absl::optional regex) : used_only_(used_only), json_(json), regex_(regex), stats_(server.stats()) {} @@ -277,17 +276,17 @@ class StatsHandler::Context : public Admin::Handler { while (!render_->nextChunk(response)) { while (stat_map_.empty()) { switch (phase_) { - case Phase::TextReadouts: - phase_ = Phase::CountersAndGauges; - startPhase(); - break; - case Phase::CountersAndGauges: - phase_ = Phase::Histograms; - startPhase(); - break; - case Phase::Histograms: - render_->render(response); - return false; + case Phase::TextReadouts: + phase_ = Phase::CountersAndGauges; + startPhase(); + break; + case Phase::CountersAndGauges: + phase_ = Phase::Histograms; + startPhase(); + break; + case Phase::Histograms: + render_->render(response); + return false; } } @@ -295,28 +294,28 @@ class StatsHandler::Context : public Admin::Handler { const std::string& name = iter->first; StatOrScopes& variant = iter->second; switch (variant.index()) { - case 0: - populateStatsForCurrentPhase(absl::get(variant)); - break; - case 1: - renderStat(name, response, variant); - break; - case 2: - renderStat(name, response, variant); - break; - case 3: - renderStat(name, response, variant); - break; - case 4: { - auto histogram = absl::get(variant); - if (!skip(histogram, name)) { - auto parent_histogram = dynamic_cast(histogram.get()); - if (parent_histogram != nullptr) { - render_->generate(response, name, *parent_histogram); - } + case 0: + populateStatsForCurrentPhase(absl::get(variant)); + break; + case 1: + renderStat(name, response, variant); + break; + case 2: + renderStat(name, response, variant); + break; + case 3: + renderStat(name, response, variant); + break; + case 4: { + auto histogram = absl::get(variant); + if (!skip(histogram, name)) { + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, name, *parent_histogram); } } } + } stat_map_.erase(iter); } return true; @@ -346,11 +345,50 @@ class StatsHandler::Context : public Admin::Handler { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); } - void populateStatsForCurrentPhase(const ScopeVec& scope_vec); - template void populateStatsFromScopes(const ScopeVec& scope); + void populateStatsForCurrentPhase(const ScopeVec& scope_vec) { + switch (phase_) { + case Phase::TextReadouts: + populateStatsFromScopes(scope_vec); + break; + case Phase::CountersAndGauges: + populateStatsFromScopes(scope_vec); + populateStatsFromScopes(scope_vec); + break; + case Phase::Histograms: + populateStatsFromScopes(scope_vec); + break; + } + } + + template void populateStatsFromScopes(const ScopeVec& scope_vec) { + for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { + Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { + stat_map_[stat->name()] = stat; + return true; + }; + scope->iterate(fn); + } + } + template - void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); - template bool skip(const SharedStatType& stat, const std::string& name); + void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant) { + auto stat = absl::get(variant); + if (skip(stat, name)) { + return; + } + render_->generate(response, name, stat->value()); + } + + template bool skip(const SharedStatType& stat, const std::string& name) { + if (used_only_ && !stat->used()) { + return true; + } + if (regex_.has_value() && !std::regex_search(name, regex_.value())) { + return true; + } + return false; + } + const bool used_only_; const bool json_; absl::optional regex_; @@ -401,102 +439,6 @@ Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& return std::make_unique(server_, used_only, json, regex); } -#if 0 -Http::Code StatsHandler::handlerStats(absl::string_view url, - Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response, AdminStream& admin_stream) { - if (server_.statsConfig().flushOnAdmin()) { - server_.flushStats(); - } - - const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url); - - const bool used_only = params.find("usedonly") != params.end(); - absl::optional regex; - if (!Utility::filterParam(params, response, regex)) { - return Http::Code::BadRequest; - } - - const absl::optional format_value = Utility::formatParam(params); - bool json = false; - if (format_value.has_value()) { - if (format_value.value() == "prometheus") { - return handlerPrometheusStats(url, response_headers, response, admin_stream); - } - if (format_value.value() == "json") { - json = true; - } else { - response.add("usage: /stats?format=json or /stats?format=prometheus \n"); - response.add("\n"); - return Http::Code::BadRequest; - } - } - - auto iter = context_map_.find(&admin_stream); - if (iter == context_map_.end()) { - auto insertion = context_map_.emplace( - &admin_stream, - std::make_unique(server_, used_only, regex, json, response_headers, response)); - iter = insertion.first; - } - Context& context = *iter->second; - - Http::Code code = context.eChunk(response); - if (code != Http::Code::Continue) { - context_map_.erase(iter); - } - - return code; -} -#endif - -void StatsHandler::Context::populateStatsForCurrentPhase(const ScopeVec& scope_vec) { - switch (phase_) { - case Phase::TextReadouts: - populateStatsFromScopes(scope_vec); - break; - case Phase::CountersAndGauges: - populateStatsFromScopes(scope_vec); - populateStatsFromScopes(scope_vec); - break; - case Phase::Histograms: - populateStatsFromScopes(scope_vec); - break; - } -} - -template -void StatsHandler::Context::populateStatsFromScopes(const ScopeVec& scope_vec) { - for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { - Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { - stat_map_[stat->name()] = stat; - return true; - }; - scope->iterate(fn); - } -} - -template -void StatsHandler::Context::renderStat(const std::string& name, Buffer::Instance& response, - StatOrScopes& variant) { - auto stat = absl::get(variant); - if (skip(stat, name)) { - return; - } - render_->generate(response, name, stat->value()); -} - -template -bool StatsHandler::Context::skip(const SharedStatType& stat, const std::string& name) { - if (used_only_ && !stat->used()) { - return true; - } - if (regex_.has_value() && !std::regex_search(name, regex_.value())) { - return true; - } - return false; -} - #if 0 struct CompareStatsAndScopes { CompareStatsAndScopes(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table) {} diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 25494dda6f24b..260c9a6414981 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -58,7 +58,6 @@ class StatsHandler : public HandlerContextBase { class Render; class TextRender; class Context; - using ContextPtr = std::unique_ptr; private: template From 0eb9ffb43e439a5825c9c73a290ad616605db2e7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 12 Feb 2022 13:43:49 -0500 Subject: [PATCH 25/74] remove unused method Signed-off-by: Joshua Marantz --- source/server/admin/admin.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index a47d34b78706a..33f6748065376 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -207,12 +207,6 @@ class AdminImpl : public Admin, return findHandler(path_and_query, admin_stream); }; } - AdminFilter::AdminServerCallbackFunction createCallbackFunction() { - return [this](absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, - Buffer::OwnedImpl& response, AdminFilter& filter) -> Http::Code { - return runCallback(path_and_query, response_headers, response, filter); - }; - } uint64_t maxRequestsPerConnection() const override { return 0; } const HttpConnectionManagerProto::ProxyStatusConfig* proxyStatusConfig() const override { return proxy_status_config_.get(); From 059a32f236b71e528cc3c88d5c405687ca4c282e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 13 Feb 2022 00:30:54 -0500 Subject: [PATCH 26/74] add more test coverage Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 15 +- source/server/admin/stats_handler.h | 2 + test/server/admin/stats_handler_test.cc | 250 ++++++++++++------------ 3 files changed, 138 insertions(+), 129 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 6ea63c72d4b48..70fc62467522e 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -204,6 +204,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { addStatJson(response, str); } +#if 0 void emitStats(Buffer::Instance& response) { auto str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::listValue(stats_array_), false /* pretty */, true); @@ -212,6 +213,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { chunk_count_ = 0; stats_array_.clear(); } +#endif void addStatJson(Buffer::Instance& response, const std::string& json) { if (first_) { @@ -222,7 +224,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } - uint32_t chunk_count_{0}; + // uint32_t chunk_count_{0}; ProtobufWkt::Struct document_; std::vector stats_array_; std::vector scope_array_; @@ -244,8 +246,8 @@ class StatsHandler::Context : public Admin::Handler { }; public: - Context(Server::Instance& server, bool used_only, bool json, absl::optional regex) - : used_only_(used_only), json_(json), regex_(regex), stats_(server.stats()) {} + Context(Stats::Store& stats, bool used_only, bool json, absl::optional regex) + : used_only_(used_only), json_(json), regex_(regex), stats_(stats) {} // Admin::Handler Http::Code start(Http::ResponseHeaderMap& response_headers) override { @@ -436,7 +438,12 @@ Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& } } - return std::make_unique(server_, used_only, json, regex); + return makeContext(server_.stats(), used_only, json, regex); +} + +Admin::HandlerPtr StatsHandler::makeContext(Stats::Store& stats, bool used_only, bool json, + const absl::optional& regex) { + return std::make_unique(stats, used_only, json, regex); } #if 0 diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 260c9a6414981..bc3e65dc4f060 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -53,6 +53,8 @@ class StatsHandler : public HandlerContextBase { Buffer::Instance& response, AdminStream&); Admin::HandlerPtr makeContext(absl::string_view path, AdminStream& admin_stream); + static Admin::HandlerPtr makeContext(Stats::Store& stats, bool used_only, bool json, + const absl::optional& regex); class JsonRender; class Render; diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 34d9749eb271b..5b627a1b26c19 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -15,6 +15,8 @@ using testing::EndsWith; using testing::HasSubstr; using testing::InSequence; using testing::Ref; +using testing::Return; +using testing::ReturnRef; using testing::StartsWith; namespace Envoy { @@ -27,16 +29,32 @@ class StatsHandlerTest { store_->addSink(sink_); } -#if 0 - static std::string - statsAsJsonHandler(std::map& all_stats, - std::map& all_text_readouts, - const std::vector& all_histograms, - const bool used_only, const absl::optional regex = absl::nullopt) { - return StatsHandler::statsAsJson(all_stats, all_text_readouts, all_histograms, used_only, regex, - true /*pretty_print*/); + std::shared_ptr setupMockedInstance() { + auto instance = std::make_shared(); + EXPECT_CALL(stats_config_, flushOnAdmin()).WillRepeatedly(Return(false)); + store_->initializeThreading(main_thread_dispatcher_, tls_); + EXPECT_CALL(*instance, stats()).WillRepeatedly(ReturnRef(*store_)); + EXPECT_CALL(*instance, statsConfig()).WillRepeatedly(ReturnRef(stats_config_)); + EXPECT_CALL(api_, customStatNamespaces()).WillRepeatedly(ReturnRef(customNamespaces())); + EXPECT_CALL(*instance, api()).WillRepeatedly(ReturnRef(api_)); + return instance; + } + + std::string statsAsJsonHandler(const bool used_only, + const absl::optional filter = absl::nullopt) { + + std::shared_ptr instance = setupMockedInstance(); + std::string url = "stats?format=json"; + if (used_only) { + url += "&usedonly"; + } + if (filter.has_value()) { + absl::StrAppend(&url, "&filter=", filter.value()); + } + std::string out; + EXPECT_EQ(Http::Code::OK, handlerStats(*instance, url, out)); + return out; } -#endif Http::Code handlerStats(Server::Instance& instance, absl::string_view url, std::string& out) { StatsHandler handler(instance); @@ -61,6 +79,7 @@ class StatsHandlerTest { } Stats::SymbolTableImpl symbol_table_; + Configuration::MockStatsConfig stats_config_; NiceMock main_thread_dispatcher_; NiceMock tls_; NiceMock api_; @@ -83,10 +102,10 @@ TEST_P(AdminStatsTest, HandlerStatsInvalidFormat) { std::string data; MockAdminStream admin_stream; Configuration::MockStatsConfig stats_config; - EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); + EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(Return(false)); MockInstance instance; - EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); - EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); + EXPECT_CALL(instance, stats()).WillRepeatedly(ReturnRef(*store_)); + EXPECT_CALL(instance, statsConfig()).WillRepeatedly(ReturnRef(stats_config)); Http::Code code = handlerStats(instance, url, data); EXPECT_EQ(Http::Code::BadRequest, code); EXPECT_EQ("usage: /stats?format=json or /stats?format=prometheus \n\n", data); @@ -96,11 +115,11 @@ TEST_P(AdminStatsTest, HandlerStatsPlainText) { const std::string url = "/stats"; std::string data, used_data; Configuration::MockStatsConfig stats_config; - EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); + EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(Return(false)); MockInstance instance; store_->initializeThreading(main_thread_dispatcher_, tls_); - EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); - EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); + EXPECT_CALL(instance, stats()).WillRepeatedly(ReturnRef(*store_)); + EXPECT_CALL(instance, statsConfig()).WillRepeatedly(ReturnRef(stats_config)); Stats::Counter& c1 = store_->counterFromString("c1"); Stats::Counter& c2 = store_->counterFromString("c2"); @@ -148,11 +167,11 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { const std::string url = "/stats?format=json"; Http::TestResponseHeaderMapImpl response_headers; Configuration::MockStatsConfig stats_config; - EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); + EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(Return(false)); MockInstance instance; store_->initializeThreading(main_thread_dispatcher_, tls_); - EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); - EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); + EXPECT_CALL(instance, stats()).WillRepeatedly(ReturnRef(*store_)); + EXPECT_CALL(instance, statsConfig()).WillRepeatedly(ReturnRef(stats_config)); Stats::Counter& c1 = store_->counterFromString("c1"); Stats::Counter& c2 = store_->counterFromString("c2"); @@ -254,42 +273,35 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { ] })EOF"; - ENVOY_LOG_MISC(error, "json: {}", data); EXPECT_THAT(expected_json_old, JsonStringEq(data)); shutdownThreading(); } -#if 0 TEST_P(AdminStatsTest, StatsAsJson) { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); - - Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); + { + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); + Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); - h2.recordValue(100); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); - store_->mergeHistograms([]() -> void {}); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); + h2.recordValue(100); - // Again record a new value in h1 so that it has both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); + store_->mergeHistograms([]() -> void {}); - store_->mergeHistograms([]() -> void {}); + // Again record a new value in h1 so that it has both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); - std::vector histograms = store_->histograms(); - std::sort(histograms.begin(), histograms.end(), - [](const Stats::ParentHistogramSharedPtr& a, - const Stats::ParentHistogramSharedPtr& b) -> bool { return a->name() < b->name(); }); - std::map all_stats; - std::map all_text_readouts; - std::string actual_json = statsAsJsonHandler(all_stats, all_text_readouts, histograms, false); + store_->mergeHistograms([]() -> void {}); + } + std::string actual_json = statsAsJsonHandler(false); const std::string expected_json = R"EOF({ "stats": [ @@ -409,31 +421,29 @@ TEST_P(AdminStatsTest, StatsAsJson) { } TEST_P(AdminStatsTest, UsedOnlyStatsAsJson) { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); - - Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); + { + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - EXPECT_EQ("h1", h1.name()); - EXPECT_EQ("h2", h2.name()); + Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); + EXPECT_EQ("h1", h1.name()); + EXPECT_EQ("h2", h2.name()); - store_->mergeHistograms([]() -> void {}); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); - // Again record a new value in h1 so that it has both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); + store_->mergeHistograms([]() -> void {}); - store_->mergeHistograms([]() -> void {}); + // Again record a new value in h1 so that it has both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); - std::map all_stats; - std::map all_text_readouts; - std::string actual_json = - statsAsJsonHandler(all_stats, all_text_readouts, store_->histograms(), true); + store_->mergeHistograms([]() -> void {}); + } + std::string actual_json = statsAsJsonHandler(true); // Expected JSON should not have h2 values as it is not used. const std::string expected_json = R"EOF({ @@ -509,32 +519,29 @@ TEST_P(AdminStatsTest, UsedOnlyStatsAsJson) { } TEST_P(AdminStatsTest, StatsAsJsonFilterString) { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); - - Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); + { + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); + Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); - h2.recordValue(100); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); - store_->mergeHistograms([]() -> void {}); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); + h2.recordValue(100); - // Again record a new value in h1 so that it has both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); + store_->mergeHistograms([]() -> void {}); - store_->mergeHistograms([]() -> void {}); + // Again record a new value in h1 so that it has both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); - std::map all_stats; - std::map all_text_readouts; - std::string actual_json = - statsAsJsonHandler(all_stats, all_text_readouts, store_->histograms(), false, - absl::optional{std::regex("[a-z]1")}); + store_->mergeHistograms([]() -> void {}); + } + std::string actual_json = statsAsJsonHandler(false, "[a-z]1"); // Because this is a filter case, we don't expect to see any stats except for those containing // "h1" in their name. @@ -611,41 +618,39 @@ TEST_P(AdminStatsTest, StatsAsJsonFilterString) { } TEST_P(AdminStatsTest, UsedOnlyStatsAsJsonFilterString) { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); + { + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - Stats::Histogram& h1 = store_->histogramFromString( - "h1_matches", Stats::Histogram::Unit::Unspecified); // Will match, be used, and print - Stats::Histogram& h2 = store_->histogramFromString( - "h2_matches", Stats::Histogram::Unit::Unspecified); // Will match but not be used - Stats::Histogram& h3 = store_->histogramFromString( - "h3_not", Stats::Histogram::Unit::Unspecified); // Will be used but not match + Stats::Histogram& h1 = store_->histogramFromString( + "h1_matches", Stats::Histogram::Unit::Unspecified); // Will match, be used, and print + Stats::Histogram& h2 = store_->histogramFromString( + "h2_matches", Stats::Histogram::Unit::Unspecified); // Will match but not be used + Stats::Histogram& h3 = store_->histogramFromString( + "h3_not", Stats::Histogram::Unit::Unspecified); // Will be used but not match - EXPECT_EQ("h1_matches", h1.name()); - EXPECT_EQ("h2_matches", h2.name()); - EXPECT_EQ("h3_not", h3.name()); + EXPECT_EQ("h1_matches", h1.name()); + EXPECT_EQ("h2_matches", h2.name()); + EXPECT_EQ("h3_not", h3.name()); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 200)); - h3.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 200)); + h3.recordValue(200); - store_->mergeHistograms([]() -> void {}); + store_->mergeHistograms([]() -> void {}); - // Again record a new value in h1 and h3 so that they have both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 100)); - h3.recordValue(100); + // Again record a new value in h1 and h3 so that they have both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 100)); + h3.recordValue(100); - store_->mergeHistograms([]() -> void {}); + store_->mergeHistograms([]() -> void {}); + } - std::map all_stats; - std::map all_text_readouts; - std::string actual_json = - statsAsJsonHandler(all_stats, all_text_readouts, store_->histograms(), true, - absl::optional{std::regex("h[12]")}); + std::string actual_json = statsAsJsonHandler(true, "h[12]"); // Expected JSON should not have h2 values as it is not used, and should not have h3 values as // they are used but do not match. @@ -720,7 +725,6 @@ TEST_P(AdminStatsTest, UsedOnlyStatsAsJsonFilterString) { EXPECT_THAT(expected_json, JsonStringEq(actual_json)); shutdownThreading(); } -#endif INSTANTIATE_TEST_SUITE_P(IpVersions, AdminInstanceTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), @@ -781,8 +785,17 @@ TEST_P(AdminInstanceTest, RecentLookups) { EXPECT_THAT(body, HasSubstr("Lookup tracking is not enabled")); EXPECT_THAT(std::string(response_headers.getContentTypeValue()), 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. + EXPECT_EQ(Http::Code::OK, + admin_.request("/stats/recentlookups/enable", "POST", response_headers, body)); + Stats::StatNamePool pool(server_.stats().symbolTable()); + pool.add("alpha"); + pool.add("beta"); + pool.add("gamma"); + pool.add("alpha"); + pool.add("beta"); + pool.add("alpha"); + EXPECT_EQ(Http::Code::OK, admin_.request("/stats/recentlookups", "GET", response_headers, body)); + EXPECT_THAT(body, HasSubstr(" 1 gamma\n 2 beta\n 3 alpha\n")); } class StatsHandlerPrometheusTest : public StatsHandlerTest { @@ -790,19 +803,6 @@ class StatsHandlerPrometheusTest : public StatsHandlerTest { Http::TestResponseHeaderMapImpl response_headers; Buffer::OwnedImpl data; MockAdminStream admin_stream; - Configuration::MockStatsConfig stats_config; - Api::MockApi api; - - std::shared_ptr setupMockedInstance() { - auto instance = std::make_shared(); - EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false)); - store_->initializeThreading(main_thread_dispatcher_, tls_); - EXPECT_CALL(*instance, stats()).WillRepeatedly(testing::ReturnRef(*store_)); - EXPECT_CALL(*instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config)); - EXPECT_CALL(api, customStatNamespaces()).WillRepeatedly(testing::ReturnRef(customNamespaces())); - EXPECT_CALL(*instance, api()).WillRepeatedly(testing::ReturnRef(api)); - return instance; - } void createTestStats() { Stats::StatNameTagVector c1Tags{{makeStat("cluster"), makeStat("c1")}}; From bda92976e71792df87d3756996f3a0cfc35e99cb Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 13 Feb 2022 10:52:56 -0500 Subject: [PATCH 27/74] clean up prometheus tests. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 35 +------------------------ source/server/admin/stats_handler.h | 5 ---- test/server/admin/stats_handler_test.cc | 14 ++++------ 3 files changed, 6 insertions(+), 48 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 70fc62467522e..154f2505b2acd 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -157,7 +157,6 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } void render(Buffer::Instance& response) override { - // emitStats(response); if (found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); (*histograms_obj_fields)["computed_quantiles"] = @@ -166,29 +165,12 @@ class StatsHandler::JsonRender : public StatsHandler::Render { (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); auto str = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); - // stats_array_.push_back(); - // ENVOY_LOG_MISC(error, "histograms: {}", str); - // response.addFragments({" ", str, "\n"}); addStatJson(response, str); } - - // auto* document_fields = document_.mutable_fields(); - //(*document_fields)["stats"] = ValueUtil::listValue(stats_array_); - // response.add(MessageUtil::getJsonStringFromMessageOrDie(document_, false /* pretty */, - // true)); response.add("]}"); } - bool nextChunk(Buffer::Instance& response) override { - return response.length() > ChunkSize; - /* - if (++chunk_count_ == 10000) { - emitStats(response); - return true; - } - return false; - */ - } + bool nextChunk(Buffer::Instance& response) override { return response.length() > ChunkSize; } private: template @@ -197,24 +179,10 @@ class StatsHandler::JsonRender : public StatsHandler::Render { auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); (*stat_obj_fields)["value"] = value; - // stats_array_.push_back(ValueUtil::structValue(stat_obj)); auto str = MessageUtil::getJsonStringFromMessageOrDie(stat_obj, false /* pretty */, true); - // ENVOY_LOG_MISC(error, "emitting: {}", str); - // response.addFragments({" ", str, ",\n"}); addStatJson(response, str); } -#if 0 - void emitStats(Buffer::Instance& response) { - auto str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::listValue(stats_array_), - false /* pretty */, true); - // ENVOY_LOG_MISC(error, "emitting: {}", str); - response.add(str); - chunk_count_ = 0; - stats_array_.clear(); - } -#endif - void addStatJson(Buffer::Instance& response, const std::string& json) { if (first_) { response.add(json); @@ -224,7 +192,6 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } - // uint32_t chunk_count_{0}; ProtobufWkt::Struct document_; std::vector stats_array_; std::vector scope_array_; diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index bc3e65dc4f060..3cf184778a741 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -39,11 +39,6 @@ class StatsHandler : public HandlerContextBase { Http::Code handlerStatsRecentLookupsEnable(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); -#if 0 - Http::Code handlerStats(absl::string_view path_and_query, - Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, - AdminStream&); -#endif Http::Code handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 5b627a1b26c19..aa7c711d2bff1 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -801,8 +801,7 @@ TEST_P(AdminInstanceTest, RecentLookups) { class StatsHandlerPrometheusTest : public StatsHandlerTest { public: Http::TestResponseHeaderMapImpl response_headers; - Buffer::OwnedImpl data; - MockAdminStream admin_stream; + std::string data; void createTestStats() { Stats::StatNameTagVector c1Tags{{makeStat("cluster"), makeStat("c1")}}; @@ -828,7 +827,6 @@ class StatsHandlerPrometheusTest : public StatsHandlerTest { } }; -#if 0 class StatsHandlerPrometheusDefaultTest : public StatsHandlerPrometheusTest, public testing::TestWithParam {}; @@ -853,9 +851,9 @@ envoy_cluster_upstream_cx_active{cluster="c2"} 12 )EOF"; - Http::Code code = handlerStats(*instance, url, response_headers, data, admin_stream); + Http::Code code = handlerStats(*instance, url, data); EXPECT_EQ(Http::Code::OK, code); - EXPECT_THAT(expected_response, data.toString()); + EXPECT_THAT(expected_response, data); shutdownThreading(); } @@ -877,7 +875,6 @@ TEST_P(StatsHandlerPrometheusWithTextReadoutsTest, StatsHandlerPrometheusWithTex createTestStats(); std::shared_ptr instance = setupMockedInstance(); - StatsHandler handler(*instance); const std::string expected_response = R"EOF(# TYPE envoy_cluster_upstream_cx_total counter envoy_cluster_upstream_cx_total{cluster="c1"} 10 @@ -892,13 +889,12 @@ envoy_control_plane_identifier{cluster="c1",text_value="cp-1"} 0 )EOF"; - Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream); + Http::Code code = handlerStats(*instance, url, data); EXPECT_EQ(Http::Code::OK, code); - EXPECT_THAT(expected_response, data.toString()); + EXPECT_THAT(expected_response, data); shutdownThreading(); } -#endif } // namespace Server } // namespace Envoy From 0d79f4ce80e57efb6d38672d43563ff13a755352 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 13 Feb 2022 22:40:07 -0500 Subject: [PATCH 28/74] cleanup. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 154f2505b2acd..18f587c97e933 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -241,6 +241,7 @@ class StatsHandler::Context : public Admin::Handler { if (response_.length() > 0) { ASSERT(response.length() == 0); response.move(response_); + ASSERT(response_.length() == 0); } while (!render_->nextChunk(response)) { while (stat_map_.empty()) { @@ -365,13 +366,10 @@ class StatsHandler::Context : public Admin::Handler { std::unique_ptr render_; - static constexpr uint32_t num_stats_per_chunk_ = 1000; Stats::Store& stats_; ScopeVec scopes_; using StatMap = std::map; StatMap stat_map_; - uint32_t stats_and_scopes_index_{0}; - uint32_t chunk_index_{0}; Phase phase_{Phase::TextReadouts}; Buffer::OwnedImpl response_; }; From ddcbcb03a9e007692ab901599dcd605e8148099d Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 14 Feb 2022 17:23:29 -0500 Subject: [PATCH 29/74] more test tweaks Signed-off-by: Joshua Marantz --- test/server/admin/stats_handler_test.cc | 141 +++++++++++------------- 1 file changed, 67 insertions(+), 74 deletions(-) diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index aa7c711d2bff1..eef544ab4da11 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -31,10 +31,10 @@ class StatsHandlerTest { std::shared_ptr setupMockedInstance() { auto instance = std::make_shared(); + EXPECT_CALL(*instance, statsConfig()).WillRepeatedly(ReturnRef(stats_config_)); EXPECT_CALL(stats_config_, flushOnAdmin()).WillRepeatedly(Return(false)); store_->initializeThreading(main_thread_dispatcher_, tls_); EXPECT_CALL(*instance, stats()).WillRepeatedly(ReturnRef(*store_)); - EXPECT_CALL(*instance, statsConfig()).WillRepeatedly(ReturnRef(stats_config_)); EXPECT_CALL(api_, customStatNamespaces()).WillRepeatedly(ReturnRef(customNamespaces())); EXPECT_CALL(*instance, api()).WillRepeatedly(ReturnRef(api_)); return instance; @@ -279,28 +279,26 @@ TEST_P(AdminStatsTest, HandlerStatsJson) { } TEST_P(AdminStatsTest, StatsAsJson) { - { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); - h2.recordValue(100); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); + h2.recordValue(100); - store_->mergeHistograms([]() -> void {}); + store_->mergeHistograms([]() -> void {}); - // Again record a new value in h1 so that it has both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); + // Again record a new value in h1 so that it has both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); - store_->mergeHistograms([]() -> void {}); - } + store_->mergeHistograms([]() -> void {}); std::string actual_json = statsAsJsonHandler(false); const std::string expected_json = R"EOF({ @@ -421,28 +419,26 @@ TEST_P(AdminStatsTest, StatsAsJson) { } TEST_P(AdminStatsTest, UsedOnlyStatsAsJson) { - { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - EXPECT_EQ("h1", h1.name()); - EXPECT_EQ("h2", h2.name()); + EXPECT_EQ("h1", h1.name()); + EXPECT_EQ("h2", h2.name()); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); - store_->mergeHistograms([]() -> void {}); + store_->mergeHistograms([]() -> void {}); - // Again record a new value in h1 so that it has both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); + // Again record a new value in h1 so that it has both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); - store_->mergeHistograms([]() -> void {}); - } + store_->mergeHistograms([]() -> void {}); std::string actual_json = statsAsJsonHandler(true); // Expected JSON should not have h2 values as it is not used. @@ -519,28 +515,27 @@ TEST_P(AdminStatsTest, UsedOnlyStatsAsJson) { } TEST_P(AdminStatsTest, StatsAsJsonFilterString) { - { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); + + Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); + Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); - Stats::Histogram& h2 = store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); + h2.recordValue(100); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 100)); - h2.recordValue(100); + store_->mergeHistograms([]() -> void {}); - store_->mergeHistograms([]() -> void {}); + // Again record a new value in h1 so that it has both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); - // Again record a new value in h1 so that it has both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); + store_->mergeHistograms([]() -> void {}); - store_->mergeHistograms([]() -> void {}); - } std::string actual_json = statsAsJsonHandler(false, "[a-z]1"); // Because this is a filter case, we don't expect to see any stats except for those containing @@ -618,37 +613,35 @@ TEST_P(AdminStatsTest, StatsAsJsonFilterString) { } TEST_P(AdminStatsTest, UsedOnlyStatsAsJsonFilterString) { - { - InSequence s; - store_->initializeThreading(main_thread_dispatcher_, tls_); + InSequence s; + store_->initializeThreading(main_thread_dispatcher_, tls_); - Stats::Histogram& h1 = store_->histogramFromString( - "h1_matches", Stats::Histogram::Unit::Unspecified); // Will match, be used, and print - Stats::Histogram& h2 = store_->histogramFromString( - "h2_matches", Stats::Histogram::Unit::Unspecified); // Will match but not be used - Stats::Histogram& h3 = store_->histogramFromString( - "h3_not", Stats::Histogram::Unit::Unspecified); // Will be used but not match + Stats::Histogram& h1 = store_->histogramFromString( + "h1_matches", Stats::Histogram::Unit::Unspecified); // Will match, be used, and print + Stats::Histogram& h2 = store_->histogramFromString( + "h2_matches", Stats::Histogram::Unit::Unspecified); // Will match but not be used + Stats::Histogram& h3 = store_->histogramFromString( + "h3_not", Stats::Histogram::Unit::Unspecified); // Will be used but not match - EXPECT_EQ("h1_matches", h1.name()); - EXPECT_EQ("h2_matches", h2.name()); - EXPECT_EQ("h3_not", h3.name()); + EXPECT_EQ("h1_matches", h1.name()); + EXPECT_EQ("h2_matches", h2.name()); + EXPECT_EQ("h3_not", h3.name()); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); - h1.recordValue(200); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 200)); - h3.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); + h1.recordValue(200); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 200)); + h3.recordValue(200); - store_->mergeHistograms([]() -> void {}); + store_->mergeHistograms([]() -> void {}); - // Again record a new value in h1 and h3 so that they have both interval and cumulative values. - // h2 should only have cumulative values. - EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); - h1.recordValue(100); - EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 100)); - h3.recordValue(100); + // Again record a new value in h1 and h3 so that they have both interval and cumulative values. + // h2 should only have cumulative values. + EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 100)); + h1.recordValue(100); + EXPECT_CALL(sink_, onHistogramComplete(Ref(h3), 100)); + h3.recordValue(100); - store_->mergeHistograms([]() -> void {}); - } + store_->mergeHistograms([]() -> void {}); std::string actual_json = statsAsJsonHandler(true, "h[12]"); From 80de8a81c9ea26d9d3688bf800774fbd42579c00 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 16 Feb 2022 10:57:45 -0500 Subject: [PATCH 30/74] post-merge cleanup Signed-off-by: Joshua Marantz --- envoy/stats/scope.h | 1 - source/server/admin/stats_handler.cc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index a8722d402f9c9..3c86df9045214 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -26,7 +26,6 @@ using HistogramOptConstRef = absl::optional>; using ConstScopeSharedPtr = std::shared_ptr; using ScopeSharedPtr = std::shared_ptr; -using ScopePtr = ScopeSharedPtr; // TODO(jmarantz): global s/ScopePtr/ScopeSharedPtr/ & remove alias // TODO(jmarantz): In the initial transformation to store Scope as shared_ptr, // we don't change all the references, as that would result in an unreviewable diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 18f587c97e933..26a88cfd95dfc 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -231,7 +231,7 @@ class StatsHandler::Context : public Admin::Handler { // can't be deleted after the initial iteration. stats_.forEachScope( [this](size_t s) { scopes_.reserve(s); }, - [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.makeConstShared()); }); + [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.getConstShared()); }); startPhase(); return Http::Code::OK; From 71444c04d5bc6a627367ed71cd053405c7f12493 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 16 Feb 2022 11:30:22 -0500 Subject: [PATCH 31/74] undo superfluous edit Signed-off-by: Joshua Marantz --- source/common/stats/thread_local_store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index e691f124dd6e6..bc95e089d2070 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -149,7 +149,7 @@ ScopeSharedPtr ThreadLocalStoreImpl::createScope(const std::string& name) { return scopeFromStatName(stat_name_storage.statName()); } -ScopePtr ThreadLocalStoreImpl::scopeFromStatName(StatName name) { +ScopeSharedPtr ThreadLocalStoreImpl::scopeFromStatName(StatName name) { auto new_scope = std::make_shared(*this, name); Thread::LockGuard lock(lock_); scopes_.emplace(new_scope.get()); From 4a941bde0878e22febbdedcae2cf432c92188750 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 16 Feb 2022 23:41:24 -0500 Subject: [PATCH 32/74] Move superfluous headers out of the .h file and into the .cc if neceesary Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 3 +++ source/server/admin/stats_handler.h | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 26a88cfd95dfc..7b394f89588ab 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -5,6 +5,7 @@ #include "envoy/admin/v3/mutex_stats.pb.h" +#include "source/common/buffer/buffer_impl.h" #include "source/common/common/empty_string.h" #include "source/common/html/utility.h" #include "source/common/http/headers.h" @@ -12,6 +13,8 @@ #include "source/server/admin/prometheus_stats.h" #include "source/server/admin/utils.h" +#include "absl/types/variant.h" + constexpr uint64_t ChunkSize = 2 * 1000 * 1000; namespace Envoy { diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 3cf184778a741..cba02a3aa71a4 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -3,18 +3,16 @@ #include #include +#include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/http/header_map.h" #include "envoy/server/admin.h" #include "envoy/server/instance.h" -#include "source/common/buffer/buffer_impl.h" #include "source/common/stats/histogram_impl.h" #include "source/server/admin/handler_ctx.h" -#include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" -#include "absl/types/variant.h" namespace Envoy { namespace Server { From c702167fbbc9646f61051bb09c9bdefbb9ac5ce0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 17 Feb 2022 10:22:22 -0500 Subject: [PATCH 33/74] use btree rather than std::map. Signed-off-by: Joshua Marantz --- source/server/admin/BUILD | 1 + source/server/admin/stats_handler.cc | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 365a5b8109a9a..159551a5f886e 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -107,6 +107,7 @@ envoy_cc_library( "//source/common/http:codes_lib", "//source/common/http:header_map_lib", "//source/common/stats:histogram_lib", + "@com_google_absl//absl/container:btree", "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 7b394f89588ab..d5996d9ffb9aa 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -13,6 +13,7 @@ #include "source/server/admin/prometheus_stats.h" #include "source/server/admin/utils.h" +#include "absl/container/btree_map.h" #include "absl/types/variant.h" constexpr uint64_t ChunkSize = 2 * 1000 * 1000; @@ -371,7 +372,7 @@ class StatsHandler::Context : public Admin::Handler { Stats::Store& stats_; ScopeVec scopes_; - using StatMap = std::map; + using StatMap = absl::btree_map; StatMap stat_map_; Phase phase_{Phase::TextReadouts}; Buffer::OwnedImpl response_; From 37f9b9a720fc5030e6c52147c9595230bd27d6bc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 17 Feb 2022 11:15:07 -0500 Subject: [PATCH 34/74] correct merge-related issues. Signed-off-by: Joshua Marantz --- test/server/admin/stats_handler_test.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 26121a3c8f618..966610cbe9d46 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -72,9 +72,12 @@ class StatsHandlerTest { EXPECT_CALL(instance, api()).WillRepeatedly(ReturnRef(api_)); EXPECT_CALL(api_, customStatNamespaces()).WillRepeatedly(ReturnRef(custom_namespaces_)); StatsHandler handler(instance); - Buffer::OwnedImpl data; + Admin::HandlerPtr context = handler.makeContext(url, admin_stream_); Http::TestResponseHeaderMapImpl response_headers; - Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream_); + Http::Code code = context->start(response_headers); + Buffer::OwnedImpl data; + while (context->nextChunk(data)) { + } return std::make_pair(code, data.toString()); } @@ -121,7 +124,7 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, AdminStatsTest, TEST_P(AdminStatsTest, HandlerStatsInvalidFormat) { const std::string url = "/stats?format=blergh"; CodeResponse code_response(handlerStats(url)); - EXPECT_EQ(Http::Code::NotFound, code_response.first); + EXPECT_EQ(Http::Code::BadRequest, code_response.first); EXPECT_EQ("usage: /stats?format=json or /stats?format=prometheus \n\n", code_response.second); } From aff664fe3fffd13c32d220eca94257c3bfaf2bcc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 19 Feb 2022 08:45:35 -0500 Subject: [PATCH 35/74] add some comments. Signed-off-by: Joshua Marantz --- source/server/admin/admin.cc | 6 +----- source/server/admin/admin.h | 22 ++++++++++++++++++++++ source/server/admin/stats_handler.cc | 6 ++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 0a5a67c86e6f5..9ef23630f4658 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -199,11 +199,7 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, MAKE_ADMIN_HANDLER(server_info_handler_.handlerServerInfo), false, false), makeHandler("/ready", "print server state, return 200 if LIVE, otherwise return 503", MAKE_ADMIN_HANDLER(server_info_handler_.handlerReady), false, false), - {"/stats", "print server stats", - [this](absl::string_view path, AdminStream& admin_stream) -> Admin::HandlerPtr { - return stats_handler_.makeContext(path, admin_stream); - }, - false, false}, + makeChunkedHandler("/stats", "print server stats", stats_handler_, false, false), makeHandler("/stats/prometheus", "print server stats in prometheus format", MAKE_ADMIN_HANDLER(stats_handler_.handlerPrometheusStats), false, false), makeHandler("/stats/recentlookups", "Show recent stat-name lookups", diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index e6880af45fb4a..4bc01446b6d87 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -228,12 +228,34 @@ class AdminImpl : public Admin, * Creates a Handler instance given a request. */ HandlerPtr findHandler(absl::string_view path_and_query, AdminStream& admin_stream); + /** * Creates a UrlHandler structure from a non-chunked callback. */ UrlHandler makeHandler(const std::string& prefix, const std::string& help_text, HandlerCb callback, bool removable, bool mutates_state); + /** + * Creates a URL prefix bound to chunked handler. Then handler is expected to + * supply a method makeContext(absl::string_view, AdminStream&). + * + * @param prefix the prefix to register + * @param help_text a help text ot display in a table in the admin home page + * @param handler the Handler object for the admin subsystem, supplying makeContext(). + * @param removeable indicates whether the handler can be removed after being added + * @param mutates_state indicates whether the handler will mutate state and therefore + * must be accessed via HTTP POST rather than GET. + * @return the UrlHandler. + */ + template + UrlHandler makeChunkedHandler(const std::string& prefix, const std::string& help_text, + Handler& handler, bool removable, bool mutates_state) { + return {prefix, help_text, + [&handler](absl::string_view path, AdminStream& admin_stream) -> Admin::HandlerPtr { + return handler.makeContext(path, admin_stream); + }, removable, mutates_state}; + } + /** * Implementation of RouteConfigProvider that returns a static null route config. */ diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index d5996d9ffb9aa..03606bced17db 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -16,7 +16,9 @@ #include "absl/container/btree_map.h" #include "absl/types/variant.h" +namespace { constexpr uint64_t ChunkSize = 2 * 1000 * 1000; +} // namespace namespace Envoy { namespace Server { @@ -76,6 +78,8 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, return Http::Code::OK; } +// Abstract class for rendering stats. Every method is called "generate" +// differing only by the data type, for simplifying call-sites. class StatsHandler::Render { public: virtual ~Render() = default; @@ -88,6 +92,7 @@ class StatsHandler::Render { virtual bool nextChunk(Buffer::Instance& response) PURE; }; +// Implements the Render interface for simple textual representation of stats. class StatsHandler::TextRender : public StatsHandler::Render { public: void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { @@ -108,6 +113,7 @@ class StatsHandler::TextRender : public StatsHandler::Render { bool nextChunk(Buffer::Instance& response) override { return response.length() > ChunkSize; } }; +// Implements the Render interface for json output. class StatsHandler::JsonRender : public StatsHandler::Render { public: JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) { From d3742cd8639d5c4a10252434826ebeb6c87a9fbe Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 20 Feb 2022 19:48:53 -0500 Subject: [PATCH 36/74] name change and comments. Signed-off-by: Joshua Marantz --- envoy/server/admin.h | 43 ++++++++++----- source/server/admin/admin.cc | 50 ++++++++--------- source/server/admin/admin.h | 25 ++++----- source/server/admin/admin_filter.cc | 4 +- source/server/admin/admin_filter.h | 4 +- source/server/admin/stats_handler.cc | 68 ++++++++++++++++++------ source/server/admin/stats_handler.h | 6 +-- source/server/config_validation/admin.cc | 4 +- source/server/config_validation/admin.h | 3 +- test/mocks/server/admin.h | 4 +- test/server/admin/admin_filter_test.cc | 4 +- test/server/admin/admin_instance.cc | 2 +- test/server/admin/admin_test.cc | 13 ++--- test/server/admin/stats_handler_test.cc | 17 +++++- 14 files changed, 159 insertions(+), 88 deletions(-) diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 8d45421ab0ec8..278603918c8db 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -70,16 +70,33 @@ class AdminStream { } /** - * Global admin HTTP endpoint for the server. + * Global admin HTTP endpoint for the server, holding a map from URL prefixes to + * handlers. When an HTTP request arrives at the admin port, the URL is linearly + * prefixed-matched against an ordered list of handlers. When a match is found, + * the handler is used to generate a Request. + * + * Requests are capable of streaming out content to the client, however, most + * requests are delivered all at once. The implementation supplies adapters for + * simplifying the creation of streaming requests based on a simple callback + * that takes a URL and generates response headers and response body. + * + * A Taxonomy of the major types involved may help clarify: + * Request a class holding state for streaming admin content to clients. + * These are re-created for each request. + * Handler a class that holds context for a family of admin requests, + * supplying one-shot callbacks for non-streamed responses, and + * for generating Request objects directly for streamed responses. + * These have the same lifetime as Admin objects. + * Admin Holds the ordered list of handlers to be prefix-matched. */ class Admin { public: virtual ~Admin() = default; - // Represents a handler for admin endpoints, allowing for chunked responses. - class Handler { + // Represents a request for admin endpoints, enabling streamed responses. + class Request { public: - virtual ~Handler() = default; + virtual ~Request() = default; /** * Initiates a handler. The URL can be supplied to the constructor if needed. @@ -102,7 +119,7 @@ class Admin { */ virtual bool nextChunk(Buffer::Instance& response) PURE; }; - using HandlerPtr = std::unique_ptr; + using RequestPtr = std::unique_ptr; /** * Callback for admin URL handlers. @@ -119,9 +136,9 @@ class Admin { Buffer::Instance& response, AdminStream& admin_stream)>; /** - * Lambda to generate a Handler. + * Lambda to generate a Request. */ - using GenHandlerCb = std::function; + using GenRequestFn = std::function; /** * Add a legacy admin handler where the entire response is written in @@ -142,14 +159,14 @@ class Admin { * * @param prefix supplies the URL prefix to handle. * @param help_text supplies the help text for the handler. - * @param callback supplies the callback to generate a Handler. + * @param callback supplies the callback to generate a Request. * @param removable if true allows the handler to be removed via removeHandler. * @param mutates_server_state indicates whether callback will mutate server state. * @return bool true if the handler was added, false if it was not added. */ - virtual bool addChunkedHandler(const std::string& prefix, const std::string& help_text, - GenHandlerCb callback, bool removable, - bool mutates_server_state) PURE; + virtual bool addStreamingHandler(const std::string& prefix, const std::string& help_text, + GenRequestFn gen_request, bool removable, + bool mutates_server_state) PURE; /** * Remove an admin handler if it is removable. @@ -216,8 +233,8 @@ class Admin { * @param code the Http::Code for the response * @return the handler */ - static HandlerPtr makeStaticTextHandler(absl::string_view response_text, Http::Code code); - static HandlerPtr makeStaticTextHandler(Buffer::Instance& response_text, Http::Code code); + static RequestPtr makeStaticTextRequest(absl::string_view response_text, Http::Code code); + static RequestPtr makeStaticTextRequest(Buffer::Instance& response_text, Http::Code code); }; } // namespace Server diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 9ef23630f4658..7a42eabeb7248 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -250,18 +250,18 @@ bool AdminImpl::createNetworkFilterChain(Network::Connection& connection, } void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) { - callbacks.addStreamFilter(std::make_shared(createHandlerFunction())); + callbacks.addStreamFilter(std::make_shared(createRequestFunction())); } namespace { // Implements a chunked handler for static text. -class StaticTextHandler : public Admin::Handler { +class StaticTextRequest : public Admin::Request { public: - StaticTextHandler(absl::string_view response_text, Http::Code code) : code_(code) { + StaticTextRequest(absl::string_view response_text, Http::Code code) : code_(code) { response_text_.add(response_text); } - StaticTextHandler(Buffer::Instance& response_text, Http::Code code) : code_(code) { + StaticTextRequest(Buffer::Instance& response_text, Http::Code code) : code_(code) { response_text_.move(response_text); } @@ -276,19 +276,19 @@ class StaticTextHandler : public Admin::Handler { const Http::Code code_; }; -// Implements a Chunked Handler implementation based on a non-chunked callback -// that generates the entire admin output in one shot. -class HandlerGasket : public Admin::Handler { +// Implements a streaming Request based on a non-streaming callback that +// generates the entire admin output in one shot. +class RequestGasket : public Admin::Request { public: - HandlerGasket(Admin::HandlerCb handler_cb, absl::string_view path_and_query, + RequestGasket(Admin::HandlerCb handler_cb, absl::string_view path_and_query, AdminStream& admin_stream) : path_and_query_(std::string(path_and_query)), handler_cb_(handler_cb), admin_stream_(admin_stream) {} - static Admin::GenHandlerCb makeGen(Admin::HandlerCb callback) { + static Admin::GenRequestFn makeGen(Admin::HandlerCb callback) { return [callback](absl::string_view path_and_query, - AdminStream& admin_stream) -> Server::Admin::HandlerPtr { - return std::make_unique(callback, path_and_query, admin_stream); + AdminStream& admin_stream) -> Server::Admin::RequestPtr { + return std::make_unique(callback, path_and_query, admin_stream); }; } @@ -310,18 +310,18 @@ class HandlerGasket : public Admin::Handler { } // namespace -Admin::HandlerPtr Admin::makeStaticTextHandler(absl::string_view response, Http::Code code) { - return std::make_unique(response, code); +Admin::RequestPtr Admin::makeStaticTextRequest(absl::string_view response, Http::Code code) { + return std::make_unique(response, code); } -Admin::HandlerPtr Admin::makeStaticTextHandler(Buffer::Instance& response, Http::Code code) { - return std::make_unique(response, code); +Admin::RequestPtr Admin::makeStaticTextRequest(Buffer::Instance& response, Http::Code code) { + return std::make_unique(response, code); } Http::Code AdminImpl::runCallback(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { - HandlerPtr handler = findHandler(path_and_query, admin_stream); + RequestPtr handler = makeRequest(path_and_query, admin_stream); Http::Code code = handler->start(response_headers); bool more_data; do { @@ -331,7 +331,7 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, return code; } -Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, +Admin::RequestPtr AdminImpl::makeRequest(absl::string_view path_and_query, AdminStream& admin_stream) { std::string::size_type query_index = path_and_query.find('?'); if (query_index == std::string::npos) { @@ -345,7 +345,7 @@ Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, if (method != Http::Headers::get().MethodValues.Post) { ENVOY_LOG(error, "admin path \"{}\" mutates state, method={} rather than POST", handler.prefix_, method); - return Admin::makeStaticTextHandler( + return Admin::makeStaticTextRequest( fmt::format("Method {} not allowed, POST required.", method), Http::Code::MethodNotAllowed); } @@ -360,7 +360,7 @@ Admin::HandlerPtr AdminImpl::findHandler(absl::string_view path_and_query, Buffer::OwnedImpl error_response; error_response.add("invalid path. "); getHelp(error_response); - return Admin::makeStaticTextHandler(error_response, Http::Code::NotFound); + return Admin::makeStaticTextRequest(error_response, Http::Code::NotFound); } std::vector AdminImpl::sortedHandlers() const { @@ -443,11 +443,11 @@ const Network::Address::Instance& AdminImpl::localAddress() { AdminImpl::UrlHandler AdminImpl::makeHandler(const std::string& prefix, const std::string& help_text, HandlerCb callback, bool removable, bool mutates_state) { - return UrlHandler{prefix, help_text, HandlerGasket::makeGen(callback), removable, mutates_state}; + return UrlHandler{prefix, help_text, RequestGasket::makeGen(callback), removable, mutates_state}; } -bool AdminImpl::addChunkedHandler(const std::string& prefix, const std::string& help_text, - GenHandlerCb callback, bool removable, bool mutates_state) { +bool AdminImpl::addStreamingHandler(const std::string& prefix, const std::string& help_text, + GenRequestFn callback, bool removable, bool mutates_state) { ASSERT(prefix.size() > 1); ASSERT(prefix[0] == '/'); @@ -472,8 +472,8 @@ bool AdminImpl::addChunkedHandler(const std::string& prefix, const std::string& bool AdminImpl::addHandler(const std::string& prefix, const std::string& help_text, HandlerCb callback, bool removable, bool mutates_state) { - return addChunkedHandler(prefix, help_text, HandlerGasket::makeGen(callback), removable, - mutates_state); + return addStreamingHandler(prefix, help_text, RequestGasket::makeGen(callback), removable, + mutates_state); } bool AdminImpl::removeHandler(const std::string& prefix) { @@ -488,7 +488,7 @@ bool AdminImpl::removeHandler(const std::string& prefix) { Http::Code AdminImpl::request(absl::string_view path_and_query, absl::string_view method, Http::ResponseHeaderMap& response_headers, std::string& body) { - AdminFilter filter(createHandlerFunction()); + AdminFilter filter(createRequestFunction()); auto request_headers = Http::RequestHeaderMapImpl::create(); request_headers->setMethod(method); diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index aab2d21fc25fd..5a33fb5792016 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -84,8 +84,9 @@ class AdminImpl : public Admin, // The prefix must start with "/" and contain at least one additional character. bool addHandler(const std::string& prefix, const std::string& help_text, HandlerCb callback, bool removable, bool mutates_server_state) override; - bool addChunkedHandler(const std::string& prefix, const std::string& help_text, - GenHandlerCb callback, bool removable, bool mutates_server_state) override; + bool addStreamingHandler(const std::string& prefix, const std::string& help_text, + GenRequestFn callback, bool removable, + bool mutates_server_state) override; bool removeHandler(const std::string& prefix) override; ConfigTracker& getConfigTracker() override; @@ -202,9 +203,9 @@ class AdminImpl : public Admin, void addListenerToHandler(Network::ConnectionHandler* handler) override; Server::Instance& server() { return server_; } - GenHandlerCb createHandlerFunction() { - return [this](absl::string_view path_and_query, AdminStream& admin_stream) -> HandlerPtr { - return findHandler(path_and_query, admin_stream); + GenRequestFn createRequestFunction() { + return [this](absl::string_view path_and_query, AdminStream& admin_stream) -> RequestPtr { + return makeRequest(path_and_query, admin_stream); }; } uint64_t maxRequestsPerConnection() const override { return 0; } @@ -219,15 +220,15 @@ class AdminImpl : public Admin, struct UrlHandler { const std::string prefix_; const std::string help_text_; - const GenHandlerCb handler_; + const GenRequestFn handler_; const bool removable_; const bool mutates_server_state_; }; /** - * Creates a Handler instance given a request. + * Creates a Request from a url. */ - HandlerPtr findHandler(absl::string_view path_and_query, AdminStream& admin_stream); + RequestPtr makeRequest(absl::string_view path_and_query, AdminStream& admin_stream); /** * Creates a UrlHandler structure from a non-chunked callback. @@ -236,8 +237,8 @@ class AdminImpl : public Admin, HandlerCb callback, bool removable, bool mutates_state); /** - * Creates a URL prefix bound to chunked handler. Then handler is expected to - * supply a method makeContext(absl::string_view, AdminStream&). + * Creates a URL prefix bound to chunked handler. Handler is expected to + * supply a method makeHandler(absl::string_view, AdminStream&). * * @param prefix the prefix to register * @param help_text a help text ot display in a table in the admin home page @@ -251,8 +252,8 @@ class AdminImpl : public Admin, UrlHandler makeChunkedHandler(const std::string& prefix, const std::string& help_text, Handler& handler, bool removable, bool mutates_state) { return {prefix, help_text, - [&handler](absl::string_view path, AdminStream& admin_stream) -> Admin::HandlerPtr { - return handler.makeContext(path, admin_stream); + [&handler](absl::string_view path, AdminStream& admin_stream) -> Admin::RequestPtr { + return handler.makeHandler(path, admin_stream); }, removable, mutates_state}; } diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 6d8abedb26aff..faa10b6a34bda 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -5,7 +5,7 @@ namespace Envoy { namespace Server { -AdminFilter::AdminFilter(Admin::GenHandlerCb admin_handler_fn) +AdminFilter::AdminFilter(Admin::GenRequestFn admin_handler_fn) : admin_handler_fn_(admin_handler_fn) {} Http::FilterHeadersStatus AdminFilter::decodeHeaders(Http::RequestHeaderMap& headers, @@ -67,7 +67,7 @@ void AdminFilter::onComplete() { auto header_map = Http::ResponseHeaderMapImpl::create(); RELEASE_ASSERT(request_headers_, ""); - Admin::HandlerPtr handler = admin_handler_fn_(path, *this); + Admin::RequestPtr handler = admin_handler_fn_(path, *this); Http::Code code = handler->start(*header_map); Utility::populateFallbackResponseHeaders(code, *header_map); decoder_callbacks_->encodeHeaders(std::move(header_map), false, diff --git a/source/server/admin/admin_filter.h b/source/server/admin/admin_filter.h index babe6ab679da4..f93fb40c93995 100644 --- a/source/server/admin/admin_filter.h +++ b/source/server/admin/admin_filter.h @@ -28,7 +28,7 @@ class AdminFilter : public Http::PassThroughFilter, absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::OwnedImpl& response, AdminFilter& filter)>; - AdminFilter(Admin::GenHandlerCb admin_handler_func); + AdminFilter(Admin::GenRequestFn admin_handler_func); // Http::StreamFilterBase // Handlers relying on the reference should use addOnDestroyCallback() @@ -57,7 +57,7 @@ class AdminFilter : public Http::PassThroughFilter, * Called when an admin request has been completely received. */ void onComplete(); - Admin::GenHandlerCb admin_handler_fn_; + Admin::GenRequestFn admin_handler_fn_; Http::RequestHeaderMap* request_headers_{}; std::list> on_destroy_callbacks_; bool end_stream_on_complete_ = true; diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 03606bced17db..e4c8e5a24d83b 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -79,17 +79,30 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, } // Abstract class for rendering stats. Every method is called "generate" -// differing only by the data type, for simplifying call-sites. +// differing only by the data type, to facilitate templatized call-sites. +// +// There are currently Json and Text implementations of this interface, and in +// #19546 an HTML version will be added to provide a hierarchical view. class StatsHandler::Render { public: virtual ~Render() = default; + + // Writes a fragment for a numeric value, for counters and gauges. virtual void generate(Buffer::Instance& response, const std::string& name, uint64_t value) PURE; + + // Writes a json fragment for a textual value, for text readouts. virtual void generate(Buffer::Instance& response, const std::string& name, const std::string& value) PURE; + + // Writes a histogram value. virtual void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) PURE; + + // Completes rendering any buffered data. virtual void render(Buffer::Instance& response) PURE; - virtual bool nextChunk(Buffer::Instance& response) PURE; + + // Determines whether the current chunk is full. + bool isChunkFull(Buffer::Instance& response) { return response.length() > ChunkSize; } }; // Implements the Render interface for simple textual representation of stats. @@ -110,7 +123,6 @@ class StatsHandler::TextRender : public StatsHandler::Render { } void render(Buffer::Instance&) override {} - bool nextChunk(Buffer::Instance& response) override { return response.length() > ChunkSize; } }; // Implements the Render interface for json output. @@ -118,16 +130,27 @@ class StatsHandler::JsonRender : public StatsHandler::Render { public: JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response) { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + // We don't create a JSON data model for the entire stats output, as that + // makes streaming difficult. Instead we emit the preamble in the + // constructor here, and create json models for each stats entry. response.add("{\"stats\":["); } void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { add(response, name, ValueUtil::numberValue(value)); } + void generate(Buffer::Instance& response, const std::string& name, const std::string& value) override { add(response, name, ValueUtil::stringValue(value)); } + + // In JSON we buffer all histograms and don't write them immediately, so we + // can, in one JSON structure, emit shared attributes of all histograms and + // each individual histogram. + // + // This is counter to the goals of streaming and chunked interfaces, but + // usually there are far fewer histograms than counters or gauges. void generate(Buffer::Instance&, const std::string& name, const Stats::ParentHistogram& histogram) override { if (!found_used_histogram_) { @@ -166,6 +189,8 @@ class StatsHandler::JsonRender : public StatsHandler::Render { computed_quantile_array_.push_back(ValueUtil::structValue(computed_quantile)); } + // Since histograms are buffered (see above), the render() method generates + // all of them. void render(Buffer::Instance& response) override { if (found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); @@ -180,8 +205,6 @@ class StatsHandler::JsonRender : public StatsHandler::Render { response.add("]}"); } - bool nextChunk(Buffer::Instance& response) override { return response.length() > ChunkSize; } - private: template void add(Buffer::Instance& response, const std::string& name, const Value& value) { @@ -212,7 +235,8 @@ class StatsHandler::JsonRender : public StatsHandler::Render { bool first_{true}; }; -class StatsHandler::Context : public Admin::Handler { +// Captures context for a streaming request, implementing the AdminHandler interface. +class StatsHandler::StreamingRequest : public Admin::Request { using ScopeVec = std::vector; using StatOrScopes = absl::variant; @@ -223,10 +247,10 @@ class StatsHandler::Context : public Admin::Handler { }; public: - Context(Stats::Store& stats, bool used_only, bool json, absl::optional regex) + StreamingRequest(Stats::Store& stats, bool used_only, bool json, absl::optional regex) : used_only_(used_only), json_(json), regex_(regex), stats_(stats) {} - // Admin::Handler + // Admin::Request Http::Code start(Http::ResponseHeaderMap& response_headers) override { if (json_) { render_ = std::make_unique(response_headers, response_); @@ -253,7 +277,7 @@ class StatsHandler::Context : public Admin::Handler { response.move(response_); ASSERT(response_.length() == 0); } - while (!render_->nextChunk(response)) { + while (!render_->isChunkFull(response)) { while (stat_map_.empty()) { switch (phase_) { case Phase::TextReadouts: @@ -300,6 +324,16 @@ class StatsHandler::Context : public Admin::Handler { } return true; } + + // To duplicate prior behavior for this class, we do three passes over all the stats: + // 1. text readouts across all scopes + // 2. counters and gauges, co-mingled, across all scopes + // 3. histograms across all scopes. + // It would be little more efficient to co-mingle all the stats, but three + // passes over the scopes is OK. In the future we may decide to organize the + // result data differently, but in the process of changing from buffering + // the entire /stats response to streaming the data out in chunks, it's easier + // to reason about if the tests don't change their expectations. void startPhase() { ASSERT(stat_map_.empty()); for (const Stats::ConstScopeSharedPtr& scope : scopes_) { @@ -321,6 +355,8 @@ class StatsHandler::Context : public Admin::Handler { } } + // Determines whether the "usedonly" and "filter" status allows the stat to be + // displayed. template bool shouldShowMetric(const StatType& stat) { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); } @@ -384,7 +420,7 @@ class StatsHandler::Context : public Admin::Handler { Buffer::OwnedImpl response_; }; -Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& /*admin_stream*/) { +Admin::RequestPtr StatsHandler::makeHandler(absl::string_view path, AdminStream& /*admin_stream*/) { if (server_.statsConfig().flushOnAdmin()) { server_.flushStats(); } @@ -395,7 +431,7 @@ Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& absl::optional regex; Buffer::OwnedImpl response; if (!Utility::filterParam(params, response, regex)) { - return Admin::makeStaticTextHandler(response, Http::Code::BadRequest); + return Admin::makeStaticTextRequest(response, Http::Code::BadRequest); } const absl::optional format_value = Utility::formatParam(params); @@ -404,21 +440,21 @@ Admin::HandlerPtr StatsHandler::makeContext(absl::string_view path, AdminStream& if (format_value.value() == "prometheus") { Buffer::OwnedImpl response; Http::Code code = prometheusStats(path, response); - return Admin::makeStaticTextHandler(response, code); + return Admin::makeStaticTextRequest(response, code); } else if (format_value.value() == "json") { json = true; } else { - return Admin::makeStaticTextHandler( + return Admin::makeStaticTextRequest( "usage: /stats?format=json or /stats?format=prometheus \n\n", Http::Code::BadRequest); } } - return makeContext(server_.stats(), used_only, json, regex); + return makeHandler(server_.stats(), used_only, json, regex); } -Admin::HandlerPtr StatsHandler::makeContext(Stats::Store& stats, bool used_only, bool json, +Admin::RequestPtr StatsHandler::makeHandler(Stats::Store& stats, bool used_only, bool json, const absl::optional& regex) { - return std::make_unique(stats, used_only, json, regex); + return std::make_unique(stats, used_only, json, regex); } #if 0 diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index cba02a3aa71a4..4bcb77a73d01b 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -45,14 +45,14 @@ class StatsHandler : public HandlerContextBase { Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); - Admin::HandlerPtr makeContext(absl::string_view path, AdminStream& admin_stream); - static Admin::HandlerPtr makeContext(Stats::Store& stats, bool used_only, bool json, + Admin::RequestPtr makeHandler(absl::string_view path, AdminStream& admin_stream); + static Admin::RequestPtr makeHandler(Stats::Store& stats, bool used_only, bool json, const absl::optional& regex); class JsonRender; class Render; class TextRender; - class Context; + class StreamingRequest; private: template diff --git a/source/server/config_validation/admin.cc b/source/server/config_validation/admin.cc index 981ba4eab1da8..d9ac638729c33 100644 --- a/source/server/config_validation/admin.cc +++ b/source/server/config_validation/admin.cc @@ -4,8 +4,8 @@ namespace Envoy { namespace Server { // Pretend that handler was added successfully. -bool ValidationAdmin::addChunkedHandler(const std::string&, const std::string&, GenHandlerCb, bool, - bool) { +bool ValidationAdmin::addStreamingHandler(const std::string&, const std::string&, GenHandlerCb, + bool, bool) { return true; } bool ValidationAdmin::addHandler(const std::string&, const std::string&, HandlerCb, bool, bool) { diff --git a/source/server/config_validation/admin.h b/source/server/config_validation/admin.h index 3c4b6dbd2d596..c1856bfdd8fa5 100644 --- a/source/server/config_validation/admin.h +++ b/source/server/config_validation/admin.h @@ -24,7 +24,8 @@ class ValidationAdmin : public Admin { nullptr) : nullptr) {} bool addHandler(const std::string&, const std::string&, HandlerCb, bool, bool) override; - bool addChunkedHandler(const std::string&, const std::string&, GenHandlerCb, bool, bool) override; + bool addStreamingHandler(const std::string&, const std::string&, GenHandlerCb, bool, + bool) override; bool removeHandler(const std::string&) override; const Network::Socket& socket() override; ConfigTracker& getConfigTracker() override; diff --git a/test/mocks/server/admin.h b/test/mocks/server/admin.h index 451b36bfbe0c6..d2f96eed5aea4 100644 --- a/test/mocks/server/admin.h +++ b/test/mocks/server/admin.h @@ -24,8 +24,8 @@ class MockAdmin : public Admin { MOCK_METHOD(bool, addHandler, (const std::string& prefix, const std::string& help_text, HandlerCb callback, bool removable, bool mutates_server_state)); - MOCK_METHOD(bool, addChunkedHandler, - (const std::string& prefix, const std::string& help_text, GenHandlerCb callback, + MOCK_METHOD(bool, addStreamingHandler, + (const std::string& prefix, const std::string& help_text, GenRequestFn callback, bool removable, bool mutates_server_state)); MOCK_METHOD(bool, removeHandler, (const std::string& prefix)); MOCK_METHOD(Network::Socket&, socket, ()); diff --git a/test/server/admin/admin_filter_test.cc b/test/server/admin/admin_filter_test.cc index 940272dc4a228..a780c5b92a415 100644 --- a/test/server/admin/admin_filter_test.cc +++ b/test/server/admin/admin_filter_test.cc @@ -25,12 +25,12 @@ class AdminFilterTest : public testing::TestWithParam callbacks_; Http::TestRequestHeaderMapImpl request_headers_; - static Admin::HandlerPtr adminHandlerCallback(absl::string_view path_and_query, + static Admin::RequestPtr adminHandlerCallback(absl::string_view path_and_query, AdminStream& admin_stream) { // silence compiler warnings for unused params UNREFERENCED_PARAMETER(path_and_query); UNREFERENCED_PARAMETER(admin_stream); - return AdminImpl::makeStaticTextHandler("OK\n", Http::Code::OK); + return AdminImpl::makeStaticTextRequest("OK\n", Http::Code::OK); } }; diff --git a/test/server/admin/admin_instance.cc b/test/server/admin/admin_instance.cc index a02e9585dd2d5..83c40aa841b44 100644 --- a/test/server/admin/admin_instance.cc +++ b/test/server/admin/admin_instance.cc @@ -10,7 +10,7 @@ AdminInstanceTest::AdminInstanceTest() : address_out_path_(TestEnvironment::temporaryPath("admin.address")), cpu_profile_path_(TestEnvironment::temporaryPath("envoy.prof")), admin_(cpu_profile_path_, server_, false), request_headers_{{":path", "/"}}, - admin_filter_(admin_.createHandlerFunction()) { + admin_filter_(admin_.createRequestFunction()) { std::list access_logs; Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, "/dev/null"}; access_logs.emplace_back(new Extensions::AccessLoggers::File::FileAccessLog( diff --git a/test/server/admin/admin_test.cc b/test/server/admin/admin_test.cc index d9086e45bff4b..9993ed8df262d 100644 --- a/test/server/admin/admin_test.cc +++ b/test/server/admin/admin_test.cc @@ -123,7 +123,7 @@ TEST_P(AdminInstanceTest, CustomHandler) { EXPECT_EQ(Http::Code::Accepted, getCallback("/foo/bar", header_map, response)); } -class ChunkedHandler : public Admin::Handler { +class ChunkedHandler : public Admin::Request { public: Http::Code start(Http::ResponseHeaderMap&) override { return Http::Code::OK; } @@ -137,13 +137,14 @@ class ChunkedHandler : public Admin::Handler { }; TEST_P(AdminInstanceTest, CustomChunkedHandler) { - auto callback = [](absl::string_view, AdminStream&) -> Admin::HandlerPtr { - Admin::HandlerPtr handler = Admin::HandlerPtr(new ChunkedHandler); + auto callback = [](absl::string_view, AdminStream&) -> Admin::RequestPtr { + Admin::RequestPtr handler = Admin::RequestPtr(new ChunkedHandler); return handler; }; // Test removable handler. - EXPECT_NO_LOGS(EXPECT_TRUE(admin_.addChunkedHandler("/foo/bar", "hello", callback, true, false))); + EXPECT_NO_LOGS( + EXPECT_TRUE(admin_.addStreamingHandler("/foo/bar", "hello", callback, true, false))); Http::TestResponseHeaderMapImpl header_map; { Buffer::OwnedImpl response; @@ -158,11 +159,11 @@ TEST_P(AdminInstanceTest, CustomChunkedHandler) { EXPECT_FALSE(admin_.removeHandler("/foo/bar")); // Add non removable handler. - EXPECT_TRUE(admin_.addChunkedHandler("/foo/bar", "hello", callback, false, false)); + EXPECT_TRUE(admin_.addStreamingHandler("/foo/bar", "hello", callback, false, false)); EXPECT_EQ(Http::Code::OK, getCallback("/foo/bar", header_map, response)); // Add again and make sure it is not there twice. - EXPECT_FALSE(admin_.addChunkedHandler("/foo/bar", "hello", callback, false, false)); + EXPECT_FALSE(admin_.addStreamingHandler("/foo/bar", "hello", callback, false, false)); // Try to remove non removable handler, and make sure it is not removed. EXPECT_FALSE(admin_.removeHandler("/foo/bar")); diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 966610cbe9d46..0a0cef15880b1 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -72,7 +72,7 @@ class StatsHandlerTest { EXPECT_CALL(instance, api()).WillRepeatedly(ReturnRef(api_)); EXPECT_CALL(api_, customStatNamespaces()).WillRepeatedly(ReturnRef(custom_namespaces_)); StatsHandler handler(instance); - Admin::HandlerPtr context = handler.makeContext(url, admin_stream_); + Admin::RequestPtr context = handler.makeHandler(url, admin_stream_); Http::TestResponseHeaderMapImpl response_headers; Http::Code code = context->start(response_headers); Buffer::OwnedImpl data; @@ -719,6 +719,21 @@ TEST_P(AdminStatsTest, SortedCountersAndGauges) { } } +TEST_P(AdminStatsTest, SortedScopes) { + // Check counters and gauges are co-mingled in sorted order in the admin output. + Stats::ScopeSharedPtr scope = store_->createScope("scope"); + scope->counterFromString("r"); + scope->counterFromString("s"); + scope->counterFromString("t"); + Stats::ScopeSharedPtr subscope = scope->createScope("subscope"); + subscope->counterFromString("x"); + for (const std::string& url : {"/stats", "/stats?format=json"}) { + CodeResponse code_response = handlerStats(url); + ASSERT_EQ(Http::Code::OK, code_response.first); + checkOrder(code_response.second, {"scope.r", "scope.s", "scope.subscope.x", "scope.t"}); + } +} + TEST_P(AdminStatsTest, SortedTextReadouts) { // Check counters and gauges are co-mingled in sorted order in the admin output. store_->textReadoutFromString("t4"); From c287a9651fe6f238e38c030388bfe17caf3f3897 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 20 Feb 2022 21:58:02 -0500 Subject: [PATCH 37/74] finish name-change edits. Signed-off-by: Joshua Marantz --- source/server/config_validation/admin.cc | 2 +- source/server/config_validation/admin.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/server/config_validation/admin.cc b/source/server/config_validation/admin.cc index d9ac638729c33..132fe44356263 100644 --- a/source/server/config_validation/admin.cc +++ b/source/server/config_validation/admin.cc @@ -4,7 +4,7 @@ namespace Envoy { namespace Server { // Pretend that handler was added successfully. -bool ValidationAdmin::addStreamingHandler(const std::string&, const std::string&, GenHandlerCb, +bool ValidationAdmin::addStreamingHandler(const std::string&, const std::string&, GenRequestFn, bool, bool) { return true; } diff --git a/source/server/config_validation/admin.h b/source/server/config_validation/admin.h index c1856bfdd8fa5..25891f5af71e5 100644 --- a/source/server/config_validation/admin.h +++ b/source/server/config_validation/admin.h @@ -24,7 +24,7 @@ class ValidationAdmin : public Admin { nullptr) : nullptr) {} bool addHandler(const std::string&, const std::string&, HandlerCb, bool, bool) override; - bool addStreamingHandler(const std::string&, const std::string&, GenHandlerCb, bool, + bool addStreamingHandler(const std::string&, const std::string&, GenRequestFn, bool, bool) override; bool removeHandler(const std::string&) override; const Network::Socket& socket() override; From 6e524823f44e0c878017b1e7fa32c9c89151708f Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 20 Feb 2022 22:30:56 -0500 Subject: [PATCH 38/74] cleanup Signed-off-by: Joshua Marantz --- envoy/server/admin.h | 4 ++-- source/server/admin/stats_handler.cc | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 278603918c8db..becda65d47e24 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -159,7 +159,7 @@ class Admin { * * @param prefix supplies the URL prefix to handle. * @param help_text supplies the help text for the handler. - * @param callback supplies the callback to generate a Request. + * @param gen_request supplies the callback to generate a Request. * @param removable if true allows the handler to be removed via removeHandler. * @param mutates_server_state indicates whether callback will mutate server state. * @return bool true if the handler was added, false if it was not added. @@ -229,7 +229,7 @@ class Admin { * Makes a chunked handler for static text. The version that takes the * Buffer::Instance& transfers the content from the passed-in buffer. * - * @param resposne_text the text to populate response with + * @param response_text the text to populate response with * @param code the Http::Code for the response * @return the handler */ diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index e4c8e5a24d83b..76a875587b38b 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -414,8 +414,7 @@ class StatsHandler::StreamingRequest : public Admin::Request { Stats::Store& stats_; ScopeVec scopes_; - using StatMap = absl::btree_map; - StatMap stat_map_; + absl::btree_map stat_map_; Phase phase_{Phase::TextReadouts}; Buffer::OwnedImpl response_; }; From d02ab701c723a04715d9eaab06e7120703986b82 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 20 Feb 2022 23:06:47 -0500 Subject: [PATCH 39/74] more comments & tests. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 57 +++++++++++++++---------- test/server/admin/stats_handler_test.cc | 5 ++- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 76a875587b38b..93cbaa7e85bac 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -271,6 +271,31 @@ class StatsHandler::StreamingRequest : public Admin::Request { return Http::Code::OK; } + // nextChunkI() Streams out the next chunk of stats to the client, visiting + // only the scopes that can plausibly contribute the next set of named + // stats. This enables us to linearly traverse the entire set of stats without + // buffering all of them and sorting. + // + // Instead we keep the a set of candidate stats to emit in stat_map_ an + // alphabetically ordered btree, which heterogeneously stores stats of all + // types and scopes. Note that there can be multiple scopes with the same + // name, so we keep same-named scopes in a vector. However leaf metrics cannot + // have duplicates. It would also be feasible to use a multi-map for this. + // + // So in start() above, we initially populate all the scopes, as well as the + // metrics contained in all scopes with an empty name. So in nextChunk we can + // emit and remove the first element of stat_map_. When we encounter a vector + // of scopes then we add the contained metrics to the map and continue + // iterating. + // + // Whenever the desired chunk size is reached we end the current chunk so that + // the current buffer can be flushed to the network. In #19898 we will + // introduce flow-control so that we don't buffer the all the serialized stats + // while waiting for a slow client. + // + // Note that we do 3 passes through all the scopes_, so that we can emit + // text-readouts first, then the intermingled counters and gauges, and finally + // the histograms. bool nextChunk(Buffer::Instance& response) override { if (response_.length() > 0) { ASSERT(response.length() == 0); @@ -361,6 +386,8 @@ class StatsHandler::StreamingRequest : public Admin::Request { return StatsHandler::shouldShowMetric(stat, used_only_, regex_); } + // Iterates over scope_vec and populates the metric types associated with the + // current phase. void populateStatsForCurrentPhase(const ScopeVec& scope_vec) { switch (phase_) { case Phase::TextReadouts: @@ -376,6 +403,9 @@ class StatsHandler::StreamingRequest : public Admin::Request { } } + // Populates all the metrics of the templatized type from scope_vec. Here we + // exploit that Scope::iterate is a generic templatized function to avoid code + // duplication. template void populateStatsFromScopes(const ScopeVec& scope_vec) { for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { @@ -386,6 +416,8 @@ class StatsHandler::StreamingRequest : public Admin::Request { } } + // Renders the templatized type, exploiting the fact that Render::generate is + // generic to avoid code duplication. template void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant) { auto stat = absl::get(variant); @@ -395,6 +427,8 @@ class StatsHandler::StreamingRequest : public Admin::Request { render_->generate(response, name, stat->value()); } + // Determines whether the specified stat should be skipped based on the + // "usedonly" and "filter" query-params. template bool skip(const SharedStatType& stat, const std::string& name) { if (used_only_ && !stat->used()) { return true; @@ -405,6 +439,7 @@ class StatsHandler::StreamingRequest : public Admin::Request { return false; } +private: const bool used_only_; const bool json_; absl::optional regex_; @@ -456,28 +491,6 @@ Admin::RequestPtr StatsHandler::makeHandler(Stats::Store& stats, bool used_only, return std::make_unique(stats, used_only, json, regex); } -#if 0 -struct CompareStatsAndScopes { - CompareStatsAndScopes(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table) {} - bool operator()(const StatOrScope& a, const StatOrScope& b) const { - return symbol_table_.lessThan(name(a), name(b)); - } - - Stats::StatName name(const StatOrScope& stat_or_scope) const { - switch (stat_or_scope.index()) { - case 0: return absl::get(stat_or_scope)->prefix(); - case 1: return absl::get(stat_or_scope)->statName(); - case 2: return absl::get(stat_or_scope)->statName(); - case 3: return absl::get(stat_or_scope)->statName(); - case 4: return absl::get(stat_or_scope)->statName(); - } - return Stats::StatName(); - } - - Stats::SymbolTable& symbol_table_; -}; -#endif - Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, Http::ResponseHeaderMap&, Buffer::Instance& response, AdminStream&) { diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 0a0cef15880b1..f376904962186 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -721,6 +721,8 @@ TEST_P(AdminStatsTest, SortedCountersAndGauges) { TEST_P(AdminStatsTest, SortedScopes) { // Check counters and gauges are co-mingled in sorted order in the admin output. + store_->counterFromString("a"); + store_->counterFromString("z"); Stats::ScopeSharedPtr scope = store_->createScope("scope"); scope->counterFromString("r"); scope->counterFromString("s"); @@ -730,7 +732,8 @@ TEST_P(AdminStatsTest, SortedScopes) { for (const std::string& url : {"/stats", "/stats?format=json"}) { CodeResponse code_response = handlerStats(url); ASSERT_EQ(Http::Code::OK, code_response.first); - checkOrder(code_response.second, {"scope.r", "scope.s", "scope.subscope.x", "scope.t"}); + checkOrder(code_response.second, + {"a", "scope.r", "scope.s", "scope.subscope.x", "scope.t", "z"}); } } From 4f28d1e1fcd4d96c39c148da2f69d7193a25631f Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 21 Feb 2022 11:31:41 -0500 Subject: [PATCH 40/74] add benchmark. Signed-off-by: Joshua Marantz --- source/server/admin/admin.h | 4 +- source/server/admin/stats_handler.cc | 40 ++---- source/server/admin/stats_handler.h | 11 +- test/server/admin/BUILD | 13 ++ test/server/admin/stats_handler_speed_test.cc | 131 ++++++++++++++++++ test/server/admin/stats_handler_test.cc | 6 +- 6 files changed, 164 insertions(+), 41 deletions(-) create mode 100644 test/server/admin/stats_handler_speed_test.cc diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 5a33fb5792016..d160f5af913f2 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -238,7 +238,7 @@ class AdminImpl : public Admin, /** * Creates a URL prefix bound to chunked handler. Handler is expected to - * supply a method makeHandler(absl::string_view, AdminStream&). + * supply a method makeRequest(absl::string_view, AdminStream&). * * @param prefix the prefix to register * @param help_text a help text ot display in a table in the admin home page @@ -253,7 +253,7 @@ class AdminImpl : public Admin, Handler& handler, bool removable, bool mutates_state) { return {prefix, help_text, [&handler](absl::string_view path, AdminStream& admin_stream) -> Admin::RequestPtr { - return handler.makeHandler(path, admin_stream); + return handler.makeRequest(path, admin_stream); }, removable, mutates_state}; } diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 93cbaa7e85bac..e5abecd86d40d 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -337,12 +337,12 @@ class StatsHandler::StreamingRequest : public Admin::Request { break; case 4: { auto histogram = absl::get(variant); - if (!skip(histogram, name)) { + //if (!skip(histogram, name)) { auto parent_histogram = dynamic_cast(histogram.get()); if (parent_histogram != nullptr) { render_->generate(response, name, *parent_histogram); } - } + //} } } stat_map_.erase(iter); @@ -380,12 +380,6 @@ class StatsHandler::StreamingRequest : public Admin::Request { } } - // Determines whether the "usedonly" and "filter" status allows the stat to be - // displayed. - template bool shouldShowMetric(const StatType& stat) { - return StatsHandler::shouldShowMetric(stat, used_only_, regex_); - } - // Iterates over scope_vec and populates the metric types associated with the // current phase. void populateStatsForCurrentPhase(const ScopeVec& scope_vec) { @@ -409,7 +403,14 @@ class StatsHandler::StreamingRequest : public Admin::Request { template void populateStatsFromScopes(const ScopeVec& scope_vec) { for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { - stat_map_[stat->name()] = stat; + if (used_only_ && !stat->used()) { + return true; + } + std::string name = stat->name(); + if (regex_.has_value() && !std::regex_search(name, regex_.value())) { + return true; + } + stat_map_[name] = stat; return true; }; scope->iterate(fn); @@ -421,24 +422,9 @@ class StatsHandler::StreamingRequest : public Admin::Request { template void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant) { auto stat = absl::get(variant); - if (skip(stat, name)) { - return; - } render_->generate(response, name, stat->value()); } - // Determines whether the specified stat should be skipped based on the - // "usedonly" and "filter" query-params. - template bool skip(const SharedStatType& stat, const std::string& name) { - if (used_only_ && !stat->used()) { - return true; - } - if (regex_.has_value() && !std::regex_search(name, regex_.value())) { - return true; - } - return false; - } - private: const bool used_only_; const bool json_; @@ -454,7 +440,7 @@ class StatsHandler::StreamingRequest : public Admin::Request { Buffer::OwnedImpl response_; }; -Admin::RequestPtr StatsHandler::makeHandler(absl::string_view path, AdminStream& /*admin_stream*/) { +Admin::RequestPtr StatsHandler::makeRequest(absl::string_view path, AdminStream& /*admin_stream*/) { if (server_.statsConfig().flushOnAdmin()) { server_.flushStats(); } @@ -483,10 +469,10 @@ Admin::RequestPtr StatsHandler::makeHandler(absl::string_view path, AdminStream& } } - return makeHandler(server_.stats(), used_only, json, regex); + return makeRequest(server_.stats(), used_only, json, regex); } -Admin::RequestPtr StatsHandler::makeHandler(Stats::Store& stats, bool used_only, bool json, +Admin::RequestPtr StatsHandler::makeRequest(Stats::Store& stats, bool used_only, bool json, const absl::optional& regex) { return std::make_unique(stats, used_only, json, regex); } diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 4bcb77a73d01b..ea775435f75d5 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -45,8 +45,8 @@ class StatsHandler : public HandlerContextBase { Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); - Admin::RequestPtr makeHandler(absl::string_view path, AdminStream& admin_stream); - static Admin::RequestPtr makeHandler(Stats::Store& stats, bool used_only, bool json, + Admin::RequestPtr makeRequest(absl::string_view path, AdminStream& admin_stream); + static Admin::RequestPtr makeRequest(Stats::Store& stats, bool used_only, bool json, const absl::optional& regex); class JsonRender; @@ -55,13 +55,6 @@ class StatsHandler : public HandlerContextBase { class StreamingRequest; private: - template - static bool shouldShowMetric(const StatType& metric, const bool used_only, - const absl::optional& regex) { - return ((!used_only || metric.used()) && - (!regex.has_value() || std::regex_search(metric.name(), regex.value()))); - } - friend class StatsHandlerTest; }; diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 9998c58138dc3..4407f74ad96bc 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -1,5 +1,6 @@ load( "//bazel:envoy_build_system.bzl", + "envoy_cc_benchmark_binary", "envoy_cc_test", "envoy_cc_test_library", "envoy_package", @@ -68,6 +69,18 @@ envoy_cc_test( ], ) +envoy_cc_benchmark_binary( + name = "stats_handler_speed_test", + srcs = ["stats_handler_speed_test.cc"], + deps = [ + "//source/common/buffer:buffer_lib", + "//source/common/http:header_map_lib", + "//source/common/stats:thread_local_store_lib", + "//source/server/admin:admin_lib", + "//source/server/admin:stats_handler_lib", + ], +) + envoy_cc_test( name = "runtime_handler_test", srcs = ["runtime_handler_test.cc"], diff --git a/test/server/admin/stats_handler_speed_test.cc b/test/server/admin/stats_handler_speed_test.cc new file mode 100644 index 0000000000000..49b4bd6852867 --- /dev/null +++ b/test/server/admin/stats_handler_speed_test.cc @@ -0,0 +1,131 @@ +// Note: this should be run with --compilation_mode=opt, and would benefit from a +// quiescent system with disabled cstate power management. + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/http/header_map_impl.h" +#include "source/common/stats/thread_local_store.h" +#include "source/server/admin/stats_handler.h" + +#include "benchmark/benchmark.h" + +namespace Envoy { +namespace Server { + +class StatsHandlerTest { +public: + StatsHandlerTest() : alloc_(symbol_table_), store_(alloc_) { + // Benchmark will be 10k clusters each with 100 counters, with 100+ + // character names. The first counter in each scope will be given a value so + // it will be included in 'usedonly'. + std::string prefix(100, 'a'); + for (uint32_t s = 0; s < 10000; ++s) { + Stats::ScopeSharedPtr scope = store_.createScope(absl::StrCat("scope_", s)); + scopes_.emplace_back(scope); + for (uint32_t c = 0; c < 100; ++c) { + Stats::Counter& counter = scope->counterFromString(absl::StrCat(prefix, "_", c)); + if (c == 0) { + counter.inc(); + } + } + } + } + + /** + * Issues an admin request against the stats saved in store_. + * + * @param path the admin endpoint to query. + * @return the Http Code and the response body as a string. + */ + uint64_t handlerStats(bool used_only, bool json, const absl::optional& filter) { + Admin::RequestPtr request = StatsHandler::makeRequest(store_, used_only, json, filter); + auto response_headers = Http::ResponseHeaderMapImpl::create(); + request->start(*response_headers); + Buffer::OwnedImpl data; + uint64_t count = 0; + bool more = true; + do { + more = request->nextChunk(data); + count += data.length(); + data.drain(data.length()); + } while (more); + return count; + } + + Stats::SymbolTableImpl symbol_table_; + Stats::AllocatorImpl alloc_; + Stats::ThreadLocalStoreImpl store_; + std::vector scopes_; +}; + +} // namespace Server +} // namespace Envoy + +Envoy::Server::StatsHandlerTest& testContext() { + MUTABLE_CONSTRUCT_ON_FIRST_USE(Envoy::Server::StatsHandlerTest); +} + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_AllCountersText(benchmark::State& state) { + Envoy::Server::StatsHandlerTest& test_context = testContext(); + for (auto _ : state) { // NOLINT + uint64_t count = test_context.handlerStats(false, false, absl::nullopt); + RELEASE_ASSERT(count > 100 * 1000 * 1000, "expected count > 100M"); // actual = 117,789,000 + } +} +BENCHMARK(BM_AllCountersText)->Unit(benchmark::kMillisecond); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_UsedCountersText(benchmark::State& state) { + Envoy::Server::StatsHandlerTest& test_context = testContext(); + for (auto _ : state) { // NOLINT + uint64_t count = test_context.handlerStats(true, false, absl::nullopt); + RELEASE_ASSERT(count > 1000 * 1000, "expected count > 1M"); + RELEASE_ASSERT(count < 2 * 1000 * 1000, "expected count < 2M"); // actual = 1,168,890 + } +} +BENCHMARK(BM_UsedCountersText)->Unit(benchmark::kMillisecond); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_FilteredCountersText(benchmark::State& state) { + Envoy::Server::StatsHandlerTest& test_context = testContext(); + absl::optional filter(std::regex("no-match")); + + for (auto _ : state) { // NOLINT + uint64_t count = test_context.handlerStats(false, false, filter); + RELEASE_ASSERT(count == 0, "expected count == 0"); + } +} +BENCHMARK(BM_FilteredCountersText)->Unit(benchmark::kMillisecond); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_AllCountersJson(benchmark::State& state) { + Envoy::Server::StatsHandlerTest& test_context = testContext(); + for (auto _ : state) { // NOLINT + uint64_t count = test_context.handlerStats(false, true, absl::nullopt); + RELEASE_ASSERT(count > 130 * 1000 * 1000, "expected count > 1130M"); // actual = 135,789,011 + } +} +BENCHMARK(BM_AllCountersJson)->Unit(benchmark::kMillisecond); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_UsedCountersJson(benchmark::State& state) { + Envoy::Server::StatsHandlerTest& test_context = testContext(); + for (auto _ : state) { // NOLINT + uint64_t count = test_context.handlerStats(true, true, absl::nullopt); + RELEASE_ASSERT(count > 1000 * 1000, "expected count > 1M"); + RELEASE_ASSERT(count < 2 * 1000 * 1000, "expected count < 2M"); // actual = 1,348,901 + } +} +BENCHMARK(BM_UsedCountersJson)->Unit(benchmark::kMillisecond); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_FilteredCountersJson(benchmark::State& state) { + Envoy::Server::StatsHandlerTest& test_context = testContext(); + absl::optional filter(std::regex("no-match")); + + for (auto _ : state) { // NOLINT + uint64_t count = test_context.handlerStats(false, true, filter); + RELEASE_ASSERT(count < 100, "expected count < 100"); // actual = 12 + } +} +BENCHMARK(BM_FilteredCountersJson)->Unit(benchmark::kMillisecond); diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index f376904962186..c0a630bb95c01 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -72,11 +72,11 @@ class StatsHandlerTest { EXPECT_CALL(instance, api()).WillRepeatedly(ReturnRef(api_)); EXPECT_CALL(api_, customStatNamespaces()).WillRepeatedly(ReturnRef(custom_namespaces_)); StatsHandler handler(instance); - Admin::RequestPtr context = handler.makeHandler(url, admin_stream_); + Admin::RequestPtr request = handler.makeRequest(url, admin_stream_); Http::TestResponseHeaderMapImpl response_headers; - Http::Code code = context->start(response_headers); + Http::Code code = request->start(response_headers); Buffer::OwnedImpl data; - while (context->nextChunk(data)) { + while (request->nextChunk(data)) { } return std::make_pair(code, data.toString()); } From 920123a61920e2c642ab1678d39149bb5bb66746 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 21 Feb 2022 11:34:38 -0500 Subject: [PATCH 41/74] format Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index e5abecd86d40d..1e41672670a12 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -337,12 +337,10 @@ class StatsHandler::StreamingRequest : public Admin::Request { break; case 4: { auto histogram = absl::get(variant); - //if (!skip(histogram, name)) { - auto parent_histogram = dynamic_cast(histogram.get()); - if (parent_histogram != nullptr) { - render_->generate(response, name, *parent_histogram); - } - //} + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, name, *parent_histogram); + } } } stat_map_.erase(iter); From 47ca61b26e36afcbac31089a57d3ff37e0793faa Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 21 Feb 2022 12:46:23 -0500 Subject: [PATCH 42/74] buffer up 64 stats before calling the json serialize function; that appears to be close to optimal performance. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 35 ++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 1e41672670a12..0814641536a0c 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -18,6 +18,7 @@ namespace { constexpr uint64_t ChunkSize = 2 * 1000 * 1000; +constexpr uint64_t JsonStatsFlushCount = 64; // This value found by iterating in benchmark. } // namespace namespace Envoy { @@ -192,6 +193,9 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // Since histograms are buffered (see above), the render() method generates // all of them. void render(Buffer::Instance& response) override { + if (!stats_array_.empty()) { + flushStats(response); + } if (found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); (*histograms_obj_fields)["computed_quantiles"] = @@ -212,11 +216,34 @@ class StatsHandler::JsonRender : public StatsHandler::Render { auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); (*stat_obj_fields)["value"] = value; - auto str = MessageUtil::getJsonStringFromMessageOrDie(stat_obj, false /* pretty */, true); - addStatJson(response, str); + stats_array_.push_back(ValueUtil::structValue(stat_obj)); + + // We build up stats_array to a certain size so we can amortize the overhead + // of entering into the JSON serialization infrastructure. If we set the + // threshold too high we buffer too much memory, likely impacting processor + // cache. The optimum threshold found after a few experiments on a local + // host appears to be between 50 and 100. + if (stats_array_.size() == JsonStatsFlushCount) { + flushStats(response); + } + } + + void flushStats(Buffer::Instance& response) { + std::string str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::listValue(stats_array_), + false /* pretty */, true); + stats_array_.clear(); + + // We are going to wind up with multiple flushes which have to serialize as + // a single array, rather than a concatenation of multiple arrays, so we add + // those in the constructor and render() method, strip off the "[" and "]" + // from each buffered serialization. + ASSERT(!str.empty()); + ASSERT(str[0] == '['); + ASSERT(str[str.size() - 1] == ']'); + addStatJson(response, absl::string_view(str).substr(1, str.size() - 2)); } - void addStatJson(Buffer::Instance& response, const std::string& json) { + void addStatJson(Buffer::Instance& response, absl::string_view json) { if (first_) { response.add(json); first_ = false; @@ -225,9 +252,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } - ProtobufWkt::Struct document_; std::vector stats_array_; - std::vector scope_array_; ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; std::vector computed_quantile_array_; From 234da74ae0ffaa55e41ab54f92a1fb109653782d Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 21 Feb 2022 22:11:54 -0500 Subject: [PATCH 43/74] add comments, const delcarations, improve var names Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 28 ++++++++++++------- test/server/admin/stats_handler_speed_test.cc | 2 +- test/server/admin/stats_handler_test.cc | 13 ++------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 0814641536a0c..3d22476792da7 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -210,6 +210,10 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } private: + // Collects a scalar metric (text-readout, counter, or gauge) into an array of + // stats, so they can all be serialized in one shot when a threshold is + // reached. Serializing each one individually results in much worse + // performance (see stats_handler_speed_test.cc). template void add(Buffer::Instance& response, const std::string& name, const Value& value) { ProtobufWkt::Struct stat_obj; @@ -228,21 +232,25 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } + // Flushes all scalar stats that were buffered in add() above. void flushStats(Buffer::Instance& response) { - std::string str = MessageUtil::getJsonStringFromMessageOrDie(ValueUtil::listValue(stats_array_), - false /* pretty */, true); + ASSERT(!stats_array_.empty()); + const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( + ValueUtil::listValue(stats_array_), false /* pretty */, true); stats_array_.clear(); // We are going to wind up with multiple flushes which have to serialize as // a single array, rather than a concatenation of multiple arrays, so we add // those in the constructor and render() method, strip off the "[" and "]" // from each buffered serialization. - ASSERT(!str.empty()); - ASSERT(str[0] == '['); - ASSERT(str[str.size() - 1] == ']'); - addStatJson(response, absl::string_view(str).substr(1, str.size() - 2)); + ASSERT(json_array.size() >= 2); + ASSERT(json_array[0] == '['); + ASSERT(json_array[json_array.size() - 1] == ']'); + addStatJson(response, absl::string_view(json_array).substr(1, json_array.size() - 2)); } + // Adds a json fragment of scalar stats to the response buffer, including a + // "," delimiter if this is not the first fragment. void addStatJson(Buffer::Instance& response, absl::string_view json) { if (first_) { response.add(json); @@ -296,10 +304,10 @@ class StatsHandler::StreamingRequest : public Admin::Request { return Http::Code::OK; } - // nextChunkI() Streams out the next chunk of stats to the client, visiting - // only the scopes that can plausibly contribute the next set of named - // stats. This enables us to linearly traverse the entire set of stats without - // buffering all of them and sorting. + // Streams out the next chunk of stats to the client, visiting only the scopes + // that can plausibly contribute the next set of named stats. This enables us + // to linearly traverse the entire set of stats without buffering all of them + // and sorting. // // Instead we keep the a set of candidate stats to emit in stat_map_ an // alphabetically ordered btree, which heterogeneously stores stats of all diff --git a/test/server/admin/stats_handler_speed_test.cc b/test/server/admin/stats_handler_speed_test.cc index 49b4bd6852867..18429e5c7c4d3 100644 --- a/test/server/admin/stats_handler_speed_test.cc +++ b/test/server/admin/stats_handler_speed_test.cc @@ -17,7 +17,7 @@ class StatsHandlerTest { // Benchmark will be 10k clusters each with 100 counters, with 100+ // character names. The first counter in each scope will be given a value so // it will be included in 'usedonly'. - std::string prefix(100, 'a'); + const std::string prefix(100, 'a'); for (uint32_t s = 0; s < 10000; ++s) { Stats::ScopeSharedPtr scope = store_.createScope(absl::StrCat("scope_", s)); scopes_.emplace_back(scope); diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index c0a630bb95c01..661f6b6c04adf 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -821,17 +821,8 @@ TEST_P(AdminInstanceTest, RecentLookups) { EXPECT_THAT(body, HasSubstr("Lookup tracking is not enabled")); EXPECT_THAT(std::string(response_headers.getContentTypeValue()), HasSubstr("text/plain")); - EXPECT_EQ(Http::Code::OK, - admin_.request("/stats/recentlookups/enable", "POST", response_headers, body)); - Stats::StatNamePool pool(server_.stats().symbolTable()); - pool.add("alpha"); - pool.add("beta"); - pool.add("gamma"); - pool.add("alpha"); - pool.add("beta"); - pool.add("alpha"); - EXPECT_EQ(Http::Code::OK, admin_.request("/stats/recentlookups", "GET", response_headers, body)); - EXPECT_THAT(body, HasSubstr(" 1 gamma\n 2 beta\n 3 alpha\n")); + // 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. } class StatsHandlerPrometheusTest : public StatsHandlerTest { From 703503516b500e0cf328155dc9e6552fa04844cc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 15:07:44 -0500 Subject: [PATCH 44/74] compile works, need to fix json. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 31 +++++++++---------- source/server/admin/stats_handler.h | 6 ++-- test/server/admin/stats_handler_speed_test.cc | 5 ++- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 50975d5bd1d68..10e433ee9c581 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -139,15 +139,15 @@ class StatsHandler::TextRender : public StatsHandler::Render { void render(Buffer::Instance&) override {} private: - const HistogramBucketsMode histogram_buckets_mode_; + const Utility::HistogramBucketsMode histogram_buckets_mode_; }; // Implements the Render interface for json output. class StatsHandler::JsonRender : public StatsHandler::Render { public: JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, - Utility::HistogramBucketsMode histogram_buckets_mode) - : histogram_buckets_mode_(histogram_buckets_mode) { + Utility::HistogramBucketsMode) /*histogram_buckets_mode) + : histogram_buckets_mode_(histogram_buckets_mode)*/ { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); // We don't create a JSON data model for the entire stats output, as that // makes streaming difficult. Instead we emit the preamble in the @@ -284,7 +284,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { std::vector computed_quantile_array_; bool found_used_histogram_{false}; bool first_{true}; - const HistogramBucketsMode histogram_buckets_mode_; + //const Utility::HistogramBucketsMode histogram_buckets_mode_; }; // Captures context for a streaming request, implementing the AdminHandler interface. @@ -515,8 +515,7 @@ Admin::RequestPtr StatsHandler::makeRequest(absl::string_view path, AdminStream& absl::Status histogram_buckets_status = Utility::histogramBucketsParam(params, histogram_buckets_mode); if (!histogram_buckets_status.ok()) { - response.add(histogram_buckets_status.message()); - return Http::Code::BadRequest; + return Admin::makeStaticTextRequest(histogram_buckets_status.message(), Http::Code::BadRequest); } const absl::optional format_value = Utility::formatParam(params); @@ -534,7 +533,7 @@ Admin::RequestPtr StatsHandler::makeRequest(absl::string_view path, AdminStream& } } - return makeRequest(server_.stats(), used_only, json, regex); + return makeRequest(server_.stats(), used_only, json, histogram_buckets_mode, regex); } Admin::RequestPtr StatsHandler::makeRequest(Stats::Store& stats, bool used_only, bool json, @@ -656,17 +655,17 @@ StatsHandler::statsAsJson(const std::map& all_stats, #endif std::string -StatsHandler::computeDisjointBucketSummary(const Stats::ParentHistogramSharedPtr& histogram) { - if (!histogram->used()) { + StatsHandler::computeDisjointBucketSummary(const Stats::ParentHistogram& histogram) { + if (!histogram.used()) { return "No recorded values"; } std::vector bucket_summary; - const Stats::HistogramStatistics& interval_statistics = histogram->intervalStatistics(); + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); const std::vector disjoint_interval_buckets = interval_statistics.computeDisjointBuckets(); const std::vector disjoint_cumulative_buckets = - histogram->cumulativeStatistics().computeDisjointBuckets(); + histogram.cumulativeStatistics().computeDisjointBuckets(); bucket_summary.reserve(supported_buckets.size()); // Make sure all vectors are the same size. ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); @@ -691,15 +690,15 @@ void StatsHandler::statsAsJsonQuantileSummaryHelper( if (shouldShowMetric(*histogram, used_only, regex)) { ProtobufWkt::Struct computed_quantile; auto* computed_quantile_fields = computed_quantile.mutable_fields(); - (*computed_quantile_fields)["name"] = ValueUtil::stringValue(histogram->name()); + (*computed_quantile_fields)["name"] = ValueUtil::stringValue(histogram.name()); - const Stats::HistogramStatistics& interval_statistics = histogram->intervalStatistics(); + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); std::vector computed_quantile_value_array; for (size_t i = 0; i < interval_statistics.supportedQuantiles().size(); ++i) { ProtobufWkt::Struct computed_quantile_value; auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); const auto& interval = interval_statistics.computedQuantiles()[i]; - const auto& cumulative = histogram->cumulativeStatistics().computedQuantiles()[i]; + const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; (*computed_quantile_value_fields)["interval"] = std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); (*computed_quantile_value_fields)["cumulative"] = @@ -740,10 +739,10 @@ void StatsHandler::statsAsJsonHistogramBucketsHelper( std::vector histogram_obj_array; for (const Stats::ParentHistogramSharedPtr& histogram : all_histograms) { if (shouldShowMetric(*histogram, used_only, regex)) { - const Stats::HistogramStatistics& interval_statistics = histogram->intervalStatistics(); + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); histogram_obj_array.push_back(statsAsJsonHistogramBucketsCreateHistogramElementHelper( interval_statistics.supportedBuckets(), computed_buckets(interval_statistics), - computed_buckets(histogram->cumulativeStatistics()), histogram->name())); + computed_buckets(histogram.cumulativeStatistics()), histogram.name())); } } if (!histogram_obj_array.empty()) { // If used histogram found. diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 34279729f2720..c330764f61fdb 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -59,16 +59,16 @@ class StatsHandler : public HandlerContextBase { private: friend class StatsHandlerTest; - static std::string computeDisjointBucketSummary(const Stats::ParentHistogramSharedPtr& histogram); + static std::string computeDisjointBucketSummary(const Stats::ParentHistogram& histogram); static void statsAsJsonQuantileSummaryHelper( Protobuf::Map& histograms_obj_container_fields, - const std::vector& all_histograms, bool used_only, + const std::vector& all_histograms, bool used_only, const absl::optional& regex); static void statsAsJsonHistogramBucketsHelper( Protobuf::Map& histograms_obj_container_fields, - const std::vector& all_histograms, bool used_only, + const std::vector& all_histograms, bool used_only, const absl::optional& regex, std::function(const Stats::HistogramStatistics&)> computed_buckets); diff --git a/test/server/admin/stats_handler_speed_test.cc b/test/server/admin/stats_handler_speed_test.cc index b24a6e42f845b..5ab839971125c 100644 --- a/test/server/admin/stats_handler_speed_test.cc +++ b/test/server/admin/stats_handler_speed_test.cc @@ -34,10 +34,9 @@ class StatsHandlerTest { * Issues an admin request against the stats saved in store_. */ uint64_t handlerStats(bool used_only, bool json, - Utility::HistogramBucketsMode histogram_buckets_mode, const absl::optional& filter) { - Admin::RequestPtr request = StatsHandler::makeRequest(store_, used_only, json, - histogram_buckets_mode, filter); + Admin::RequestPtr request = StatsHandler::makeRequest( + store_, used_only, json, Utility::HistogramBucketsMode::NoBuckets, filter); auto response_headers = Http::ResponseHeaderMapImpl::create(); request->start(*response_headers); Buffer::OwnedImpl data; From ecc27080f7ac9eacbed4cc46ea05e0dc5b475ae5 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 20:01:41 -0500 Subject: [PATCH 45/74] tests pass Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 396 ++++++++++----------------- 1 file changed, 152 insertions(+), 244 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 10e433ee9c581..da78f24c965a9 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -15,6 +15,8 @@ #include "absl/container/btree_map.h" #include "absl/types/variant.h" +using UInt64Vec = std::vector; + namespace { constexpr uint64_t ChunkSize = 2 * 1000 * 1000; constexpr uint64_t JsonStatsFlushCount = 64; // This value found by iterating in benchmark. @@ -130,8 +132,7 @@ class StatsHandler::TextRender : public StatsHandler::Render { response.addFragments({name, ": ", histogram.bucketSummary(), "\n"}); break; case Utility::HistogramBucketsMode::Disjoint: - response.addFragments({name, ": ", StatsHandler::computeDisjointBucketSummary(histogram), - "\n"}); + addDisjointBuckets(name, histogram, response); break; } } @@ -139,6 +140,42 @@ class StatsHandler::TextRender : public StatsHandler::Render { void render(Buffer::Instance&) override {} private: + // Computes disjoint buckets as text and adds them to the response buffer. + void addDisjointBuckets( + const std::string& name, const Stats::ParentHistogram& histogram, Buffer::Instance& response) { + if (!histogram.used()) { + response.addFragments({name, ": No recorded values\n"}); + return; + } + response.addFragments({name, ": "}); + std::vector bucket_summary; + + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); + const UInt64Vec disjoint_interval_buckets = interval_statistics.computeDisjointBuckets(); + const UInt64Vec disjoint_cumulative_buckets = + histogram.cumulativeStatistics().computeDisjointBuckets(); + // Make sure all vectors are the same size. + ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); + ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); + size_t min_size = std::min({disjoint_interval_buckets.size(), disjoint_cumulative_buckets.size(), + supported_buckets.size()}); + std::vector bucket_strings; + bucket_strings.reserve(min_size); + for (size_t i = 0; i < min_size; ++i) { + if (i != 0) { + bucket_summary.push_back(" "); + } + bucket_strings.push_back(fmt::format("B{:g}({},{})", supported_buckets[i], + disjoint_interval_buckets[i], + disjoint_cumulative_buckets[i])); + bucket_summary.push_back(bucket_strings.back()); + + } + bucket_summary.push_back("\n"); + response.addFragments(bucket_summary); + } + const Utility::HistogramBucketsMode histogram_buckets_mode_; }; @@ -146,8 +183,8 @@ class StatsHandler::TextRender : public StatsHandler::Render { class StatsHandler::JsonRender : public StatsHandler::Render { public: JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, - Utility::HistogramBucketsMode) /*histogram_buckets_mode) - : histogram_buckets_mode_(histogram_buckets_mode)*/ { + Utility::HistogramBucketsMode histogram_buckets_mode) + : histogram_buckets_mode_(histogram_buckets_mode) { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); // We don't create a JSON data model for the entire stats output, as that // makes streaming difficult. Instead we emit the preamble in the @@ -156,12 +193,12 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { - add(response, name, ValueUtil::numberValue(value)); + addScalar(response, name, ValueUtil::numberValue(value)); } void generate(Buffer::Instance& response, const std::string& name, const std::string& value) override { - add(response, name, ValueUtil::stringValue(value)); + addScalar(response, name, ValueUtil::stringValue(value)); } // In JSON we buffer all histograms and don't write them immediately, so we @@ -170,42 +207,25 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // // This is counter to the goals of streaming and chunked interfaces, but // usually there are far fewer histograms than counters or gauges. - void generate(Buffer::Instance&, const std::string& name, + void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) override { - if (!found_used_histogram_) { - auto* histograms_obj_fields = histograms_obj_.mutable_fields(); - - // It is not possible for the supported quantiles to differ across histograms, so it is ok - // to send them once. - Stats::HistogramStatisticsImpl empty_statistics; - std::vector supported_quantile_array; - for (double quantile : empty_statistics.supportedQuantiles()) { - supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); - } - (*histograms_obj_fields)["supported_quantiles"] = - ValueUtil::listValue(supported_quantile_array); - found_used_histogram_ = true; - } - - ProtobufWkt::Struct computed_quantile; - auto* computed_quantile_fields = computed_quantile.mutable_fields(); - (*computed_quantile_fields)["name"] = ValueUtil::stringValue(name); - - std::vector computed_quantile_value_array; - for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { - ProtobufWkt::Struct computed_quantile_value; - auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); - const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; - const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; - (*computed_quantile_value_fields)["interval"] = - std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); - (*computed_quantile_value_fields)["cumulative"] = - std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); - - computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); + switch (histogram_buckets_mode_) { + case Utility::HistogramBucketsMode::NoBuckets: + bucketSummary(name, histogram); + break; + case Utility::HistogramBucketsMode::Cumulative: + bucketsHelper(name, histogram, + [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { + return histogram_statistics.computedBuckets(); + }); + break; + case Utility::HistogramBucketsMode::Disjoint: + bucketsHelper(name, histogram, + [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { + return histogram_statistics.computeDisjointBuckets(); + }); + break; } - (*computed_quantile_fields)["values"] = ValueUtil::listValue(computed_quantile_value_array); - computed_quantile_array_.push_back(ValueUtil::structValue(computed_quantile)); } // Since histograms are buffered (see above), the render() method generates @@ -214,15 +234,20 @@ class StatsHandler::JsonRender : public StatsHandler::Render { if (!stats_array_.empty()) { flushStats(response); } - if (found_used_histogram_) { - auto* histograms_obj_fields = histograms_obj_.mutable_fields(); - (*histograms_obj_fields)["computed_quantiles"] = - ValueUtil::listValue(computed_quantile_array_); + if (!histogram_array_.empty()) { auto* histograms_obj_container_fields = histograms_obj_container_.mutable_fields(); - (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); + if (found_used_histogram_) { + ASSERT(histogram_buckets_mode_ == Utility::HistogramBucketsMode::NoBuckets); + auto* histograms_obj_fields = histograms_obj_.mutable_fields(); + (*histograms_obj_fields)["computed_quantiles"] = ValueUtil::listValue(histogram_array_); + (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); + } else { + ASSERT(histogram_buckets_mode_ != Utility::HistogramBucketsMode::NoBuckets); + (*histograms_obj_container_fields)["histograms"] = ValueUtil::listValue(histogram_array_); + } auto str = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); - addStatJson(response, str); + addStatAsRenderedJson(response, str); } response.add("]}"); } @@ -233,12 +258,16 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // reached. Serializing each one individually results in much worse // performance (see stats_handler_speed_test.cc). template - void add(Buffer::Instance& response, const std::string& name, const Value& value) { + void addScalar(Buffer::Instance& response, const std::string& name, const Value& value) { ProtobufWkt::Struct stat_obj; auto* stat_obj_fields = stat_obj.mutable_fields(); (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); (*stat_obj_fields)["value"] = value; - stats_array_.push_back(ValueUtil::structValue(stat_obj)); + addJson(response, ValueUtil::structValue(stat_obj)); + } + + void addJson(Buffer::Instance& response, ProtobufWkt::Value json) { + stats_array_.push_back(json); // We build up stats_array to a certain size so we can amortize the overhead // of entering into the JSON serialization infrastructure. If we set the @@ -264,12 +293,12 @@ class StatsHandler::JsonRender : public StatsHandler::Render { ASSERT(json_array.size() >= 2); ASSERT(json_array[0] == '['); ASSERT(json_array[json_array.size() - 1] == ']'); - addStatJson(response, absl::string_view(json_array).substr(1, json_array.size() - 2)); + addStatAsRenderedJson(response, absl::string_view(json_array).substr(1, json_array.size() - 2)); } // Adds a json fragment of scalar stats to the response buffer, including a // "," delimiter if this is not the first fragment. - void addStatJson(Buffer::Instance& response, absl::string_view json) { + void addStatAsRenderedJson(Buffer::Instance& response, absl::string_view json) { if (first_) { response.add(json); first_ = false; @@ -278,13 +307,84 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } + void bucketSummary(const std::string& name, const Stats::ParentHistogram& histogram) { + if (!found_used_histogram_) { + auto* histograms_obj_fields = histograms_obj_.mutable_fields(); + + // It is not possible for the supported quantiles to differ across histograms, so it is ok + // to send them once. + Stats::HistogramStatisticsImpl empty_statistics; + std::vector supported_quantile_array; + for (double quantile : empty_statistics.supportedQuantiles()) { + supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); + } + (*histograms_obj_fields)["supported_quantiles"] = + ValueUtil::listValue(supported_quantile_array); + found_used_histogram_ = true; + } + + ProtobufWkt::Struct computed_quantile; + auto* computed_quantile_fields = computed_quantile.mutable_fields(); + (*computed_quantile_fields)["name"] = ValueUtil::stringValue(name); + + std::vector computed_quantile_value_array; + for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { + ProtobufWkt::Struct computed_quantile_value; + auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); + const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; + const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; + (*computed_quantile_value_fields)["interval"] = + std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); + (*computed_quantile_value_fields)["cumulative"] = + std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); + + computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); + } + (*computed_quantile_fields)["values"] = ValueUtil::listValue(computed_quantile_value_array); + histogram_array_.push_back(ValueUtil::structValue(computed_quantile)); + } + + void bucketsHelper( + const std::string& name, const Stats::ParentHistogram& histogram, + std::function buckets_fn) { + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); + UInt64Vec interval_buckets = buckets_fn(interval_statistics); + UInt64Vec cumulative_buckets = buckets_fn(histogram.cumulativeStatistics()); + + // Make sure all vectors are the same size. + ASSERT(interval_buckets.size() == cumulative_buckets.size()); + ASSERT(cumulative_buckets.size() == supported_buckets.size()); + size_t min_size = + std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); + + ProtobufWkt::Struct histogram_obj; + auto* histogram_obj_fields = histogram_obj.mutable_fields(); + (*histogram_obj_fields)["name"] = ValueUtil::stringValue(name); + + std::vector bucket_array; + for (size_t i = 0; i < min_size; ++i) { + ProtobufWkt::Struct bucket; + auto* bucket_fields = bucket.mutable_fields(); + (*bucket_fields)["upper_bound"] = ValueUtil::numberValue(supported_buckets[i]); + + // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. + (*bucket_fields)["interval"] = ValueUtil::numberValue(interval_buckets[i]); + (*bucket_fields)["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); + bucket_array.push_back(ValueUtil::structValue(bucket)); + } + (*histogram_obj_fields)["buckets"] = ValueUtil::listValue(bucket_array); + //addJson(response, ValueUtil::structValue(histogram_obj)); + histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); + } + std::vector stats_array_; ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; - std::vector computed_quantile_array_; + std::vector histogram_array_; bool found_used_histogram_{false}; bool first_{true}; - //const Utility::HistogramBucketsMode histogram_buckets_mode_; + const Utility::HistogramBucketsMode histogram_buckets_mode_; }; // Captures context for a streaming request, implementing the AdminHandler interface. @@ -588,197 +688,5 @@ Http::Code StatsHandler::handlerContention(absl::string_view, return Http::Code::OK; } -#if 0 -std::string -StatsHandler::statsAsJson(const std::map& all_stats, - const std::map& text_readouts, - const std::vector& all_histograms, - const bool used_only, const absl::optional& regex, - Utility::HistogramBucketsMode histogram_buckets_mode, - const bool pretty_print) { - - ProtobufWkt::Struct document; - std::vector stats_array; - for (const auto& text_readout : text_readouts) { - ProtobufWkt::Struct stat_obj; - auto* stat_obj_fields = stat_obj.mutable_fields(); - (*stat_obj_fields)["name"] = ValueUtil::stringValue(text_readout.first); - (*stat_obj_fields)["value"] = ValueUtil::stringValue(text_readout.second); - stats_array.push_back(ValueUtil::structValue(stat_obj)); - } - for (const auto& stat : all_stats) { - ProtobufWkt::Struct stat_obj; - auto* stat_obj_fields = stat_obj.mutable_fields(); - (*stat_obj_fields)["name"] = ValueUtil::stringValue(stat.first); - - // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. - (*stat_obj_fields)["value"] = ValueUtil::numberValue(stat.second); - stats_array.push_back(ValueUtil::structValue(stat_obj)); - } - - ProtobufWkt::Struct histograms_obj_container; - auto* histograms_obj_container_fields = histograms_obj_container.mutable_fields(); - - // Display bucket data if histogram_buckets query parameter is used, otherwise output contains - // quantile summary data. - switch (histogram_buckets_mode) { - case Utility::HistogramBucketsMode::NoBuckets: - statsAsJsonQuantileSummaryHelper(*histograms_obj_container_fields, all_histograms, used_only, - regex); - break; - case Utility::HistogramBucketsMode::Cumulative: - statsAsJsonHistogramBucketsHelper( - *histograms_obj_container_fields, all_histograms, used_only, regex, - [](const Stats::HistogramStatistics& histogram_statistics) -> std::vector { - return histogram_statistics.computedBuckets(); - }); - break; - case Utility::HistogramBucketsMode::Disjoint: - statsAsJsonHistogramBucketsHelper( - *histograms_obj_container_fields, all_histograms, used_only, regex, - [](const Stats::HistogramStatistics& histogram_statistics) -> std::vector { - return histogram_statistics.computeDisjointBuckets(); - }); - break; - } - - // Add histograms to output if used histogram found. - if (histograms_obj_container_fields->contains("histograms")) { - stats_array.push_back(ValueUtil::structValue(histograms_obj_container)); - } - - auto* document_fields = document.mutable_fields(); - (*document_fields)["stats"] = ValueUtil::listValue(stats_array); - - return MessageUtil::getJsonStringFromMessageOrDie(document, pretty_print, true); -} -#endif - -std::string - StatsHandler::computeDisjointBucketSummary(const Stats::ParentHistogram& histogram) { - if (!histogram.used()) { - return "No recorded values"; - } - std::vector bucket_summary; - const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); - const std::vector disjoint_interval_buckets = - interval_statistics.computeDisjointBuckets(); - const std::vector disjoint_cumulative_buckets = - histogram.cumulativeStatistics().computeDisjointBuckets(); - bucket_summary.reserve(supported_buckets.size()); - // Make sure all vectors are the same size. - ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); - ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); - size_t min_size = std::min({disjoint_interval_buckets.size(), disjoint_cumulative_buckets.size(), - supported_buckets.size()}); - for (size_t i = 0; i < min_size; ++i) { - bucket_summary.push_back(fmt::format("B{:g}({},{})", supported_buckets[i], - disjoint_interval_buckets[i], - disjoint_cumulative_buckets[i])); - } - return absl::StrJoin(bucket_summary, " "); -} - -#if 0 -void StatsHandler::statsAsJsonQuantileSummaryHelper( - Protobuf::Map& histograms_obj_container_fields, - const std::vector& all_histograms, bool used_only, - const absl::optional& regex) { - std::vector computed_quantile_array; - for (const Stats::ParentHistogramSharedPtr& histogram : all_histograms) { - if (shouldShowMetric(*histogram, used_only, regex)) { - ProtobufWkt::Struct computed_quantile; - auto* computed_quantile_fields = computed_quantile.mutable_fields(); - (*computed_quantile_fields)["name"] = ValueUtil::stringValue(histogram.name()); - - const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - std::vector computed_quantile_value_array; - for (size_t i = 0; i < interval_statistics.supportedQuantiles().size(); ++i) { - ProtobufWkt::Struct computed_quantile_value; - auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); - const auto& interval = interval_statistics.computedQuantiles()[i]; - const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; - (*computed_quantile_value_fields)["interval"] = - std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); - (*computed_quantile_value_fields)["cumulative"] = - std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); - - computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); - } - (*computed_quantile_fields)["values"] = ValueUtil::listValue(computed_quantile_value_array); - computed_quantile_array.push_back(ValueUtil::structValue(computed_quantile)); - } - } - - if (!computed_quantile_array.empty()) { // If used histogram found. - ProtobufWkt::Struct histograms_obj; - auto* histograms_obj_fields = histograms_obj.mutable_fields(); - - // It is not possible for the supported quantiles to differ across histograms, so it is ok - // to send them once. - Stats::HistogramStatisticsImpl empty_statistics; - std::vector supported_quantile_array; - for (double quantile : empty_statistics.supportedQuantiles()) { - supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); - } - (*histograms_obj_fields)["supported_quantiles"] = - ValueUtil::listValue(supported_quantile_array); - - (*histograms_obj_fields)["computed_quantiles"] = ValueUtil::listValue(computed_quantile_array); - - histograms_obj_container_fields["histograms"] = ValueUtil::structValue(histograms_obj); - } -} - -void StatsHandler::statsAsJsonHistogramBucketsHelper( - Protobuf::Map& histograms_obj_container_fields, - const std::vector& all_histograms, bool used_only, - const absl::optional& regex, - std::function(const Stats::HistogramStatistics&)> computed_buckets) { - std::vector histogram_obj_array; - for (const Stats::ParentHistogramSharedPtr& histogram : all_histograms) { - if (shouldShowMetric(*histogram, used_only, regex)) { - const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - histogram_obj_array.push_back(statsAsJsonHistogramBucketsCreateHistogramElementHelper( - interval_statistics.supportedBuckets(), computed_buckets(interval_statistics), - computed_buckets(histogram.cumulativeStatistics()), histogram.name())); - } - } - if (!histogram_obj_array.empty()) { // If used histogram found. - histograms_obj_container_fields["histograms"] = ValueUtil::listValue(histogram_obj_array); - } -} - -ProtobufWkt::Value StatsHandler::statsAsJsonHistogramBucketsCreateHistogramElementHelper( - Stats::ConstSupportedBuckets& supported_buckets, const std::vector& interval_buckets, - const std::vector& cumulative_buckets, const std::string& name) { - ProtobufWkt::Struct histogram_obj; - auto* histogram_obj_fields = histogram_obj.mutable_fields(); - (*histogram_obj_fields)["name"] = ValueUtil::stringValue(name); - - // Make sure all vectors are the same size. - ASSERT(interval_buckets.size() == cumulative_buckets.size()); - ASSERT(cumulative_buckets.size() == supported_buckets.size()); - size_t min_size = - std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); - - std::vector bucket_array; - for (size_t i = 0; i < min_size; ++i) { - ProtobufWkt::Struct bucket; - auto* bucket_fields = bucket.mutable_fields(); - (*bucket_fields)["upper_bound"] = ValueUtil::numberValue(supported_buckets[i]); - - // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. - (*bucket_fields)["interval"] = ValueUtil::numberValue(interval_buckets[i]); - (*bucket_fields)["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); - bucket_array.push_back(ValueUtil::structValue(bucket)); - } - (*histogram_obj_fields)["buckets"] = ValueUtil::listValue(bucket_array); - - return ValueUtil::structValue(histogram_obj); -} -#endif - } // namespace Server } // namespace Envoy From 4e6274798c36f2804f83ff333f6e8e4b3cc48df4 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 20:15:20 -0500 Subject: [PATCH 46/74] get tests working & add comments. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 88 +++++++++++-------- test/server/admin/stats_handler_speed_test.cc | 3 +- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index da78f24c965a9..f821683174c0d 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -125,24 +125,24 @@ class StatsHandler::TextRender : public StatsHandler::Render { void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) override { switch (histogram_buckets_mode_) { - case Utility::HistogramBucketsMode::NoBuckets: - response.addFragments({name, ": ", histogram.quantileSummary(), "\n"}); - break; - case Utility::HistogramBucketsMode::Cumulative: - response.addFragments({name, ": ", histogram.bucketSummary(), "\n"}); - break; - case Utility::HistogramBucketsMode::Disjoint: - addDisjointBuckets(name, histogram, response); - break; + case Utility::HistogramBucketsMode::NoBuckets: + response.addFragments({name, ": ", histogram.quantileSummary(), "\n"}); + break; + case Utility::HistogramBucketsMode::Cumulative: + response.addFragments({name, ": ", histogram.bucketSummary(), "\n"}); + break; + case Utility::HistogramBucketsMode::Disjoint: + addDisjointBuckets(name, histogram, response); + break; } } void render(Buffer::Instance&) override {} - private: +private: // Computes disjoint buckets as text and adds them to the response buffer. - void addDisjointBuckets( - const std::string& name, const Stats::ParentHistogram& histogram, Buffer::Instance& response) { + void addDisjointBuckets(const std::string& name, const Stats::ParentHistogram& histogram, + Buffer::Instance& response) { if (!histogram.used()) { response.addFragments({name, ": No recorded values\n"}); return; @@ -158,8 +158,8 @@ class StatsHandler::TextRender : public StatsHandler::Render { // Make sure all vectors are the same size. ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); - size_t min_size = std::min({disjoint_interval_buckets.size(), disjoint_cumulative_buckets.size(), - supported_buckets.size()}); + size_t min_size = std::min({disjoint_interval_buckets.size(), + disjoint_cumulative_buckets.size(), supported_buckets.size()}); std::vector bucket_strings; bucket_strings.reserve(min_size); for (size_t i = 0; i < min_size; ++i) { @@ -170,7 +170,6 @@ class StatsHandler::TextRender : public StatsHandler::Render { disjoint_interval_buckets[i], disjoint_cumulative_buckets[i])); bucket_summary.push_back(bucket_strings.back()); - } bucket_summary.push_back("\n"); response.addFragments(bucket_summary); @@ -192,10 +191,14 @@ class StatsHandler::JsonRender : public StatsHandler::Render { response.add("{\"stats\":["); } + // Buffers a JSON fragment for a numeric stats, flushing to the response + // buffer once we exceed JsonStatsFlushCount stats. void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { addScalar(response, name, ValueUtil::numberValue(value)); } + // Buffers a JSON fragment for a text-readout stat, flushing to the response + // buffer once we exceed JsonStatsFlushCount stats. void generate(Buffer::Instance& response, const std::string& name, const std::string& value) override { addScalar(response, name, ValueUtil::stringValue(value)); @@ -207,24 +210,28 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // // This is counter to the goals of streaming and chunked interfaces, but // usually there are far fewer histograms than counters or gauges. + // + // We can further optimize this by streaming out the histograms object, one + // histogram at a time, in case buffering all the histograms in Envoy + // buffers up too much memory. void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) override { switch (histogram_buckets_mode_) { - case Utility::HistogramBucketsMode::NoBuckets: - bucketSummary(name, histogram); - break; - case Utility::HistogramBucketsMode::Cumulative: - bucketsHelper(name, histogram, - [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { - return histogram_statistics.computedBuckets(); - }); - break; - case Utility::HistogramBucketsMode::Disjoint: - bucketsHelper(name, histogram, - [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { - return histogram_statistics.computeDisjointBuckets(); - }); - break; + case Utility::HistogramBucketsMode::NoBuckets: + summarizeBuckets(name, histogram); + break; + case Utility::HistogramBucketsMode::Cumulative: + collectBuckets(name, histogram, + [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { + return histogram_statistics.computedBuckets(); + }); + break; + case Utility::HistogramBucketsMode::Disjoint: + collectBuckets(name, histogram, + [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { + return histogram_statistics.computeDisjointBuckets(); + }); + break; } } @@ -266,6 +273,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { addJson(response, ValueUtil::structValue(stat_obj)); } + // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. void addJson(Buffer::Instance& response, ProtobufWkt::Value json) { stats_array_.push_back(json); @@ -279,7 +287,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } - // Flushes all scalar stats that were buffered in add() above. + // Flushes all stats that were buffered in addJson() above. void flushStats(Buffer::Instance& response) { ASSERT(!stats_array_.empty()); const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( @@ -307,7 +315,12 @@ class StatsHandler::JsonRender : public StatsHandler::Render { } } - void bucketSummary(const std::string& name, const Stats::ParentHistogram& histogram) { + // Summarizes the buckets in the specified histogram, collecting JSON objects. + // Note, we do not flush this buffer to the network when it grows large, and + // if this becomes an issue it should be possible to do, noting that we are + // one or two levels nesting below the list of scalar stats due to the Envoy + // stats json schema, where histograms are grouped together. + void summarizeBuckets(const std::string& name, const Stats::ParentHistogram& histogram) { if (!found_used_histogram_) { auto* histograms_obj_fields = histograms_obj_.mutable_fields(); @@ -344,9 +357,10 @@ class StatsHandler::JsonRender : public StatsHandler::Render { histogram_array_.push_back(ValueUtil::structValue(computed_quantile)); } - void bucketsHelper( - const std::string& name, const Stats::ParentHistogram& histogram, - std::function buckets_fn) { + // Collects the buckets from the specified histogram, using either the cumulative + // or disjoint views, as controlled by buckets_fn. + void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, + std::function buckets_fn) { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); UInt64Vec interval_buckets = buckets_fn(interval_statistics); @@ -356,7 +370,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { ASSERT(interval_buckets.size() == cumulative_buckets.size()); ASSERT(cumulative_buckets.size() == supported_buckets.size()); size_t min_size = - std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); + std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); ProtobufWkt::Struct histogram_obj; auto* histogram_obj_fields = histogram_obj.mutable_fields(); @@ -374,7 +388,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { bucket_array.push_back(ValueUtil::structValue(bucket)); } (*histogram_obj_fields)["buckets"] = ValueUtil::listValue(bucket_array); - //addJson(response, ValueUtil::structValue(histogram_obj)); + // addJson(response, ValueUtil::structValue(histogram_obj)); histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); } diff --git a/test/server/admin/stats_handler_speed_test.cc b/test/server/admin/stats_handler_speed_test.cc index 5ab839971125c..a354fe8ac52ec 100644 --- a/test/server/admin/stats_handler_speed_test.cc +++ b/test/server/admin/stats_handler_speed_test.cc @@ -33,8 +33,7 @@ class StatsHandlerTest { /** * Issues an admin request against the stats saved in store_. */ - uint64_t handlerStats(bool used_only, bool json, - const absl::optional& filter) { + uint64_t handlerStats(bool used_only, bool json, const absl::optional& filter) { Admin::RequestPtr request = StatsHandler::makeRequest( store_, used_only, json, Utility::HistogramBucketsMode::NoBuckets, filter); auto response_headers = Http::ResponseHeaderMapImpl::create(); From 49a632e750da8b1db8fe0c7f0ae550dda51c86bb Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 20:23:27 -0500 Subject: [PATCH 47/74] remove spurious declarations. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 2 +- source/server/admin/stats_handler.h | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index f821683174c0d..9a121e5b73ac2 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -214,7 +214,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // We can further optimize this by streaming out the histograms object, one // histogram at a time, in case buffering all the histograms in Envoy // buffers up too much memory. - void generate(Buffer::Instance& response, const std::string& name, + void generate(Buffer::Instance&, const std::string& name, const Stats::ParentHistogram& histogram) override { switch (histogram_buckets_mode_) { case Utility::HistogramBucketsMode::NoBuckets: diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index c330764f61fdb..c6322379189e5 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -58,24 +58,6 @@ class StatsHandler : public HandlerContextBase { private: friend class StatsHandlerTest; - - static std::string computeDisjointBucketSummary(const Stats::ParentHistogram& histogram); - - static void statsAsJsonQuantileSummaryHelper( - Protobuf::Map& histograms_obj_container_fields, - const std::vector& all_histograms, bool used_only, - const absl::optional& regex); - - static void statsAsJsonHistogramBucketsHelper( - Protobuf::Map& histograms_obj_container_fields, - const std::vector& all_histograms, bool used_only, - const absl::optional& regex, - std::function(const Stats::HistogramStatistics&)> computed_buckets); - - static ProtobufWkt::Value statsAsJsonHistogramBucketsCreateHistogramElementHelper( - Stats::ConstSupportedBuckets& supported_buckets, - const std::vector& interval_buckets, - const std::vector& cumulative_buckets, const std::string& name); }; } // namespace Server From f35e21b8fa0cac3faa03f242b3538c1d1ade5a2a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 20:26:53 -0500 Subject: [PATCH 48/74] remove spurious friend declaration Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index c6322379189e5..1135f7b0d6055 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -55,9 +55,6 @@ class StatsHandler : public HandlerContextBase { class Render; class TextRender; class StreamingRequest; - -private: - friend class StatsHandlerTest; }; } // namespace Server From 805e7b5a36e8df2a921ba108bdcb2e7d62a24596 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 20:46:33 -0500 Subject: [PATCH 49/74] Clean up map syntax with a nickname and refs. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 54 +++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 9a121e5b73ac2..0a223e08c2bf2 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -23,6 +23,9 @@ constexpr uint64_t JsonStatsFlushCount = 64; // This value found by iterating in } // namespace namespace Envoy { + +using ProtoMap = Protobuf::Map; + namespace Server { const uint64_t RecentLookupsCapacity = 100; @@ -242,15 +245,15 @@ class StatsHandler::JsonRender : public StatsHandler::Render { flushStats(response); } if (!histogram_array_.empty()) { - auto* histograms_obj_container_fields = histograms_obj_container_.mutable_fields(); + ProtoMap& histograms_obj_container_fields = *histograms_obj_container_.mutable_fields(); if (found_used_histogram_) { ASSERT(histogram_buckets_mode_ == Utility::HistogramBucketsMode::NoBuckets); - auto* histograms_obj_fields = histograms_obj_.mutable_fields(); - (*histograms_obj_fields)["computed_quantiles"] = ValueUtil::listValue(histogram_array_); - (*histograms_obj_container_fields)["histograms"] = ValueUtil::structValue(histograms_obj_); + ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); + histograms_obj_fields["computed_quantiles"] = ValueUtil::listValue(histogram_array_); + histograms_obj_container_fields["histograms"] = ValueUtil::structValue(histograms_obj_); } else { ASSERT(histogram_buckets_mode_ != Utility::HistogramBucketsMode::NoBuckets); - (*histograms_obj_container_fields)["histograms"] = ValueUtil::listValue(histogram_array_); + histograms_obj_container_fields["histograms"] = ValueUtil::listValue(histogram_array_); } auto str = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); @@ -267,9 +270,9 @@ class StatsHandler::JsonRender : public StatsHandler::Render { template void addScalar(Buffer::Instance& response, const std::string& name, const Value& value) { ProtobufWkt::Struct stat_obj; - auto* stat_obj_fields = stat_obj.mutable_fields(); - (*stat_obj_fields)["name"] = ValueUtil::stringValue(name); - (*stat_obj_fields)["value"] = value; + ProtoMap& stat_obj_fields = *stat_obj.mutable_fields(); + stat_obj_fields["name"] = ValueUtil::stringValue(name); + stat_obj_fields["value"] = value; addJson(response, ValueUtil::structValue(stat_obj)); } @@ -322,8 +325,6 @@ class StatsHandler::JsonRender : public StatsHandler::Render { // stats json schema, where histograms are grouped together. void summarizeBuckets(const std::string& name, const Stats::ParentHistogram& histogram) { if (!found_used_histogram_) { - auto* histograms_obj_fields = histograms_obj_.mutable_fields(); - // It is not possible for the supported quantiles to differ across histograms, so it is ok // to send them once. Stats::HistogramStatisticsImpl empty_statistics; @@ -331,29 +332,31 @@ class StatsHandler::JsonRender : public StatsHandler::Render { for (double quantile : empty_statistics.supportedQuantiles()) { supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); } - (*histograms_obj_fields)["supported_quantiles"] = - ValueUtil::listValue(supported_quantile_array); + + ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); + histograms_obj_fields["supported_quantiles"] = ValueUtil::listValue(supported_quantile_array); found_used_histogram_ = true; } ProtobufWkt::Struct computed_quantile; - auto* computed_quantile_fields = computed_quantile.mutable_fields(); - (*computed_quantile_fields)["name"] = ValueUtil::stringValue(name); + ProtoMap& computed_quantile_fields = *computed_quantile.mutable_fields(); + computed_quantile_fields["name"] = ValueUtil::stringValue(name); std::vector computed_quantile_value_array; for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { ProtobufWkt::Struct computed_quantile_value; - auto* computed_quantile_value_fields = computed_quantile_value.mutable_fields(); + ProtoMap& computed_quantile_value_fields = + *computed_quantile_value.mutable_fields(); const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; - (*computed_quantile_value_fields)["interval"] = + computed_quantile_value_fields["interval"] = std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); - (*computed_quantile_value_fields)["cumulative"] = + computed_quantile_value_fields["cumulative"] = std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); } - (*computed_quantile_fields)["values"] = ValueUtil::listValue(computed_quantile_value_array); + computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); histogram_array_.push_back(ValueUtil::structValue(computed_quantile)); } @@ -373,21 +376,22 @@ class StatsHandler::JsonRender : public StatsHandler::Render { std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); ProtobufWkt::Struct histogram_obj; - auto* histogram_obj_fields = histogram_obj.mutable_fields(); - (*histogram_obj_fields)["name"] = ValueUtil::stringValue(name); + ProtoMap& histogram_obj_fields = + *histogram_obj.mutable_fields(); + histogram_obj_fields["name"] = ValueUtil::stringValue(name); std::vector bucket_array; for (size_t i = 0; i < min_size; ++i) { ProtobufWkt::Struct bucket; - auto* bucket_fields = bucket.mutable_fields(); - (*bucket_fields)["upper_bound"] = ValueUtil::numberValue(supported_buckets[i]); + ProtoMap& bucket_fields = *bucket.mutable_fields(); + bucket_fields["upper_bound"] = ValueUtil::numberValue(supported_buckets[i]); // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. - (*bucket_fields)["interval"] = ValueUtil::numberValue(interval_buckets[i]); - (*bucket_fields)["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); + bucket_fields["interval"] = ValueUtil::numberValue(interval_buckets[i]); + bucket_fields["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); bucket_array.push_back(ValueUtil::structValue(bucket)); } - (*histogram_obj_fields)["buckets"] = ValueUtil::listValue(bucket_array); + histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); // addJson(response, ValueUtil::structValue(histogram_obj)); histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); } From dee72d854e845402d60131ad29f53cb41ea7336a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 20:59:11 -0500 Subject: [PATCH 50/74] more cleanup Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 9 ++++----- source/server/admin/stats_handler.h | 5 ----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 0a223e08c2bf2..84725d2edf4e9 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -88,7 +88,7 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, // // There are currently Json and Text implementations of this interface, and in // #19546 an HTML version will be added to provide a hierarchical view. -class StatsHandler::Render { +class Render { public: virtual ~Render() = default; @@ -111,7 +111,7 @@ class StatsHandler::Render { }; // Implements the Render interface for simple textual representation of stats. -class StatsHandler::TextRender : public StatsHandler::Render { +class TextRender : public Render { public: explicit TextRender(Utility::HistogramBucketsMode histogram_buckets_mode) : histogram_buckets_mode_(histogram_buckets_mode) {} @@ -182,7 +182,7 @@ class StatsHandler::TextRender : public StatsHandler::Render { }; // Implements the Render interface for json output. -class StatsHandler::JsonRender : public StatsHandler::Render { +class JsonRender : public Render { public: JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, Utility::HistogramBucketsMode histogram_buckets_mode) @@ -392,7 +392,6 @@ class StatsHandler::JsonRender : public StatsHandler::Render { bucket_array.push_back(ValueUtil::structValue(bucket)); } histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); - // addJson(response, ValueUtil::structValue(histogram_obj)); histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); } @@ -406,7 +405,7 @@ class StatsHandler::JsonRender : public StatsHandler::Render { }; // Captures context for a streaming request, implementing the AdminHandler interface. -class StatsHandler::StreamingRequest : public Admin::Request { +class StreamingRequest : public Admin::Request { using ScopeVec = std::vector; using StatOrScopes = absl::variant; diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index 1135f7b0d6055..aaff19eb119aa 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -50,11 +50,6 @@ class StatsHandler : public HandlerContextBase { static Admin::RequestPtr makeRequest(Stats::Store& stats, bool used_only, bool json, Utility::HistogramBucketsMode histogram_buckets_mode, const absl::optional& regex); - - class JsonRender; - class Render; - class TextRender; - class StreamingRequest; }; } // namespace Server From ca81c8d8d59ce4006bbd47da1a7bc2ed60a62a28 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 21:44:09 -0500 Subject: [PATCH 51/74] split out the Request and Render class implementations into their own files. Signed-off-by: Joshua Marantz --- source/server/admin/BUILD | 12 +- source/server/admin/stats_handler.cc | 544 +-------------------------- source/server/admin/stats_handler.h | 1 - source/server/admin/stats_render.cc | 292 ++++++++++++++ source/server/admin/stats_render.h | 117 ++++++ source/server/admin/stats_request.cc | 146 +++++++ source/server/admin/stats_request.h | 102 +++++ tools/code_format/check_format.py | 3 +- 8 files changed, 671 insertions(+), 546 deletions(-) create mode 100644 source/server/admin/stats_render.cc create mode 100644 source/server/admin/stats_render.h create mode 100644 source/server/admin/stats_request.cc create mode 100644 source/server/admin/stats_request.h diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 159551a5f886e..e4ad07551b216 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -93,8 +93,16 @@ envoy_cc_library( envoy_cc_library( name = "stats_handler_lib", - srcs = ["stats_handler.cc"], - hdrs = ["stats_handler.h"], + srcs = [ + "stats_handler.cc", + "stats_render.cc", + "stats_request.cc", + ], + hdrs = [ + "stats_handler.h", + "stats_render.h", + "stats_request.h", + ], deps = [ ":handler_ctx_lib", ":prometheus_stats_lib", diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 84725d2edf4e9..2b659dac1b051 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -7,25 +7,13 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/common/empty_string.h" -#include "source/common/html/utility.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" #include "source/server/admin/prometheus_stats.h" - -#include "absl/container/btree_map.h" -#include "absl/types/variant.h" - -using UInt64Vec = std::vector; - -namespace { -constexpr uint64_t ChunkSize = 2 * 1000 * 1000; -constexpr uint64_t JsonStatsFlushCount = 64; // This value found by iterating in benchmark. -} // namespace +#include "source/server/admin/stats_request.h" namespace Envoy { -using ProtoMap = Protobuf::Map; - namespace Server { const uint64_t RecentLookupsCapacity = 100; @@ -83,534 +71,6 @@ Http::Code StatsHandler::handlerStatsRecentLookupsEnable(absl::string_view, return Http::Code::OK; } -// Abstract class for rendering stats. Every method is called "generate" -// differing only by the data type, to facilitate templatized call-sites. -// -// There are currently Json and Text implementations of this interface, and in -// #19546 an HTML version will be added to provide a hierarchical view. -class Render { -public: - virtual ~Render() = default; - - // Writes a fragment for a numeric value, for counters and gauges. - virtual void generate(Buffer::Instance& response, const std::string& name, uint64_t value) PURE; - - // Writes a json fragment for a textual value, for text readouts. - virtual void generate(Buffer::Instance& response, const std::string& name, - const std::string& value) PURE; - - // Writes a histogram value. - virtual void generate(Buffer::Instance& response, const std::string& name, - const Stats::ParentHistogram& histogram) PURE; - - // Completes rendering any buffered data. - virtual void render(Buffer::Instance& response) PURE; - - // Determines whether the current chunk is full. - bool isChunkFull(Buffer::Instance& response) { return response.length() > ChunkSize; } -}; - -// Implements the Render interface for simple textual representation of stats. -class TextRender : public Render { -public: - explicit TextRender(Utility::HistogramBucketsMode histogram_buckets_mode) - : histogram_buckets_mode_(histogram_buckets_mode) {} - - void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { - response.addFragments({name, ": ", absl::StrCat(value), "\n"}); - } - - void generate(Buffer::Instance& response, const std::string& name, - const std::string& value) override { - response.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); - } - - void generate(Buffer::Instance& response, const std::string& name, - const Stats::ParentHistogram& histogram) override { - switch (histogram_buckets_mode_) { - case Utility::HistogramBucketsMode::NoBuckets: - response.addFragments({name, ": ", histogram.quantileSummary(), "\n"}); - break; - case Utility::HistogramBucketsMode::Cumulative: - response.addFragments({name, ": ", histogram.bucketSummary(), "\n"}); - break; - case Utility::HistogramBucketsMode::Disjoint: - addDisjointBuckets(name, histogram, response); - break; - } - } - - void render(Buffer::Instance&) override {} - -private: - // Computes disjoint buckets as text and adds them to the response buffer. - void addDisjointBuckets(const std::string& name, const Stats::ParentHistogram& histogram, - Buffer::Instance& response) { - if (!histogram.used()) { - response.addFragments({name, ": No recorded values\n"}); - return; - } - response.addFragments({name, ": "}); - std::vector bucket_summary; - - const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); - const UInt64Vec disjoint_interval_buckets = interval_statistics.computeDisjointBuckets(); - const UInt64Vec disjoint_cumulative_buckets = - histogram.cumulativeStatistics().computeDisjointBuckets(); - // Make sure all vectors are the same size. - ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); - ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); - size_t min_size = std::min({disjoint_interval_buckets.size(), - disjoint_cumulative_buckets.size(), supported_buckets.size()}); - std::vector bucket_strings; - bucket_strings.reserve(min_size); - for (size_t i = 0; i < min_size; ++i) { - if (i != 0) { - bucket_summary.push_back(" "); - } - bucket_strings.push_back(fmt::format("B{:g}({},{})", supported_buckets[i], - disjoint_interval_buckets[i], - disjoint_cumulative_buckets[i])); - bucket_summary.push_back(bucket_strings.back()); - } - bucket_summary.push_back("\n"); - response.addFragments(bucket_summary); - } - - const Utility::HistogramBucketsMode histogram_buckets_mode_; -}; - -// Implements the Render interface for json output. -class JsonRender : public Render { -public: - JsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, - Utility::HistogramBucketsMode histogram_buckets_mode) - : histogram_buckets_mode_(histogram_buckets_mode) { - response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); - // We don't create a JSON data model for the entire stats output, as that - // makes streaming difficult. Instead we emit the preamble in the - // constructor here, and create json models for each stats entry. - response.add("{\"stats\":["); - } - - // Buffers a JSON fragment for a numeric stats, flushing to the response - // buffer once we exceed JsonStatsFlushCount stats. - void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override { - addScalar(response, name, ValueUtil::numberValue(value)); - } - - // Buffers a JSON fragment for a text-readout stat, flushing to the response - // buffer once we exceed JsonStatsFlushCount stats. - void generate(Buffer::Instance& response, const std::string& name, - const std::string& value) override { - addScalar(response, name, ValueUtil::stringValue(value)); - } - - // In JSON we buffer all histograms and don't write them immediately, so we - // can, in one JSON structure, emit shared attributes of all histograms and - // each individual histogram. - // - // This is counter to the goals of streaming and chunked interfaces, but - // usually there are far fewer histograms than counters or gauges. - // - // We can further optimize this by streaming out the histograms object, one - // histogram at a time, in case buffering all the histograms in Envoy - // buffers up too much memory. - void generate(Buffer::Instance&, const std::string& name, - const Stats::ParentHistogram& histogram) override { - switch (histogram_buckets_mode_) { - case Utility::HistogramBucketsMode::NoBuckets: - summarizeBuckets(name, histogram); - break; - case Utility::HistogramBucketsMode::Cumulative: - collectBuckets(name, histogram, - [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { - return histogram_statistics.computedBuckets(); - }); - break; - case Utility::HistogramBucketsMode::Disjoint: - collectBuckets(name, histogram, - [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { - return histogram_statistics.computeDisjointBuckets(); - }); - break; - } - } - - // Since histograms are buffered (see above), the render() method generates - // all of them. - void render(Buffer::Instance& response) override { - if (!stats_array_.empty()) { - flushStats(response); - } - if (!histogram_array_.empty()) { - ProtoMap& histograms_obj_container_fields = *histograms_obj_container_.mutable_fields(); - if (found_used_histogram_) { - ASSERT(histogram_buckets_mode_ == Utility::HistogramBucketsMode::NoBuckets); - ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); - histograms_obj_fields["computed_quantiles"] = ValueUtil::listValue(histogram_array_); - histograms_obj_container_fields["histograms"] = ValueUtil::structValue(histograms_obj_); - } else { - ASSERT(histogram_buckets_mode_ != Utility::HistogramBucketsMode::NoBuckets); - histograms_obj_container_fields["histograms"] = ValueUtil::listValue(histogram_array_); - } - auto str = MessageUtil::getJsonStringFromMessageOrDie( - ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); - addStatAsRenderedJson(response, str); - } - response.add("]}"); - } - -private: - // Collects a scalar metric (text-readout, counter, or gauge) into an array of - // stats, so they can all be serialized in one shot when a threshold is - // reached. Serializing each one individually results in much worse - // performance (see stats_handler_speed_test.cc). - template - void addScalar(Buffer::Instance& response, const std::string& name, const Value& value) { - ProtobufWkt::Struct stat_obj; - ProtoMap& stat_obj_fields = *stat_obj.mutable_fields(); - stat_obj_fields["name"] = ValueUtil::stringValue(name); - stat_obj_fields["value"] = value; - addJson(response, ValueUtil::structValue(stat_obj)); - } - - // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. - void addJson(Buffer::Instance& response, ProtobufWkt::Value json) { - stats_array_.push_back(json); - - // We build up stats_array to a certain size so we can amortize the overhead - // of entering into the JSON serialization infrastructure. If we set the - // threshold too high we buffer too much memory, likely impacting processor - // cache. The optimum threshold found after a few experiments on a local - // host appears to be between 50 and 100. - if (stats_array_.size() == JsonStatsFlushCount) { - flushStats(response); - } - } - - // Flushes all stats that were buffered in addJson() above. - void flushStats(Buffer::Instance& response) { - ASSERT(!stats_array_.empty()); - const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( - ValueUtil::listValue(stats_array_), false /* pretty */, true); - stats_array_.clear(); - - // We are going to wind up with multiple flushes which have to serialize as - // a single array, rather than a concatenation of multiple arrays, so we add - // those in the constructor and render() method, strip off the "[" and "]" - // from each buffered serialization. - ASSERT(json_array.size() >= 2); - ASSERT(json_array[0] == '['); - ASSERT(json_array[json_array.size() - 1] == ']'); - addStatAsRenderedJson(response, absl::string_view(json_array).substr(1, json_array.size() - 2)); - } - - // Adds a json fragment of scalar stats to the response buffer, including a - // "," delimiter if this is not the first fragment. - void addStatAsRenderedJson(Buffer::Instance& response, absl::string_view json) { - if (first_) { - response.add(json); - first_ = false; - } else { - response.addFragments({",", json}); - } - } - - // Summarizes the buckets in the specified histogram, collecting JSON objects. - // Note, we do not flush this buffer to the network when it grows large, and - // if this becomes an issue it should be possible to do, noting that we are - // one or two levels nesting below the list of scalar stats due to the Envoy - // stats json schema, where histograms are grouped together. - void summarizeBuckets(const std::string& name, const Stats::ParentHistogram& histogram) { - if (!found_used_histogram_) { - // It is not possible for the supported quantiles to differ across histograms, so it is ok - // to send them once. - Stats::HistogramStatisticsImpl empty_statistics; - std::vector supported_quantile_array; - for (double quantile : empty_statistics.supportedQuantiles()) { - supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); - } - - ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); - histograms_obj_fields["supported_quantiles"] = ValueUtil::listValue(supported_quantile_array); - found_used_histogram_ = true; - } - - ProtobufWkt::Struct computed_quantile; - ProtoMap& computed_quantile_fields = *computed_quantile.mutable_fields(); - computed_quantile_fields["name"] = ValueUtil::stringValue(name); - - std::vector computed_quantile_value_array; - for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { - ProtobufWkt::Struct computed_quantile_value; - ProtoMap& computed_quantile_value_fields = - *computed_quantile_value.mutable_fields(); - const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; - const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; - computed_quantile_value_fields["interval"] = - std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); - computed_quantile_value_fields["cumulative"] = - std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); - - computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); - } - computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); - histogram_array_.push_back(ValueUtil::structValue(computed_quantile)); - } - - // Collects the buckets from the specified histogram, using either the cumulative - // or disjoint views, as controlled by buckets_fn. - void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, - std::function buckets_fn) { - const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); - UInt64Vec interval_buckets = buckets_fn(interval_statistics); - UInt64Vec cumulative_buckets = buckets_fn(histogram.cumulativeStatistics()); - - // Make sure all vectors are the same size. - ASSERT(interval_buckets.size() == cumulative_buckets.size()); - ASSERT(cumulative_buckets.size() == supported_buckets.size()); - size_t min_size = - std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); - - ProtobufWkt::Struct histogram_obj; - ProtoMap& histogram_obj_fields = - *histogram_obj.mutable_fields(); - histogram_obj_fields["name"] = ValueUtil::stringValue(name); - - std::vector bucket_array; - for (size_t i = 0; i < min_size; ++i) { - ProtobufWkt::Struct bucket; - ProtoMap& bucket_fields = *bucket.mutable_fields(); - bucket_fields["upper_bound"] = ValueUtil::numberValue(supported_buckets[i]); - - // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. - bucket_fields["interval"] = ValueUtil::numberValue(interval_buckets[i]); - bucket_fields["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); - bucket_array.push_back(ValueUtil::structValue(bucket)); - } - histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); - histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); - } - - std::vector stats_array_; - ProtobufWkt::Struct histograms_obj_; - ProtobufWkt::Struct histograms_obj_container_; - std::vector histogram_array_; - bool found_used_histogram_{false}; - bool first_{true}; - const Utility::HistogramBucketsMode histogram_buckets_mode_; -}; - -// Captures context for a streaming request, implementing the AdminHandler interface. -class StreamingRequest : public Admin::Request { - using ScopeVec = std::vector; - using StatOrScopes = absl::variant; - enum class Phase { - TextReadouts, - CountersAndGauges, - Histograms, - }; - -public: - StreamingRequest(Stats::Store& stats, bool used_only, bool json, - Utility::HistogramBucketsMode histogram_buckets_mode, - absl::optional regex) - : used_only_(used_only), json_(json), histogram_buckets_mode_(histogram_buckets_mode), - regex_(regex), stats_(stats) {} - - // Admin::Request - Http::Code start(Http::ResponseHeaderMap& response_headers) override { - if (json_) { - render_ = std::make_unique(response_headers, response_, histogram_buckets_mode_); - } else { - render_ = std::make_unique(histogram_buckets_mode_); - } - - // Populate the top-level scopes and the stats underneath any scopes with an empty name. - // We will have to de-dup, but we can do that after sorting. - // - // First capture all the scopes and hold onto them with a SharedPtr so they - // can't be deleted after the initial iteration. - stats_.forEachScope( - [this](size_t s) { scopes_.reserve(s); }, - [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.getConstShared()); }); - - startPhase(); - return Http::Code::OK; - } - - // Streams out the next chunk of stats to the client, visiting only the scopes - // that can plausibly contribute the next set of named stats. This enables us - // to linearly traverse the entire set of stats without buffering all of them - // and sorting. - // - // Instead we keep the a set of candidate stats to emit in stat_map_ an - // alphabetically ordered btree, which heterogeneously stores stats of all - // types and scopes. Note that there can be multiple scopes with the same - // name, so we keep same-named scopes in a vector. However leaf metrics cannot - // have duplicates. It would also be feasible to use a multi-map for this. - // - // So in start() above, we initially populate all the scopes, as well as the - // metrics contained in all scopes with an empty name. So in nextChunk we can - // emit and remove the first element of stat_map_. When we encounter a vector - // of scopes then we add the contained metrics to the map and continue - // iterating. - // - // Whenever the desired chunk size is reached we end the current chunk so that - // the current buffer can be flushed to the network. In #19898 we will - // introduce flow-control so that we don't buffer the all the serialized stats - // while waiting for a slow client. - // - // Note that we do 3 passes through all the scopes_, so that we can emit - // text-readouts first, then the intermingled counters and gauges, and finally - // the histograms. - bool nextChunk(Buffer::Instance& response) override { - if (response_.length() > 0) { - ASSERT(response.length() == 0); - response.move(response_); - ASSERT(response_.length() == 0); - } - while (!render_->isChunkFull(response)) { - while (stat_map_.empty()) { - switch (phase_) { - case Phase::TextReadouts: - phase_ = Phase::CountersAndGauges; - startPhase(); - break; - case Phase::CountersAndGauges: - phase_ = Phase::Histograms; - startPhase(); - break; - case Phase::Histograms: - render_->render(response); - return false; - } - } - - auto iter = stat_map_.begin(); - const std::string& name = iter->first; - StatOrScopes& variant = iter->second; - switch (variant.index()) { - case 0: - populateStatsForCurrentPhase(absl::get(variant)); - break; - case 1: - renderStat(name, response, variant); - break; - case 2: - renderStat(name, response, variant); - break; - case 3: - renderStat(name, response, variant); - break; - case 4: { - auto histogram = absl::get(variant); - auto parent_histogram = dynamic_cast(histogram.get()); - if (parent_histogram != nullptr) { - render_->generate(response, name, *parent_histogram); - } - } - } - stat_map_.erase(iter); - } - return true; - } - - // To duplicate prior behavior for this class, we do three passes over all the stats: - // 1. text readouts across all scopes - // 2. counters and gauges, co-mingled, across all scopes - // 3. histograms across all scopes. - // It would be little more efficient to co-mingle all the stats, but three - // passes over the scopes is OK. In the future we may decide to organize the - // result data differently, but in the process of changing from buffering - // the entire /stats response to streaming the data out in chunks, it's easier - // to reason about if the tests don't change their expectations. - void startPhase() { - ASSERT(stat_map_.empty()); - for (const Stats::ConstScopeSharedPtr& scope : scopes_) { - StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; - if (variant.index() == absl::variant_npos) { - variant = ScopeVec(); - } - absl::get(variant).emplace_back(scope); - } - - // Populate stat_map with all the counters found in all the scopes with an - // empty prefix. - auto iter = stat_map_.find(""); - if (iter != stat_map_.end()) { - StatOrScopes variant = std::move(iter->second); - stat_map_.erase(iter); - auto& scope_vec = absl::get(variant); - populateStatsForCurrentPhase(scope_vec); - } - } - - // Iterates over scope_vec and populates the metric types associated with the - // current phase. - void populateStatsForCurrentPhase(const ScopeVec& scope_vec) { - switch (phase_) { - case Phase::TextReadouts: - populateStatsFromScopes(scope_vec); - break; - case Phase::CountersAndGauges: - populateStatsFromScopes(scope_vec); - populateStatsFromScopes(scope_vec); - break; - case Phase::Histograms: - populateStatsFromScopes(scope_vec); - break; - } - } - - // Populates all the metrics of the templatized type from scope_vec. Here we - // exploit that Scope::iterate is a generic templatized function to avoid code - // duplication. - template void populateStatsFromScopes(const ScopeVec& scope_vec) { - for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { - Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { - if (used_only_ && !stat->used()) { - return true; - } - std::string name = stat->name(); - if (regex_.has_value() && !std::regex_search(name, regex_.value())) { - return true; - } - stat_map_[name] = stat; - return true; - }; - scope->iterate(fn); - } - } - - // Renders the templatized type, exploiting the fact that Render::generate is - // generic to avoid code duplication. - template - void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant) { - auto stat = absl::get(variant); - render_->generate(response, name, stat->value()); - } - -private: - const bool used_only_; - const bool json_; - const Utility::HistogramBucketsMode histogram_buckets_mode_; - absl::optional regex_; - absl::optional format_value_; - - std::unique_ptr render_; - - Stats::Store& stats_; - ScopeVec scopes_; - absl::btree_map stat_map_; - Phase phase_{Phase::TextReadouts}; - Buffer::OwnedImpl response_; -}; - Admin::RequestPtr StatsHandler::makeRequest(absl::string_view path, AdminStream& /*admin_stream*/) { if (server_.statsConfig().flushOnAdmin()) { server_.flushStats(); @@ -656,7 +116,7 @@ Admin::RequestPtr StatsHandler::makeRequest(absl::string_view path, AdminStream& Admin::RequestPtr StatsHandler::makeRequest(Stats::Store& stats, bool used_only, bool json, Utility::HistogramBucketsMode histogram_buckets_mode, const absl::optional& regex) { - return std::make_unique(stats, used_only, json, histogram_buckets_mode, regex); + return std::make_unique(stats, used_only, json, histogram_buckets_mode, regex); } Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query, diff --git a/source/server/admin/stats_handler.h b/source/server/admin/stats_handler.h index aaff19eb119aa..9735d9f86e06f 100644 --- a/source/server/admin/stats_handler.h +++ b/source/server/admin/stats_handler.h @@ -9,7 +9,6 @@ #include "envoy/server/admin.h" #include "envoy/server/instance.h" -#include "source/common/stats/histogram_impl.h" #include "source/server/admin/handler_ctx.h" #include "source/server/admin/utils.h" diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc new file mode 100644 index 0000000000000..75930f696f591 --- /dev/null +++ b/source/server/admin/stats_render.cc @@ -0,0 +1,292 @@ +#include "source/server/admin/stats_render.h" + +#include "source/common/html/utility.h" +#include "source/common/stats/histogram_impl.h" + +namespace { +constexpr uint64_t JsonStatsFlushCount = 64; // This value found by iterating in benchmark. +} +namespace Envoy { + +using ProtoMap = Protobuf::Map; + +namespace Server { + +void StatsTextRender::generate(Buffer::Instance& response, const std::string& name, + uint64_t value) { + response.addFragments({name, ": ", absl::StrCat(value), "\n"}); +} + +void StatsTextRender::generate(Buffer::Instance& response, const std::string& name, + const std::string& value) { + response.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); +} + +void StatsTextRender::generate(Buffer::Instance& response, const std::string& name, + const Stats::ParentHistogram& histogram) { + switch (histogram_buckets_mode_) { + case Utility::HistogramBucketsMode::NoBuckets: + response.addFragments({name, ": ", histogram.quantileSummary(), "\n"}); + break; + case Utility::HistogramBucketsMode::Cumulative: + response.addFragments({name, ": ", histogram.bucketSummary(), "\n"}); + break; + case Utility::HistogramBucketsMode::Disjoint: + addDisjointBuckets(name, histogram, response); + break; + } +} + +void StatsTextRender::render(Buffer::Instance&) {} + +// Computes disjoint buckets as text and adds them to the response buffer. +void StatsTextRender::addDisjointBuckets(const std::string& name, + const Stats::ParentHistogram& histogram, + Buffer::Instance& response) { + if (!histogram.used()) { + response.addFragments({name, ": No recorded values\n"}); + return; + } + response.addFragments({name, ": "}); + std::vector bucket_summary; + + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); + const UInt64Vec disjoint_interval_buckets = interval_statistics.computeDisjointBuckets(); + const UInt64Vec disjoint_cumulative_buckets = + histogram.cumulativeStatistics().computeDisjointBuckets(); + // Make sure all vectors are the same size. + ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); + ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); + size_t min_size = std::min({disjoint_interval_buckets.size(), disjoint_cumulative_buckets.size(), + supported_buckets.size()}); + std::vector bucket_strings; + bucket_strings.reserve(min_size); + for (size_t i = 0; i < min_size; ++i) { + if (i != 0) { + bucket_summary.push_back(" "); + } + bucket_strings.push_back(fmt::format("B{:g}({},{})", supported_buckets[i], + disjoint_interval_buckets[i], + disjoint_cumulative_buckets[i])); + bucket_summary.push_back(bucket_strings.back()); + } + bucket_summary.push_back("\n"); + response.addFragments(bucket_summary); +} + +StatsJsonRender::StatsJsonRender(Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response, + Utility::HistogramBucketsMode histogram_buckets_mode) + : histogram_buckets_mode_(histogram_buckets_mode) { + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + // We don't create a JSON data model for the entire stats output, as that + // makes streaming difficult. Instead we emit the preamble in the + // constructor here, and create json models for each stats entry. + response.add("{\"stats\":["); +} + +// Buffers a JSON fragment for a numeric stats, flushing to the response +// buffer once we exceed JsonStatsFlushCount stats. +void StatsJsonRender::generate(Buffer::Instance& response, const std::string& name, + uint64_t value) { + addScalar(response, name, ValueUtil::numberValue(value)); +} + +// Buffers a JSON fragment for a text-readout stat, flushing to the response +// buffer once we exceed JsonStatsFlushCount stats. +void StatsJsonRender::generate(Buffer::Instance& response, const std::string& name, + const std::string& value) { + addScalar(response, name, ValueUtil::stringValue(value)); +} + +// In JSON we buffer all histograms and don't write them immediately, so we +// can, in one JSON structure, emit shared attributes of all histograms and +// each individual histogram. +// +// This is counter to the goals of streaming and chunked interfaces, but +// usually there are far fewer histograms than counters or gauges. +// +// We can further optimize this by streaming out the histograms object, one +// histogram at a time, in case buffering all the histograms in Envoy +// buffers up too much memory. +void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, + const Stats::ParentHistogram& histogram) { + switch (histogram_buckets_mode_) { + case Utility::HistogramBucketsMode::NoBuckets: + summarizeBuckets(name, histogram); + break; + case Utility::HistogramBucketsMode::Cumulative: + collectBuckets(name, histogram, + [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { + return histogram_statistics.computedBuckets(); + }); + break; + case Utility::HistogramBucketsMode::Disjoint: + collectBuckets(name, histogram, + [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { + return histogram_statistics.computeDisjointBuckets(); + }); + break; + } +} + +// Since histograms are buffered (see above), the render() method generates +// all of them. +void StatsJsonRender::render(Buffer::Instance& response) { + if (!stats_array_.empty()) { + flushStats(response); + } + if (!histogram_array_.empty()) { + ProtoMap& histograms_obj_container_fields = *histograms_obj_container_.mutable_fields(); + if (found_used_histogram_) { + ASSERT(histogram_buckets_mode_ == Utility::HistogramBucketsMode::NoBuckets); + ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); + histograms_obj_fields["computed_quantiles"] = ValueUtil::listValue(histogram_array_); + histograms_obj_container_fields["histograms"] = ValueUtil::structValue(histograms_obj_); + } else { + ASSERT(histogram_buckets_mode_ != Utility::HistogramBucketsMode::NoBuckets); + histograms_obj_container_fields["histograms"] = ValueUtil::listValue(histogram_array_); + } + auto str = MessageUtil::getJsonStringFromMessageOrDie( + ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); + addStatAsRenderedJson(response, str); + } + response.add("]}"); +} + +// Collects a scalar metric (text-readout, counter, or gauge) into an array of +// stats, so they can all be serialized in one shot when a threshold is +// reached. Serializing each one individually results in much worse +// performance (see stats_handler_speed_test.cc). +template +void StatsJsonRender::addScalar(Buffer::Instance& response, const std::string& name, + const Value& value) { + ProtobufWkt::Struct stat_obj; + ProtoMap& stat_obj_fields = *stat_obj.mutable_fields(); + stat_obj_fields["name"] = ValueUtil::stringValue(name); + stat_obj_fields["value"] = value; + addJson(response, ValueUtil::structValue(stat_obj)); +} + +// Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. +void StatsJsonRender::addJson(Buffer::Instance& response, ProtobufWkt::Value json) { + stats_array_.push_back(json); + + // We build up stats_array to a certain size so we can amortize the overhead + // of entering into the JSON serialization infrastructure. If we set the + // threshold too high we buffer too much memory, likely impacting processor + // cache. The optimum threshold found after a few experiments on a local + // host appears to be between 50 and 100. + if (stats_array_.size() == JsonStatsFlushCount) { + flushStats(response); + } +} + +// Flushes all stats that were buffered in addJson() above. +void StatsJsonRender::flushStats(Buffer::Instance& response) { + ASSERT(!stats_array_.empty()); + const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( + ValueUtil::listValue(stats_array_), false /* pretty */, true); + stats_array_.clear(); + + // We are going to wind up with multiple flushes which have to serialize as + // a single array, rather than a concatenation of multiple arrays, so we add + // those in the constructor and render() method, strip off the "[" and "]" + // from each buffered serialization. + ASSERT(json_array.size() >= 2); + ASSERT(json_array[0] == '['); + ASSERT(json_array[json_array.size() - 1] == ']'); + addStatAsRenderedJson(response, absl::string_view(json_array).substr(1, json_array.size() - 2)); +} + +// Adds a json fragment of scalar stats to the response buffer, including a +// "," delimiter if this is not the first fragment. +void StatsJsonRender::addStatAsRenderedJson(Buffer::Instance& response, absl::string_view json) { + if (first_) { + response.add(json); + first_ = false; + } else { + response.addFragments({",", json}); + } +} + +// Summarizes the buckets in the specified histogram, collecting JSON objects. +// Note, we do not flush this buffer to the network when it grows large, and +// if this becomes an issue it should be possible to do, noting that we are +// one or two levels nesting below the list of scalar stats due to the Envoy +// stats json schema, where histograms are grouped together. +void StatsJsonRender::summarizeBuckets(const std::string& name, + const Stats::ParentHistogram& histogram) { + if (!found_used_histogram_) { + // It is not possible for the supported quantiles to differ across histograms, so it is ok + // to send them once. + Stats::HistogramStatisticsImpl empty_statistics; + std::vector supported_quantile_array; + for (double quantile : empty_statistics.supportedQuantiles()) { + supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); + } + + ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); + histograms_obj_fields["supported_quantiles"] = ValueUtil::listValue(supported_quantile_array); + found_used_histogram_ = true; + } + + ProtobufWkt::Struct computed_quantile; + ProtoMap& computed_quantile_fields = *computed_quantile.mutable_fields(); + computed_quantile_fields["name"] = ValueUtil::stringValue(name); + + std::vector computed_quantile_value_array; + for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { + ProtobufWkt::Struct computed_quantile_value; + ProtoMap& computed_quantile_value_fields = *computed_quantile_value.mutable_fields(); + const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; + const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; + computed_quantile_value_fields["interval"] = + std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); + computed_quantile_value_fields["cumulative"] = + std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); + + computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); + } + computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); + histogram_array_.push_back(ValueUtil::structValue(computed_quantile)); +} + +// Collects the buckets from the specified histogram, using either the cumulative +// or disjoint views, as controlled by buckets_fn. +void StatsJsonRender::collectBuckets( + const std::string& name, const Stats::ParentHistogram& histogram, + std::function buckets_fn) { + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); + UInt64Vec interval_buckets = buckets_fn(interval_statistics); + UInt64Vec cumulative_buckets = buckets_fn(histogram.cumulativeStatistics()); + + // Make sure all vectors are the same size. + ASSERT(interval_buckets.size() == cumulative_buckets.size()); + ASSERT(cumulative_buckets.size() == supported_buckets.size()); + size_t min_size = + std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()}); + + ProtobufWkt::Struct histogram_obj; + ProtoMap& histogram_obj_fields = *histogram_obj.mutable_fields(); + histogram_obj_fields["name"] = ValueUtil::stringValue(name); + + std::vector bucket_array; + for (size_t i = 0; i < min_size; ++i) { + ProtobufWkt::Struct bucket; + ProtoMap& bucket_fields = *bucket.mutable_fields(); + bucket_fields["upper_bound"] = ValueUtil::numberValue(supported_buckets[i]); + + // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. + bucket_fields["interval"] = ValueUtil::numberValue(interval_buckets[i]); + bucket_fields["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); + bucket_array.push_back(ValueUtil::structValue(bucket)); + } + histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); + histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); +} + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h new file mode 100644 index 0000000000000..c7939ee1fc4af --- /dev/null +++ b/source/server/admin/stats_render.h @@ -0,0 +1,117 @@ +#include "envoy/stats/stats.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/server/admin/utils.h" + +namespace { +constexpr uint64_t ChunkSize = 2 * 1000 * 1000; +using UInt64Vec = std::vector; +} // namespace + +namespace Envoy { +namespace Server { + +// Abstract class for rendering stats. Every method is called "generate" +// differing only by the data type, to facilitate templatized call-sites. +// +// There are currently Json and Text implementations of this interface, and in +// #19546 an HTML version will be added to provide a hierarchical view. +class StatsRender { +public: + virtual ~StatsRender() = default; + + // Writes a fragment for a numeric value, for counters and gauges. + virtual void generate(Buffer::Instance& response, const std::string& name, uint64_t value) PURE; + + // Writes a json fragment for a textual value, for text readouts. + virtual void generate(Buffer::Instance& response, const std::string& name, + const std::string& value) PURE; + + // Writes a histogram value. + virtual void generate(Buffer::Instance& response, const std::string& name, + const Stats::ParentHistogram& histogram) PURE; + + // Completes rendering any buffered data. + virtual void render(Buffer::Instance& response) PURE; + + // Determines whether the current chunk is full. + bool isChunkFull(Buffer::Instance& response) { return response.length() > ChunkSize; } +}; + +// Implements the Render interface for simple textual representation of stats. +class StatsTextRender : public StatsRender { +public: + explicit StatsTextRender(Utility::HistogramBucketsMode histogram_buckets_mode) + : histogram_buckets_mode_(histogram_buckets_mode) {} + + // StatsRender + void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override; + void generate(Buffer::Instance& response, const std::string& name, + const std::string& value) override; + void generate(Buffer::Instance& response, const std::string& name, + const Stats::ParentHistogram& histogram) override; + void render(Buffer::Instance&) override; + +private: + // Computes disjoint buckets as text and adds them to the response buffer. + void addDisjointBuckets(const std::string& name, const Stats::ParentHistogram& histogram, + Buffer::Instance& response); + + const Utility::HistogramBucketsMode histogram_buckets_mode_; +}; + +// Implements the Render interface for json output. +class StatsJsonRender : public StatsRender { +public: + StatsJsonRender(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, + Utility::HistogramBucketsMode histogram_buckets_mode); + + // StatsRender + void generate(Buffer::Instance& response, const std::string& name, uint64_t value) override; + void generate(Buffer::Instance& response, const std::string& name, + const std::string& value) override; + void generate(Buffer::Instance&, const std::string& name, + const Stats::ParentHistogram& histogram) override; + void render(Buffer::Instance& response) override; + +private: + // Collects a scalar metric (text-readout, counter, or gauge) into an array of + // stats, so they can all be serialized in one shot when a threshold is + // reached. Serializing each one individually results in much worse + // performance (see stats_handler_speed_test.cc). + template + void addScalar(Buffer::Instance& response, const std::string& name, const Value& value); + + // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. + void addJson(Buffer::Instance& response, ProtobufWkt::Value json); + + // Flushes all stats that were buffered in addJson() above. + void flushStats(Buffer::Instance& response); + + // Adds a json fragment of scalar stats to the response buffer, including a + // "," delimiter if this is not the first fragment. + void addStatAsRenderedJson(Buffer::Instance& response, absl::string_view json); + + // Summarizes the buckets in the specified histogram, collecting JSON objects. + // Note, we do not flush this buffer to the network when it grows large, and + // if this becomes an issue it should be possible to do, noting that we are + // one or two levels nesting below the list of scalar stats due to the Envoy + // stats json schema, where histograms are grouped together. + void summarizeBuckets(const std::string& name, const Stats::ParentHistogram& histogram); + + // Collects the buckets from the specified histogram, using either the cumulative + // or disjoint views, as controlled by buckets_fn. + void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, + std::function buckets_fn); + + std::vector stats_array_; + ProtobufWkt::Struct histograms_obj_; + ProtobufWkt::Struct histograms_obj_container_; + std::vector histogram_array_; + bool found_used_histogram_{false}; + bool first_{true}; + const Utility::HistogramBucketsMode histogram_buckets_mode_; +}; + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/stats_request.cc b/source/server/admin/stats_request.cc new file mode 100644 index 0000000000000..206ed766c6994 --- /dev/null +++ b/source/server/admin/stats_request.cc @@ -0,0 +1,146 @@ +#include "source/server/admin/stats_request.h" + +namespace Envoy { +namespace Server { + +StatsRequest::StatsRequest(Stats::Store& stats, bool used_only, bool json, + Utility::HistogramBucketsMode histogram_buckets_mode, + absl::optional regex) + : used_only_(used_only), json_(json), histogram_buckets_mode_(histogram_buckets_mode), + regex_(regex), stats_(stats) {} + +Http::Code StatsRequest::start(Http::ResponseHeaderMap& response_headers) { + if (json_) { + render_ = + std::make_unique(response_headers, response_, histogram_buckets_mode_); + } else { + render_ = std::make_unique(histogram_buckets_mode_); + } + + // Populate the top-level scopes and the stats underneath any scopes with an empty name. + // We will have to de-dup, but we can do that after sorting. + // + // First capture all the scopes and hold onto them with a SharedPtr so they + // can't be deleted after the initial iteration. + stats_.forEachScope( + [this](size_t s) { scopes_.reserve(s); }, + [this](const Stats::Scope& scope) { scopes_.emplace_back(scope.getConstShared()); }); + + startPhase(); + return Http::Code::OK; +} + +bool StatsRequest::nextChunk(Buffer::Instance& response) { + if (response_.length() > 0) { + ASSERT(response.length() == 0); + response.move(response_); + ASSERT(response_.length() == 0); + } + while (!render_->isChunkFull(response)) { + while (stat_map_.empty()) { + switch (phase_) { + case Phase::TextReadouts: + phase_ = Phase::CountersAndGauges; + startPhase(); + break; + case Phase::CountersAndGauges: + phase_ = Phase::Histograms; + startPhase(); + break; + case Phase::Histograms: + render_->render(response); + return false; + } + } + + auto iter = stat_map_.begin(); + const std::string& name = iter->first; + StatOrScopes& variant = iter->second; + switch (variant.index()) { + case 0: + populateStatsForCurrentPhase(absl::get(variant)); + break; + case 1: + renderStat(name, response, variant); + break; + case 2: + renderStat(name, response, variant); + break; + case 3: + renderStat(name, response, variant); + break; + case 4: { + auto histogram = absl::get(variant); + auto parent_histogram = dynamic_cast(histogram.get()); + if (parent_histogram != nullptr) { + render_->generate(response, name, *parent_histogram); + } + } + } + stat_map_.erase(iter); + } + return true; +} + +void StatsRequest::startPhase() { + ASSERT(stat_map_.empty()); + for (const Stats::ConstScopeSharedPtr& scope : scopes_) { + StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; + if (variant.index() == absl::variant_npos) { + variant = ScopeVec(); + } + absl::get(variant).emplace_back(scope); + } + + // Populate stat_map with all the counters found in all the scopes with an + // empty prefix. + auto iter = stat_map_.find(""); + if (iter != stat_map_.end()) { + StatOrScopes variant = std::move(iter->second); + stat_map_.erase(iter); + auto& scope_vec = absl::get(variant); + populateStatsForCurrentPhase(scope_vec); + } +} + +void StatsRequest::populateStatsForCurrentPhase(const ScopeVec& scope_vec) { + switch (phase_) { + case Phase::TextReadouts: + populateStatsFromScopes(scope_vec); + break; + case Phase::CountersAndGauges: + populateStatsFromScopes(scope_vec); + populateStatsFromScopes(scope_vec); + break; + case Phase::Histograms: + populateStatsFromScopes(scope_vec); + break; + } +} + +template void StatsRequest::populateStatsFromScopes(const ScopeVec& scope_vec) { + for (const Stats::ConstScopeSharedPtr& scope : scope_vec) { + Stats::IterateFn fn = [this](const Stats::RefcountPtr& stat) -> bool { + if (used_only_ && !stat->used()) { + return true; + } + std::string name = stat->name(); + if (regex_.has_value() && !std::regex_search(name, regex_.value())) { + return true; + } + stat_map_[name] = stat; + return true; + }; + scope->iterate(fn); + } +} + +template +void StatsRequest::renderStat(const std::string& name, Buffer::Instance& response, + StatOrScopes& variant) { + auto stat = absl::get(variant); + render_->generate(response, name, stat->value()); +} + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/stats_request.h b/source/server/admin/stats_request.h new file mode 100644 index 0000000000000..9bb4df80d4533 --- /dev/null +++ b/source/server/admin/stats_request.h @@ -0,0 +1,102 @@ +#pragma once + +#include "envoy/server/admin.h" + +#include "source/server/admin/stats_render.h" +#include "source/server/admin/utils.h" + +#include "absl/container/btree_map.h" +#include "absl/types/variant.h" + +namespace Envoy { +namespace Server { + +// Captures context for a streaming request, implementing the AdminHandler interface. +class StatsRequest : public Admin::Request { + using ScopeVec = std::vector; + using StatOrScopes = absl::variant; + enum class Phase { + TextReadouts, + CountersAndGauges, + Histograms, + }; + +public: + StatsRequest(Stats::Store& stats, bool used_only, bool json, + Utility::HistogramBucketsMode histogram_buckets_mode, + absl::optional regex); + + // Admin::Request + Http::Code start(Http::ResponseHeaderMap& response_headers) override; + + // Streams out the next chunk of stats to the client, visiting only the scopes + // that can plausibly contribute the next set of named stats. This enables us + // to linearly traverse the entire set of stats without buffering all of them + // and sorting. + // + // Instead we keep the a set of candidate stats to emit in stat_map_ an + // alphabetically ordered btree, which heterogeneously stores stats of all + // types and scopes. Note that there can be multiple scopes with the same + // name, so we keep same-named scopes in a vector. However leaf metrics cannot + // have duplicates. It would also be feasible to use a multi-map for this. + // + // So in start() above, we initially populate all the scopes, as well as the + // metrics contained in all scopes with an empty name. So in nextChunk we can + // emit and remove the first element of stat_map_. When we encounter a vector + // of scopes then we add the contained metrics to the map and continue + // iterating. + // + // Whenever the desired chunk size is reached we end the current chunk so that + // the current buffer can be flushed to the network. In #19898 we will + // introduce flow-control so that we don't buffer the all the serialized stats + // while waiting for a slow client. + // + // Note that we do 3 passes through all the scopes_, so that we can emit + // text-readouts first, then the intermingled counters and gauges, and finally + // the histograms. + bool nextChunk(Buffer::Instance& response) override; + + // To duplicate prior behavior for this class, we do three passes over all the stats: + // 1. text readouts across all scopes + // 2. counters and gauges, co-mingled, across all scopes + // 3. histograms across all scopes. + // It would be little more efficient to co-mingle all the stats, but three + // passes over the scopes is OK. In the future we may decide to organize the + // result data differently, but in the process of changing from buffering + // the entire /stats response to streaming the data out in chunks, it's easier + // to reason about if the tests don't change their expectations. + void startPhase(); + + // Iterates over scope_vec and populates the metric types associated with the + // current phase. + void populateStatsForCurrentPhase(const ScopeVec& scope_vec); + + // Populates all the metrics of the templatized type from scope_vec. Here we + // exploit that Scope::iterate is a generic templatized function to avoid code + // duplication. + template void populateStatsFromScopes(const ScopeVec& scope_vec); + + // Renders the templatized type, exploiting the fact that Render::generate is + // generic to avoid code duplication. + template + void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); + +private: + const bool used_only_; + const bool json_; + const Utility::HistogramBucketsMode histogram_buckets_mode_; + absl::optional regex_; + absl::optional format_value_; + + std::unique_ptr render_; + + Stats::Store& stats_; + ScopeVec scopes_; + absl::btree_map stat_map_; + Phase phase_{Phase::TextReadouts}; + Buffer::OwnedImpl response_; +}; + +} // namespace Server +} // namespace Envoy diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index 2f21109318965..86a0f299fe5e6 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -86,7 +86,8 @@ "./contrib/squash/filters/http/source/squash_filter.h", "./contrib/squash/filters/http/source/squash_filter.cc", "./source/server/admin/utils.h", "./source/server/admin/utils.cc", "./source/server/admin/stats_handler.h", - "./source/server/admin/stats_handler.cc", "./source/server/admin/prometheus_stats.h", + "./source/server/admin/stats_handler.cc", "./source/server/admin/stats_request.cc", + "./source/server/admin/stats_request.h", "./source/server/admin/prometheus_stats.h", "./source/server/admin/prometheus_stats.cc", "./tools/clang_tools/api_booster/main.cc", "./tools/clang_tools/api_booster/proto_cxx_utils.cc", "./source/common/version/version.cc") From b8660f58566a87fd8123430f9c9b1489e6b4d269 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 22:05:42 -0500 Subject: [PATCH 52/74] split up libraries Signed-off-by: Joshua Marantz --- source/server/admin/BUILD | 47 +++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index e4ad07551b216..74ab911c02a4f 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -91,31 +91,64 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "stats_request_lib", + srcs = [ + "stats_request.cc", + ], + hdrs = [ + "stats_request.h", + ], + deps = [ + ":handler_ctx_lib", + ":prometheus_stats_lib", + ":stats_render_lib", + ":utils_lib", + "//envoy/http:codes_interface", + "//envoy/server:admin_interface", + "//source/common/http:codes_lib", + "//source/common/http:header_map_lib", + "//source/common/stats:histogram_lib", + "@com_google_absl//absl/container:btree", + ], +) + +envoy_cc_library( + name = "stats_render_lib", + srcs = [ + "stats_render.cc", + ], + hdrs = [ + "stats_render.h", + ], + deps = [ + ":utils_lib", + "//source/common/buffer:buffer_lib", + "//source/common/html:utility_lib", + "//source/common/stats:histogram_lib", + ], +) + envoy_cc_library( name = "stats_handler_lib", srcs = [ "stats_handler.cc", - "stats_render.cc", - "stats_request.cc", ], hdrs = [ "stats_handler.h", - "stats_render.h", - "stats_request.h", ], deps = [ ":handler_ctx_lib", ":prometheus_stats_lib", + ":stats_render_lib", + ":stats_request_lib", ":utils_lib", "//envoy/http:codes_interface", "//envoy/server:admin_interface", "//envoy/server:instance_interface", "//source/common/buffer:buffer_lib", - "//source/common/html:utility_lib", "//source/common/http:codes_lib", "//source/common/http:header_map_lib", - "//source/common/stats:histogram_lib", - "@com_google_absl//absl/container:btree", "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) From ddeaba12876806db7b0d2b93c2533f4f943eb017 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 22:25:05 -0500 Subject: [PATCH 53/74] cleanup Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 75930f696f591..ca2ad644dde95 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -237,11 +237,22 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, computed_quantile_fields["name"] = ValueUtil::stringValue(name); std::vector computed_quantile_value_array; - for (size_t i = 0; i < histogram.intervalStatistics().supportedQuantiles().size(); ++i) { + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + const std::vector& computed_quantiles = interval_statistics.computedQuantiles(); + const std::vector& cumulative_quantiles = + histogram.cumulativeStatistics().computedQuantiles(); + size_t min_size = std::min({ + computed_quantiles.size(), + cumulative_quantiles.size(), + interval_statistics.supportedQuantiles().size()}); + ASSERT(min_size == computed_quantiles.size()); + ASSERT(min_size == cumulative_quantiles.size()); + + for (size_t i = 0; i < min_size; ++i) { ProtobufWkt::Struct computed_quantile_value; ProtoMap& computed_quantile_value_fields = *computed_quantile_value.mutable_fields(); - const auto& interval = histogram.intervalStatistics().computedQuantiles()[i]; - const auto& cumulative = histogram.cumulativeStatistics().computedQuantiles()[i]; + const auto& interval = computed_quantiles[i]; + const auto& cumulative = cumulative_quantiles[i]; computed_quantile_value_fields["interval"] = std::isnan(interval) ? ValueUtil::nullValue() : ValueUtil::numberValue(interval); computed_quantile_value_fields["cumulative"] = @@ -260,8 +271,8 @@ void StatsJsonRender::collectBuckets( std::function buckets_fn) { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); - UInt64Vec interval_buckets = buckets_fn(interval_statistics); - UInt64Vec cumulative_buckets = buckets_fn(histogram.cumulativeStatistics()); + const UInt64Vec& interval_buckets = buckets_fn(interval_statistics); + const UInt64Vec& cumulative_buckets = buckets_fn(histogram.cumulativeStatistics()); // Make sure all vectors are the same size. ASSERT(interval_buckets.size() == cumulative_buckets.size()); From d2405e9c2f8ce3e3f8fbe6a7db72e4ab645acd46 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 22:40:00 -0500 Subject: [PATCH 54/74] Reduce copying of vectors, which was needed because the disjoint variants were computed vs others that were referenced. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 37 ++++++++++++++--------------- source/server/admin/stats_render.h | 9 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index ca2ad644dde95..dee7671ce3e9f 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -116,19 +116,21 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, case Utility::HistogramBucketsMode::NoBuckets: summarizeBuckets(name, histogram); break; - case Utility::HistogramBucketsMode::Cumulative: - collectBuckets(name, histogram, - [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { - return histogram_statistics.computedBuckets(); - }); + case Utility::HistogramBucketsMode::Cumulative: { + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + const UInt64Vec& interval_buckets = interval_statistics.computedBuckets(); + const UInt64Vec& cumulative_buckets = histogram.cumulativeStatistics().computedBuckets(); + collectBuckets(name, histogram, interval_buckets, cumulative_buckets); break; - case Utility::HistogramBucketsMode::Disjoint: - collectBuckets(name, histogram, - [](const Stats::HistogramStatistics& histogram_statistics) -> UInt64Vec { - return histogram_statistics.computeDisjointBuckets(); - }); + } + case Utility::HistogramBucketsMode::Disjoint: { + const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); + const UInt64Vec& interval_buckets = interval_statistics.computeDisjointBuckets(); + const UInt64Vec& cumulative_buckets = histogram.cumulativeStatistics().computeDisjointBuckets(); + collectBuckets(name, histogram, interval_buckets, cumulative_buckets); break; } + } } // Since histograms are buffered (see above), the render() method generates @@ -241,10 +243,8 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, const std::vector& computed_quantiles = interval_statistics.computedQuantiles(); const std::vector& cumulative_quantiles = histogram.cumulativeStatistics().computedQuantiles(); - size_t min_size = std::min({ - computed_quantiles.size(), - cumulative_quantiles.size(), - interval_statistics.supportedQuantiles().size()}); + size_t min_size = std::min({computed_quantiles.size(), cumulative_quantiles.size(), + interval_statistics.supportedQuantiles().size()}); ASSERT(min_size == computed_quantiles.size()); ASSERT(min_size == cumulative_quantiles.size()); @@ -266,13 +266,12 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, // Collects the buckets from the specified histogram, using either the cumulative // or disjoint views, as controlled by buckets_fn. -void StatsJsonRender::collectBuckets( - const std::string& name, const Stats::ParentHistogram& histogram, - std::function buckets_fn) { +void StatsJsonRender::collectBuckets(const std::string& name, + const Stats::ParentHistogram& histogram, + const UInt64Vec& interval_buckets, + const UInt64Vec& cumulative_buckets) { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); - const UInt64Vec& interval_buckets = buckets_fn(interval_statistics); - const UInt64Vec& cumulative_buckets = buckets_fn(histogram.cumulativeStatistics()); // Make sure all vectors are the same size. ASSERT(interval_buckets.size() == cumulative_buckets.size()); diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index c7939ee1fc4af..ab986971078ef 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -5,7 +5,6 @@ namespace { constexpr uint64_t ChunkSize = 2 * 1000 * 1000; -using UInt64Vec = std::vector; } // namespace namespace Envoy { @@ -36,6 +35,9 @@ class StatsRender { // Determines whether the current chunk is full. bool isChunkFull(Buffer::Instance& response) { return response.length() > ChunkSize; } + +protected: + using UInt64Vec = std::vector; }; // Implements the Render interface for simple textual representation of stats. @@ -99,10 +101,9 @@ class StatsJsonRender : public StatsRender { // stats json schema, where histograms are grouped together. void summarizeBuckets(const std::string& name, const Stats::ParentHistogram& histogram); - // Collects the buckets from the specified histogram, using either the cumulative - // or disjoint views, as controlled by buckets_fn. + // Collects the buckets from the specified histogram. void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, - std::function buckets_fn); + const UInt64Vec& interval_buckets, const UInt64Vec& cumulative_buckets); std::vector stats_array_; ProtobufWkt::Struct histograms_obj_; From a1140e643ab85a273733f151ebbd5fd3edb2dd6c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 22:43:32 -0500 Subject: [PATCH 55/74] syntax cleanup Signed-off-by: Joshua Marantz --- source/server/admin/BUILD | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 74ab911c02a4f..31e152e722c06 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -93,12 +93,8 @@ envoy_cc_library( envoy_cc_library( name = "stats_request_lib", - srcs = [ - "stats_request.cc", - ], - hdrs = [ - "stats_request.h", - ], + srcs = ["stats_request.cc"], + hdrs = ["stats_request.h"], deps = [ ":handler_ctx_lib", ":prometheus_stats_lib", @@ -115,12 +111,8 @@ envoy_cc_library( envoy_cc_library( name = "stats_render_lib", - srcs = [ - "stats_render.cc", - ], - hdrs = [ - "stats_render.h", - ], + srcs = ["stats_render.cc"], + hdrs = ["stats_render.h"], deps = [ ":utils_lib", "//source/common/buffer:buffer_lib", @@ -131,12 +123,8 @@ envoy_cc_library( envoy_cc_library( name = "stats_handler_lib", - srcs = [ - "stats_handler.cc", - ], - hdrs = [ - "stats_handler.h", - ], + srcs = ["stats_handler.cc"], + hdrs = ["stats_handler.h"], deps = [ ":handler_ctx_lib", ":prometheus_stats_lib", From 27b748c0d6a2137aca2054d4adba3964ad481c60 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 2 Mar 2022 22:49:27 -0500 Subject: [PATCH 56/74] clarify ownership of returned value for computeDisjointBuckets() Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index dee7671ce3e9f..42d9aa1000e98 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -125,8 +125,8 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, } case Utility::HistogramBucketsMode::Disjoint: { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - const UInt64Vec& interval_buckets = interval_statistics.computeDisjointBuckets(); - const UInt64Vec& cumulative_buckets = histogram.cumulativeStatistics().computeDisjointBuckets(); + const UInt64Vec interval_buckets = interval_statistics.computeDisjointBuckets(); + const UInt64Vec cumulative_buckets = histogram.cumulativeStatistics().computeDisjointBuckets(); collectBuckets(name, histogram, interval_buckets, cumulative_buckets); break; } From b9152a75e404262de366970157af21782e4c7ca1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 8 Mar 2022 17:28:45 -0500 Subject: [PATCH 57/74] add unit test for the Render classes. Signed-off-by: Joshua Marantz --- test/server/admin/BUILD | 13 + test/server/admin/stats_render_test.cc | 346 +++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 test/server/admin/stats_render_test.cc diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 78383718d1251..99d3b1148862c 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -70,6 +70,19 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "stats_render_test", + srcs = ["stats_render_test.cc"], + deps = [ + "//source/common/stats:thread_local_store_lib", + "//source/server/admin:stats_render_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/stats:stats_mocks", + "//test/mocks/thread_local:thread_local_mocks", + "//test/test_common:utility_lib", + ], +) + envoy_cc_benchmark_binary( name = "stats_handler_speed_test", srcs = ["stats_handler_speed_test.cc"], diff --git a/test/server/admin/stats_render_test.cc b/test/server/admin/stats_render_test.cc new file mode 100644 index 0000000000000..96713fc0532a7 --- /dev/null +++ b/test/server/admin/stats_render_test.cc @@ -0,0 +1,346 @@ +#include + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/stats/thread_local_store.h" +#include "source/server/admin/stats_render.h" + +#include "test/mocks/event/mocks.h" +#include "test/mocks/stats/mocks.h" +#include "test/mocks/thread_local/mocks.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +using testing::NiceMock; + +namespace Envoy { +namespace Server { + +class StatsRenderTest : public testing::Test { +protected: + StatsRenderTest() : alloc_(symbol_table_), store_(alloc_) { + store_.addSink(sink_); + store_.initializeThreading(main_thread_dispatcher_, tls_); + } + + ~StatsRenderTest() { + tls_.shutdownGlobalThreading(); + store_.shutdownThreading(); + tls_.shutdownThread(); + } + + template + std::string render(StatsRender& render, absl::string_view name, const T& value) { + render.generate(response_, std::string(name), value); + render.render(response_); + return response_.toString(); + } + + Stats::ParentHistogram& populateHistogram(const std::string& name, + const std::vector& vals) { + Stats::Histogram& h = store_.histogramFromString(name, Stats::Histogram::Unit::Unspecified); + for (uint64_t val : vals) { + h.recordValue(val); + } + store_.mergeHistograms([]() -> void {}); + return dynamic_cast(h); + } + + Stats::SymbolTableImpl symbol_table_; + Stats::AllocatorImpl alloc_; + NiceMock sink_; + NiceMock main_thread_dispatcher_; + NiceMock tls_; + Stats::ThreadLocalStoreImpl store_; + Http::TestResponseHeaderMapImpl response_headers_; + Buffer::OwnedImpl response_; +}; + +TEST_F(StatsRenderTest, TextInt) { + StatsTextRender renderer(Utility::HistogramBucketsMode::NoBuckets); + EXPECT_EQ("name: 42\n", render(renderer, "name", 42)); +} + +TEST_F(StatsRenderTest, TextString) { + StatsTextRender renderer(Utility::HistogramBucketsMode::NoBuckets); + EXPECT_EQ("name: \"abc 123 ~!@#$%^&*()-_=+;:'",<.>/?\"\n", + render(renderer, "name", "abc 123 ~!@#$%^&*()-_=+;:'\",<.>/?")); +} + +TEST_F(StatsRenderTest, TextHistogramNoBuckets) { + StatsTextRender renderer(Utility::HistogramBucketsMode::NoBuckets); + constexpr absl::string_view expected = + "h1: P0(200.0,200.0) P25(207.5,207.5) P50(302.5,302.5) P75(306.25,306.25) " + "P90(308.5,308.5) P95(309.25,309.25) P99(309.85,309.85) P99.5(309.925,309.925) " + "P99.9(309.985,309.985) P100(310.0,310.0)\n"; + EXPECT_EQ(expected, render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300}))); +} + +TEST_F(StatsRenderTest, TextHistogramCumulative) { + StatsTextRender renderer(Utility::HistogramBucketsMode::Cumulative); + constexpr absl::string_view expected = + "h1: B0.5(0,0) B1(0,0) B5(0,0) B10(0,0) B25(0,0) B50(0,0) B100(0,0) B250(1,1) " + "B500(3,3) B1000(3,3) B2500(3,3) B5000(3,3) B10000(3,3) B30000(3,3) B60000(3,3) " + "B300000(3,3) B600000(3,3) B1.8e+06(3,3) B3.6e+06(3,3)\n"; + EXPECT_EQ(expected, render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300}))); +} + +TEST_F(StatsRenderTest, TextHistogramDisjoint) { + StatsTextRender renderer(Utility::HistogramBucketsMode::Disjoint); + constexpr absl::string_view expected = + "h1: B0.5(0,0) B1(0,0) B5(0,0) B10(0,0) B25(0,0) B50(0,0) B100(0,0) B250(1,1) B500(2,2) " + "B1000(0,0) B2500(0,0) B5000(0,0) B10000(0,0) B30000(0,0) B60000(0,0) B300000(0,0) " + "B600000(0,0) B1.8e+06(0,0) B3.6e+06(0,0)\n"; + EXPECT_EQ(expected, render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300}))); +} + +TEST_F(StatsRenderTest, JsonInt) { + StatsJsonRender renderer(response_headers_, response_, Utility::HistogramBucketsMode::NoBuckets); + const std::string expected = R"EOF({"stats":[ {"value":42, "name":"name"}]})EOF"; + EXPECT_THAT(render(renderer, "name", 42), JsonStringEq(expected)); +} + +TEST_F(StatsRenderTest, JsonString) { + StatsJsonRender renderer(response_headers_, response_, Utility::HistogramBucketsMode::NoBuckets); + const std::string expected = R"EOF({ + "stats": [{"value": "abc 123 ~!@#$%^&*()-_=+;:'\",\u003c.\u003e/?", + "name": "name"}]})EOF"; + EXPECT_THAT(render(renderer, "name", "abc 123 ~!@#$%^&*()-_=+;:'\",<.>/?"), + JsonStringEq(expected)); +} + +TEST_F(StatsRenderTest, JsonHistogramNoBuckets) { + StatsJsonRender renderer(response_headers_, response_, Utility::HistogramBucketsMode::NoBuckets); + const std::string expected = R"EOF( +{ + "stats": [{ + "histograms": { + "supported_quantiles": [0, 25, 50, 75, 90, 95, 99, 99.5, 99.9, 100], + "computed_quantiles": [{ + "values": [{ + "cumulative": 200, + "interval": 200 + }, { + "interval": 207.5, + "cumulative": 207.5 + }, { + "interval": 302.5, + "cumulative": 302.5 + }, { + "cumulative": 306.25, + "interval": 306.25 + }, { + "cumulative": 308.5, + "interval": 308.5 + }, { + "cumulative": 309.25, + "interval": 309.25 + }, { + "interval": 309.85, + "cumulative": 309.85 + }, { + "cumulative": 309.925, + "interval": 309.925 + }, { + "interval": 309.985, + "cumulative": 309.985 + }, { + "cumulative": 310, + "interval": 310 + }], + "name": "h1" + }] + } + }] +} + )EOF"; + EXPECT_THAT(render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300})), + JsonStringEq(expected)); +} + +TEST_F(StatsRenderTest, JsonHistogramCumulative) { + StatsJsonRender renderer(response_headers_, response_, Utility::HistogramBucketsMode::Cumulative); + const std::string expected = R"EOF( +{ + "stats": [{ + "histograms": [{ + "name": "h1", + "buckets": [{ + "upper_bound": 0.5, + "interval": 0, + "cumulative": 0 + }, { + "upper_bound": 1, + "interval": 0, + "cumulative": 0 + }, { + "interval": 0, + "upper_bound": 5, + "cumulative": 0 + }, { + "interval": 0, + "upper_bound": 10, + "cumulative": 0 + }, { + "interval": 0, + "upper_bound": 25, + "cumulative": 0 + }, { + "cumulative": 0, + "upper_bound": 50, + "interval": 0 + }, { + "upper_bound": 100, + "interval": 0, + "cumulative": 0 + }, { + "upper_bound": 250, + "cumulative": 1, + "interval": 1 + }, { + "upper_bound": 500, + "interval": 3, + "cumulative": 3 + }, { + "cumulative": 3, + "upper_bound": 1000, + "interval": 3 + }, { + "upper_bound": 2500, + "cumulative": 3, + "interval": 3 + }, { + "cumulative": 3, + "interval": 3, + "upper_bound": 5000 + }, { + "upper_bound": 10000, + "cumulative": 3, + "interval": 3 + }, { + "upper_bound": 30000, + "interval": 3, + "cumulative": 3 + }, { + "cumulative": 3, + "upper_bound": 60000, + "interval": 3 + }, { + "interval": 3, + "upper_bound": 300000, + "cumulative": 3 + }, { + "upper_bound": 600000, + "interval": 3, + "cumulative": 3 + }, { + "interval": 3, + "cumulative": 3, + "upper_bound": 1800000 + }, { + "upper_bound": 3600000, + "interval": 3, + "cumulative": 3 + }] + }] + }] +} + )EOF"; + EXPECT_THAT(render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300})), + JsonStringEq(expected)); +} + +TEST_F(StatsRenderTest, JsonHistogramDisjoint) { + StatsJsonRender renderer(response_headers_, response_, Utility::HistogramBucketsMode::Disjoint); + const std::string expected = R"EOF( +{ + "stats": [{ + "histograms": [{ + "buckets": [{ + "interval": 0, + "cumulative": 0, + "upper_bound": 0.5 + }, { + "cumulative": 0, + "interval": 0, + "upper_bound": 1 + }, { + "cumulative": 0, + "interval": 0, + "upper_bound": 5 + }, { + "upper_bound": 10, + "interval": 0, + "cumulative": 0 + }, { + "interval": 0, + "upper_bound": 25, + "cumulative": 0 + }, { + "cumulative": 0, + "upper_bound": 50, + "interval": 0 + }, { + "cumulative": 0, + "upper_bound": 100, + "interval": 0 + }, { + "interval": 1, + "cumulative": 1, + "upper_bound": 250 + }, { + "cumulative": 2, + "upper_bound": 500, + "interval": 2 + }, { + "interval": 0, + "cumulative": 0, + "upper_bound": 1000 + }, { + "interval": 0, + "cumulative": 0, + "upper_bound": 2500 + }, { + "interval": 0, + "upper_bound": 5000, + "cumulative": 0 + }, { + "upper_bound": 10000, + "interval": 0, + "cumulative": 0 + }, { + "cumulative": 0, + "interval": 0, + "upper_bound": 30000 + }, { + "upper_bound": 60000, + "interval": 0, + "cumulative": 0 + }, { + "upper_bound": 300000, + "interval": 0, + "cumulative": 0 + }, { + "cumulative": 0, + "upper_bound": 600000, + "interval": 0 + }, { + "upper_bound": 1800000, + "interval": 0, + "cumulative": 0 + }, { + "interval": 0, + "cumulative": 0, + "upper_bound": 3600000 + }], + "name": "h1" + }] + }] +} + )EOF"; + EXPECT_THAT(render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300})), + JsonStringEq(expected)); +} + +} // namespace Server +} // namespace Envoy From b5ed846af41061fab7b24bec86da6eedb0d018dc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 8 Mar 2022 22:37:15 -0500 Subject: [PATCH 58/74] add override Signed-off-by: Joshua Marantz --- test/server/admin/stats_render_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server/admin/stats_render_test.cc b/test/server/admin/stats_render_test.cc index 96713fc0532a7..7b4f7ffd6f9b0 100644 --- a/test/server/admin/stats_render_test.cc +++ b/test/server/admin/stats_render_test.cc @@ -23,7 +23,7 @@ class StatsRenderTest : public testing::Test { store_.initializeThreading(main_thread_dispatcher_, tls_); } - ~StatsRenderTest() { + ~StatsRenderTest() override { tls_.shutdownGlobalThreading(); store_.shutdownThreading(); tls_.shutdownThread(); From 4e34dba3f49197b6af65da2c0944411b34bb6bee Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 10 Mar 2022 16:39:03 -0500 Subject: [PATCH 59/74] review comments Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 8 ++++---- source/server/admin/stats_render.h | 6 +++--- source/server/admin/stats_request.cc | 4 ++-- source/server/admin/stats_request.h | 8 ++++++++ test/server/admin/stats_render_test.cc | 2 +- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 42d9aa1000e98..21add2106a8e1 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -37,7 +37,7 @@ void StatsTextRender::generate(Buffer::Instance& response, const std::string& na } } -void StatsTextRender::render(Buffer::Instance&) {} +void StatsTextRender::finalize(Buffer::Instance&) {} // Computes disjoint buckets as text and adds them to the response buffer. void StatsTextRender::addDisjointBuckets(const std::string& name, @@ -133,9 +133,9 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, } } -// Since histograms are buffered (see above), the render() method generates +// Since histograms are buffered (see above), the finalize() method generates // all of them. -void StatsJsonRender::render(Buffer::Instance& response) { +void StatsJsonRender::finalize(Buffer::Instance& response) { if (!stats_array_.empty()) { flushStats(response); } @@ -194,7 +194,7 @@ void StatsJsonRender::flushStats(Buffer::Instance& response) { // We are going to wind up with multiple flushes which have to serialize as // a single array, rather than a concatenation of multiple arrays, so we add - // those in the constructor and render() method, strip off the "[" and "]" + // those in the constructor and finalize() method, strip off the "[" and "]" // from each buffered serialization. ASSERT(json_array.size() >= 2); ASSERT(json_array[0] == '['); diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index ab986971078ef..f1873da83519a 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -31,7 +31,7 @@ class StatsRender { const Stats::ParentHistogram& histogram) PURE; // Completes rendering any buffered data. - virtual void render(Buffer::Instance& response) PURE; + virtual void finalize(Buffer::Instance& response) PURE; // Determines whether the current chunk is full. bool isChunkFull(Buffer::Instance& response) { return response.length() > ChunkSize; } @@ -52,7 +52,7 @@ class StatsTextRender : public StatsRender { const std::string& value) override; void generate(Buffer::Instance& response, const std::string& name, const Stats::ParentHistogram& histogram) override; - void render(Buffer::Instance&) override; + void finalize(Buffer::Instance&) override; private: // Computes disjoint buckets as text and adds them to the response buffer. @@ -74,7 +74,7 @@ class StatsJsonRender : public StatsRender { const std::string& value) override; void generate(Buffer::Instance&, const std::string& name, const Stats::ParentHistogram& histogram) override; - void render(Buffer::Instance& response) override; + void finalize(Buffer::Instance& response) override; private: // Collects a scalar metric (text-readout, counter, or gauge) into an array of diff --git a/source/server/admin/stats_request.cc b/source/server/admin/stats_request.cc index 206ed766c6994..9906578441221 100644 --- a/source/server/admin/stats_request.cc +++ b/source/server/admin/stats_request.cc @@ -48,7 +48,7 @@ bool StatsRequest::nextChunk(Buffer::Instance& response) { startPhase(); break; case Phase::Histograms: - render_->render(response); + render_->finalize(response); return false; } } @@ -93,7 +93,7 @@ void StatsRequest::startPhase() { } // Populate stat_map with all the counters found in all the scopes with an - // empty prefix. + // empty prefix. This is needed to seed the iteration for the current phase. auto iter = stat_map_.find(""); if (iter != stat_map_.end()) { StatOrScopes variant = std::move(iter->second); diff --git a/source/server/admin/stats_request.h b/source/server/admin/stats_request.h index 9bb4df80d4533..5e098e5214ed6 100644 --- a/source/server/admin/stats_request.h +++ b/source/server/admin/stats_request.h @@ -16,6 +16,14 @@ class StatsRequest : public Admin::Request { using ScopeVec = std::vector; using StatOrScopes = absl::variant; + + // In order to keep the output consistent with the fully buffered behavior + // prior to the chunked implementation that buffered each type, we iterate + // over all scopes for each type. This enables the complex chunking + // implementation to pass the tests that capture the buffered behavior. There + // is not a significant cost to this, but in a future PR we may choose to + // co-mingle the types. Note that histograms are groups together in the data + // JSON data model, so we won't be able to fully co-mingle. enum class Phase { TextReadouts, CountersAndGauges, diff --git a/test/server/admin/stats_render_test.cc b/test/server/admin/stats_render_test.cc index 7b4f7ffd6f9b0..5e77a510d5cc1 100644 --- a/test/server/admin/stats_render_test.cc +++ b/test/server/admin/stats_render_test.cc @@ -32,7 +32,7 @@ class StatsRenderTest : public testing::Test { template std::string render(StatsRender& render, absl::string_view name, const T& value) { render.generate(response_, std::string(name), value); - render.render(response_); + render.finalize(response_); return response_.toString(); } From 91cd01258d049af94ee6a343f3cc2932c85f04ed Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 10 Mar 2022 16:46:25 -0500 Subject: [PATCH 60/74] add comment suggeseting Prometheus strategy. Signed-off-by: Joshua Marantz --- source/server/admin/stats_handler.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 2b659dac1b051..4e2600cbbcd0c 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -99,6 +99,13 @@ Admin::RequestPtr StatsHandler::makeRequest(absl::string_view path, AdminStream& bool json = false; if (format_value.has_value()) { if (format_value.value() == "prometheus") { + // TODO(#16139): modify streaming algorithm to cover Prometheus. + // + // This may be easiest to accomplish by populating the set + // with tagExtractedName(), and allowing for vectors of + // stats as multiples will have the same tag-extracted names. + // Ideally we'd find a way to do this without slowing down + // the non-Prometheus implementations. Buffer::OwnedImpl response; Http::Code code = prometheusStats(path, response); return Admin::makeStaticTextRequest(response, code); From cb935f3f30422cfaf95a789cf17ca8b83045abe8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 10 Mar 2022 17:52:54 -0500 Subject: [PATCH 61/74] add a test for StatsRequest that covers chunking behavior. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.h | 7 -- source/server/admin/stats_request.cc | 2 +- source/server/admin/stats_request.h | 4 + test/server/admin/BUILD | 13 +++ test/server/admin/stats_request_test.cc | 107 ++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 test/server/admin/stats_request_test.cc diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index f1873da83519a..968347155006a 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -3,10 +3,6 @@ #include "source/common/buffer/buffer_impl.h" #include "source/server/admin/utils.h" -namespace { -constexpr uint64_t ChunkSize = 2 * 1000 * 1000; -} // namespace - namespace Envoy { namespace Server { @@ -33,9 +29,6 @@ class StatsRender { // Completes rendering any buffered data. virtual void finalize(Buffer::Instance& response) PURE; - // Determines whether the current chunk is full. - bool isChunkFull(Buffer::Instance& response) { return response.length() > ChunkSize; } - protected: using UInt64Vec = std::vector; }; diff --git a/source/server/admin/stats_request.cc b/source/server/admin/stats_request.cc index 9906578441221..99585d954f5b5 100644 --- a/source/server/admin/stats_request.cc +++ b/source/server/admin/stats_request.cc @@ -36,7 +36,7 @@ bool StatsRequest::nextChunk(Buffer::Instance& response) { response.move(response_); ASSERT(response_.length() == 0); } - while (!render_->isChunkFull(response)) { + while (response.length() < chunk_size_) { while (stat_map_.empty()) { switch (phase_) { case Phase::TextReadouts: diff --git a/source/server/admin/stats_request.h b/source/server/admin/stats_request.h index 5e098e5214ed6..419b384dde4bf 100644 --- a/source/server/admin/stats_request.h +++ b/source/server/admin/stats_request.h @@ -90,6 +90,9 @@ class StatsRequest : public Admin::Request { template void renderStat(const std::string& name, Buffer::Instance& response, StatOrScopes& variant); + // Sets the chunk size. + void setChunkSize(uint64_t chunk_size) { chunk_size_ = chunk_size; } + private: const bool used_only_; const bool json_; @@ -104,6 +107,7 @@ class StatsRequest : public Admin::Request { absl::btree_map stat_map_; Phase phase_{Phase::TextReadouts}; Buffer::OwnedImpl response_; + uint64_t chunk_size_{2 * 1000 * 1000}; }; } // namespace Server diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 99d3b1148862c..f4308179a6e5f 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -83,6 +83,19 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "stats_request_test", + srcs = ["stats_request_test.cc"], + deps = [ + "//source/common/stats:thread_local_store_lib", + "//source/server/admin:stats_request_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/stats:stats_mocks", + "//test/mocks/thread_local:thread_local_mocks", + "//test/test_common:utility_lib", + ], +) + envoy_cc_benchmark_binary( name = "stats_handler_speed_test", srcs = ["stats_handler_speed_test.cc"], diff --git a/test/server/admin/stats_request_test.cc b/test/server/admin/stats_request_test.cc new file mode 100644 index 0000000000000..e63b4c5110dcf --- /dev/null +++ b/test/server/admin/stats_request_test.cc @@ -0,0 +1,107 @@ +#include + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/stats/thread_local_store.h" +#include "source/server/admin/stats_request.h" + +#include "test/mocks/event/mocks.h" +#include "test/mocks/stats/mocks.h" +#include "test/mocks/thread_local/mocks.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +using testing::NiceMock; +using testing::StartsWith; + +namespace Envoy { +namespace Server { + +class StatsRequestTest : public testing::Test { +protected: + StatsRequestTest() : pool_(symbol_table_), alloc_(symbol_table_), store_(alloc_) { + store_.addSink(sink_); + store_.initializeThreading(main_thread_dispatcher_, tls_); + } + + ~StatsRequestTest() override { + tls_.shutdownGlobalThreading(); + store_.shutdownThreading(); + tls_.shutdownThread(); + } + + std::unique_ptr makeRequest(bool used_only, bool json) { + return std::make_unique(store_, used_only, json, + Utility::HistogramBucketsMode::NoBuckets, absl::nullopt); + } + + // Executes a request, counting the chunks that were generated. + uint32_t iterateChunks(StatsRequest& request) { + Http::TestResponseHeaderMapImpl response_headers; + Http::Code code = request.start(response_headers); + EXPECT_EQ(Http::Code::OK, code); + Buffer::OwnedImpl data; + uint32_t num_chunks = 0; + bool more = true; + do { + more = request.nextChunk(data); + uint64_t size = data.length(); + if (size > 0) { + ++num_chunks; + data.drain(size); + } + } while (more); + return num_chunks; + } + + // Executes a request, returning the rendered buffer as a string. + std::string response(StatsRequest& request) { + Http::TestResponseHeaderMapImpl response_headers; + Http::Code code = request.start(response_headers); + EXPECT_EQ(Http::Code::OK, code); + Buffer::OwnedImpl data; + while (request.nextChunk(data)) { + } + return data.toString(); + } + + Stats::StatName makeStatName(absl::string_view name) { return pool_.add(name); } + + Stats::SymbolTableImpl symbol_table_; + Stats::StatNamePool pool_; + Stats::AllocatorImpl alloc_; + NiceMock sink_; + NiceMock main_thread_dispatcher_; + NiceMock tls_; + Stats::ThreadLocalStoreImpl store_; + Buffer::OwnedImpl response_; +}; + +TEST_F(StatsRequestTest, Empty) { EXPECT_EQ(0, iterateChunks(*makeRequest(false, false))); } + +TEST_F(StatsRequestTest, OneStat) { + store_.counterFromStatName(makeStatName("foo")); + EXPECT_EQ(1, iterateChunks(*makeRequest(false, false))); +} + +TEST_F(StatsRequestTest, ManyStatsSmallChunkSize) { + for (uint32_t i = 0; i < 100; ++i) { + store_.counterFromStatName(makeStatName(absl::StrCat("foo", i))); + } + std::unique_ptr request = makeRequest(false, false); + request->setChunkSize(100); + EXPECT_EQ(9, iterateChunks(*request)); +} + +TEST_F(StatsRequestTest, OneStatUsedOnly) { + store_.counterFromStatName(makeStatName("foo")); + EXPECT_EQ(0, iterateChunks(*makeRequest(true, false))); +} + +TEST_F(StatsRequestTest, OneStatJson) { + store_.counterFromStatName(makeStatName("foo")); + EXPECT_THAT(response(*makeRequest(false, true)), StartsWith("{")); +} + +} // namespace Server +} // namespace Envoy From 2f4c4b9185f51345f2895a0bc512c724adbf5ff3 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 11 Mar 2022 10:12:49 -0500 Subject: [PATCH 62/74] Remove superfluous special-case for scopes with empty prefix, and avoid using absl::btree_map::iterator after a map mutation. Signed-off-by: Joshua Marantz --- source/server/admin/stats_request.cc | 48 +++++++++++++++------------- source/server/admin/stats_request.h | 3 ++ 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/source/server/admin/stats_request.cc b/source/server/admin/stats_request.cc index 99585d954f5b5..07353df44f3ad 100644 --- a/source/server/admin/stats_request.cc +++ b/source/server/admin/stats_request.cc @@ -54,36 +54,48 @@ bool StatsRequest::nextChunk(Buffer::Instance& response) { } auto iter = stat_map_.begin(); - const std::string& name = iter->first; - StatOrScopes& variant = iter->second; - switch (variant.index()) { - case 0: + StatOrScopes variant = std::move(iter->second); + StatOrScopesIndex index = static_cast(variant.index()); + switch (index) { + case StatOrScopesIndex::Scopes: + // Erase the current element before adding new ones, as absl::btree_map + // does not have stable iterators. When we hit leaf stats we will erase + // second, so that we can use the name held as a map key, and don't need + // to re-serialize the name from the symbol table. + stat_map_.erase(iter); populateStatsForCurrentPhase(absl::get(variant)); break; - case 1: - renderStat(name, response, variant); + case StatOrScopesIndex::TextReadout: + renderStat(iter->first, response, variant); + stat_map_.erase(iter); break; - case 2: - renderStat(name, response, variant); + case StatOrScopesIndex::Counter: + renderStat(iter->first, response, variant); + stat_map_.erase(iter); break; - case 3: - renderStat(name, response, variant); + case StatOrScopesIndex::Gauge: + renderStat(iter->first, response, variant); + stat_map_.erase(iter); break; - case 4: { + case StatOrScopesIndex::Histogram: { auto histogram = absl::get(variant); auto parent_histogram = dynamic_cast(histogram.get()); if (parent_histogram != nullptr) { - render_->generate(response, name, *parent_histogram); + render_->generate(response, iter->first, *parent_histogram); } + stat_map_.erase(iter); } } - stat_map_.erase(iter); } return true; } void StatsRequest::startPhase() { ASSERT(stat_map_.empty()); + + // Insert all the scopes in the alphabetically ordered map. As we iterate + // through the map we'll erase the scopes and replace them with the stats held + // in the scopes. for (const Stats::ConstScopeSharedPtr& scope : scopes_) { StatOrScopes& variant = stat_map_[stats_.symbolTable().toString(scope->prefix())]; if (variant.index() == absl::variant_npos) { @@ -91,16 +103,6 @@ void StatsRequest::startPhase() { } absl::get(variant).emplace_back(scope); } - - // Populate stat_map with all the counters found in all the scopes with an - // empty prefix. This is needed to seed the iteration for the current phase. - auto iter = stat_map_.find(""); - if (iter != stat_map_.end()) { - StatOrScopes variant = std::move(iter->second); - stat_map_.erase(iter); - auto& scope_vec = absl::get(variant); - populateStatsForCurrentPhase(scope_vec); - } } void StatsRequest::populateStatsForCurrentPhase(const ScopeVec& scope_vec) { diff --git a/source/server/admin/stats_request.h b/source/server/admin/stats_request.h index 419b384dde4bf..7ee4d7e00fed7 100644 --- a/source/server/admin/stats_request.h +++ b/source/server/admin/stats_request.h @@ -17,6 +17,9 @@ class StatsRequest : public Admin::Request { using StatOrScopes = absl::variant; + // Ordered to match the StatsOrScopes variant. + enum class StatOrScopesIndex { Scopes, TextReadout, Counter, Gauge, Histogram }; + // In order to keep the output consistent with the fully buffered behavior // prior to the chunked implementation that buffered each type, we iterate // over all scopes for each type. This enables the complex chunking From 126bddc8897d74d688373acd2b9a14ae99febb37 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 16 Mar 2022 19:49:44 -0400 Subject: [PATCH 63/74] Address Pradeep's comments. Signed-off-by: Joshua Marantz --- envoy/server/admin.h | 4 ++-- source/server/admin/admin.cc | 10 ++++++---- source/server/admin/stats_render.cc | 6 ++++-- test/server/admin/stats_render_test.cc | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/envoy/server/admin.h b/envoy/server/admin.h index becda65d47e24..640ddf0320373 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -226,12 +226,12 @@ class Admin { virtual uint32_t concurrency() const PURE; /** - * Makes a chunked handler for static text. The version that takes the + * Makes a chunked request for static text. The version that takes the * Buffer::Instance& transfers the content from the passed-in buffer. * * @param response_text the text to populate response with * @param code the Http::Code for the response - * @return the handler + * @return the request */ static RequestPtr makeStaticTextRequest(absl::string_view response_text, Http::Code code); static RequestPtr makeStaticTextRequest(Buffer::Instance& response_text, Http::Code code); diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 565c56881d14c..b5e0afe2a9281 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -255,7 +255,7 @@ void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) namespace { -// Implements a chunked handler for static text. +// Implements a chunked request for static text. class StaticTextRequest : public Admin::Request { public: StaticTextRequest(absl::string_view response_text, Http::Code code) : code_(code) { @@ -321,11 +321,11 @@ Admin::RequestPtr Admin::makeStaticTextRequest(Buffer::Instance& response, Http: Http::Code AdminImpl::runCallback(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream& admin_stream) { - RequestPtr handler = makeRequest(path_and_query, admin_stream); - Http::Code code = handler->start(response_headers); + RequestPtr request = makeRequest(path_and_query, admin_stream); + Http::Code code = request->start(response_headers); bool more_data; do { - more_data = handler->nextChunk(response); + more_data = request->nextChunk(response); } while (more_data); Memory::Utils::tryShrinkHeap(); return code; @@ -338,6 +338,8 @@ Admin::RequestPtr AdminImpl::makeRequest(absl::string_view path_and_query, query_index = path_and_query.size(); } + // TODO(jmarantz): consider using an absl::btree_map or std::map rather than + // linearly searching for the first prefix match. for (const UrlHandler& handler : handlers_) { if (path_and_query.compare(0, query_index, handler.prefix_) == 0) { if (handler.mutates_server_state_) { diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 21add2106a8e1..a89ac00563166 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -4,8 +4,10 @@ #include "source/common/stats/histogram_impl.h" namespace { -constexpr uint64_t JsonStatsFlushCount = 64; // This value found by iterating in benchmark. -} +// This value found by iterating in stats_handler_speed_test.cc, maximizing the +// performance of the Json tests, and then rounding to a power of 2. +constexpr uint64_t JsonStatsFlushCount = 64; +} // namespace namespace Envoy { using ProtoMap = Protobuf::Map; diff --git a/test/server/admin/stats_render_test.cc b/test/server/admin/stats_render_test.cc index 5e77a510d5cc1..714d506ef7384 100644 --- a/test/server/admin/stats_render_test.cc +++ b/test/server/admin/stats_render_test.cc @@ -70,9 +70,9 @@ TEST_F(StatsRenderTest, TextString) { TEST_F(StatsRenderTest, TextHistogramNoBuckets) { StatsTextRender renderer(Utility::HistogramBucketsMode::NoBuckets); constexpr absl::string_view expected = - "h1: P0(200.0,200.0) P25(207.5,207.5) P50(302.5,302.5) P75(306.25,306.25) " + "h1: P0(200,200) P25(207.5,207.5) P50(302.5,302.5) P75(306.25,306.25) " "P90(308.5,308.5) P95(309.25,309.25) P99(309.85,309.85) P99.5(309.925,309.925) " - "P99.9(309.985,309.985) P100(310.0,310.0)\n"; + "P99.9(309.985,309.985) P100(310,310)\n"; EXPECT_EQ(expected, render<>(renderer, "h1", populateHistogram("h1", {200, 300, 300}))); } From a099aa59d96572cce40af518b561aca3e4b2938a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 09:52:16 -0400 Subject: [PATCH 64/74] review comments round 1 Signed-off-by: Joshua Marantz --- source/server/admin/admin.cc | 2 +- source/server/admin/admin.h | 4 ++-- source/server/admin/stats_render.cc | 21 ++++++++++++++------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index b5e0afe2a9281..a7f6f486d9a1c 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -199,7 +199,7 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, MAKE_ADMIN_HANDLER(server_info_handler_.handlerServerInfo), false, false), makeHandler("/ready", "print server state, return 200 if LIVE, otherwise return 503", MAKE_ADMIN_HANDLER(server_info_handler_.handlerReady), false, false), - makeChunkedHandler("/stats", "print server stats", stats_handler_, false, false), + makeStreamingHandler("/stats", "print server stats", stats_handler_, false, false), makeHandler("/stats/prometheus", "print server stats in prometheus format", MAKE_ADMIN_HANDLER(stats_handler_.handlerPrometheusStats), false, false), makeHandler("/stats/recentlookups", "Show recent stat-name lookups", diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 3618e6f156d78..151d10ba6eee9 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -250,8 +250,8 @@ class AdminImpl : public Admin, * @return the UrlHandler. */ template - UrlHandler makeChunkedHandler(const std::string& prefix, const std::string& help_text, - Handler& handler, bool removable, bool mutates_state) { + UrlHandler makeStreamingHandler(const std::string& prefix, const std::string& help_text, + Handler& handler, bool removable, bool mutates_state) { return {prefix, help_text, [&handler](absl::string_view path, AdminStream& admin_stream) -> Admin::RequestPtr { return handler.makeRequest(path, admin_stream); diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index a89ac00563166..086b7984b613b 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -60,8 +60,8 @@ void StatsTextRender::addDisjointBuckets(const std::string& name, // Make sure all vectors are the same size. ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); - size_t min_size = std::min({disjoint_interval_buckets.size(), disjoint_cumulative_buckets.size(), - supported_buckets.size()}); + const size_t min_size = std::min({disjoint_interval_buckets.size(), + disjoint_cumulative_buckets.size(), supported_buckets.size()}); std::vector bucket_strings; bucket_strings.reserve(min_size); for (size_t i = 0; i < min_size; ++i) { @@ -154,7 +154,13 @@ void StatsJsonRender::finalize(Buffer::Instance& response) { } auto str = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); - addStatAsRenderedJson(response, str); + + // Protobuf json serialization can yield an empty string (printing an + // untrappable error message to stdout) if it receives an invalid input, so + // we exclude that here. + if (!str.empty()) { + addStatAsRenderedJson(response, str); + } } response.add("]}"); } @@ -182,7 +188,7 @@ void StatsJsonRender::addJson(Buffer::Instance& response, ProtobufWkt::Value jso // threshold too high we buffer too much memory, likely impacting processor // cache. The optimum threshold found after a few experiments on a local // host appears to be between 50 and 100. - if (stats_array_.size() == JsonStatsFlushCount) { + if (stats_array_.size() >= JsonStatsFlushCount) { flushStats(response); } } @@ -198,7 +204,7 @@ void StatsJsonRender::flushStats(Buffer::Instance& response) { // a single array, rather than a concatenation of multiple arrays, so we add // those in the constructor and finalize() method, strip off the "[" and "]" // from each buffered serialization. - ASSERT(json_array.size() >= 2); + ASSERT(json_array.size() >= 3); ASSERT(json_array[0] == '['); ASSERT(json_array[json_array.size() - 1] == ']'); addStatAsRenderedJson(response, absl::string_view(json_array).substr(1, json_array.size() - 2)); @@ -207,6 +213,7 @@ void StatsJsonRender::flushStats(Buffer::Instance& response) { // Adds a json fragment of scalar stats to the response buffer, including a // "," delimiter if this is not the first fragment. void StatsJsonRender::addStatAsRenderedJson(Buffer::Instance& response, absl::string_view json) { + ASSERT(!json.empty()); if (first_) { response.add(json); first_ = false; @@ -245,8 +252,8 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, const std::vector& computed_quantiles = interval_statistics.computedQuantiles(); const std::vector& cumulative_quantiles = histogram.cumulativeStatistics().computedQuantiles(); - size_t min_size = std::min({computed_quantiles.size(), cumulative_quantiles.size(), - interval_statistics.supportedQuantiles().size()}); + const size_t min_size = std::min({computed_quantiles.size(), cumulative_quantiles.size(), + interval_statistics.supportedQuantiles().size()}); ASSERT(min_size == computed_quantiles.size()); ASSERT(min_size == cumulative_quantiles.size()); From d8529f391dd0fc0ce9d9f9ca71cc3e69a7cb9d77 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 10:05:27 -0400 Subject: [PATCH 65/74] convert one list value. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 086b7984b613b..b7c579aaaf644 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -233,13 +233,14 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, // It is not possible for the supported quantiles to differ across histograms, so it is ok // to send them once. Stats::HistogramStatisticsImpl empty_statistics; - std::vector supported_quantile_array; + auto supported_quantile_array = std::make_unique(); for (double quantile : empty_statistics.supportedQuantiles()) { - supported_quantile_array.push_back(ValueUtil::numberValue(quantile * 100)); + *supported_quantile_array->add_values() = ValueUtil::numberValue(quantile * 100); } ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); - histograms_obj_fields["supported_quantiles"] = ValueUtil::listValue(supported_quantile_array); + histograms_obj_fields["supported_quantiles"].set_allocated_list_value( + supported_quantile_array.release()); found_used_histogram_ = true; } From 94be4b75ab62fa4e346cf12c8f04a8258777c074 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 10:35:47 -0400 Subject: [PATCH 66/74] simplify direct population of ListValue. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 12 ++++++++---- test/server/admin/stats_handler_test.cc | 8 ++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index b7c579aaaf644..6aa5451478c96 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -233,14 +233,18 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, // It is not possible for the supported quantiles to differ across histograms, so it is ok // to send them once. Stats::HistogramStatisticsImpl empty_statistics; - auto supported_quantile_array = std::make_unique(); + //auto supported_quantile_array = std::make_unique(); + ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); + ProtobufWkt::ListValue* supported_quantile_array = + histograms_obj_fields["supported_quantiles"].mutable_list_value(); + for (double quantile : empty_statistics.supportedQuantiles()) { *supported_quantile_array->add_values() = ValueUtil::numberValue(quantile * 100); } - ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); - histograms_obj_fields["supported_quantiles"].set_allocated_list_value( - supported_quantile_array.release()); + //ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); + //histograms_obj_fields["supported_quantiles"].set_allocated_list_value( + // supported_quantile_array.release()); found_used_histogram_ = true; } diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 839cd64e9790a..21a192c34540a 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -1022,7 +1022,7 @@ TEST_P(AdminStatsTest, SortedCountersAndGauges) { store_->counterFromString("s3"); store_->counterFromString("s1"); store_->gaugeFromString("s2", Stats::Gauge::ImportMode::Accumulate); - for (const std::string& url : {"/stats", "/stats?format=json"}) { + for (absl::string_view url : {"/stats", "/stats?format=json"}) { const CodeResponse code_response = handlerStats(url); ASSERT_EQ(Http::Code::OK, code_response.first); checkOrder(code_response.second, {"s1", "s2", "s3", "s4"}); @@ -1039,7 +1039,7 @@ TEST_P(AdminStatsTest, SortedScopes) { scope->counterFromString("t"); Stats::ScopeSharedPtr subscope = scope->createScope("subscope"); subscope->counterFromString("x"); - for (const std::string& url : {"/stats", "/stats?format=json"}) { + for (absl::string_view url : {"/stats", "/stats?format=json"}) { CodeResponse code_response = handlerStats(url); ASSERT_EQ(Http::Code::OK, code_response.first); checkOrder(code_response.second, @@ -1053,7 +1053,7 @@ TEST_P(AdminStatsTest, SortedTextReadouts) { store_->textReadoutFromString("t3"); store_->textReadoutFromString("t1"); store_->textReadoutFromString("t2"); - for (const std::string& url : {"/stats", "/stats?format=json"}) { + for (absl::string_view url : {"/stats", "/stats?format=json"}) { const CodeResponse code_response = handlerStats(url); ASSERT_EQ(Http::Code::OK, code_response.first); checkOrder(code_response.second, {"t1", "t2", "t3", "t4"}); @@ -1065,7 +1065,7 @@ TEST_P(AdminStatsTest, SortedHistograms) { store_->histogramFromString("h3", Stats::Histogram::Unit::Unspecified); store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified); store_->histogramFromString("h2", Stats::Histogram::Unit::Unspecified); - for (const std::string& url : {"/stats", "/stats?format=json"}) { + for (absl::string_view url : {"/stats", "/stats?format=json"}) { const CodeResponse code_response = handlerStats(url); ASSERT_EQ(Http::Code::OK, code_response.first); checkOrder(code_response.second, {"h1", "h2", "h3", "h4"}); From 81e37cc6b3d975dcb8b5c66c1db1d5b7a96570b5 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 11:09:33 -0400 Subject: [PATCH 67/74] more listValue conversions Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 33 ++++++++++++++++++++--------- source/server/admin/stats_render.h | 3 ++- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 6aa5451478c96..9e43ddcebdee2 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -86,6 +86,14 @@ StatsJsonRender::StatsJsonRender(Http::ResponseHeaderMap& response_headers, // makes streaming difficult. Instead we emit the preamble in the // constructor here, and create json models for each stats entry. response.add("{\"stats\":["); + histogram_array_ = std::make_unique(); + /* + switch (histogram_buckets_mode) { + case Utility::HistogramBucketsMode::NoBuckets: + case Utility::HistogramBucketsMode::Cumulative: + case Utility::HistogramBucketsMode::Disjoint: + } + */ } // Buffers a JSON fragment for a numeric stats, flushing to the response @@ -141,16 +149,18 @@ void StatsJsonRender::finalize(Buffer::Instance& response) { if (!stats_array_.empty()) { flushStats(response); } - if (!histogram_array_.empty()) { + if (histogram_array_->values_size() > 0) { ProtoMap& histograms_obj_container_fields = *histograms_obj_container_.mutable_fields(); if (found_used_histogram_) { ASSERT(histogram_buckets_mode_ == Utility::HistogramBucketsMode::NoBuckets); ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); - histograms_obj_fields["computed_quantiles"] = ValueUtil::listValue(histogram_array_); + histograms_obj_fields["computed_quantiles"].set_allocated_list_value( + histogram_array_.release()); histograms_obj_container_fields["histograms"] = ValueUtil::structValue(histograms_obj_); } else { ASSERT(histogram_buckets_mode_ != Utility::HistogramBucketsMode::NoBuckets); - histograms_obj_container_fields["histograms"] = ValueUtil::listValue(histogram_array_); + histograms_obj_container_fields["histograms"].set_allocated_list_value( + histogram_array_.release()); } auto str = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::structValue(histograms_obj_container_), false /* pretty */, true); @@ -275,11 +285,12 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); } computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); - histogram_array_.push_back(ValueUtil::structValue(computed_quantile)); + //*supported_quantile_array->add_values() = ValueUtil::numberValue(quantile * 100); + *histogram_array_->add_values() = ValueUtil::structValue(computed_quantile); } -// Collects the buckets from the specified histogram, using either the cumulative -// or disjoint views, as controlled by buckets_fn. +// Collects the buckets from the specified histogram, using either the +// cumulative or disjoint views, as controlled by buckets_fn. void StatsJsonRender::collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, const UInt64Vec& interval_buckets, @@ -297,7 +308,9 @@ void StatsJsonRender::collectBuckets(const std::string& name, ProtoMap& histogram_obj_fields = *histogram_obj.mutable_fields(); histogram_obj_fields["name"] = ValueUtil::stringValue(name); - std::vector bucket_array; + ProtobufWkt::ListValue* bucket_array = histogram_obj_fields["buckets"].mutable_list_value(); + + //std::vector bucket_array; for (size_t i = 0; i < min_size; ++i) { ProtobufWkt::Struct bucket; ProtoMap& bucket_fields = *bucket.mutable_fields(); @@ -306,10 +319,10 @@ void StatsJsonRender::collectBuckets(const std::string& name, // ValueUtil::numberValue does unnecessary conversions from uint64_t values to doubles. bucket_fields["interval"] = ValueUtil::numberValue(interval_buckets[i]); bucket_fields["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); - bucket_array.push_back(ValueUtil::structValue(bucket)); + *bucket_array->add_values() = ValueUtil::structValue(bucket); } - histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); - histogram_array_.push_back(ValueUtil::structValue(histogram_obj)); + //histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); + *histogram_array_->add_values() = ValueUtil::structValue(histogram_obj); } } // namespace Server diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index 968347155006a..df5027032e2c3 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -101,7 +101,8 @@ class StatsJsonRender : public StatsRender { std::vector stats_array_; ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; - std::vector histogram_array_; + std::unique_ptr histogram_array_; + //std::vector histogram_array_; bool found_used_histogram_{false}; bool first_{true}; const Utility::HistogramBucketsMode histogram_buckets_mode_; From 0e0bf3d295409a466707feac9ca53c3b01bde9b0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 11:22:43 -0400 Subject: [PATCH 68/74] commented-out changes which convert stats_array_ to a ListValue. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 14 +++++++++++--- source/server/admin/stats_render.h | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 9e43ddcebdee2..689827ccb5823 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -6,6 +6,7 @@ namespace { // This value found by iterating in stats_handler_speed_test.cc, maximizing the // performance of the Json tests, and then rounding to a power of 2. +//constexpr int64_t JsonStatsFlushCount = 64; // protobuf size() functions return signed results. constexpr uint64_t JsonStatsFlushCount = 64; } // namespace namespace Envoy { @@ -146,6 +147,7 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, // Since histograms are buffered (see above), the finalize() method generates // all of them. void StatsJsonRender::finalize(Buffer::Instance& response) { + //if (stats_array_.values_size() > 0) { if (!stats_array_.empty()) { flushStats(response); } @@ -191,6 +193,7 @@ void StatsJsonRender::addScalar(Buffer::Instance& response, const std::string& n // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. void StatsJsonRender::addJson(Buffer::Instance& response, ProtobufWkt::Value json) { + //*stats_array_.add_values() = json; stats_array_.push_back(json); // We build up stats_array to a certain size so we can amortize the overhead @@ -199,15 +202,19 @@ void StatsJsonRender::addJson(Buffer::Instance& response, ProtobufWkt::Value jso // cache. The optimum threshold found after a few experiments on a local // host appears to be between 50 and 100. if (stats_array_.size() >= JsonStatsFlushCount) { + //if (stats_array_.values_size() >= JsonStatsFlushCount) { flushStats(response); } } // Flushes all stats that were buffered in addJson() above. void StatsJsonRender::flushStats(Buffer::Instance& response) { + //ASSERT(stats_array_.values_size() > 0); ASSERT(!stats_array_.empty()); const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::listValue(stats_array_), false /* pretty */, true); + //stats_array_, false /* pretty */, true); + //stats_array_.clear_values(); stats_array_.clear(); // We are going to wind up with multiple flushes which have to serialize as @@ -262,7 +269,8 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, ProtoMap& computed_quantile_fields = *computed_quantile.mutable_fields(); computed_quantile_fields["name"] = ValueUtil::stringValue(name); - std::vector computed_quantile_value_array; + ProtobufWkt::ListValue* computed_quantile_value_array = + computed_quantile_fields["values"].mutable_list_value(); const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); const std::vector& computed_quantiles = interval_statistics.computedQuantiles(); const std::vector& cumulative_quantiles = @@ -282,9 +290,9 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, computed_quantile_value_fields["cumulative"] = std::isnan(cumulative) ? ValueUtil::nullValue() : ValueUtil::numberValue(cumulative); - computed_quantile_value_array.push_back(ValueUtil::structValue(computed_quantile_value)); + *computed_quantile_value_array->add_values() = ValueUtil::structValue(computed_quantile_value); } - computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); + //computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); //*supported_quantile_array->add_values() = ValueUtil::numberValue(quantile * 100); *histogram_array_->add_values() = ValueUtil::structValue(computed_quantile); } diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index df5027032e2c3..0f67a79e335da 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -98,6 +98,7 @@ class StatsJsonRender : public StatsRender { void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, const UInt64Vec& interval_buckets, const UInt64Vec& cumulative_buckets); + //ProtobufWkt::ListValue stats_array_; std::vector stats_array_; ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; From 7dc9a55d114e2e02f2daf4ae16b10c6616829169 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 11:26:46 -0400 Subject: [PATCH 69/74] change commenting-out to ifdefs. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 30 ++++++++++++++++++++++------- source/server/admin/stats_render.h | 6 +++++- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 689827ccb5823..b11c6e3c15d6f 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -6,8 +6,11 @@ namespace { // This value found by iterating in stats_handler_speed_test.cc, maximizing the // performance of the Json tests, and then rounding to a power of 2. -//constexpr int64_t JsonStatsFlushCount = 64; // protobuf size() functions return signed results. +#if STATS_ARRAY_LIST_VALUE +constexpr int64_t JsonStatsFlushCount = 64; // protobuf size() functions return signed results. +#else constexpr uint64_t JsonStatsFlushCount = 64; +#endif } // namespace namespace Envoy { @@ -147,8 +150,11 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, // Since histograms are buffered (see above), the finalize() method generates // all of them. void StatsJsonRender::finalize(Buffer::Instance& response) { - //if (stats_array_.values_size() > 0) { +#if STATS_ARRAY_LIST_VALUE + if (stats_array_.values_size() > 0) { +#else if (!stats_array_.empty()) { +#endif flushStats(response); } if (histogram_array_->values_size() > 0) { @@ -193,29 +199,39 @@ void StatsJsonRender::addScalar(Buffer::Instance& response, const std::string& n // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. void StatsJsonRender::addJson(Buffer::Instance& response, ProtobufWkt::Value json) { - //*stats_array_.add_values() = json; +#if STATS_ARRAY_LIST_VALUE + *stats_array_.add_values() = json; +#else stats_array_.push_back(json); +#endif // We build up stats_array to a certain size so we can amortize the overhead // of entering into the JSON serialization infrastructure. If we set the // threshold too high we buffer too much memory, likely impacting processor // cache. The optimum threshold found after a few experiments on a local // host appears to be between 50 and 100. +#if STATS_ARRAY_LIST_VALUE + if (stats_array_.values_size() >= JsonStatsFlushCount) { +#else if (stats_array_.size() >= JsonStatsFlushCount) { - //if (stats_array_.values_size() >= JsonStatsFlushCount) { +#endif flushStats(response); } } // Flushes all stats that were buffered in addJson() above. void StatsJsonRender::flushStats(Buffer::Instance& response) { - //ASSERT(stats_array_.values_size() > 0); +#if STATS_ARRAY_LIST_VALUE + ASSERT(stats_array_.values_size() > 0); + const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( + stats_array_, false /* pretty */, true); + stats_array_.clear_values(); +#else ASSERT(!stats_array_.empty()); const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::listValue(stats_array_), false /* pretty */, true); - //stats_array_, false /* pretty */, true); - //stats_array_.clear_values(); stats_array_.clear(); +#endif // We are going to wind up with multiple flushes which have to serialize as // a single array, rather than a concatenation of multiple arrays, so we add diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index 0f67a79e335da..605cfc742045f 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -98,8 +98,12 @@ class StatsJsonRender : public StatsRender { void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, const UInt64Vec& interval_buckets, const UInt64Vec& cumulative_buckets); - //ProtobufWkt::ListValue stats_array_; +#define STATS_ARRAY_LIST_VALUE 0 +#if STATS_ARRAY_LIST_VALUE + ProtobufWkt::ListValue stats_array_; +#else std::vector stats_array_; +#endif ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; std::unique_ptr histogram_array_; From 72743e5a9f7631ed7135d03f60c3c3d05e3ee7a8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 11:45:06 -0400 Subject: [PATCH 70/74] pass a protobuf-value by const ref. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 2 +- source/server/admin/stats_render.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index b11c6e3c15d6f..8dc9e0a39ca22 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -198,7 +198,7 @@ void StatsJsonRender::addScalar(Buffer::Instance& response, const std::string& n } // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. -void StatsJsonRender::addJson(Buffer::Instance& response, ProtobufWkt::Value json) { +void StatsJsonRender::addJson(Buffer::Instance& response, const ProtobufWkt::Value& json) { #if STATS_ARRAY_LIST_VALUE *stats_array_.add_values() = json; #else diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index 605cfc742045f..3e33d2ccdc14a 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -78,7 +78,7 @@ class StatsJsonRender : public StatsRender { void addScalar(Buffer::Instance& response, const std::string& name, const Value& value); // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. - void addJson(Buffer::Instance& response, ProtobufWkt::Value json); + void addJson(Buffer::Instance& response, const ProtobufWkt::Value& json); // Flushes all stats that were buffered in addJson() above. void flushStats(Buffer::Instance& response); From 5e62221867274bd27e8829b147a33ab4f3d53c6e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 11:47:56 -0400 Subject: [PATCH 71/74] Remove experiment of converting stats_rray_ to a ProtobufWkt::ListValue, which failed tests. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 23 ----------------------- source/server/admin/stats_render.h | 5 ----- 2 files changed, 28 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 8dc9e0a39ca22..1d4d5e3ab7ec7 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -6,11 +6,7 @@ namespace { // This value found by iterating in stats_handler_speed_test.cc, maximizing the // performance of the Json tests, and then rounding to a power of 2. -#if STATS_ARRAY_LIST_VALUE -constexpr int64_t JsonStatsFlushCount = 64; // protobuf size() functions return signed results. -#else constexpr uint64_t JsonStatsFlushCount = 64; -#endif } // namespace namespace Envoy { @@ -150,11 +146,7 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, // Since histograms are buffered (see above), the finalize() method generates // all of them. void StatsJsonRender::finalize(Buffer::Instance& response) { -#if STATS_ARRAY_LIST_VALUE - if (stats_array_.values_size() > 0) { -#else if (!stats_array_.empty()) { -#endif flushStats(response); } if (histogram_array_->values_size() > 0) { @@ -199,39 +191,24 @@ void StatsJsonRender::addScalar(Buffer::Instance& response, const std::string& n // Adds a JSON stat to our buffer, flushing to response every JsonStatsFlushCount stats. void StatsJsonRender::addJson(Buffer::Instance& response, const ProtobufWkt::Value& json) { -#if STATS_ARRAY_LIST_VALUE - *stats_array_.add_values() = json; -#else stats_array_.push_back(json); -#endif // We build up stats_array to a certain size so we can amortize the overhead // of entering into the JSON serialization infrastructure. If we set the // threshold too high we buffer too much memory, likely impacting processor // cache. The optimum threshold found after a few experiments on a local // host appears to be between 50 and 100. -#if STATS_ARRAY_LIST_VALUE - if (stats_array_.values_size() >= JsonStatsFlushCount) { -#else if (stats_array_.size() >= JsonStatsFlushCount) { -#endif flushStats(response); } } // Flushes all stats that were buffered in addJson() above. void StatsJsonRender::flushStats(Buffer::Instance& response) { -#if STATS_ARRAY_LIST_VALUE - ASSERT(stats_array_.values_size() > 0); - const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( - stats_array_, false /* pretty */, true); - stats_array_.clear_values(); -#else ASSERT(!stats_array_.empty()); const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::listValue(stats_array_), false /* pretty */, true); stats_array_.clear(); -#endif // We are going to wind up with multiple flushes which have to serialize as // a single array, rather than a concatenation of multiple arrays, so we add diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index 3e33d2ccdc14a..a9f4a715072a1 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -98,12 +98,7 @@ class StatsJsonRender : public StatsRender { void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, const UInt64Vec& interval_buckets, const UInt64Vec& cumulative_buckets); -#define STATS_ARRAY_LIST_VALUE 0 -#if STATS_ARRAY_LIST_VALUE - ProtobufWkt::ListValue stats_array_; -#else std::vector stats_array_; -#endif ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; std::unique_ptr histogram_array_; From 9fc63d09e804bb2a9d4d3598aabc80072207e0f1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Mar 2022 12:04:11 -0400 Subject: [PATCH 72/74] remove commented-out code and format. Signed-off-by: Joshua Marantz --- source/server/admin/stats_render.cc | 22 ++++------------------ source/server/admin/stats_render.h | 1 - 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 1d4d5e3ab7ec7..58341acc7405f 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -61,7 +61,7 @@ void StatsTextRender::addDisjointBuckets(const std::string& name, ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size()); const size_t min_size = std::min({disjoint_interval_buckets.size(), - disjoint_cumulative_buckets.size(), supported_buckets.size()}); + disjoint_cumulative_buckets.size(), supported_buckets.size()}); std::vector bucket_strings; bucket_strings.reserve(min_size); for (size_t i = 0; i < min_size; ++i) { @@ -87,13 +87,6 @@ StatsJsonRender::StatsJsonRender(Http::ResponseHeaderMap& response_headers, // constructor here, and create json models for each stats entry. response.add("{\"stats\":["); histogram_array_ = std::make_unique(); - /* - switch (histogram_buckets_mode) { - case Utility::HistogramBucketsMode::NoBuckets: - case Utility::HistogramBucketsMode::Cumulative: - case Utility::HistogramBucketsMode::Disjoint: - } - */ } // Buffers a JSON fragment for a numeric stats, flushing to the response @@ -206,6 +199,8 @@ void StatsJsonRender::addJson(Buffer::Instance& response, const ProtobufWkt::Val // Flushes all stats that were buffered in addJson() above. void StatsJsonRender::flushStats(Buffer::Instance& response) { ASSERT(!stats_array_.empty()); + // TODO(jmarantz): when #20428 lands we can serialize the scalar stat + // values without using the slow protobuf serializer. const std::string json_array = MessageUtil::getJsonStringFromMessageOrDie( ValueUtil::listValue(stats_array_), false /* pretty */, true); stats_array_.clear(); @@ -243,7 +238,6 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, // It is not possible for the supported quantiles to differ across histograms, so it is ok // to send them once. Stats::HistogramStatisticsImpl empty_statistics; - //auto supported_quantile_array = std::make_unique(); ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); ProtobufWkt::ListValue* supported_quantile_array = histograms_obj_fields["supported_quantiles"].mutable_list_value(); @@ -252,9 +246,6 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, *supported_quantile_array->add_values() = ValueUtil::numberValue(quantile * 100); } - //ProtoMap& histograms_obj_fields = *histograms_obj_.mutable_fields(); - //histograms_obj_fields["supported_quantiles"].set_allocated_list_value( - // supported_quantile_array.release()); found_used_histogram_ = true; } @@ -269,7 +260,7 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, const std::vector& cumulative_quantiles = histogram.cumulativeStatistics().computedQuantiles(); const size_t min_size = std::min({computed_quantiles.size(), cumulative_quantiles.size(), - interval_statistics.supportedQuantiles().size()}); + interval_statistics.supportedQuantiles().size()}); ASSERT(min_size == computed_quantiles.size()); ASSERT(min_size == cumulative_quantiles.size()); @@ -285,8 +276,6 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, *computed_quantile_value_array->add_values() = ValueUtil::structValue(computed_quantile_value); } - //computed_quantile_fields["values"] = ValueUtil::listValue(computed_quantile_value_array); - //*supported_quantile_array->add_values() = ValueUtil::numberValue(quantile * 100); *histogram_array_->add_values() = ValueUtil::structValue(computed_quantile); } @@ -308,10 +297,8 @@ void StatsJsonRender::collectBuckets(const std::string& name, ProtobufWkt::Struct histogram_obj; ProtoMap& histogram_obj_fields = *histogram_obj.mutable_fields(); histogram_obj_fields["name"] = ValueUtil::stringValue(name); - ProtobufWkt::ListValue* bucket_array = histogram_obj_fields["buckets"].mutable_list_value(); - //std::vector bucket_array; for (size_t i = 0; i < min_size; ++i) { ProtobufWkt::Struct bucket; ProtoMap& bucket_fields = *bucket.mutable_fields(); @@ -322,7 +309,6 @@ void StatsJsonRender::collectBuckets(const std::string& name, bucket_fields["cumulative"] = ValueUtil::numberValue(cumulative_buckets[i]); *bucket_array->add_values() = ValueUtil::structValue(bucket); } - //histogram_obj_fields["buckets"] = ValueUtil::listValue(bucket_array); *histogram_array_->add_values() = ValueUtil::structValue(histogram_obj); } diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index a9f4a715072a1..b9a7e2bf446b2 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -102,7 +102,6 @@ class StatsJsonRender : public StatsRender { ProtobufWkt::Struct histograms_obj_; ProtobufWkt::Struct histograms_obj_container_; std::unique_ptr histogram_array_; - //std::vector histogram_array_; bool found_used_histogram_{false}; bool first_{true}; const Utility::HistogramBucketsMode histogram_buckets_mode_; From 9731b0387f45320296f54d4748f86b5b8937c9c7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 31 Mar 2022 22:14:00 -0400 Subject: [PATCH 73/74] review comments Signed-off-by: Joshua Marantz --- .bazelversion | 2 +- envoy/server/admin.h | 2 +- source/server/admin/admin.cc | 2 -- source/server/admin/stats_render.cc | 19 +++++++++++-------- source/server/admin/stats_render.h | 6 ++---- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.bazelversion b/.bazelversion index 0062ac971805f..831446cbd27a6 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -5.0.0 +5.1.0 diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 640ddf0320373..928b1350f2ffb 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -226,7 +226,7 @@ class Admin { virtual uint32_t concurrency() const PURE; /** - * Makes a chunked request for static text. The version that takes the + * Makes a request for streamed static text. The version that takes the * Buffer::Instance& transfers the content from the passed-in buffer. * * @param response_text the text to populate response with diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index a7f6f486d9a1c..629b8fcda0171 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -338,8 +338,6 @@ Admin::RequestPtr AdminImpl::makeRequest(absl::string_view path_and_query, query_index = path_and_query.size(); } - // TODO(jmarantz): consider using an absl::btree_map or std::map rather than - // linearly searching for the first prefix match. for (const UrlHandler& handler : handlers_) { if (path_and_query.compare(0, query_index, handler.prefix_) == 0) { if (handler.mutates_server_state_) { diff --git a/source/server/admin/stats_render.cc b/source/server/admin/stats_render.cc index 58341acc7405f..d1656511dc18b 100644 --- a/source/server/admin/stats_render.cc +++ b/source/server/admin/stats_render.cc @@ -54,8 +54,9 @@ void StatsTextRender::addDisjointBuckets(const std::string& name, const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); - const UInt64Vec disjoint_interval_buckets = interval_statistics.computeDisjointBuckets(); - const UInt64Vec disjoint_cumulative_buckets = + const std::vector disjoint_interval_buckets = + interval_statistics.computeDisjointBuckets(); + const std::vector disjoint_cumulative_buckets = histogram.cumulativeStatistics().computeDisjointBuckets(); // Make sure all vectors are the same size. ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size()); @@ -121,15 +122,17 @@ void StatsJsonRender::generate(Buffer::Instance&, const std::string& name, break; case Utility::HistogramBucketsMode::Cumulative: { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - const UInt64Vec& interval_buckets = interval_statistics.computedBuckets(); - const UInt64Vec& cumulative_buckets = histogram.cumulativeStatistics().computedBuckets(); + const std::vector& interval_buckets = interval_statistics.computedBuckets(); + const std::vector& cumulative_buckets = + histogram.cumulativeStatistics().computedBuckets(); collectBuckets(name, histogram, interval_buckets, cumulative_buckets); break; } case Utility::HistogramBucketsMode::Disjoint: { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); - const UInt64Vec interval_buckets = interval_statistics.computeDisjointBuckets(); - const UInt64Vec cumulative_buckets = histogram.cumulativeStatistics().computeDisjointBuckets(); + const std::vector interval_buckets = interval_statistics.computeDisjointBuckets(); + const std::vector cumulative_buckets = + histogram.cumulativeStatistics().computeDisjointBuckets(); collectBuckets(name, histogram, interval_buckets, cumulative_buckets); break; } @@ -283,8 +286,8 @@ void StatsJsonRender::summarizeBuckets(const std::string& name, // cumulative or disjoint views, as controlled by buckets_fn. void StatsJsonRender::collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, - const UInt64Vec& interval_buckets, - const UInt64Vec& cumulative_buckets) { + const std::vector& interval_buckets, + const std::vector& cumulative_buckets) { const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics(); Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets(); diff --git a/source/server/admin/stats_render.h b/source/server/admin/stats_render.h index b9a7e2bf446b2..b31608271b095 100644 --- a/source/server/admin/stats_render.h +++ b/source/server/admin/stats_render.h @@ -28,9 +28,6 @@ class StatsRender { // Completes rendering any buffered data. virtual void finalize(Buffer::Instance& response) PURE; - -protected: - using UInt64Vec = std::vector; }; // Implements the Render interface for simple textual representation of stats. @@ -96,7 +93,8 @@ class StatsJsonRender : public StatsRender { // Collects the buckets from the specified histogram. void collectBuckets(const std::string& name, const Stats::ParentHistogram& histogram, - const UInt64Vec& interval_buckets, const UInt64Vec& cumulative_buckets); + const std::vector& interval_buckets, + const std::vector& cumulative_buckets); std::vector stats_array_; ProtobufWkt::Struct histograms_obj_; From 92e115e6768b669a7fd47c8d418d8b10db350f9e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 5 Apr 2022 11:52:43 -0400 Subject: [PATCH 74/74] remove accidental bazelversion commit Signed-off-by: Joshua Marantz --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index 831446cbd27a6..0062ac971805f 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -5.1.0 +5.0.0