From ed31d092f04ec13f5c5ed46c29e2aec241ea4862 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 31 Oct 2018 12:48:47 -0400 Subject: [PATCH 001/106] checkpoint Signed-off-by: Joshua Marantz --- source/common/stats/heap_stat_data.cc | 9 ++++++--- source/common/stats/heap_stat_data.h | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index 7177ea19087c..bd4f115a9f88 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -7,15 +7,18 @@ namespace Envoy { namespace Stats { -HeapStatData::HeapStatData(absl::string_view key) { - StringUtil::strlcpy(name_, key.data(), key.size() + 1); +HeapStatData::HeapStatData(SymbolEncoding& encoding) { + encoding.moveToStorage(name_encoding_); } -HeapStatDataAllocator::HeapStatDataAllocator() {} +HeapStatDataAllocator::HeapStatDataAllocator(SymbolTable& symbol_table) + : table_(symbol_table) {} HeapStatDataAllocator::~HeapStatDataAllocator() { ASSERT(stats_.empty()); } HeapStatData* HeapStatDataAllocator::alloc(absl::string_view name) { + SymbolVec symbol_vec = table_.encode(name); + // Any expected truncation of name is done at the callsite. No truncation is // required to use this allocator. Note that data must be freed by calling // its free() method, and not by destruction, thus the more complex use of diff --git a/source/common/stats/heap_stat_data.h b/source/common/stats/heap_stat_data.h index dc14303ec626..7ac6415bb7aa 100644 --- a/source/common/stats/heap_stat_data.h +++ b/source/common/stats/heap_stat_data.h @@ -29,14 +29,14 @@ struct HeapStatData { */ const char* name() const { return name_; } - static HeapStatData* alloc(absl::string_view name); + static HeapStatData* alloc(const SymbolEncoding& encoding); void free(); std::atomic value_{0}; std::atomic pending_increment_{0}; std::atomic flags_{0}; std::atomic ref_count_{1}; - char name_[]; + char name_encoding_[]; private: /** From 1cd7a7b75ca495412dbcec93a927a1b7f2c7ac3b Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 2 Nov 2018 13:17:50 -0400 Subject: [PATCH 002/106] got heap_stat_data_test working. Signed-off-by: Joshua Marantz --- include/envoy/stats/BUILD | 8 +- include/envoy/stats/stat_data_allocator.h | 11 +- include/envoy/stats/stats.h | 25 ++--- include/envoy/stats/symbol_table.h | 85 +++++++++++++- source/common/stats/BUILD | 3 + source/common/stats/heap_stat_data.cc | 51 ++++----- source/common/stats/heap_stat_data.h | 105 +++++++++++------- source/common/stats/histogram_impl.h | 20 ++-- source/common/stats/isolated_store_impl.cc | 6 +- source/common/stats/metric_impl.h | 19 +++- source/common/stats/raw_stat_data.cc | 3 + source/common/stats/raw_stat_data.h | 40 +++++++ .../common/stats/stat_data_allocator_impl.h | 82 ++++++-------- source/common/stats/symbol_table_impl.cc | 24 +++- source/common/stats/symbol_table_impl.h | 86 ++++---------- test/common/stats/heap_stat_data_test.cc | 57 +++++++--- test/test_common/utility.cc | 3 +- test/test_common/utility.h | 6 +- 18 files changed, 389 insertions(+), 245 deletions(-) diff --git a/include/envoy/stats/BUILD b/include/envoy/stats/BUILD index 9f1e513135b6..9162d1b1ca41 100644 --- a/include/envoy/stats/BUILD +++ b/include/envoy/stats/BUILD @@ -25,12 +25,18 @@ envoy_cc_library( "tag_extractor.h", "tag_producer.h", ], - deps = ["//include/envoy/common:interval_set_interface"], + deps = [ + ":symbol_table_interface", + "//include/envoy/common:interval_set_interface", + ], ) envoy_cc_library( name = "symbol_table_interface", hdrs = ["symbol_table.h"], + deps = [ + "//source/common/common:hash_lib", + ], ) envoy_cc_library( diff --git a/include/envoy/stats/stat_data_allocator.h b/include/envoy/stats/stat_data_allocator.h index 01d2593f34aa..38d5147eff1c 100644 --- a/include/envoy/stats/stat_data_allocator.h +++ b/include/envoy/stats/stat_data_allocator.h @@ -34,8 +34,8 @@ class StatDataAllocator { * @return CounterSharedPtr a counter, or nullptr if allocation failed, in which case * tag_extracted_name and tags are not moved. */ - virtual CounterSharedPtr makeCounter(absl::string_view name, std::string&& tag_extracted_name, - std::vector&& tags) PURE; + virtual CounterSharedPtr makeCounter(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) PURE; /** * @param name the full name of the stat. @@ -44,14 +44,17 @@ class StatDataAllocator { * @return GaugeSharedPtr a gauge, or nullptr if allocation failed, in which case * tag_extracted_name and tags are not moved. */ - virtual GaugeSharedPtr makeGauge(absl::string_view name, std::string&& tag_extracted_name, - std::vector&& tags) PURE; + virtual GaugeSharedPtr makeGauge(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) PURE; /** * Determines whether this stats allocator requires bounded stat-name size. */ virtual bool requiresBoundedStatNameSize() const PURE; + virtual const SymbolTable& symbolTable() const PURE; + virtual SymbolTable& symbolTable() PURE; + // TODO(jmarantz): create a parallel mechanism to instantiate histograms. At // the moment, histograms don't fit the same pattern of counters and gaugaes // as they are not actually created in the context of a stats allocator. diff --git a/include/envoy/stats/stats.h b/include/envoy/stats/stats.h index af67d7ba869a..4f70ff8e52d6 100644 --- a/include/envoy/stats/stats.h +++ b/include/envoy/stats/stats.h @@ -6,12 +6,13 @@ #include #include "envoy/common/pure.h" - +#include "envoy/stats/symbol_table.h" #include "absl/strings/string_view.h" namespace Envoy { namespace Stats { +class StatDataAllocator; struct Tag; /** @@ -32,32 +33,28 @@ class Metric { virtual std::string name() const PURE; /** - * Returns the full name of the Metric as a nul-terminated string. The - * intention is use this as a hash-map key, so that the stat name storage - * is not duplicated in every map. You cannot use name() above for this, - * as it returns a std::string by value, as not all stat implementations - * containe the name as a std::string. - * - * Note that in the future, the plan is to replace this method with one that - * returns a reference to a symbolized representation of the elaborated string - * (see source/common/stats/symbol_table_impl.h). + * Returns the full name of the Metric as an encoded array of symbols. */ - virtual const char* nameCStr() const PURE; + virtual StatName statName() const PURE; /** * Returns a vector of configurable tags to identify this Metric. */ - virtual const std::vector& tags() const PURE; + virtual std::vector tags() const PURE; /** - * Returns the name of the Metric with the portions designated as tags removed. + * Returns the name of the Metric with the portions designated as tags + * removed as a string. */ - virtual const std::string& tagExtractedName() const PURE; + virtual std::string tagExtractedName() const PURE; /** * Indicates whether this metric has been updated since the server was started. */ virtual bool used() const PURE; + + virtual StatDataAllocator& allocator() PURE; + virtual const StatDataAllocator& allocator() const PURE; }; /** diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 3f8152a6c859..3b0be37792b0 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -1,13 +1,94 @@ #pragma once +#include "common/common/hash.h" + namespace Envoy { namespace Stats { -// Interface for referencing a stat name. -class StatName; +/** Efficient byte-encoded storage an array of tokens, which are typically < 127 */ +using SymbolStorage = uint8_t[]; // Interface for managing symbol tables. class SymbolTable; +/** + * Efficiently represents a stat name using a variable-length array of uint8_t. + * This class does not own the backing store for this array; the backing-store + * can be held in StatNameStorage, or it can be packed more tightly into another + * object. + * + * For large numbers of clusters, there are a huge number of StatNames so + * avoiding extra per-stat pointers has a significant memory impact. + */ +class StatName { +public: + // Constructs a StatName object directly referencing the storage of another + // StatName. + explicit StatName(const SymbolStorage symbol_array) : symbol_array_(symbol_array) {} + + // Constructs an empty StatName object. + StatName() : symbol_array_(nullptr) {} + + // Constructs a StatName object with new storage, which must be of size + // src.numBytesIncludingLenggth(). This is used in the a flow where we first + // construct a StatName for lookup in a cache, and then on a miss need/ to + // store the data directly. + StatName(const StatName& src, SymbolStorage memory); + + std::string toString(const SymbolTable& table) const; + + /** + * Note that this hash function will return a different hash than that of + * the elaborated string. + * + * @return uint64_t a hash of the underlying representation. + */ + uint64_t hash() const { + const char* cdata = reinterpret_cast(data()); + return HashUtil::xxHash64(absl::string_view(cdata, numBytes())); + } + + bool operator==(const StatName& rhs) const { + const uint64_t sz = numBytes(); + return sz == rhs.numBytes() && memcmp(data(), rhs.data(), sz * sizeof(uint8_t)) == 0; + } + bool operator!=(const StatName& rhs) const { return !(*this == rhs); } + + /** + * @return uint64_t the number of bytes in the symbol array, excluding the two-byte + * overhead for the size itself. + */ + uint64_t numBytes() const { + return symbol_array_[0] | (static_cast(symbol_array_[1]) << 8); + } + + const uint8_t* symbolArray() const { return symbol_array_; } + + /** + * @return uint64_t the number of bytes in the symbol array, including the two-byte + * overhead for the size itself. + */ + uint64_t numBytesIncludingLength() const { return numBytes() + 2; } + + void copyToStorage(SymbolStorage storage) { + memcpy(storage, symbol_array_, numBytesIncludingLength()); + } + +protected: + friend SymbolTable; + friend class StatNameTest; + friend class StatNameJoiner; + friend class StatNameStorage; + + /** + * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). + */ + const uint8_t* data() const { + return symbol_array_ + 2; + } + + const uint8_t* symbol_array_; +}; + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index d51b43a51e1d..616b584271c8 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -13,6 +13,7 @@ envoy_cc_library( srcs = ["heap_stat_data.cc"], hdrs = ["heap_stat_data.h"], deps = [ + ":metric_impl_lib", ":stat_data_allocator_lib", "//source/common/common:assert_lib", "//source/common/common:hash_lib", @@ -52,8 +53,10 @@ envoy_cc_library( envoy_cc_library( name = "metric_impl_lib", + srcs = ["metric_impl.cc"], hdrs = ["metric_impl.h"], deps = [ + ":symbol_table_lib", "//include/envoy/stats:stats_interface", "//source/common/common:assert_lib", ], diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index bd4f115a9f88..c44eebfa3e76 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -7,34 +7,40 @@ namespace Envoy { namespace Stats { -HeapStatData::HeapStatData(SymbolEncoding& encoding) { - encoding.moveToStorage(name_encoding_); +HeapStatDataAllocator::~HeapStatDataAllocator() { + ASSERT(stats_.empty()); } -HeapStatDataAllocator::HeapStatDataAllocator(SymbolTable& symbol_table) - : table_(symbol_table) {} +HeapStatData* HeapStatData::alloc(StatName stat_name, SymbolTable& symbol_table) { + void* memory = ::malloc(sizeof(HeapStatData) + stat_name.numBytesIncludingLength()); + ASSERT(memory); + symbol_table.incRefCount(stat_name); + return new (memory) HeapStatData(stat_name); +} -HeapStatDataAllocator::~HeapStatDataAllocator() { ASSERT(stats_.empty()); } +void HeapStatData::free(SymbolTable& symbol_table) { + symbol_table.free(statName()); + this->~HeapStatData(); + ::free(this); // matches malloc() call above. +} -HeapStatData* HeapStatDataAllocator::alloc(absl::string_view name) { - SymbolVec symbol_vec = table_.encode(name); - // Any expected truncation of name is done at the callsite. No truncation is - // required to use this allocator. Note that data must be freed by calling - // its free() method, and not by destruction, thus the more complex use of - // unique_ptr. - std::unique_ptr> data( - HeapStatData::alloc(name), [](HeapStatData* d) { d->free(); }); +HeapStatData& HeapStatDataAllocator::alloc(StatName name) { + std::unique_ptr> data_ptr( + HeapStatData::alloc(name, symbolTable()), [this](HeapStatData* d) { + d->free(symbolTable()); + }); Thread::ReleasableLockGuard lock(mutex_); - auto ret = stats_.insert(data.get()); + auto ret = stats_.insert(data_ptr.get()); HeapStatData* existing_data = *ret.first; lock.release(); if (ret.second) { - return data.release(); + //symbolTable().incRefCount(existing_data->statName()); + return *data_ptr.release(); } ++existing_data->ref_count_; - return existing_data; + return *existing_data; } void HeapStatDataAllocator::free(HeapStatData& data) { @@ -49,18 +55,7 @@ void HeapStatDataAllocator::free(HeapStatData& data) { ASSERT(key_removed == 1); } - data.free(); -} - -HeapStatData* HeapStatData::alloc(absl::string_view name) { - void* memory = ::malloc(sizeof(HeapStatData) + name.size() + 1); - ASSERT(memory); - return new (memory) HeapStatData(name); -} - -void HeapStatData::free() { - this->~HeapStatData(); - ::free(this); // matches malloc() call above. + data.free(symbolTable()); } template class StatDataAllocatorImpl; diff --git a/source/common/stats/heap_stat_data.h b/source/common/stats/heap_stat_data.h index 7ac6415bb7aa..30006c7a5a28 100644 --- a/source/common/stats/heap_stat_data.h +++ b/source/common/stats/heap_stat_data.h @@ -7,7 +7,11 @@ #include "common/common/hash.h" #include "common/common/thread.h" #include "common/common/thread_annotations.h" +#include "common/stats/metric_impl.h" #include "common/stats/stat_data_allocator_impl.h" +#include "common/stats/symbol_table_impl.h" + +#include "envoy/stats/stats.h" #include "absl/container/flat_hash_set.h" @@ -19,71 +23,90 @@ namespace Stats { * so that it can be allocated efficiently from the heap on demand. */ struct HeapStatData { - /** - * @returns absl::string_view the name as a string_view. - */ - absl::string_view key() const { return name_; } + private: + explicit HeapStatData(StatName stat_name) { stat_name.copyToStorage(symbol_storage_); } + + public: + static HeapStatData* alloc(StatName stat_name, SymbolTable& symbol_table); - /** - * @returns std::string the name as a const char*. - */ - const char* name() const { return name_; } + void free(SymbolTable& symbol_table); + StatName statName() const { return StatName(symbol_storage_); } - static HeapStatData* alloc(const SymbolEncoding& encoding); - void free(); + bool operator==(const HeapStatData& rhs) const { return statName() == rhs.statName(); } + uint64_t hash() const { return statName().hash(); } std::atomic value_{0}; std::atomic pending_increment_{0}; std::atomic flags_{0}; std::atomic ref_count_{1}; - char name_encoding_[]; - -private: - /** - * You cannot construct/destruct a HeapStatData directly with new/delete as - * it's variable-size. Use alloc()/free() methods above. - */ - explicit HeapStatData(absl::string_view name); - ~HeapStatData() {} + SymbolStorage symbol_storage_; }; -/** - * Implementation of StatDataAllocator using a pure heap-based strategy, so that - * Envoy implementations that do not require hot-restart can use less memory. - */ +template +class HeapStat : public Stat { +public: + HeapStat(HeapStatData& data, StatDataAllocatorImpl& alloc, + absl::string_view tag_extracted_name, const std::vector& tags) + : Stat(data, alloc, tag_extracted_name, tags) {} + + StatName statName() const override { return this->data_.statName(); } +}; + +// Partially implements a StatDataAllocator, leaving alloc & free for subclasses. +// We templatize on StatData rather than defining a virtual base StatData class +// for performance reasons; stat increment is on the hot path. +// +// The two production derivations cover using a fixed block of shared-memory for +// hot restart stat continuity, and heap allocation for more efficient RAM usage +// for when hot-restart is not required. +// +// Also note that RawStatData needs to live in a shared memory block, and it's +// possible, but not obvious, that a vptr would be usable across processes. In +// any case, RawStatData is allocated from a shared-memory block rather than via +// new, so the usual C++ compiler assistance for setting up vptrs will not be +// available. This could be resolved with placed new, or another nesting level. class HeapStatDataAllocator : public StatDataAllocatorImpl { public: - HeapStatDataAllocator(); - ~HeapStatDataAllocator(); + HeapStatDataAllocator(SymbolTable& symbol_table) : StatDataAllocatorImpl(symbol_table) {} + virtual ~HeapStatDataAllocator(); - // StatDataAllocatorImpl - HeapStatData* alloc(absl::string_view name) override; + HeapStatData& alloc(StatName name); void free(HeapStatData& data) override; // StatDataAllocator bool requiresBoundedStatNameSize() const override { return false; } -private: + CounterSharedPtr makeCounter(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) override { + return std::make_shared>>( + alloc(name), *this, tag_extracted_name, tags); + } + + GaugeSharedPtr makeGauge(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) override { + return std::make_shared>>( + alloc(name), *this, tag_extracted_name, tags); + } + + + private: struct HeapStatHash { - size_t operator()(const HeapStatData* a) const { return HashUtil::xxHash64(a->key()); } + size_t operator()(const HeapStatData* a) const { return a->hash(); } }; struct HeapStatCompare { - bool operator()(const HeapStatData* a, const HeapStatData* b) const { - return (a->key() == b->key()); - } + bool operator()(const HeapStatData* a, const HeapStatData* b) const { return *a == *b; } }; - // TODO(jmarantz): See https://github.com/envoyproxy/envoy/pull/3927 and - // https://github.com/envoyproxy/envoy/issues/3585, which can help reorganize - // the heap stats using a ref-counted symbol table to compress the stat strings. - using StatSet = absl::flat_hash_set; - // An unordered set of HeapStatData pointers which keys off the key() - // field in each object. This necessitates a custom comparator and hasher. + // field in each object. This necessitates a custom comparator and hasher, which key off of the + // StatNamePtr's own StatNamePtrHash and StatNamePtrCompare operators. + using StatSet = absl::flat_hash_set; StatSet stats_ GUARDED_BY(mutex_); - // A mutex is needed here to protect the stats_ object from both alloc() and free() operations. - // Although alloc() operations are called under existing locking, free() operations are made from - // the destructors of the individual stat objects, which are not protected by locks. + + // A mutex is needed here to protect both the stats_ object from both + // alloc() and free() operations. Although alloc() operations are called under existing locking, + // free() operations are made from the destructors of the individual stat objects, which are not + // protected by locks. Thread::MutexBasicLockable mutex_; }; diff --git a/source/common/stats/histogram_impl.h b/source/common/stats/histogram_impl.h index fc6b39944ba3..dd8a71dc9cc3 100644 --- a/source/common/stats/histogram_impl.h +++ b/source/common/stats/histogram_impl.h @@ -44,13 +44,9 @@ class HistogramStatisticsImpl : public HistogramStatistics, NonCopyable { */ class HistogramImpl : public Histogram, public MetricImpl { public: - HistogramImpl(const std::string& name, Store& parent, std::string&& tag_extracted_name, - std::vector&& tags) - : MetricImpl(std::move(tag_extracted_name), std::move(tags)), parent_(parent), name_(name) {} - - // Stats:;Metric - std::string name() const override { return name_; } - const char* nameCStr() const override { return name_.c_str(); } + HistogramImpl(StatName name, Store& parent, const std::string& tag_extracted_name, + const std::vector& tags, StatDataAllocator& alloc) + : MetricImpl(tag_extracted_name, tags, alloc.symbolTable()), name_(name), parent_(parent) {} // Stats::Histogram void recordValue(uint64_t value) override { parent_.deliverHistogramToSinks(*this, value); } @@ -58,10 +54,10 @@ class HistogramImpl : public Histogram, public MetricImpl { bool used() const override { return true; } private: + StatName name_; + // This is used for delivering the histogram data to sinks. Store& parent_; - - const std::string name_; }; /** @@ -73,9 +69,9 @@ class NullHistogramImpl : public Histogram { NullHistogramImpl() {} ~NullHistogramImpl() {} std::string name() const override { return ""; } - const char* nameCStr() const override { return ""; } - const std::string& tagExtractedName() const override { CONSTRUCT_ON_FIRST_USE(std::string, ""); } - const std::vector& tags() const override { CONSTRUCT_ON_FIRST_USE(std::vector, {}); } + StatName statName() const override { return StatName(); } + std::string tagExtractedName() const override { return ""; } + std::vector tags() const override { return std::vector(); } void recordValue(uint64_t) override {} bool used() const override { return false; } }; diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index d85d38ef0321..6bf2d309d702 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -14,14 +14,12 @@ namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() : counters_([this](const std::string& name) -> CounterSharedPtr { - std::string tag_extracted_name = name; - std::vector tags; - return alloc_.makeCounter(name, std::move(tag_extracted_name), std::move(tags)); + return alloc_.makeCounter(name, "", std::vector()); }), gauges_([this](const std::string& name) -> GaugeSharedPtr { std::string tag_extracted_name = name; std::vector tags; - return alloc_.makeGauge(name, std::move(tag_extracted_name), std::move(tags)); + return alloc_.makeGauge(name, "", std::vector()); }), histograms_([this](const std::string& name) -> HistogramSharedPtr { return std::make_shared(name, *this, std::string(name), std::vector()); diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index 3e65e0d79d86..95d02b6ce59b 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -3,10 +3,12 @@ #include #include +#include "envoy/stats/stat_data_allocator.h" #include "envoy/stats/stats.h" #include "envoy/stats/tag.h" #include "common/common/assert.h" +#include "common/stats/symbol_table_impl.h" namespace Envoy { namespace Stats { @@ -20,11 +22,13 @@ namespace Stats { */ class MetricImpl : public virtual Metric { public: - MetricImpl(std::string&& tag_extracted_name, std::vector&& tags) - : tag_extracted_name_(std::move(tag_extracted_name)), tags_(std::move(tags)) {} + MetricImpl(absl::string_view tag_extracted_name, const std::vector& tags, + SymbolTable& symbol_table); + ~MetricImpl(); - const std::string& tagExtractedName() const override { return tag_extracted_name_; } - const std::vector& tags() const override { return tags_; } + std::string name() const override { return statName().toString(symbolTable()); } + std::string tagExtractedName() const override; + std::vector tags() const override; protected: /** @@ -35,8 +39,11 @@ class MetricImpl : public virtual Metric { }; private: - const std::string tag_extracted_name_; - const std::vector tags_; + StatName tagExtractedStatName() const; + const SymbolTable& symbolTable() const { return allocator().symbolTable(); } + SymbolTable& symbolTable() { return allocator().symbolTable(); } + + std::unique_ptr storage_; }; } // namespace Stats diff --git a/source/common/stats/raw_stat_data.cc b/source/common/stats/raw_stat_data.cc index dc03bba090c6..99342301a5b6 100644 --- a/source/common/stats/raw_stat_data.cc +++ b/source/common/stats/raw_stat_data.cc @@ -22,6 +22,9 @@ uint64_t roundUpMultipleNaturalAlignment(uint64_t val) { } // namespace +RawStatDataAllocator::~RawStatDataAllocator() { +} + // Normally the compiler would do this, but because name_ is a flexible-array-length // element, the compiler can't. RawStatData is put into an array in HotRestartImpl, so // it's important that each element starts on the required alignment for the type. diff --git a/source/common/stats/raw_stat_data.h b/source/common/stats/raw_stat_data.h index e5b169275963..06a93e3d27eb 100644 --- a/source/common/stats/raw_stat_data.h +++ b/source/common/stats/raw_stat_data.h @@ -90,10 +90,50 @@ struct RawStatData { char name_[]; }; +template +class RawStat : public Stat { +public: + RawStat(StatName stat_name, RawStatData& data, StatDataAllocatorImpl& alloc, + absl::string_view tag_extracted_name, const std::vector& tags) + : Stat(data, alloc, tag_extracted_name, tags), + stat_name_storage_(stat_name, alloc.symbolTable()) {} + + StatName statName() const override { return stat_name_storage_.statName(); } + + private: + StatNameStorage stat_name_storage_; +}; + class RawStatDataAllocator : public StatDataAllocatorImpl { public: + RawStatDataAllocator(SymbolTable& symbol_table) : StatDataAllocatorImpl(symbol_table) {} + ~RawStatDataAllocator(); + + virtual RawStatData* alloc(absl::string_view name) PURE; + RawStatData* allocStatName(StatName stat_name) { return alloc(stat_name.toString(symbolTable())); } + // StatDataAllocator bool requiresBoundedStatNameSize() const override { return true; } + + template std::shared_ptr makeStat( + StatName name, absl::string_view tag_extracted_name, const std::vector& tags) { + RawStatData* raw_stat_data = allocStatName(name); + if (raw_stat_data == nullptr) { + return nullptr; + } + return std::make_shared>( + name, *raw_stat_data, *this, tag_extracted_name, tags); + } + + CounterSharedPtr makeCounter(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) override { + return makeStat>(name, tag_extracted_name, tags); + } + + GaugeSharedPtr makeGauge(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) override { + return makeStat>(name, tag_extracted_name, tags); + } }; } // namespace Stats diff --git a/source/common/stats/stat_data_allocator_impl.h b/source/common/stats/stat_data_allocator_impl.h index 81df5f426957..afcf644a2e12 100644 --- a/source/common/stats/stat_data_allocator_impl.h +++ b/source/common/stats/stat_data_allocator_impl.h @@ -29,11 +29,7 @@ namespace Stats { // available. This could be resolved with placed new, or another nesting level. template class StatDataAllocatorImpl : public StatDataAllocator { public: - // StatDataAllocator - CounterSharedPtr makeCounter(absl::string_view name, std::string&& tag_extracted_name, - std::vector&& tags) override; - GaugeSharedPtr makeGauge(absl::string_view name, std::string&& tag_extracted_name, - std::vector&& tags) override; + explicit StatDataAllocatorImpl(SymbolTable& symbol_table) : symbol_table_(symbol_table) {} /** * @param name the full name of the stat. @@ -42,7 +38,7 @@ template class StatDataAllocatorImpl : public StatDataAllocator * by name if one already exists with the same name. This is used for intra-process * scope swapping as well as inter-process hot restart. */ - virtual StatData* alloc(absl::string_view name) PURE; + //virtual StatData* alloc(StatName name) PURE; /** * Free a raw stat data block. The allocator should handle reference counting and only truly @@ -50,6 +46,15 @@ template class StatDataAllocatorImpl : public StatDataAllocator * @param data the data returned by alloc(). */ virtual void free(StatData& data) PURE; + + SymbolTable& symbolTable() override { return symbol_table_; } + const SymbolTable& symbolTable() const override { return symbol_table_; } + + private: + // SymbolTable encodes encodes stat names as back into strings. This does not + // get guarded by a mutex, since it has its own internal mutex to guarantee + // thread safety. + SymbolTable& symbol_table_; }; /** @@ -62,14 +67,10 @@ template class StatDataAllocatorImpl : public StatDataAllocator template class CounterImpl : public Counter, public MetricImpl { public: CounterImpl(StatData& data, StatDataAllocatorImpl& alloc, - std::string&& tag_extracted_name, std::vector&& tags) - : MetricImpl(std::move(tag_extracted_name), std::move(tags)), data_(data), alloc_(alloc) {} + absl::string_view tag_extracted_name, const std::vector& tags) + : MetricImpl(tag_extracted_name, tags, alloc.symbolTable()), data_(data), alloc_(alloc) {} ~CounterImpl() { alloc_.free(data_); } - // Stats::Metric - std::string name() const override { return std::string(data_.name()); } - const char* nameCStr() const override { return data_.name(); } - // Stats::Counter void add(uint64_t amount) override { data_.value_ += amount; @@ -83,7 +84,10 @@ template class CounterImpl : public Counter, public MetricImpl bool used() const override { return data_.flags_ & Flags::Used; } uint64_t value() const override { return data_.value_; } -private: + StatDataAllocator& allocator() override { return alloc_; } + const StatDataAllocator& allocator() const override { return alloc_; } + +protected: StatData& data_; StatDataAllocatorImpl& alloc_; }; @@ -96,10 +100,13 @@ class NullCounterImpl : public Counter { public: NullCounterImpl() {} ~NullCounterImpl() {} + + // Stats::Metric std::string name() const override { return ""; } - const char* nameCStr() const override { return ""; } - const std::string& tagExtractedName() const override { CONSTRUCT_ON_FIRST_USE(std::string, ""); } - const std::vector& tags() const override { CONSTRUCT_ON_FIRST_USE(std::vector, {}); } + StatName statName() const override { return StatName(); } + + std::string tagExtractedName() const override { return ""; } + std::vector tags() const override { return std::vector(); } void add(uint64_t) override {} void inc() override {} uint64_t latch() override { return 0; } @@ -114,14 +121,10 @@ class NullCounterImpl : public Counter { template class GaugeImpl : public Gauge, public MetricImpl { public: GaugeImpl(StatData& data, StatDataAllocatorImpl& alloc, - std::string&& tag_extracted_name, std::vector&& tags) - : MetricImpl(std::move(tag_extracted_name), std::move(tags)), data_(data), alloc_(alloc) {} + absl::string_view tag_extracted_name, const std::vector& tags) + : MetricImpl(tag_extracted_name, tags, alloc.symbolTable()), data_(data), alloc_(alloc) {} ~GaugeImpl() { alloc_.free(data_); } - // Stats::Metric - std::string name() const override { return std::string(data_.name()); } - const char* nameCStr() const override { return data_.name(); } - // Stats::Gauge virtual void add(uint64_t amount) override { data_.value_ += amount; @@ -141,7 +144,10 @@ template class GaugeImpl : public Gauge, public MetricImpl { virtual uint64_t value() const override { return data_.value_; } bool used() const override { return data_.flags_ & Flags::Used; } -private: + StatDataAllocator& allocator() override { return alloc_; } + const StatDataAllocator& allocator() const override { return alloc_; } + +protected: StatData& data_; StatDataAllocatorImpl& alloc_; }; @@ -155,9 +161,9 @@ class NullGaugeImpl : public Gauge { NullGaugeImpl() {} ~NullGaugeImpl() {} std::string name() const override { return ""; } - const char* nameCStr() const override { return ""; } - const std::string& tagExtractedName() const override { CONSTRUCT_ON_FIRST_USE(std::string, ""); } - const std::vector& tags() const override { CONSTRUCT_ON_FIRST_USE(std::vector, {}); } + StatName statName() const override { return StatName(); } + std::string tagExtractedName() const override { return ""; } + std::vector tags() const override { return std::vector(); } void add(uint64_t) override {} void inc() override {} void dec() override {} @@ -167,29 +173,5 @@ class NullGaugeImpl : public Gauge { uint64_t value() const override { return 0; } }; -template -CounterSharedPtr StatDataAllocatorImpl::makeCounter(absl::string_view name, - std::string&& tag_extracted_name, - std::vector&& tags) { - StatData* data = alloc(name); - if (data == nullptr) { - return nullptr; - } - return std::make_shared>(*data, *this, std::move(tag_extracted_name), - std::move(tags)); -} - -template -GaugeSharedPtr StatDataAllocatorImpl::makeGauge(absl::string_view name, - std::string&& tag_extracted_name, - std::vector&& tags) { - StatData* data = alloc(name); - if (data == nullptr) { - return nullptr; - } - return std::make_shared>(*data, *this, std::move(tag_extracted_name), - std::move(tags)); -} - } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 6ca095032e02..615730ffcda7 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -12,6 +12,16 @@ namespace Stats { static const uint32_t SpilloverMask = 0x80; static const uint32_t Low7Bits = 0x7f; +StatName::StatName(const StatName& src, SymbolStorage memory) + : symbol_array_(memory) { + memcpy(memory, src.symbolArray(), src.numBytesIncludingLength()); + //src.symbol_array_ = nullptr; // transfers ownership. +} + +std::string StatName::toString(const SymbolTable& table) const { + return table.decode(data(), numBytes()); +} + SymbolEncoding::~SymbolEncoding() { ASSERT(vec_.empty()); } void SymbolEncoding::addSymbol(Symbol symbol) { @@ -63,13 +73,14 @@ static inline uint8_t* saveLengthToBytesReturningNext(uint64_t length, uint8_t* return bytes; } -void SymbolEncoding::moveToStorage(SymbolStorage symbol_array) { +uint64_t SymbolEncoding::moveToStorage(SymbolStorage symbol_array) { uint64_t sz = size(); symbol_array = saveLengthToBytesReturningNext(sz, symbol_array); if (sz != 0) { memcpy(symbol_array, vec_.data(), sz * sizeof(uint8_t)); } vec_.clear(); // Logically transfer ownership, enabling empty assert on destruct. + return sz + 2; } SymbolTable::SymbolTable() @@ -133,7 +144,7 @@ std::string SymbolTable::decode(const SymbolVec& symbols) const { return absl::StrJoin(name_tokens, "."); } -void SymbolTable::free(StatName stat_name) { +void SymbolTable::adjustRefCount(StatName stat_name, int adjustment) { // Before taking the lock, decode the array of symbols from the SymbolStorage. SymbolVec symbols = SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.numBytes()); @@ -145,7 +156,8 @@ void SymbolTable::free(StatName stat_name) { auto encode_search = encode_map_.find(decode_search->second); ASSERT(encode_search != encode_map_.end()); - encode_search->second.ref_count_--; + encode_search->second.ref_count_ += adjustment; + // If that was the last remaining client usage of the symbol, erase the the current // mappings and add the now-unused symbol to the reuse pool. if (encode_search->second.ref_count_ == 0) { @@ -222,6 +234,12 @@ StatNameStorage::StatNameStorage(absl::string_view name, SymbolTable& table) { encoding.moveToStorage(bytes_.get()); } +StatNameStorage::StatNameStorage(StatName src, SymbolTable& /*table*/) { + uint64_t size = src.numBytesIncludingLength(); + bytes_ = std::make_unique(size); + memcpy(bytes_.get(), src.data(), size); +} + StatNameStorage::~StatNameStorage() { // StatNameStorage is not fully RAII: you must call free(SymbolTable&) to // decrement the reference counts held by the SymbolTable on behalf of diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index a9770dfb91ba..4ed9f438ccb3 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -9,6 +9,7 @@ #include #include "envoy/common/exception.h" +#include "envoy/stats/symbol_table.h" #include "common/common/assert.h" #include "common/common/hash.h" @@ -17,6 +18,7 @@ #include "common/common/thread.h" #include "common/common/utility.h" +#include "absl/container/flat_hash_map.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" @@ -63,7 +65,7 @@ class SymbolEncoding { /** * Returns the number of bytes required to represent StatName as a uint8_t - * array. + * array, including the encoded size. */ uint64_t bytesRequired() const { return size() + 2 /* size encoded as 2 bytes */; } @@ -77,8 +79,9 @@ class SymbolEncoding { * must have been allocated with bytesRequired() bytes. * * @param array destination memory to receive the encoded bytes. + * @return uint64_t the number of bytes transferred. */ - void moveToStorage(SymbolStorage array); + uint64_t moveToStorage(SymbolStorage array); private: std::vector vec_; @@ -170,7 +173,18 @@ class SymbolTable { * * @param symbol_vec the vector of symbols to be freed. */ - void free(StatName stat_name); + void free(StatName stat_name) { adjustRefCount(stat_name, -1); } + + /** + * 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 symbol_vec the vector of symbols to be freed. + */ + void incRefCount(StatName stat_name) { adjustRefCount(stat_name, 1); }; private: friend class StatName; @@ -195,6 +209,8 @@ class SymbolTable { std::string decode(const SymbolStorage symbol_vec, uint64_t size) const; std::string decode(const SymbolVec& symbols) const; + void adjustRefCount(StatName stat_name, int adjustment); + /** * Convenience function for encode(), symbolizing one string segment at a time. * @@ -247,6 +263,7 @@ class StatNameStorage { public: StatNameStorage(absl::string_view name, SymbolTable& table); StatNameStorage(StatNameStorage&& src) : bytes_(std::move(src.bytes_)) {} + StatNameStorage(StatName src, SymbolTable& table); /** * Before allowing a StatNameStorage to be destroyed, you must call free() @@ -265,71 +282,12 @@ class StatNameStorage { /** * @return StatName a reference to the owned storage. */ - inline StatName statName() const; // implementation below. + StatName statName() const { return StatName(bytes_.get()); } private: std::unique_ptr bytes_; }; -/** - * Efficiently represents a stat name using a variable-length array of uint8_t. - * This class does not own the backing store for this array; the backing-store - * can be held in StatNameStorage, or it can be packed more tightly into another - * object. - * - * For large numbers of clusters, there are a huge number of StatNames so - * avoiding extra per-stat pointers has a significant memory impact. - */ -class StatName { -public: - explicit StatName(const SymbolStorage symbol_array) : symbol_array_(symbol_array) {} - StatName() : symbol_array_(nullptr) {} - - std::string toString(const SymbolTable& table) const { return table.decode(data(), numBytes()); } - - /** - * Note that this hash function will return a different hash than that of - * the elaborated string. - * - * @return uint64_t a hash of the underlying symbol vector, since StatNames - * are uniquely defined by their symbol vectors. - */ - uint64_t hash() const { - const char* cdata = reinterpret_cast(data()); - return HashUtil::xxHash64(absl::string_view(cdata, numBytes())); - } - - // Compares on the underlying symbol vectors. - // NB: operator==(std::vector) checks size first, then compares equality for each element. - bool operator==(const StatName& rhs) const { - const uint64_t sz = numBytes(); - return sz == rhs.numBytes() && memcmp(data(), rhs.data(), sz * sizeof(uint8_t)) == 0; - } - bool operator!=(const StatName& rhs) const { return !(*this == rhs); } - -protected: - friend SymbolTable; - friend class StatNameTest; - friend class StatNameJoiner; - - /** - * @return uint64_t the number of bytes in the symbol array, excluding the two-byte - * overhead for the size itself. - */ - uint64_t numBytes() const { - return symbol_array_[0] | (static_cast(symbol_array_[1]) << 8); - } - - /** - * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). - */ - const uint8_t* data() const { return symbol_array_ + 2; } - - const uint8_t* symbol_array_; -}; - -StatName StatNameStorage::statName() const { return StatName(bytes_.get()); } - /** * 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 @@ -368,7 +326,7 @@ struct StatNameCompare { // Value-templatized hash-map with StatName key. template -using StatNameHashMap = std::unordered_map; +using StatNameHashMap = absl::flat_hash_map; // Helper class for sorting StatNames. struct StatNameLessThan { diff --git a/test/common/stats/heap_stat_data_test.cc b/test/common/stats/heap_stat_data_test.cc index bb22ed394522..7270939d6d84 100644 --- a/test/common/stats/heap_stat_data_test.cc +++ b/test/common/stats/heap_stat_data_test.cc @@ -2,6 +2,7 @@ #include "common/stats/heap_stat_data.h" #include "common/stats/stats_options_impl.h" +#include "common/stats/symbol_table_impl.h" #include "test/test_common/logging.h" @@ -10,32 +11,60 @@ namespace Envoy { namespace Stats { +class HeapStatDataTest : public testing::Test { + protected: + HeapStatDataTest() : alloc_(symbol_table_) {} + ~HeapStatDataTest() { clearStorage(); } + + StatNameStorage makeStatStorage(absl::string_view name) { + return StatNameStorage(name, symbol_table_); + } + + StatName makeStat(absl::string_view name) { + stat_name_storage_.emplace_back(makeStatStorage(name)); + return stat_name_storage_.back().statName(); + } + + void clearStorage() { + for (auto& stat_name_storage : stat_name_storage_) { + stat_name_storage.free(symbol_table_); + } + stat_name_storage_.clear(); + EXPECT_EQ(0, symbol_table_.numSymbols()); + } + + + + SymbolTable symbol_table_; + HeapStatDataAllocator alloc_; + std::vector stat_name_storage_; +}; + // No truncation occurs in the implementation of HeapStatData. -TEST(HeapStatDataTest, HeapNoTruncate) { +TEST_F(HeapStatDataTest, HeapNoTruncate) { StatsOptionsImpl stats_options; - HeapStatDataAllocator alloc; const std::string long_string(stats_options.maxNameLength() + 1, 'A'); + StatName stat_name = makeStat(long_string); HeapStatData* stat{}; - EXPECT_NO_LOGS(stat = alloc.alloc(long_string)); - EXPECT_EQ(stat->key(), long_string); - alloc.free(*stat); -} + EXPECT_NO_LOGS(stat = &alloc_.alloc(stat_name)); + EXPECT_EQ(stat->statName(), stat_name); + alloc_.free(*stat); +}; // Note: a similar test using RawStatData* is in test/server/hot_restart_impl_test.cc. -TEST(HeapStatDataTest, HeapAlloc) { - HeapStatDataAllocator alloc; - HeapStatData* stat_1 = alloc.alloc("ref_name"); +TEST_F(HeapStatDataTest, HeapAlloc) { + HeapStatData* stat_1 = &alloc_.alloc(makeStat("ref_name")); ASSERT_NE(stat_1, nullptr); - HeapStatData* stat_2 = alloc.alloc("ref_name"); + HeapStatData* stat_2 = &alloc_.alloc(makeStat("ref_name")); ASSERT_NE(stat_2, nullptr); - HeapStatData* stat_3 = alloc.alloc("not_ref_name"); + HeapStatData* stat_3 = &alloc_.alloc(makeStat("not_ref_name")); ASSERT_NE(stat_3, nullptr); EXPECT_EQ(stat_1, stat_2); EXPECT_NE(stat_1, stat_3); EXPECT_NE(stat_2, stat_3); - alloc.free(*stat_1); - alloc.free(*stat_2); - alloc.free(*stat_3); + alloc_.free(*stat_1); + alloc_.free(*stat_2); + alloc_.free(*stat_3); } } // namespace Stats diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index 002d315ac023..a71ecdfe0711 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -341,7 +341,8 @@ bool TestHeaderMapImpl::has(const LowerCaseString& key) { return get(key) != nul namespace Stats { MockedTestAllocator::MockedTestAllocator(const StatsOptions& stats_options) - : alloc_(stats_options) { + : RawStatDataAllocator(symbol_table_), + alloc_(stats_options) { ON_CALL(*this, alloc(_)).WillByDefault(Invoke([this](absl::string_view name) -> RawStatData* { return alloc_.alloc(name); })); diff --git a/test/test_common/utility.h b/test/test_common/utility.h index dc528663bc84..620a10469cd8 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -406,7 +406,9 @@ namespace Stats { */ class TestAllocator : public RawStatDataAllocator { public: - TestAllocator(const StatsOptions& stats_options) : stats_options_(stats_options) {} + TestAllocator(const StatsOptions& stats_options) + : RawStatDataAllocator(symbol_table_), + stats_options_(stats_options) {} ~TestAllocator() { EXPECT_TRUE(stats_.empty()); } RawStatData* alloc(absl::string_view name) override { @@ -435,6 +437,7 @@ class TestAllocator : public RawStatDataAllocator { private: static void freeAdapter(RawStatData* data) { ::free(data); } std::unordered_map> stats_; + SymbolTable symbol_table_; const StatsOptions& stats_options_; }; @@ -446,6 +449,7 @@ class MockedTestAllocator : public RawStatDataAllocator { MOCK_METHOD1(alloc, RawStatData*(absl::string_view name)); MOCK_METHOD1(free, void(RawStatData& data)); + SymbolTable symbol_table_; TestAllocator alloc_; }; From d62f533150e49a3c4099e7c0ae8ef53937f8deb6 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 6 Nov 2018 20:23:35 -0500 Subject: [PATCH 003/106] all tests working; but still taking locks in hot-path. Signed-off-by: Joshua Marantz --- include/envoy/stats/scope.h | 11 +- include/envoy/stats/stats.h | 5 +- include/envoy/stats/symbol_table.h | 8 +- source/common/http/user_agent.cc | 3 +- source/common/stats/BUILD | 14 ++ source/common/stats/heap_stat_data.cc | 14 +- source/common/stats/heap_stat_data.h | 22 +- source/common/stats/histogram_impl.h | 26 ++- source/common/stats/isolated_store_impl.cc | 40 +--- source/common/stats/isolated_store_impl.h | 35 ++- source/common/stats/metric_impl.cc | 94 ++++++++ source/common/stats/metric_impl.h | 21 +- source/common/stats/raw_stat_data.cc | 3 +- source/common/stats/raw_stat_data.h | 20 +- source/common/stats/scope_prefixer.cc | 58 +++++ source/common/stats/scope_prefixer.h | 52 +++++ .../common/stats/stat_data_allocator_impl.h | 47 ++-- source/common/stats/symbol_table_impl.cc | 48 ++++- source/common/stats/symbol_table_impl.h | 88 ++++++-- source/common/stats/thread_local_store.cc | 202 ++++++++++++------ source/common/stats/thread_local_store.h | 97 ++++++--- source/common/stats/utility.cc | 12 +- source/common/stats/utility.h | 4 +- source/common/upstream/upstream_impl.h | 5 +- source/exe/main_common.cc | 6 +- source/exe/main_common.h | 2 +- .../extensions/stat_sinks/hystrix/hystrix.cc | 3 +- source/server/hot_restart_impl.cc | 5 +- source/server/hot_restart_impl.h | 2 +- source/server/hot_restart_nop_impl.h | 2 +- test/common/http/user_agent_test.cc | 2 +- test/common/stats/heap_stat_data_test.cc | 4 +- test/common/stats/source_impl_test.cc | 13 +- .../stats/thread_local_store_speed_test.cc | 3 +- test/common/stats/thread_local_store_test.cc | 3 + test/common/tcp_proxy/tcp_proxy_test.cc | 6 +- .../upstream/resource_manager_impl_test.cc | 2 +- .../common/statsd/udp_statsd_test.cc | 4 + .../stats_sinks/hystrix/hystrix_test.cc | 14 +- test/integration/server.cc | 5 +- test/integration/server.h | 41 +++- test/mocks/server/mocks.cc | 2 +- test/mocks/server/mocks.h | 1 + test/mocks/stats/mocks.cc | 65 ++++-- test/mocks/stats/mocks.h | 99 +++++---- test/mocks/upstream/host.h | 1 + test/server/hot_restart_impl_test.cc | 5 +- test/server/http/admin_test.cc | 12 +- test/test_common/utility.cc | 3 +- test/test_common/utility.h | 3 +- 50 files changed, 879 insertions(+), 358 deletions(-) create mode 100644 source/common/stats/metric_impl.cc create mode 100644 source/common/stats/scope_prefixer.cc create mode 100644 source/common/stats/scope_prefixer.h diff --git a/include/envoy/stats/scope.h b/include/envoy/stats/scope.h index ef913edea6e0..70be7f9222a0 100644 --- a/include/envoy/stats/scope.h +++ b/include/envoy/stats/scope.h @@ -2,11 +2,11 @@ #include #include -#include #include "envoy/common/pure.h" #include "envoy/stats/histogram.h" #include "envoy/stats/stats_options.h" +#include "envoy/stats/symbol_table.h" namespace Envoy { namespace Stats { @@ -44,16 +44,19 @@ class Scope { /** * @return a counter within the scope's namespace. */ + virtual Counter& counterx(StatName name) PURE; virtual Counter& counter(const std::string& name) PURE; /** * @return a gauge within the scope's namespace. */ + virtual Gauge& gaugex(StatName name) PURE; virtual Gauge& gauge(const std::string& name) PURE; /** * @return a histogram within the scope's namespace with a particular value type. */ + virtual Histogram& histogramx(StatName name) PURE; virtual Histogram& histogram(const std::string& name) PURE; /** @@ -61,6 +64,12 @@ class Scope { * maximum allowable object name length and stat suffix length. */ virtual const Stats::StatsOptions& statsOptions() const PURE; + + /** + * @return a reference to the symbol table. + */ + virtual const SymbolTable& symbolTable() const PURE; + virtual SymbolTable& symbolTable() PURE; }; } // namespace Stats diff --git a/include/envoy/stats/stats.h b/include/envoy/stats/stats.h index 4f70ff8e52d6..bb144fc47d62 100644 --- a/include/envoy/stats/stats.h +++ b/include/envoy/stats/stats.h @@ -7,6 +7,7 @@ #include "envoy/common/pure.h" #include "envoy/stats/symbol_table.h" + #include "absl/strings/string_view.h" namespace Envoy { @@ -53,8 +54,8 @@ class Metric { */ virtual bool used() const PURE; - virtual StatDataAllocator& allocator() PURE; - virtual const StatDataAllocator& allocator() const PURE; + virtual SymbolTable& symbolTable() PURE; + virtual const SymbolTable& symbolTable() const PURE; }; /** diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 3b0be37792b0..ceae58a366c5 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -74,6 +74,10 @@ class StatName { memcpy(storage, symbol_array_, numBytesIncludingLength()); } +#ifndef ENVOY_CONFIG_COVERAGE + void debugPrint(); +#endif + protected: friend SymbolTable; friend class StatNameTest; @@ -83,9 +87,7 @@ class StatName { /** * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). */ - const uint8_t* data() const { - return symbol_array_ + 2; - } + const uint8_t* data() const { return symbol_array_ + 2; } const uint8_t* symbol_array_; }; diff --git a/source/common/http/user_agent.cc b/source/common/http/user_agent.cc index 842e7a642b20..1e47ba500a6d 100644 --- a/source/common/http/user_agent.cc +++ b/source/common/http/user_agent.cc @@ -20,7 +20,8 @@ void UserAgent::completeConnectionLength(Stats::Timespan& span) { return; } - scope_->histogram(prefix_ + "downstream_cx_length_ms").recordValue(span.getRawDuration().count()); + Stats::Histogram& histogram = scope_->histogram(prefix_ + "downstream_cx_length_ms"); + histogram.recordValue(span.getRawDuration().count()); } void UserAgent::initializeFromHeaders(const HeaderMap& headers, const std::string& prefix, diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 616b584271c8..3f4909cd4678 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -44,6 +44,7 @@ envoy_cc_library( hdrs = ["isolated_store_impl.h"], deps = [ ":histogram_lib", + ":scope_prefixer_lib", ":stats_lib", ":stats_options_lib", "//include/envoy/stats:stats_macros", @@ -74,6 +75,17 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "scope_prefixer_lib", + srcs = ["scope_prefixer.cc"], + hdrs = ["scope_prefixer.h"], + deps = [ + ":symbol_table_lib", + ":utility_lib", + "//include/envoy/stats:stats_interface", + ], +) + envoy_cc_library( name = "source_impl_lib", srcs = ["source_impl.cc"], @@ -185,6 +197,7 @@ envoy_cc_library( hdrs = ["thread_local_store.h"], deps = [ ":heap_stat_data_lib", + ":scope_prefixer_lib", ":stats_lib", ":stats_matcher_lib", ":tag_producer_lib", @@ -196,4 +209,5 @@ envoy_cc_library( name = "utility_lib", srcs = ["utility.cc"], hdrs = ["utility.h"], + deps = [":symbol_table_lib"], ) diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index c44eebfa3e76..33ddabf6fc55 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -7,11 +7,9 @@ namespace Envoy { namespace Stats { -HeapStatDataAllocator::~HeapStatDataAllocator() { - ASSERT(stats_.empty()); -} +HeapStatDataAllocator::~HeapStatDataAllocator() { ASSERT(stats_.empty()); } -HeapStatData* HeapStatData::alloc(StatName stat_name, SymbolTable& symbol_table) { +HeapStatData* HeapStatData::alloc(StatName stat_name, SymbolTable& symbol_table) { void* memory = ::malloc(sizeof(HeapStatData) + stat_name.numBytesIncludingLength()); ASSERT(memory); symbol_table.incRefCount(stat_name); @@ -24,19 +22,17 @@ void HeapStatData::free(SymbolTable& symbol_table) { ::free(this); // matches malloc() call above. } - HeapStatData& HeapStatDataAllocator::alloc(StatName name) { std::unique_ptr> data_ptr( - HeapStatData::alloc(name, symbolTable()), [this](HeapStatData* d) { - d->free(symbolTable()); - }); + HeapStatData::alloc(name, symbolTable()), + [this](HeapStatData* d) { d->free(symbolTable()); }); Thread::ReleasableLockGuard lock(mutex_); auto ret = stats_.insert(data_ptr.get()); HeapStatData* existing_data = *ret.first; lock.release(); if (ret.second) { - //symbolTable().incRefCount(existing_data->statName()); + // symbolTable().incRefCount(existing_data->statName()); return *data_ptr.release(); } ++existing_data->ref_count_; diff --git a/source/common/stats/heap_stat_data.h b/source/common/stats/heap_stat_data.h index 30006c7a5a28..016826946d40 100644 --- a/source/common/stats/heap_stat_data.h +++ b/source/common/stats/heap_stat_data.h @@ -4,6 +4,8 @@ #include #include +#include "envoy/stats/stats.h" + #include "common/common/hash.h" #include "common/common/thread.h" #include "common/common/thread_annotations.h" @@ -11,8 +13,6 @@ #include "common/stats/stat_data_allocator_impl.h" #include "common/stats/symbol_table_impl.h" -#include "envoy/stats/stats.h" - #include "absl/container/flat_hash_set.h" namespace Envoy { @@ -23,10 +23,10 @@ namespace Stats { * so that it can be allocated efficiently from the heap on demand. */ struct HeapStatData { - private: +private: explicit HeapStatData(StatName stat_name) { stat_name.copyToStorage(symbol_storage_); } - public: +public: static HeapStatData* alloc(StatName stat_name, SymbolTable& symbol_table); void free(SymbolTable& symbol_table); @@ -42,8 +42,7 @@ struct HeapStatData { SymbolStorage symbol_storage_; }; -template -class HeapStat : public Stat { +template class HeapStat : public Stat { public: HeapStat(HeapStatData& data, StatDataAllocatorImpl& alloc, absl::string_view tag_extracted_name, const std::vector& tags) @@ -78,18 +77,17 @@ class HeapStatDataAllocator : public StatDataAllocatorImpl { CounterSharedPtr makeCounter(StatName name, absl::string_view tag_extracted_name, const std::vector& tags) override { - return std::make_shared>>( - alloc(name), *this, tag_extracted_name, tags); + return std::make_shared>>(alloc(name), *this, + tag_extracted_name, tags); } GaugeSharedPtr makeGauge(StatName name, absl::string_view tag_extracted_name, const std::vector& tags) override { - return std::make_shared>>( - alloc(name), *this, tag_extracted_name, tags); + return std::make_shared>>(alloc(name), *this, + tag_extracted_name, tags); } - - private: +private: struct HeapStatHash { size_t operator()(const HeapStatData* a) const { return a->hash(); } }; diff --git a/source/common/stats/histogram_impl.h b/source/common/stats/histogram_impl.h index dd8a71dc9cc3..1a9928b0de8a 100644 --- a/source/common/stats/histogram_impl.h +++ b/source/common/stats/histogram_impl.h @@ -45,16 +45,24 @@ class HistogramStatisticsImpl : public HistogramStatistics, NonCopyable { class HistogramImpl : public Histogram, public MetricImpl { public: HistogramImpl(StatName name, Store& parent, const std::string& tag_extracted_name, - const std::vector& tags, StatDataAllocator& alloc) - : MetricImpl(tag_extracted_name, tags, alloc.symbolTable()), name_(name), parent_(parent) {} + const std::vector& tags) + : MetricImpl(tag_extracted_name, tags, parent.symbolTable()), + name_(name, parent.symbolTable()), parent_(parent) {} + ~HistogramImpl() { + name_.free(symbolTable()); + MetricImpl::clear(); + } // Stats::Histogram void recordValue(uint64_t value) override { parent_.deliverHistogramToSinks(*this, value); } bool used() const override { return true; } + StatName statName() const override { return name_.statName(); } + const SymbolTable& symbolTable() const override { return parent_.symbolTable(); } + SymbolTable& symbolTable() override { return parent_.symbolTable(); } private: - StatName name_; + StatNameStorage name_; // This is used for delivering the histogram data to sinks. Store& parent_; @@ -64,16 +72,12 @@ class HistogramImpl : public Histogram, public MetricImpl { * Null histogram implementation. * No-ops on all calls and requires no underlying metric or data. */ -class NullHistogramImpl : public Histogram { +class NullHistogramImpl : public Histogram, NullMetricImpl { public: - NullHistogramImpl() {} - ~NullHistogramImpl() {} - std::string name() const override { return ""; } - StatName statName() const override { return StatName(); } - std::string tagExtractedName() const override { return ""; } - std::vector tags() const override { return std::vector(); } + explicit NullHistogramImpl(SymbolTable& symbol_table): NullMetricImpl(symbol_table) {} + ~NullHistogramImpl() { MetricImpl::clear(); } + void recordValue(uint64_t) override {} - bool used() const override { return false; } }; } // namespace Stats diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 6bf2d309d702..31b7adbc0967 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -7,46 +7,28 @@ #include "common/common/utility.h" #include "common/stats/histogram_impl.h" +#include "common/stats/scope_prefixer.h" #include "common/stats/utility.h" namespace Envoy { namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() - : counters_([this](const std::string& name) -> CounterSharedPtr { - return alloc_.makeCounter(name, "", std::vector()); + : alloc_(symbol_table_), + counters_([this](StatName name) -> CounterSharedPtr { + return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), + std::vector()); }), - gauges_([this](const std::string& name) -> GaugeSharedPtr { - std::string tag_extracted_name = name; - std::vector tags; - return alloc_.makeGauge(name, "", std::vector()); + gauges_([this](StatName name) -> GaugeSharedPtr { + return alloc_.makeGauge(name, name.toString(alloc_.symbolTable()), std::vector()); }), - histograms_([this](const std::string& name) -> HistogramSharedPtr { - return std::make_shared(name, *this, std::string(name), std::vector()); + histograms_([this](StatName name) -> HistogramSharedPtr { + return std::make_shared( + name, *this, name.toString(alloc_.symbolTable()), std::vector()); }) {} -struct IsolatedScopeImpl : public Scope { - IsolatedScopeImpl(IsolatedStoreImpl& parent, const std::string& prefix) - : parent_(parent), prefix_(Utility::sanitizeStatsName(prefix)) {} - - // Stats::Scope - ScopePtr createScope(const std::string& name) override { - return ScopePtr{new IsolatedScopeImpl(parent_, prefix_ + name)}; - } - void deliverHistogramToSinks(const Histogram&, uint64_t) override {} - Counter& counter(const std::string& name) override { return parent_.counter(prefix_ + name); } - Gauge& gauge(const std::string& name) override { return parent_.gauge(prefix_ + name); } - Histogram& histogram(const std::string& name) override { - return parent_.histogram(prefix_ + name); - } - const Stats::StatsOptions& statsOptions() const override { return parent_.statsOptions(); } - - IsolatedStoreImpl& parent_; - const std::string prefix_; -}; - ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { - return ScopePtr{new IsolatedScopeImpl(*this, name)}; + return std::make_unique(name, *this); } } // namespace Stats diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 7765fd50e3e7..8af11350bc6c 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -12,8 +12,11 @@ #include "common/common/utility.h" #include "common/stats/heap_stat_data.h" #include "common/stats/stats_options_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/utility.h" +#include "absl/container/flat_hash_map.h" + namespace Envoy { namespace Stats { @@ -22,18 +25,18 @@ namespace Stats { */ template class IsolatedStatsCache { public: - typedef std::function(const std::string& name)> Allocator; + using Allocator = std::function(StatName name)>; IsolatedStatsCache(Allocator alloc) : alloc_(alloc) {} - Base& get(const std::string& name) { + Base& get(StatName name) { auto stat = stats_.find(name); if (stat != stats_.end()) { return *stat->second; } std::shared_ptr new_stat = alloc_(name); - stats_.emplace(name, new_stat); + stats_.emplace(new_stat->statName(), new_stat); return *new_stat; } @@ -48,7 +51,7 @@ template class IsolatedStatsCache { } private: - std::unordered_map> stats_; + StatNameHashMap> stats_; Allocator alloc_; }; @@ -57,15 +60,17 @@ class IsolatedStoreImpl : public Store { IsolatedStoreImpl(); // Stats::Scope - Counter& counter(const std::string& name) override { return counters_.get(name); } + Counter& counterx(StatName name) override { return counters_.get(name); } ScopePtr createScope(const std::string& name) override; void deliverHistogramToSinks(const Histogram&, uint64_t) override {} - Gauge& gauge(const std::string& name) override { return gauges_.get(name); } - Histogram& histogram(const std::string& name) override { + Gauge& gaugex(StatName name) override { return gauges_.get(name); } + Histogram& histogramx(StatName name) override { Histogram& histogram = histograms_.get(name); return histogram; } const Stats::StatsOptions& statsOptions() const override { return stats_options_; } + const SymbolTable& symbolTable() const override { return symbol_table_; } + virtual SymbolTable& symbolTable() override { return symbol_table_; } // Stats::Store std::vector counters() const override { return counters_.toVector(); } @@ -74,7 +79,23 @@ class IsolatedStoreImpl : public Store { return std::vector{}; } + Counter& counter(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return counterx(storage.statName()); + } + Gauge& gauge(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return gaugex(storage.statName()); + } + Histogram& histogram(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return histogramx(storage.statName()); + } + + + private: + SymbolTable symbol_table_; HeapStatDataAllocator alloc_; IsolatedStatsCache counters_; IsolatedStatsCache gauges_; diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc new file mode 100644 index 000000000000..8681097e6602 --- /dev/null +++ b/source/common/stats/metric_impl.cc @@ -0,0 +1,94 @@ +#include "common/stats/metric_impl.h" + +#include "envoy/stats/tag.h" + +#include "common/stats/symbol_table_impl.h" + +namespace Envoy { +namespace Stats { + +MetricImpl::MetricImpl(absl::string_view tag_extracted_name, const std::vector& tags, + SymbolTable& symbol_table) { + ASSERT(tags.size() < 256); + + // Encode all the names and tags into transient storage so we can count the + // required bytes. + SymbolEncoding tag_extracted_stat_name = symbol_table.encode(tag_extracted_name); + size_t total_size = 1 /* for tags.size() */ + tag_extracted_stat_name.bytesRequired(); + std::vector> sym_vecs; + sym_vecs.resize(tags.size()); + int index = 0; + for (auto tag : tags) { + auto& vecs = sym_vecs[index++]; + SymbolEncoding x = symbol_table.encode(tag.name_); + vecs.first.swap(x); + SymbolEncoding y = symbol_table.encode(tag.value_); + vecs.second.swap(y); + total_size += vecs.first.bytesRequired() + vecs.second.bytesRequired(); + } + storage_.reset(new uint8_t[total_size]); + uint8_t* p = &storage_[0]; + *p++ = tags.size(); + p += tag_extracted_stat_name.moveToStorage(p); + for (auto& sym_vec_pair : sym_vecs) { + p += sym_vec_pair.first.moveToStorage(p); + p += sym_vec_pair.second.moveToStorage(p); + } + ASSERT(p == &storage_[0] + total_size); +} + +void MetricImpl::clear() { + SymbolTable& symbol_table = symbolTable(); + uint8_t* p = &storage_[0]; + uint32_t num_tags = *p++; + p += tagExtractedStatName().numBytesIncludingLength(); + symbol_table.free(tagExtractedStatName()); + for (size_t i = 0; i < num_tags; ++i) { + Tag tag; + StatName name(p); + p += name.numBytesIncludingLength(); + symbol_table.free(name); + StatName value(p); + p += value.numBytesIncludingLength(); + symbol_table.free(value); + } + storage_.reset(); +} + +MetricImpl::~MetricImpl() { + // The storage must be cleaned by a subclass of MetricImpl in its + // destructor, because the symbol-table is owned by the subclass. + // Simply call MetricImpl::clear() in the subclass dtor. + ASSERT(storage_ == nullptr); +} + +std::string MetricImpl::tagExtractedName() const { + return tagExtractedStatName().toString(symbolTable()); +} + +StatName MetricImpl::tagExtractedStatName() const { return StatName(&storage_[1]); } + +std::vector MetricImpl::tags() const { + uint8_t* p = &storage_[0]; + uint32_t num_tags = *p++; + p += tagExtractedStatName().numBytesIncludingLength(); + + std::vector tags; + tags.reserve(num_tags); + const SymbolTable& symbol_table = symbolTable(); + + for (size_t i = 0; i < num_tags; ++i) { + Tag tag; + StatName name(p); + tag.name_ = name.toString(symbol_table); + p += name.numBytesIncludingLength(); + StatName value(p); + tag.value_ = value.toString(symbol_table); + p += value.numBytesIncludingLength(); + tags.emplace_back(tag); + } + return tags; +} + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index 95d02b6ce59b..3c9652c7ba53 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -38,13 +38,30 @@ class MetricImpl : public virtual Metric { static const uint8_t Used = 0x1; }; + void clear(); + private: StatName tagExtractedStatName() const; - const SymbolTable& symbolTable() const { return allocator().symbolTable(); } - SymbolTable& symbolTable() { return allocator().symbolTable(); } std::unique_ptr storage_; }; +class NullMetricImpl : public MetricImpl { + public: + explicit NullMetricImpl(SymbolTable& symbol_table) : + MetricImpl("", std::vector(), symbol_table), symbol_table_(symbol_table), + stat_name_storage_("", symbol_table) {} + ~NullMetricImpl() { stat_name_storage_.free(symbol_table_); } + + const SymbolTable& symbolTable() const override { return symbol_table_; } + SymbolTable& symbolTable() override { return symbol_table_; } + bool used() const override { return false; } + StatName statName() const override { return stat_name_storage_.statName(); } + + private: + SymbolTable& symbol_table_; + StatNameStorage stat_name_storage_; +}; + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/raw_stat_data.cc b/source/common/stats/raw_stat_data.cc index 99342301a5b6..5139e7499a14 100644 --- a/source/common/stats/raw_stat_data.cc +++ b/source/common/stats/raw_stat_data.cc @@ -22,8 +22,7 @@ uint64_t roundUpMultipleNaturalAlignment(uint64_t val) { } // namespace -RawStatDataAllocator::~RawStatDataAllocator() { -} +RawStatDataAllocator::~RawStatDataAllocator() {} // Normally the compiler would do this, but because name_ is a flexible-array-length // element, the compiler can't. RawStatData is put into an array in HotRestartImpl, so diff --git a/source/common/stats/raw_stat_data.h b/source/common/stats/raw_stat_data.h index 06a93e3d27eb..e40df8b934e9 100644 --- a/source/common/stats/raw_stat_data.h +++ b/source/common/stats/raw_stat_data.h @@ -90,17 +90,17 @@ struct RawStatData { char name_[]; }; -template -class RawStat : public Stat { +template class RawStat : public Stat { public: RawStat(StatName stat_name, RawStatData& data, StatDataAllocatorImpl& alloc, - absl::string_view tag_extracted_name, const std::vector& tags) + absl::string_view tag_extracted_name, const std::vector& tags) : Stat(data, alloc, tag_extracted_name, tags), stat_name_storage_(stat_name, alloc.symbolTable()) {} + ~RawStat() { stat_name_storage_.free(this->symbolTable()); } StatName statName() const override { return stat_name_storage_.statName(); } - private: +private: StatNameStorage stat_name_storage_; }; @@ -110,19 +110,21 @@ class RawStatDataAllocator : public StatDataAllocatorImpl { ~RawStatDataAllocator(); virtual RawStatData* alloc(absl::string_view name) PURE; - RawStatData* allocStatName(StatName stat_name) { return alloc(stat_name.toString(symbolTable())); } + RawStatData* allocStatName(StatName stat_name) { + return alloc(stat_name.toString(symbolTable())); + } // StatDataAllocator bool requiresBoundedStatNameSize() const override { return true; } - template std::shared_ptr makeStat( - StatName name, absl::string_view tag_extracted_name, const std::vector& tags) { + template + std::shared_ptr makeStat(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) { RawStatData* raw_stat_data = allocStatName(name); if (raw_stat_data == nullptr) { return nullptr; } - return std::make_shared>( - name, *raw_stat_data, *this, tag_extracted_name, tags); + return std::make_shared>(name, *raw_stat_data, *this, tag_extracted_name, tags); } CounterSharedPtr makeCounter(StatName name, absl::string_view tag_extracted_name, diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc new file mode 100644 index 000000000000..874fc11300bb --- /dev/null +++ b/source/common/stats/scope_prefixer.cc @@ -0,0 +1,58 @@ +#include "common/stats/scope_prefixer.h" + +#include "common/stats/symbol_table_impl.h" +#include "common/stats/utility.h" +#include "envoy/stats/scope.h" + +namespace Envoy { +namespace Stats { + +// Variant of ScopePrefixer that owns the scope being prefixed, and will +// delete it upon destruction. +ScopePrefixer::ScopePrefixer(absl::string_view prefix, Scope* scope) + : prefix_(Utility::sanitizeStatsName(prefix), scope->symbolTable()), + scope_(scope), + owns_scope_(true) {} + +// Variant of ScopePrefixer that references the scope being prefixed, but does +// not own it. +ScopePrefixer::ScopePrefixer(absl::string_view prefix, Scope& scope) + : prefix_(Utility::sanitizeStatsName(prefix), scope.symbolTable()), + scope_(&scope), + owns_scope_(false) {} + +ScopePrefixer::~ScopePrefixer() { + prefix_.free(scope_->symbolTable()); + if (owns_scope_) { + delete scope_; + } +} + +ScopePtr ScopePrefixer::createScope(const std::string& name) { + //StatNameStorage scope_stat_name(name, symbolTable()); // Takes a lock. + //StatNameStorage joiner(prefix_.statName(), scope_stat_name.statName()); + return std::make_unique(prefix_.statName().toString(symbolTable()) + "." + name, + *scope_); +} + +Counter& ScopePrefixer::counterx(StatName name) { + StatNameJoiner joiner(prefix_.statName(), name); + return scope_->counterx(joiner.statName()); +} + +Gauge& ScopePrefixer::gaugex(StatName name) { + StatNameJoiner joiner(prefix_.statName(), name); + return scope_->gaugex(joiner.statName()); +} + +Histogram& ScopePrefixer::histogramx(StatName name) { + StatNameJoiner joiner(prefix_.statName(), name); + return scope_->histogramx(joiner.statName()); +} + +void ScopePrefixer::deliverHistogramToSinks(const Histogram& histograms, uint64_t val) { + scope_->deliverHistogramToSinks(histograms, val); +} + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h new file mode 100644 index 000000000000..6288d18d0a22 --- /dev/null +++ b/source/common/stats/scope_prefixer.h @@ -0,0 +1,52 @@ +#include "common/stats/symbol_table_impl.h" +#include "envoy/stats/scope.h" + +namespace Envoy { +namespace Stats { + +// Implements a Scope that delegates to a passed-in scope, prefixing all names +// prior to creation. +class ScopePrefixer : public Scope { + public: + // Variant of ScopePrefixer that owns the scope being prefixed, and will + // delete it upon destruction. + ScopePrefixer(absl::string_view prefix, Scope* scope); + + // Variant of ScopePrefixer that references the scope being prefixed, but does + // not own it. + ScopePrefixer(absl::string_view prefix, Scope& scope); + + virtual ~ScopePrefixer(); + + // Scope + ScopePtr createScope(const std::string& name) override; + Counter& counterx(StatName name) override; + Gauge& gaugex(StatName name) override; + Histogram& histogramx(StatName name) override; + void deliverHistogramToSinks(const Histogram& histograms, uint64_t val) override; + + Counter& counter(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return counterx(storage.statName()); + } + Gauge& gauge(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return gaugex(storage.statName()); + } + Histogram& histogram(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return histogramx(storage.statName()); + } + + const Stats::StatsOptions& statsOptions() const override { return scope_->statsOptions(); } + const SymbolTable& symbolTable() const override { return scope_->symbolTable(); } + virtual SymbolTable& symbolTable() override { return scope_->symbolTable(); } + + private: + StatNameStorage prefix_; + Scope* scope_; + const bool owns_scope_; +}; + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/stat_data_allocator_impl.h b/source/common/stats/stat_data_allocator_impl.h index afcf644a2e12..da0b0b3da590 100644 --- a/source/common/stats/stat_data_allocator_impl.h +++ b/source/common/stats/stat_data_allocator_impl.h @@ -38,7 +38,7 @@ template class StatDataAllocatorImpl : public StatDataAllocator * by name if one already exists with the same name. This is used for intra-process * scope swapping as well as inter-process hot restart. */ - //virtual StatData* alloc(StatName name) PURE; + // virtual StatData* alloc(StatName name) PURE; /** * Free a raw stat data block. The allocator should handle reference counting and only truly @@ -50,7 +50,7 @@ template class StatDataAllocatorImpl : public StatDataAllocator SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& symbolTable() const override { return symbol_table_; } - private: +private: // SymbolTable encodes encodes stat names as back into strings. This does not // get guarded by a mutex, since it has its own internal mutex to guarantee // thread safety. @@ -69,7 +69,10 @@ template class CounterImpl : public Counter, public MetricImpl CounterImpl(StatData& data, StatDataAllocatorImpl& alloc, absl::string_view tag_extracted_name, const std::vector& tags) : MetricImpl(tag_extracted_name, tags, alloc.symbolTable()), data_(data), alloc_(alloc) {} - ~CounterImpl() { alloc_.free(data_); } + ~CounterImpl() { + alloc_.free(data_); + MetricImpl::clear(); + } // Stats::Counter void add(uint64_t amount) override { @@ -84,8 +87,8 @@ template class CounterImpl : public Counter, public MetricImpl bool used() const override { return data_.flags_ & Flags::Used; } uint64_t value() const override { return data_.value_; } - StatDataAllocator& allocator() override { return alloc_; } - const StatDataAllocator& allocator() const override { return alloc_; } + const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } + SymbolTable& symbolTable() override { return alloc_.symbolTable(); } protected: StatData& data_; @@ -96,22 +99,15 @@ template class CounterImpl : public Counter, public MetricImpl * Null counter implementation. * No-ops on all calls and requires no underlying metric or data. */ -class NullCounterImpl : public Counter { +class NullCounterImpl : public Counter, NullMetricImpl { public: - NullCounterImpl() {} - ~NullCounterImpl() {} - - // Stats::Metric - std::string name() const override { return ""; } - StatName statName() const override { return StatName(); } + explicit NullCounterImpl(SymbolTable& symbol_table): NullMetricImpl(symbol_table) {} + ~NullCounterImpl() { MetricImpl::clear(); } - std::string tagExtractedName() const override { return ""; } - std::vector tags() const override { return std::vector(); } void add(uint64_t) override {} void inc() override {} uint64_t latch() override { return 0; } void reset() override {} - bool used() const override { return false; } uint64_t value() const override { return 0; } }; @@ -123,7 +119,10 @@ template class GaugeImpl : public Gauge, public MetricImpl { GaugeImpl(StatData& data, StatDataAllocatorImpl& alloc, absl::string_view tag_extracted_name, const std::vector& tags) : MetricImpl(tag_extracted_name, tags, alloc.symbolTable()), data_(data), alloc_(alloc) {} - ~GaugeImpl() { alloc_.free(data_); } + ~GaugeImpl() { + alloc_.free(data_); + MetricImpl::clear(); + } // Stats::Gauge virtual void add(uint64_t amount) override { @@ -144,8 +143,8 @@ template class GaugeImpl : public Gauge, public MetricImpl { virtual uint64_t value() const override { return data_.value_; } bool used() const override { return data_.flags_ & Flags::Used; } - StatDataAllocator& allocator() override { return alloc_; } - const StatDataAllocator& allocator() const override { return alloc_; } + const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } + SymbolTable& symbolTable() override { return alloc_.symbolTable(); } protected: StatData& data_; @@ -156,20 +155,16 @@ template class GaugeImpl : public Gauge, public MetricImpl { * Null gauge implementation. * No-ops on all calls and requires no underlying metric or data. */ -class NullGaugeImpl : public Gauge { +class NullGaugeImpl : public Gauge, NullMetricImpl { public: - NullGaugeImpl() {} - ~NullGaugeImpl() {} - std::string name() const override { return ""; } - StatName statName() const override { return StatName(); } - std::string tagExtractedName() const override { return ""; } - std::vector tags() const override { return std::vector(); } + explicit NullGaugeImpl(SymbolTable& symbol_table): NullMetricImpl(symbol_table) {} + ~NullGaugeImpl() { MetricImpl::clear(); } + void add(uint64_t) override {} void inc() override {} void dec() override {} void set(uint64_t) override {} void sub(uint64_t) override {} - bool used() const override { return false; } uint64_t value() const override { return 0; } }; diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 615730ffcda7..3da1c4fc9faf 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -1,5 +1,6 @@ #include "common/stats/symbol_table_impl.h" +#include #include #include #include @@ -12,16 +13,35 @@ namespace Stats { static const uint32_t SpilloverMask = 0x80; static const uint32_t Low7Bits = 0x7f; -StatName::StatName(const StatName& src, SymbolStorage memory) - : symbol_array_(memory) { +StatName::StatName(const StatName& src, SymbolStorage memory) : symbol_array_(memory) { memcpy(memory, src.symbolArray(), src.numBytesIncludingLength()); - //src.symbol_array_ = nullptr; // transfers ownership. + // src.symbol_array_ = nullptr; // transfers ownership. } std::string StatName::toString(const SymbolTable& table) const { return table.decode(data(), numBytes()); } +#ifndef ENVOY_CONFIG_COVERAGE +void StatName::debugPrint() { + if (symbol_array_ == nullptr) { + std::cout << "Null StatName" << std::endl << std::flush; + } else { + uint64_t nbytes = numBytes(); + std::cout << "numBytes=" << nbytes << ":"; + for (uint64_t i = 0; i < nbytes; ++i) { + std::cout << " " << static_cast(data()[i]); + } + SymbolVec encoding = SymbolEncoding::decodeSymbols(data(), numBytes()); + std::cout << ", numSymbols=" << encoding.size() << ":"; + for (Symbol symbol : encoding) { + std::cout << " " << symbol; + } + std::cout << std::endl << std::flush; + } +} +#endif + SymbolEncoding::~SymbolEncoding() { ASSERT(vec_.empty()); } void SymbolEncoding::addSymbol(Symbol symbol) { @@ -228,16 +248,34 @@ bool SymbolTable::lessThan(const StatName& a, const StatName& b) const { return av.size() < bv.size(); } +#ifndef ENVOY_CONFIG_COVERAGE +void SymbolTable::debugPrint() const { + Thread::LockGuard lock(lock_); + std::vector symbols; + for (auto p : decode_map_) { + symbols.push_back(p.first); + } + std::sort(symbols.begin(), symbols.end()); + for (Symbol symbol : symbols) { + const std::string& token = decode_map_.find(symbol)->second; + const SharedSymbol& shared_symbol = encode_map_.find(token)->second; + std::cout << symbol << ": '" << token << "' (" << shared_symbol.ref_count_ << ")" << std::endl; + } + std::cout << std::flush; +} +#endif + StatNameStorage::StatNameStorage(absl::string_view name, SymbolTable& table) { SymbolEncoding encoding = table.encode(name); bytes_ = std::make_unique(encoding.bytesRequired()); encoding.moveToStorage(bytes_.get()); } -StatNameStorage::StatNameStorage(StatName src, SymbolTable& /*table*/) { +StatNameStorage::StatNameStorage(StatName src, SymbolTable& table) { uint64_t size = src.numBytesIncludingLength(); bytes_ = std::make_unique(size); - memcpy(bytes_.get(), src.data(), size); + src.copyToStorage(bytes_.get()); + table.incRefCount(statName()); } StatNameStorage::~StatNameStorage() { diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 4ed9f438ccb3..c5ec564f0712 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -83,6 +83,8 @@ class SymbolEncoding { */ uint64_t moveToStorage(SymbolStorage array); + void swap(SymbolEncoding& src) { vec_.swap(src.vec_); } + private: std::vector vec_; }; @@ -186,6 +188,10 @@ class SymbolTable { */ void incRefCount(StatName stat_name) { adjustRefCount(stat_name, 1); }; +#ifndef ENVOY_CONFIG_COVERAGE + void debugPrint() const; +#endif + private: friend class StatName; friend class StatNameTest; @@ -254,10 +260,49 @@ class SymbolTable { std::stack pool_ GUARDED_BY(lock_); }; +/** + * 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 elaborted form, and does not require locking the SymbolTable. + * + * The caveat is that this representation does not bump reference counts on + * for 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 acessingm the + * SymbolTable or, in particular, taking its lock. + */ +class StatNameJoiner { +public: + StatNameJoiner(StatName a, StatName b); + StatNameJoiner(const std::vector& stat_names); + + /** + * @return StatName a reference to the joined StatName. + */ + StatName statName() const { return StatName(bytes_.get()); } + +private: + uint8_t* alloc(uint64_t num_bytes); + + std::unique_ptr bytes_; +}; + /** * Holds backing storage for a StatName. Usage of this is not required, as some * applications may want to hold multiple StatName objects in one contiguous * uint8_t array, or embed the characters directly in another structure. + * + * This is intended for embedding in other data structures that have access + * to a SymbolTable. StatNameStorage::free(symbol_table) must be called prior + * to allowing the StatNameStorage object to be destructed, otherwise an assert + * will fire to guard against symbol-table leaks. + * + * Thus this class is inconvenient to directly use as temp storage for building + * a StatName from a string. Instead it should be used via StatNameTempStorage. */ class StatNameStorage { public: @@ -284,36 +329,30 @@ class StatNameStorage { */ StatName statName() const { return StatName(bytes_.get()); } + /* + template T append(StatName suffix, std::function f) { + StatNameStorage joiner(statName(), suffix); + f(joiner.statName()); + } + */ + private: std::unique_ptr bytes_; }; -/** - * 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 elaborted form, and does not require locking the SymbolTable. - * - * The caveat is that this representation does not bump reference counts on - * for the referenced Symbols in the SymbolTable, so it's only valid as long - * as the joined StatNames. - */ -class StatNameJoiner { -public: - StatNameJoiner(StatName a, StatName b); - StatNameJoiner(const std::vector& stat_names); - - /** - * @return StatName a reference to the joined StatName. - */ - StatName statName() const { return StatName(bytes_.get()); } - -private: - uint8_t* alloc(uint64_t num_bytes); +class StatNameTempStorage : public StatNameStorage { + public: + StatNameTempStorage(absl::string_view name, SymbolTable& table) + : StatNameStorage(name, table), symbol_table_(table) {} + StatNameTempStorage(StatName src, SymbolTable& table) + : StatNameStorage(src, table), symbol_table_(table) {} + ~StatNameTempStorage() { free(symbol_table_); } - std::unique_ptr bytes_; + private: + SymbolTable& symbol_table_; }; + // Helper class for constructing hash-tables with StatName keys. struct StatNameHash { size_t operator()(const StatName& a) const { return a.hash(); } @@ -328,6 +367,9 @@ struct StatNameCompare { template using StatNameHashMap = absl::flat_hash_map; +// Hash-set of StatNames +using StatNameHashSet = absl::flat_hash_set; + // Helper class for sorting StatNames. struct StatNameLessThan { StatNameLessThan(const SymbolTable& symbol_table) : symbol_table_(symbol_table) {} diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 000409d92c6e..543fe14cf544 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -13,6 +13,7 @@ #include "envoy/stats/stats_options.h" #include "common/common/lock_guard.h" +#include "common/stats/scope_prefixer.h" #include "common/stats/stats_matcher_impl.h" #include "common/stats/tag_producer_impl.h" @@ -26,18 +27,25 @@ ThreadLocalStoreImpl::ThreadLocalStoreImpl(const StatsOptions& stats_options, : stats_options_(stats_options), alloc_(alloc), default_scope_(createScope("")), tag_producer_(std::make_unique()), stats_matcher_(std::make_unique()), - num_last_resort_stats_(default_scope_->counter("stats.overflow")), source_(*this) {} + stats_overflow_("stats.overflow", alloc.symbolTable()), + num_last_resort_stats_(default_scope_->counterx(stats_overflow_.statName())), + heap_allocator_(alloc.symbolTable()), + source_(*this), + null_counter_(alloc.symbolTable()), + null_gauge_(alloc.symbolTable()), + null_histogram_(alloc.symbolTable()) {} ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(shutting_down_); default_scope_.reset(); ASSERT(scopes_.empty()); + stats_overflow_.free(symbolTable()); } std::vector ThreadLocalStoreImpl::counters() const { // Handle de-dup due to overlapping scopes. std::vector ret; - CharStarHashSet names; + StatNameHashSet names; Thread::LockGuard lock(lock_); for (ScopeImpl* scope : scopes_) { for (auto& counter : scope->central_cache_.counters_) { @@ -51,7 +59,7 @@ std::vector ThreadLocalStoreImpl::counters() const { } ScopePtr ThreadLocalStoreImpl::createScope(const std::string& name) { - std::unique_ptr new_scope(new ScopeImpl(*this, name)); + auto new_scope = std::make_unique(*this, name); Thread::LockGuard lock(lock_); scopes_.emplace(new_scope.get()); return std::move(new_scope); @@ -60,7 +68,7 @@ ScopePtr ThreadLocalStoreImpl::createScope(const std::string& name) { std::vector ThreadLocalStoreImpl::gauges() const { // Handle de-dup due to overlapping scopes. std::vector ret; - CharStarHashSet names; + StatNameHashSet names; Thread::LockGuard lock(lock_); for (ScopeImpl* scope : scopes_) { for (auto& gauge : scope->central_cache_.gauges_) { @@ -149,10 +157,12 @@ void ThreadLocalStoreImpl::releaseScopeCrossThread(ScopeImpl* scope) { } } +/* std::string ThreadLocalStoreImpl::getTagsForName(const std::string& name, std::vector& tags) const { return tag_producer_->produceTags(name, tags); } +*/ void ThreadLocalStoreImpl::clearScopeFromCaches(uint64_t scope_id) { // If we are shutting down we no longer perform cache flushes as workers may be shutting down @@ -186,16 +196,67 @@ absl::string_view ThreadLocalStoreImpl::truncateStatNameIfNeeded(absl::string_vi std::atomic ThreadLocalStoreImpl::ScopeImpl::next_scope_id_; -ThreadLocalStoreImpl::ScopeImpl::~ScopeImpl() { parent_.releaseScopeCrossThread(this); } +ThreadLocalStoreImpl::ScopeImpl::ScopeImpl(ThreadLocalStoreImpl& parent, const std::string& prefix) + : scope_id_(next_scope_id_++), parent_(parent), + prefix_(Utility::sanitizeStatsName(prefix), parent.symbolTable()) {} + +ThreadLocalStoreImpl::ScopeImpl::~ScopeImpl() { + parent_.releaseScopeCrossThread(this); + prefix_.free(symbolTable()); +} + +/* +void ThreadLocalStoreImpl::ScopeImpl::extractTagsAndTruncate( + StatName& name, std::unique_ptr& truncated_name_storage, + std::vector& tags, + std::string& tag_extracted_name) { + + // Tag extraction occurs on the original, untruncated name so the extraction + // can complete properly, even if the tag values are partially truncated. + std::string name_str = name.toString(parent_.symbolTable()); + tag_extracted_name = parent_.getTagsForName(name_str, tags); + absl::string_view truncated_name = parent_.truncateStatNameIfNeeded(name_str); + if (truncated_name.size() < name_str.size()) { + truncated_name_storage = std::make_unique(truncated_name, symbolTable()); + name = truncated_name_storage->statName(); + } + }*/ + +// Manages the truncation and tag-extration of stat names. Tag extraction occurs +// on the original, untruncated name so the extraction can complete properly, +// even if the tag values are partially truncated. +class TruncationExtraction { + public: + TruncationExtraction(ThreadLocalStoreImpl& tls, StatName name) : stat_name_(name) { + std::string name_str = name.toString(tls.symbolTable()); + tag_extracted_name_ = tls.tagProducer().produceTags(name_str, tags_); + absl::string_view truncated_name = tls.truncateStatNameIfNeeded(name_str); + if (truncated_name.size() < name_str.size()) { + truncated_name_storage_ = std::make_unique( + truncated_name, tls.symbolTable()); + stat_name_ = truncated_name_storage_->statName(); + } + } + + const std::vector& tags() { return tags_; } + const std::string& tagExtractedName() { return tag_extracted_name_; } + StatName truncatedStatName() { return stat_name_; } + + private: + std::vector tags_; + std::string tag_extracted_name_; + std::unique_ptr truncated_name_storage_; + StatName stat_name_; +}; template StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( - const std::string& name, StatMap>& central_cache_map, + StatName name, StatMap>& central_cache_map, MakeStatFn make_stat, StatMap>* tls_cache) { // If we have a valid cache entry, return it. if (tls_cache) { - auto pos = tls_cache->find(name.c_str()); + auto pos = tls_cache->find(name); if (pos != tls_cache->end()) { return *pos->second; } @@ -204,39 +265,34 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( // We must now look in the central store so we must be locked. We grab a reference to the // central store location. It might contain nothing. In this case, we allocate a new stat. Thread::LockGuard lock(parent_.lock_); - auto p = central_cache_map.find(name.c_str()); + auto p = central_cache_map.find(name); std::shared_ptr* central_ref = nullptr; if (p != central_cache_map.end()) { central_ref = &(p->second); } else { - std::vector tags; - - // Tag extraction occurs on the original, untruncated name so the extraction - // can complete properly, even if the tag values are partially truncated. - std::string tag_extracted_name = parent_.getTagsForName(name, tags); - absl::string_view truncated_name = parent_.truncateStatNameIfNeeded(name); - std::shared_ptr stat = - make_stat(parent_.alloc_, truncated_name, std::move(tag_extracted_name), std::move(tags)); + TruncationExtraction extraction(parent_, name); + std::shared_ptr stat = make_stat(parent_.alloc_, extraction.truncatedStatName(), + extraction.tagExtractedName(), extraction.tags()); if (stat == nullptr) { parent_.num_last_resort_stats_.inc(); - stat = make_stat(parent_.heap_allocator_, truncated_name, std::move(tag_extracted_name), - std::move(tags)); + stat = make_stat(parent_.heap_allocator_, extraction.truncatedStatName(), + extraction.tagExtractedName(), extraction.tags()); ASSERT(stat != nullptr); } - central_ref = ¢ral_cache_map[stat->nameCStr()]; + central_ref = ¢ral_cache_map[stat->statName()]; *central_ref = stat; } // If we have a TLS cache, insert the stat. if (tls_cache) { - tls_cache->insert(std::make_pair((*central_ref)->nameCStr(), *central_ref)); + tls_cache->insert(std::make_pair((*central_ref)->statName(), *central_ref)); } // Finally we return the reference. return **central_ref; } -Counter& ThreadLocalStoreImpl::ScopeImpl::counter(const std::string& name) { +Counter& ThreadLocalStoreImpl::ScopeImpl::counterx(StatName name) { // Determine the final name based on the prefix and the passed name. // // Note that we can do map.find(final_name.c_str()), but we cannot do @@ -246,12 +302,12 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counter(const std::string& name) { // after we construct the stat we can insert it into the required maps. This // strategy costs an extra hash lookup for each miss, but saves time // re-copying the string and significant memory overhead. - std::string final_name = prefix_ + name; + StatNameJoiner final_name(prefix_.statName(), name); // TODO(ambuc): If stats_matcher_ depends on regexes, this operation (on the hot path) could // become prohibitively expensive. Revisit this usage in the future. - if (parent_.stats_matcher_->rejects(final_name)) { - return null_counter_; + if (parent_.stats_matcher_->rejects(final_name.statName().toString(symbolTable()))) { + return parent_.null_counter_; } // We now find the TLS cache. This might remain null if we don't have TLS @@ -262,10 +318,10 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counter(const std::string& name) { } return safeMakeStat( - final_name, central_cache_.counters_, - [](StatDataAllocator& allocator, absl::string_view name, std::string&& tag_extracted_name, - std::vector&& tags) -> CounterSharedPtr { - return allocator.makeCounter(name, std::move(tag_extracted_name), std::move(tags)); + final_name.statName(), central_cache_.counters_, + [](StatDataAllocator& allocator, StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) -> CounterSharedPtr { + return allocator.makeCounter(name, tag_extracted_name, tags); }, tls_cache); } @@ -286,8 +342,8 @@ void ThreadLocalStoreImpl::ScopeImpl::deliverHistogramToSinks(const Histogram& h } } -Gauge& ThreadLocalStoreImpl::ScopeImpl::gauge(const std::string& name) { - // See comments in counter(). There is no super clean way (via templates or otherwise) to +Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugex(StatName name) { + // See comments in counterx(). There is no super clean way (via templates or otherwise) to // share this code so I'm leaving it largely duplicated for now. // // Note that we can do map.find(final_name.c_str()), but we cannot do @@ -295,11 +351,11 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gauge(const std::string& name) { // a temporary, and address sanitization errors would follow. Instead we must // do a find() first, using tha if it succeeds. If it fails, then after we // construct the stat we can insert it into the required maps. - std::string final_name = prefix_ + name; + StatNameJoiner final_name(prefix_.statName(), name); // See warning/comments in counter(). - if (parent_.stats_matcher_->rejects(final_name)) { - return null_gauge_; + if (parent_.stats_matcher_->rejects(final_name.statName().toString(symbolTable()))) { + return parent_.null_gauge_; } StatMap* tls_cache = nullptr; @@ -308,16 +364,16 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gauge(const std::string& name) { } return safeMakeStat( - final_name, central_cache_.gauges_, - [](StatDataAllocator& allocator, absl::string_view name, std::string&& tag_extracted_name, - std::vector&& tags) -> GaugeSharedPtr { - return allocator.makeGauge(name, std::move(tag_extracted_name), std::move(tags)); + final_name.statName(), central_cache_.gauges_, + [](StatDataAllocator& allocator, StatName name, absl::string_view tag_extracted_name, + const std::vector& tags) -> GaugeSharedPtr { + return allocator.makeGauge(name, tag_extracted_name, tags); }, tls_cache); } -Histogram& ThreadLocalStoreImpl::ScopeImpl::histogram(const std::string& name) { - // See comments in counter(). There is no super clean way (via templates or otherwise) to +Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramx(StatName name) { + // See comments in counterx(). There is no super clean way (via templates or otherwise) to // share this code so I'm leaving it largely duplicated for now. // // Note that we can do map.find(final_name.c_str()), but we cannot do @@ -325,83 +381,89 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogram(const std::string& name) { // a temporary, and address sanitization errors would follow. Instead we must // do a find() first, using tha if it succeeds. If it fails, then after we // construct the stat we can insert it into the required maps. - std::string final_name = prefix_ + name; + StatNameJoiner final_name(prefix_.statName(), name); + StatName final_stat_name = final_name.statName(); - // See warning/comments in counter(). - if (parent_.stats_matcher_->rejects(final_name)) { - return null_histogram_; + // See warning/comments in counterx(). + if (parent_.stats_matcher_->rejects(final_stat_name.toString(symbolTable()))) { + return parent_.null_histogram_; } StatMap* tls_cache = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { tls_cache = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].parent_histograms_; - auto p = tls_cache->find(final_name.c_str()); + auto p = tls_cache->find(final_stat_name); if (p != tls_cache->end()) { return *p->second; } } Thread::LockGuard lock(parent_.lock_); - auto p = central_cache_.histograms_.find(final_name.c_str()); + auto p = central_cache_.histograms_.find(final_stat_name); ParentHistogramImplSharedPtr* central_ref = nullptr; if (p != central_cache_.histograms_.end()) { central_ref = &p->second; } else { - std::vector tags; - std::string tag_extracted_name = parent_.getTagsForName(final_name, tags); + TruncationExtraction extraction(parent_, final_stat_name); auto stat = std::make_shared( - final_name, parent_, *this, std::move(tag_extracted_name), std::move(tags)); - central_ref = ¢ral_cache_.histograms_[stat->nameCStr()]; + extraction.truncatedStatName(), parent_, *this, extraction.tagExtractedName(), + extraction.tags()); + central_ref = ¢ral_cache_.histograms_[stat->statName()]; *central_ref = stat; } if (tls_cache != nullptr) { - tls_cache->insert(std::make_pair((*central_ref)->nameCStr(), *central_ref)); + tls_cache->insert(std::make_pair((*central_ref)->statName(), *central_ref)); } return **central_ref; } -Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(const std::string& name, +Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(StatName name, ParentHistogramImpl& parent) { - if (parent_.stats_matcher_->rejects(name)) { - return null_histogram_; + if (parent_.stats_matcher_->rejects(name.toString(symbolTable()))) { + return parent_.null_histogram_; } - // See comments in counter() which explains the logic here. + // See comments in counterx() which explains the logic here. StatMap* tls_cache = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { tls_cache = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].histograms_; - auto p = tls_cache->find(name.c_str()); + auto p = tls_cache->find(name); if (p != tls_cache->end()) { return *p->second; } } std::vector tags; - std::string tag_extracted_name = parent_.getTagsForName(name, tags); + std::string tag_extracted_name = parent_.tagProducer().produceTags( + name.toString(symbolTable()), tags); TlsHistogramSharedPtr hist_tls_ptr = std::make_shared( - name, std::move(tag_extracted_name), std::move(tags)); + name, tag_extracted_name, tags, symbolTable()); parent.addTlsHistogram(hist_tls_ptr); if (tls_cache) { - tls_cache->insert(std::make_pair(hist_tls_ptr->nameCStr(), hist_tls_ptr)); + tls_cache->insert(std::make_pair(hist_tls_ptr->statName(), hist_tls_ptr)); } return *hist_tls_ptr; } -ThreadLocalHistogramImpl::ThreadLocalHistogramImpl(const std::string& name, - std::string&& tag_extracted_name, - std::vector&& tags) - : MetricImpl(std::move(tag_extracted_name), std::move(tags)), current_active_(0), flags_(0), - created_thread_id_(std::this_thread::get_id()), name_(name) { +ThreadLocalHistogramImpl::ThreadLocalHistogramImpl(StatName name, + absl::string_view tag_extracted_name, + const std::vector& tags, + SymbolTable& symbol_table) + : MetricImpl(tag_extracted_name, tags, symbol_table), current_active_(0), flags_(0), + created_thread_id_(std::this_thread::get_id()), name_(name, symbol_table), + symbol_table_(symbol_table) { histograms_[0] = hist_alloc(); histograms_[1] = hist_alloc(); } ThreadLocalHistogramImpl::~ThreadLocalHistogramImpl() { + MetricImpl::clear(); + name_.free(symbolTable()); hist_free(histograms_[0]); hist_free(histograms_[1]); } @@ -418,21 +480,23 @@ void ThreadLocalHistogramImpl::merge(histogram_t* target) { hist_clear(*other_histogram); } -ParentHistogramImpl::ParentHistogramImpl(const std::string& name, Store& parent, - TlsScope& tls_scope, std::string&& tag_extracted_name, - std::vector&& tags) - : MetricImpl(std::move(tag_extracted_name), std::move(tags)), parent_(parent), +ParentHistogramImpl::ParentHistogramImpl(StatName name, Store& parent, + TlsScope& tls_scope, absl::string_view tag_extracted_name, + const std::vector& tags) + : MetricImpl(tag_extracted_name, tags, parent.symbolTable()), parent_(parent), tls_scope_(tls_scope), interval_histogram_(hist_alloc()), cumulative_histogram_(hist_alloc()), interval_statistics_(interval_histogram_), cumulative_statistics_(cumulative_histogram_), - merged_(false), name_(name) {} + merged_(false), name_(name, parent.symbolTable()) {} ParentHistogramImpl::~ParentHistogramImpl() { + MetricImpl::clear(); + name_.free(symbolTable()); hist_free(interval_histogram_); hist_free(cumulative_histogram_); } void ParentHistogramImpl::recordValue(uint64_t value) { - Histogram& tls_histogram = tls_scope_.tlsHistogram(name(), *this); + Histogram& tls_histogram = tls_scope_.tlsHistogram(statName(), *this); tls_histogram.recordValue(value); parent_.deliverHistogramToSinks(*this, value); } diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 74f70c51f8f2..1ab812ff74cd 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -12,6 +12,7 @@ #include "common/stats/heap_stat_data.h" #include "common/stats/histogram_impl.h" #include "common/stats/source_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stats/utility.h" #include "absl/container/flat_hash_map.h" @@ -28,8 +29,8 @@ namespace Stats { */ class ThreadLocalHistogramImpl : public Histogram, public MetricImpl { public: - ThreadLocalHistogramImpl(const std::string& name, std::string&& tag_extracted_name, - std::vector&& tags); + ThreadLocalHistogramImpl(StatName name, absl::string_view tag_extracted_name, + const std::vector& tags, SymbolTable& symbol_table); ~ThreadLocalHistogramImpl(); void merge(histogram_t* target); @@ -49,8 +50,9 @@ class ThreadLocalHistogramImpl : public Histogram, public MetricImpl { bool used() const override { return flags_ & Flags::Used; } // Stats::Metric - std::string name() const override { return name_; } - const char* nameCStr() const override { return name_.c_str(); } + StatName statName() const override { return name_.statName(); } + SymbolTable& symbolTable() override { return symbol_table_; } + const SymbolTable& symbolTable() const override { return symbol_table_; } private: uint64_t otherHistogramIndex() const { return 1 - current_active_; } @@ -58,7 +60,8 @@ class ThreadLocalHistogramImpl : public Histogram, public MetricImpl { histogram_t* histograms_[2]; std::atomic flags_; std::thread::id created_thread_id_; - const std::string name_; + StatNameStorage name_; + SymbolTable& symbol_table_; }; typedef std::shared_ptr TlsHistogramSharedPtr; @@ -70,8 +73,8 @@ class TlsScope; */ class ParentHistogramImpl : public ParentHistogram, public MetricImpl { public: - ParentHistogramImpl(const std::string& name, Store& parent, TlsScope& tlsScope, - std::string&& tag_extracted_name, std::vector&& tags); + ParentHistogramImpl(StatName name, Store& parent, TlsScope& tlsScope, + absl::string_view tag_extracted_name, const std::vector& tags); ~ParentHistogramImpl(); void addTlsHistogram(const TlsHistogramSharedPtr& hist_ptr); @@ -93,8 +96,9 @@ class ParentHistogramImpl : public ParentHistogram, public MetricImpl { const std::string summary() const override; // Stats::Metric - std::string name() const override { return name_; } - const char* nameCStr() const override { return name_.c_str(); } + StatName statName() const override { return name_.statName(); } + SymbolTable& symbolTable() override { return parent_.symbolTable(); } + const SymbolTable& symbolTable() const override { return parent_.symbolTable(); } private: bool usedLockHeld() const EXCLUSIVE_LOCKS_REQUIRED(merge_lock_); @@ -108,7 +112,7 @@ class ParentHistogramImpl : public ParentHistogram, public MetricImpl { mutable Thread::MutexBasicLockable merge_lock_; std::list tls_histograms_ GUARDED_BY(merge_lock_); bool merged_; - const std::string name_; + StatNameStorage name_; }; typedef std::shared_ptr ParentHistogramImplSharedPtr; @@ -125,7 +129,7 @@ class TlsScope : public Scope { * @return a ThreadLocalHistogram within the scope's namespace. * @param name name of the histogram with scope prefix attached. */ - virtual Histogram& tlsHistogram(const std::string& name, ParentHistogramImpl& parent) PURE; + virtual Histogram& tlsHistogram(StatName name, ParentHistogramImpl& parent) PURE; }; /** @@ -138,15 +142,19 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ~ThreadLocalStoreImpl(); // Stats::Scope + Counter& counterx(StatName name) override { return default_scope_->counterx(name); } Counter& counter(const std::string& name) override { return default_scope_->counter(name); } ScopePtr createScope(const std::string& name) override; void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override { return default_scope_->deliverHistogramToSinks(histogram, value); } + Gauge& gaugex(StatName name) override { return default_scope_->gaugex(name); } Gauge& gauge(const std::string& name) override { return default_scope_->gauge(name); } - Histogram& histogram(const std::string& name) override { - return default_scope_->histogram(name); - }; + Histogram& histogramx(StatName name) override { return default_scope_->histogramx(name); } + Histogram& histogram(const std::string& name) override { return default_scope_->histogram(name); } + const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } + SymbolTable& symbolTable() override { return alloc_.symbolTable(); } + const TagProducer& tagProducer() const { return *tag_producer_; } // Stats::Store std::vector counters() const override; @@ -170,9 +178,10 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Source& source() override { return source_; } const Stats::StatsOptions& statsOptions() const override { return stats_options_; } + absl::string_view truncateStatNameIfNeeded(absl::string_view name); private: - template using StatMap = CharStarHashMap; + template using StatMap = StatNameHashMap; struct TlsCacheEntry { StatMap counters_; @@ -188,27 +197,40 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo }; struct ScopeImpl : public TlsScope { - ScopeImpl(ThreadLocalStoreImpl& parent, const std::string& prefix) - : scope_id_(next_scope_id_++), parent_(parent), - prefix_(Utility::sanitizeStatsName(prefix)) {} + ScopeImpl(ThreadLocalStoreImpl& parent, const std::string& prefix); ~ScopeImpl(); // Stats::Scope - Counter& counter(const std::string& name) override; - ScopePtr createScope(const std::string& name) override { - return parent_.createScope(prefix_ + name); - } + Counter& counterx(StatName name) override; void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override; - Gauge& gauge(const std::string& name) override; - Histogram& histogram(const std::string& name) override; - Histogram& tlsHistogram(const std::string& name, ParentHistogramImpl& parent) override; + Gauge& gaugex(StatName name) override; + Histogram& histogramx(StatName name) override; + Histogram& tlsHistogram(StatName name, ParentHistogramImpl& parent) override; const Stats::StatsOptions& statsOptions() const override { return parent_.statsOptions(); } + ScopePtr createScope(const std::string& name) override { + return parent_.createScope(prefix_.statName().toString(symbolTable()) + "." + name); + } + const SymbolTable& symbolTable() const override { return parent_.symbolTable(); } + SymbolTable& symbolTable() override { return parent_.symbolTable(); } + + Counter& counter(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return counterx(storage.statName()); + } + Gauge& gauge(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return gaugex(storage.statName()); + } + Histogram& histogram(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return histogramx(storage.statName()); + } template using MakeStatFn = - std::function(StatDataAllocator&, absl::string_view name, - std::string&& tag_extracted_name, - std::vector&& tags)>; + std::function(StatDataAllocator&, StatName name, + absl::string_view tag_extracted_name, + const std::vector& tags)>; /** * Makes a stat either by looking it up in the central cache, @@ -223,19 +245,20 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo */ template StatType& - safeMakeStat(const std::string& name, StatMap>& central_cache_map, + safeMakeStat(StatName name, StatMap>& central_cache_map, MakeStatFn make_stat, StatMap>* tls_cache); + void extractTagsAndTruncate( + StatName& name, std::unique_ptr& truncated_name_storage, + std::vector& tags, + std::string& tag_extracted_name); + static std::atomic next_scope_id_; const uint64_t scope_id_; ThreadLocalStoreImpl& parent_; - const std::string prefix_; + StatNameStorage prefix_; CentralCacheEntry central_cache_; - - NullCounterImpl null_counter_; - NullGaugeImpl null_gauge_; - NullHistogramImpl null_histogram_; }; struct TlsCache : public ThreadLocal::ThreadLocalObject { @@ -253,7 +276,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void clearScopeFromCaches(uint64_t scope_id); void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb mergeCb); - absl::string_view truncateStatNameIfNeeded(absl::string_view name); const Stats::StatsOptions& stats_options_; StatDataAllocator& alloc_; @@ -267,9 +289,14 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatsMatcherPtr stats_matcher_; std::atomic shutting_down_{}; std::atomic merge_in_progress_{}; + StatNameStorage stats_overflow_; Counter& num_last_resort_stats_; HeapStatDataAllocator heap_allocator_; SourceImpl source_; + + NullCounterImpl null_counter_; + NullGaugeImpl null_gauge_; + NullHistogramImpl null_histogram_; }; } // namespace Stats diff --git a/source/common/stats/utility.cc b/source/common/stats/utility.cc index afc4e8c4f4ca..7d4cd4cb2d37 100644 --- a/source/common/stats/utility.cc +++ b/source/common/stats/utility.cc @@ -3,11 +3,19 @@ #include #include +#include "absl/strings/match.h" + namespace Envoy { namespace Stats { -std::string Utility::sanitizeStatsName(const std::string& name) { - std::string stats_name = name; +std::string Utility::sanitizeStatsName(absl::string_view name) { + if (absl::EndsWith(name, ".")) { + name.remove_suffix(1); + } + if (absl::StartsWith(name, ".")) { + name.remove_prefix(1); + } + std::string stats_name = std::string(name); std::replace(stats_name.begin(), stats_name.end(), ':', '_'); std::replace(stats_name.begin(), stats_name.end(), '\0', '_'); return stats_name; diff --git a/source/common/stats/utility.h b/source/common/stats/utility.h index 18081360640c..f1cf255e47d7 100644 --- a/source/common/stats/utility.h +++ b/source/common/stats/utility.h @@ -2,6 +2,8 @@ #include +#include "absl/strings/string_view.h" + namespace Envoy { namespace Stats { @@ -12,7 +14,7 @@ class Utility { public: // ':' is a reserved char in statsd. Do a character replacement to avoid costly inline // translations later. - static std::string sanitizeStatsName(const std::string& name); + static std::string sanitizeStatsName(absl::string_view name); }; } // namespace Stats diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 368978622fc0..906e458e220d 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -77,8 +77,9 @@ class HostDescriptionImpl : virtual public HostDescription { Config::MetadataEnvoyLbKeys::get().CANARY) .bool_value()), metadata_(std::make_shared(metadata)), - locality_(locality), stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), - POOL_GAUGE(stats_store_))}, + locality_(locality), + stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), + POOL_GAUGE(stats_store_))}, priority_(priority) {} // Upstream::HostDescription diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index 4c26e73ba572..012c692f0b3d 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -53,11 +53,11 @@ MainCommonBase::MainCommonBase(OptionsImpl& options, Event::TimeSystem& time_sys case Server::Mode::Serve: { #ifdef ENVOY_HOT_RESTART if (!options.hotRestartDisabled()) { - restarter_.reset(new Server::HotRestartImpl(options_)); + restarter_.reset(new Server::HotRestartImpl(options_, symbol_table_)); } #endif if (restarter_.get() == nullptr) { - restarter_.reset(new Server::HotRestartNopImpl()); + restarter_.reset(new Server::HotRestartNopImpl(symbol_table_)); } tls_.reset(new ThreadLocal::InstanceImpl); @@ -78,7 +78,7 @@ MainCommonBase::MainCommonBase(OptionsImpl& options, Event::TimeSystem& time_sys break; } case Server::Mode::Validate: - restarter_.reset(new Server::HotRestartNopImpl()); + restarter_.reset(new Server::HotRestartNopImpl(symbol_table_)); logging_context_ = std::make_unique(options_.logLevel(), options_.logFormat(), restarter_->logLock()); break; diff --git a/source/exe/main_common.h b/source/exe/main_common.h index e4bd84ed9768..c1a25d7dae4d 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -60,7 +60,7 @@ class MainCommonBase { protected: Envoy::OptionsImpl& options_; - + Stats::SymbolTable symbol_table_; Server::ComponentFactory& component_factory_; std::unique_ptr tls_; diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 6c05d4a66c27..e6aad776c8f1 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -329,7 +329,8 @@ void HystrixSink::flush(Stats::Source& source) { for (const Stats::ParentHistogramSharedPtr& histogram : source.cachedHistograms()) { if (histogram->tagExtractedName() == "cluster.upstream_rq_time") { // TODO(mrice32): add an Envoy utility function to look up and return a tag for a metric. - auto it = std::find_if(histogram->tags().begin(), histogram->tags().end(), + auto tags = histogram->tags(); + auto it = std::find_if(tags.begin(), tags.end(), [](const Stats::Tag& tag) { return (tag.name_ == Config::TagNames::get().CLUSTER_NAME); }); diff --git a/source/server/hot_restart_impl.cc b/source/server/hot_restart_impl.cc index 9d92877e511c..64e8c23d7f41 100644 --- a/source/server/hot_restart_impl.cc +++ b/source/server/hot_restart_impl.cc @@ -120,8 +120,9 @@ std::string SharedMemory::version(uint64_t max_num_stats, stats_options.maxNameLength()); } -HotRestartImpl::HotRestartImpl(Options& options) - : options_(options), stats_set_options_(blockMemHashOptions(options.maxStats())), +HotRestartImpl::HotRestartImpl(Options& options, Stats::SymbolTable& symbol_table) + : Stats::RawStatDataAllocator(symbol_table), + options_(options), stats_set_options_(blockMemHashOptions(options.maxStats())), shmem_(SharedMemory::initialize( RawStatDataSet::numBytes(stats_set_options_, options_.statsOptions()), options_)), log_lock_(shmem_.log_lock_), access_log_lock_(shmem_.access_log_lock_), diff --git a/source/server/hot_restart_impl.h b/source/server/hot_restart_impl.h index 99d307e98551..d145f15bfd4a 100644 --- a/source/server/hot_restart_impl.h +++ b/source/server/hot_restart_impl.h @@ -119,7 +119,7 @@ class HotRestartImpl : public HotRestart, public Stats::RawStatDataAllocator, Logger::Loggable { public: - HotRestartImpl(Options& options); + HotRestartImpl(Options& options, Stats::SymbolTable& symbol_table); // Server::HotRestart void drainParentListeners() override; diff --git a/source/server/hot_restart_nop_impl.h b/source/server/hot_restart_nop_impl.h index bc31f897f29a..756bb8d217fd 100644 --- a/source/server/hot_restart_nop_impl.h +++ b/source/server/hot_restart_nop_impl.h @@ -15,7 +15,7 @@ namespace Server { */ class HotRestartNopImpl : public Server::HotRestart { public: - HotRestartNopImpl() {} + explicit HotRestartNopImpl(Stats::SymbolTable& symbol_table) : stats_allocator_(symbol_table) {} // Server::HotRestart void drainParentListeners() override {} diff --git a/test/common/http/user_agent_test.cc b/test/common/http/user_agent_test.cc index d69b99c84051..a6869add3912 100644 --- a/test/common/http/user_agent_test.cc +++ b/test/common/http/user_agent_test.cc @@ -18,7 +18,7 @@ namespace Http { TEST(UserAgentTest, All) { Stats::MockStore stat_store; - NiceMock original_histogram; + NiceMock original_histogram(stat_store.symbolTable()); Event::SimulatedTimeSystem time_system; Stats::Timespan span(original_histogram, time_system); diff --git a/test/common/stats/heap_stat_data_test.cc b/test/common/stats/heap_stat_data_test.cc index 7270939d6d84..ee69a6cacb15 100644 --- a/test/common/stats/heap_stat_data_test.cc +++ b/test/common/stats/heap_stat_data_test.cc @@ -12,7 +12,7 @@ namespace Envoy { namespace Stats { class HeapStatDataTest : public testing::Test { - protected: +protected: HeapStatDataTest() : alloc_(symbol_table_) {} ~HeapStatDataTest() { clearStorage(); } @@ -33,8 +33,6 @@ class HeapStatDataTest : public testing::Test { EXPECT_EQ(0, symbol_table_.numSymbols()); } - - SymbolTable symbol_table_; HeapStatDataAllocator alloc_; std::vector stat_name_storage_; diff --git a/test/common/stats/source_impl_test.cc b/test/common/stats/source_impl_test.cc index 8668626534c7..1d8edf4da83b 100644 --- a/test/common/stats/source_impl_test.cc +++ b/test/common/stats/source_impl_test.cc @@ -23,21 +23,22 @@ TEST(SourceImplTest, Caching) { ON_CALL(store, histograms()).WillByDefault(ReturnPointee(&stored_histograms)); SourceImpl source(store); + SymbolTable symbol_table; // Once cached, new values should not be reflected by the return value. - stored_counters.push_back(std::make_shared()); + stored_counters.push_back(std::make_shared(symbol_table)); EXPECT_EQ(source.cachedCounters(), stored_counters); - stored_counters.push_back(std::make_shared()); + stored_counters.push_back(std::make_shared(symbol_table)); EXPECT_NE(source.cachedCounters(), stored_counters); - stored_gauges.push_back(std::make_shared()); + stored_gauges.push_back(std::make_shared(symbol_table)); EXPECT_EQ(source.cachedGauges(), stored_gauges); - stored_gauges.push_back(std::make_shared()); + stored_gauges.push_back(std::make_shared(symbol_table)); EXPECT_NE(source.cachedGauges(), stored_gauges); - stored_histograms.push_back(std::make_shared()); + stored_histograms.push_back(std::make_shared(symbol_table)); EXPECT_EQ(source.cachedHistograms(), stored_histograms); - stored_histograms.push_back(std::make_shared()); + stored_histograms.push_back(std::make_shared(symbol_table)); EXPECT_NE(source.cachedHistograms(), stored_histograms); // After clearing, the new values should be reflected in the cache. diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 86ab8f30fa58..136476c1bd46 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -19,7 +19,7 @@ namespace Envoy { class ThreadLocalStorePerf { public: - ThreadLocalStorePerf() : store_(options_, heap_alloc_) { + ThreadLocalStorePerf() : heap_alloc_(symbol_table_), store_(options_, heap_alloc_) { store_.setTagProducer(std::make_unique(stats_config_)); } @@ -42,6 +42,7 @@ class ThreadLocalStorePerf { } private: + Stats::SymbolTable symbol_table_; Stats::StatsOptionsImpl options_; Event::SimulatedTimeSystem time_system_; Stats::HeapStatDataAllocator heap_alloc_; diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 318269311304..0bc484746555 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -45,6 +45,7 @@ class StatsThreadLocalStoreTest : public testing::Test { store_->addSink(sink_); } + Stats::SymbolTable symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; StatsOptionsImpl options_; @@ -616,6 +617,8 @@ TEST_F(StatsMatcherTLSTest, TestExclusionRegex) { class HeapStatsThreadLocalStoreTest : public StatsThreadLocalStoreTest { public: + HeapStatsThreadLocalStoreTest() : heap_alloc_(symbol_table_) {} + void SetUp() override { resetStoreWithAlloc(heap_alloc_); // Note: we do not call StatsThreadLocalStoreTest::SetUp here as that diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 193820f2cba6..0b9043598a97 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -467,9 +467,10 @@ class TcpProxyTest : public testing::Test { Event::TestTimeSystem& timeSystem() { return factory_context_.timeSystem(); } + NiceMock factory_context_; ConfigSharedPtr config_; + std::unique_ptr filter_; NiceMock filter_callbacks_; - NiceMock factory_context_; std::vector>> upstream_hosts_{}; std::vector>> upstream_connections_{}; std::vector>> @@ -478,7 +479,6 @@ class TcpProxyTest : public testing::Test { std::vector>> conn_pool_handles_; NiceMock conn_pool_; Tcp::ConnectionPool::UpstreamCallbacks* upstream_callbacks_; - std::unique_ptr filter_; StringViewSaver access_log_data_; Network::Address::InstanceConstSharedPtr upstream_local_address_; Network::Address::InstanceConstSharedPtr upstream_remote_address_; @@ -1097,10 +1097,10 @@ class TcpProxyRoutingTest : public testing::Test { Event::TestTimeSystem& timeSystem() { return factory_context_.timeSystem(); } + NiceMock factory_context_; ConfigSharedPtr config_; NiceMock connection_; NiceMock filter_callbacks_; - NiceMock factory_context_; std::unique_ptr filter_; }; diff --git a/test/common/upstream/resource_manager_impl_test.cc b/test/common/upstream/resource_manager_impl_test.cc index 73610ab81c5f..8e6ade898207 100644 --- a/test/common/upstream/resource_manager_impl_test.cc +++ b/test/common/upstream/resource_manager_impl_test.cc @@ -18,8 +18,8 @@ namespace Upstream { TEST(ResourceManagerImplTest, RuntimeResourceManager) { NiceMock runtime; - NiceMock gauge; NiceMock store; + NiceMock gauge; ON_CALL(store, gauge(_)).WillByDefault(ReturnRef(gauge)); diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index ef6a6e29723a..f155acd8bba7 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -178,6 +178,7 @@ TEST(UdpStatsdSinkTest, CheckActualStatsWithCustomPrefix) { } TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { + Stats::SymbolTable symbol_table; NiceMock source; auto writer_ptr = std::make_shared>(); NiceMock tls_; @@ -185,6 +186,7 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { std::vector tags = {Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}; auto counter = std::make_shared>(); + counter->symbol_table_ = &symbol_table; counter->name_ = "test_counter"; counter->used_ = true; counter->latch_ = 1; @@ -197,6 +199,7 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { counter->used_ = false; auto gauge = std::make_shared>(); + gauge->symbol_table_ = &symbol_table; gauge->name_ = "test_gauge"; gauge->value_ = 1; gauge->used_ = true; @@ -208,6 +211,7 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { sink.flush(source); NiceMock timer; + timer.symbol_table_ = &symbol_table; timer.name_ = "test_timer"; timer.tags_ = tags; EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), diff --git a/test/extensions/stats_sinks/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/hystrix/hystrix_test.cc index 564da63373ee..fba640b28bd9 100644 --- a/test/extensions/stats_sinks/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/hystrix/hystrix_test.cc @@ -460,17 +460,9 @@ TEST_F(HystrixSinkTest, HistogramTest) { // Create histogram for the Hystrix sink to read. auto histogram = std::make_shared>(); histogram->name_ = "cluster." + cluster1_name_ + ".upstream_rq_time"; - const std::string tag_extracted_name = "cluster.upstream_rq_time"; - ON_CALL(*histogram, tagExtractedName()) - .WillByDefault(testing::ReturnRefOfCopy(tag_extracted_name)); - std::vector tags; - Stats::Tag tag = { - Config::TagNames::get().CLUSTER_NAME, // name_ - cluster1_name_ // value_ - }; - tags.emplace_back(tag); - ON_CALL(*histogram, tags()).WillByDefault(testing::ReturnRef(tags)); - + histogram->tag_extracted_name_ = "cluster.upstream_rq_time"; + histogram->tags_.emplace_back( + Stats::Tag{.name_ = Config::TagNames::get().CLUSTER_NAME, .value_ = cluster1_name_ }); histogram->used_ = true; // Init with data such that the quantile value is equal to the quantile. diff --git a/test/integration/server.cc b/test/integration/server.cc index d50c897440c5..f131f54c0d8c 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -131,11 +131,12 @@ void IntegrationTestServer::onWorkerListenerRemoved() { void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion version, bool deterministic) { OptionsImpl options(Server::createTestOptionsImpl(config_path_, "", version)); - Server::HotRestartNopImpl restarter; + Stats::SymbolTable symbol_table; + Server::HotRestartNopImpl restarter(symbol_table); Thread::MutexBasicLockable lock; ThreadLocal::InstanceImpl tls; - Stats::HeapStatDataAllocator stats_allocator; + Stats::HeapStatDataAllocator stats_allocator(symbol_table); Stats::StatsOptionsImpl stats_options; Stats::ThreadLocalStoreImpl stats_store(stats_options, stats_allocator); stat_store_ = &stats_store; diff --git a/test/integration/server.h b/test/integration/server.h index 3c231022b973..33c6d7cb5dfc 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -75,21 +75,36 @@ class TestScopeWrapper : public Scope { wrapped_scope_->deliverHistogramToSinks(histogram, value); } - Counter& counter(const std::string& name) override { + Counter& counterx(StatName name) override { Thread::LockGuard lock(lock_); - return wrapped_scope_->counter(name); + return wrapped_scope_->counterx(name); } - Gauge& gauge(const std::string& name) override { + Gauge& gaugex(StatName name) override { Thread::LockGuard lock(lock_); - return wrapped_scope_->gauge(name); + return wrapped_scope_->gaugex(name); } - Histogram& histogram(const std::string& name) override { + Histogram& histogramx(StatName name) override { Thread::LockGuard lock(lock_); - return wrapped_scope_->histogram(name); + return wrapped_scope_->histogramx(name); + } + + Counter& counter(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return counterx(storage.statName()); + } + Gauge& gauge(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return gaugex(storage.statName()); + } + Histogram& histogram(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return histogramx(storage.statName()); } + const SymbolTable& symbolTable() const override { return wrapped_scope_->symbolTable(); } + SymbolTable& symbolTable() override { return wrapped_scope_->symbolTable(); } const StatsOptions& statsOptions() const override { return stats_options_; } private: @@ -106,6 +121,10 @@ class TestIsolatedStoreImpl : public StoreRoot { public: TestIsolatedStoreImpl() : source_(*this) {} // Stats::Scope + Counter& counterx(StatName name) override { + Thread::LockGuard lock(lock_); + return store_.counterx(name); + } Counter& counter(const std::string& name) override { Thread::LockGuard lock(lock_); return store_.counter(name); @@ -115,15 +134,25 @@ class TestIsolatedStoreImpl : public StoreRoot { return ScopePtr{new TestScopeWrapper(lock_, store_.createScope(name))}; } void deliverHistogramToSinks(const Histogram&, uint64_t) override {} + Gauge& gaugex(StatName name) override { + Thread::LockGuard lock(lock_); + return store_.gaugex(name); + } Gauge& gauge(const std::string& name) override { Thread::LockGuard lock(lock_); return store_.gauge(name); } + Histogram& histogramx(StatName name) override { + Thread::LockGuard lock(lock_); + return store_.histogramx(name); + } Histogram& histogram(const std::string& name) override { Thread::LockGuard lock(lock_); return store_.histogram(name); } const StatsOptions& statsOptions() const override { return stats_options_; } + const SymbolTable& symbolTable() const override { return store_.symbolTable(); } + SymbolTable& symbolTable() override { return store_.symbolTable(); } // Stats::Store std::vector counters() const override { diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index 0aaf6fb20607..7640384f4879 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -64,7 +64,7 @@ MockGuardDog::MockGuardDog() : watch_dog_(new NiceMock()) { } MockGuardDog::~MockGuardDog() {} -MockHotRestart::MockHotRestart() { +MockHotRestart::MockHotRestart() : stats_allocator_(symbol_table_) { ON_CALL(*this, logLock()).WillByDefault(ReturnRef(log_lock_)); ON_CALL(*this, accessLogLock()).WillByDefault(ReturnRef(access_log_lock_)); ON_CALL(*this, statsAllocator()).WillByDefault(ReturnRef(stats_allocator_)); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index dba2ace1723f..af62bd18c52d 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -187,6 +187,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::StatDataAllocator&()); private: + Stats::SymbolTable symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::HeapStatDataAllocator stats_allocator_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 5b1dbf98df98..6eeeb61fdfc7 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -13,9 +13,35 @@ using testing::ReturnRef; namespace Envoy { namespace Stats { +MockMetric::MockMetric() : symbol_table_(nullptr), name_(*this) { + //ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); + //ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); +} +MockMetric::MockMetric(SymbolTable& symbol_table) : symbol_table_(&symbol_table), name_(*this) { + //ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); + //ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); +} +MockMetric::~MockMetric() {} + +MockMetric::MetricName::~MetricName() { + if (stat_name_storage_ != nullptr) { + stat_name_storage_->free(*mock_metric_.symbol_table_); + } +} + +void MockMetric::MetricName::MetricName::operator=(absl::string_view name) { + name_ = std::string(name); + if (mock_metric_.symbol_table_ != nullptr) { + stat_name_storage_ = std::make_unique(name, *mock_metric_.symbol_table_); + } +} + MockCounter::MockCounter() { - ON_CALL(*this, tagExtractedName()).WillByDefault(ReturnRef(name_)); - ON_CALL(*this, tags()).WillByDefault(ReturnRef(tags_)); + ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); + ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); + ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); +} +MockCounter::MockCounter(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); @@ -23,8 +49,10 @@ MockCounter::MockCounter() { MockCounter::~MockCounter() {} MockGauge::MockGauge() { - ON_CALL(*this, tagExtractedName()).WillByDefault(ReturnRef(name_)); - ON_CALL(*this, tags()).WillByDefault(ReturnRef(tags_)); + ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); + ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); +} +MockGauge::MockGauge(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); } @@ -36,10 +64,14 @@ MockHistogram::MockHistogram() { store_->deliverHistogramToSinks(*this, value); } })); - ON_CALL(*this, tagExtractedName()).WillByDefault(ReturnRef(name_)); - ON_CALL(*this, tags()).WillByDefault(ReturnRef(tags_)); } - +MockHistogram::MockHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { + ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { + if (store_ != nullptr) { + store_->deliverHistogramToSinks(*this, value); + } + })); +} MockHistogram::~MockHistogram() {} MockParentHistogram::MockParentHistogram() { @@ -48,13 +80,21 @@ MockParentHistogram::MockParentHistogram() { store_->deliverHistogramToSinks(*this, value); } })); - ON_CALL(*this, tagExtractedName()).WillByDefault(ReturnRef(name_)); - ON_CALL(*this, tags()).WillByDefault(ReturnRef(tags_)); ON_CALL(*this, intervalStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); } - +MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) + : MockMetric(symbol_table) { + ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { + if (store_ != nullptr) { + store_->deliverHistogramToSinks(*this, value); + } + })); + ON_CALL(*this, intervalStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); + ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); + ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); +} MockParentHistogram::~MockParentHistogram() {} MockSource::MockSource() { @@ -68,10 +108,11 @@ MockSource::~MockSource() {} MockSink::MockSink() {} MockSink::~MockSink() {} -MockStore::MockStore() { +MockStore::MockStore() : counter_(symbol_table_) { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); + ON_CALL(*this, counterx(_)).WillByDefault(ReturnRef(counter_)); ON_CALL(*this, histogram(_)).WillByDefault(Invoke([this](const std::string& name) -> Histogram& { - auto* histogram = new NiceMock; + auto* histogram = new NiceMock(symbol_table_); histogram->name_ = name; histogram->store_ = this; histograms_.emplace_back(histogram); diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index d70e9c6a37d5..db3fc72af878 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -22,21 +22,60 @@ namespace Envoy { namespace Stats { -class MockCounter : public Counter { -public: - MockCounter(); - ~MockCounter(); +class MockMetric : public virtual Metric { + public: + explicit MockMetric(SymbolTable& symbol_table); + MockMetric(); + ~MockMetric(); + + // This bit of C++ subterfuge allows us to support the wealth of tests that + // do metric->name_ = "foo" even though names are more complex now. Note + // that the statName is only populated if there is a symbol table. + class MetricName { + public: + explicit MetricName(MockMetric& mock_metric) : mock_metric_(mock_metric) {} + ~MetricName(); + + void operator=(absl::string_view str); + + std::string name() const { return name_; } + StatName statName() const { return stat_name_storage_->statName(); } + + private: + MockMetric& mock_metric_; + std::string name_; + std::unique_ptr stat_name_storage_; + }; + + SymbolTable& symbolTable() override { return *symbol_table_; } + const SymbolTable& symbolTable() const override { return *symbol_table_; } // Note: cannot be mocked because it is accessed as a Property in a gmock EXPECT_CALL. This // creates a deadlock in gmock and is an unintended use of mock functions. - std::string name() const override { return name_; }; - const char* nameCStr() const override { return name_.c_str(); }; + std::string name() const override { return name_.name(); } + StatName statName() const override { return name_.statName(); } + //MOCK_CONST_METHOD0(tags, std::vector()); + std::vector tags() const override { return tags_; } + std::string tagExtractedName() const override { + return tag_extracted_name_.empty() ? name() : tag_extracted_name_; + } + //MOCK_CONST_METHOD0(tagExtractedName, std::string()); + + SymbolTable* symbol_table_; + MetricName name_; + std::string tag_extracted_name_; + std::vector tags_; +}; + +class MockCounter : public Counter, public MockMetric { +public: + MockCounter(); + explicit MockCounter(SymbolTable& symbol_table); + ~MockCounter(); MOCK_METHOD1(add, void(uint64_t amount)); MOCK_METHOD0(inc, void()); MOCK_METHOD0(latch, uint64_t()); - MOCK_CONST_METHOD0(tagExtractedName, const std::string&()); - MOCK_CONST_METHOD0(tags, const std::vector&()); MOCK_METHOD0(reset, void()); MOCK_CONST_METHOD0(used, bool()); MOCK_CONST_METHOD0(value, uint64_t()); @@ -44,25 +83,17 @@ class MockCounter : public Counter { bool used_; uint64_t value_; uint64_t latch_; - std::string name_; - std::vector tags_; }; -class MockGauge : public Gauge { +class MockGauge : public Gauge, public MockMetric { public: MockGauge(); + explicit MockGauge(SymbolTable& symbol_table); ~MockGauge(); - // Note: cannot be mocked because it is accessed as a Property in a gmock EXPECT_CALL. This - // creates a deadlock in gmock and is an unintended use of mock functions. - std::string name() const override { return name_; }; - const char* nameCStr() const override { return name_.c_str(); }; - MOCK_METHOD1(add, void(uint64_t amount)); MOCK_METHOD0(dec, void()); MOCK_METHOD0(inc, void()); - MOCK_CONST_METHOD0(tagExtractedName, const std::string&()); - MOCK_CONST_METHOD0(tags, const std::vector&()); MOCK_METHOD1(set, void(uint64_t value)); MOCK_METHOD1(sub, void(uint64_t amount)); MOCK_CONST_METHOD0(used, bool()); @@ -70,51 +101,34 @@ class MockGauge : public Gauge { bool used_; uint64_t value_; - std::string name_; - std::vector tags_; }; -class MockHistogram : public Histogram { +class MockHistogram : public Histogram, public MockMetric { public: MockHistogram(); + explicit MockHistogram(SymbolTable& symbol_table); ~MockHistogram(); - // Note: cannot be mocked because it is accessed as a Property in a gmock EXPECT_CALL. This - // creates a deadlock in gmock and is an unintended use of mock functions. - std::string name() const override { return name_; }; - const char* nameCStr() const override { return name_.c_str(); }; - - MOCK_CONST_METHOD0(tagExtractedName, const std::string&()); - MOCK_CONST_METHOD0(tags, const std::vector&()); MOCK_METHOD1(recordValue, void(uint64_t value)); MOCK_CONST_METHOD0(used, bool()); - std::string name_; - std::vector tags_; Store* store_; }; -class MockParentHistogram : public ParentHistogram { +class MockParentHistogram : public ParentHistogram, public MockMetric { public: MockParentHistogram(); + explicit MockParentHistogram(SymbolTable& symbol_table); ~MockParentHistogram(); - // Note: cannot be mocked because it is accessed as a Property in a gmock EXPECT_CALL. This - // creates a deadlock in gmock and is an unintended use of mock functions. - std::string name() const override { return name_; }; - const char* nameCStr() const override { return name_.c_str(); }; void merge() override {} const std::string summary() const override { return ""; }; MOCK_CONST_METHOD0(used, bool()); - MOCK_CONST_METHOD0(tagExtractedName, const std::string&()); - MOCK_CONST_METHOD0(tags, const std::vector&()); MOCK_METHOD1(recordValue, void(uint64_t value)); MOCK_CONST_METHOD0(cumulativeStatistics, const HistogramStatistics&()); MOCK_CONST_METHOD0(intervalStatistics, const HistogramStatistics&()); - std::string name_; - std::vector tags_; bool used_; Store* store_; std::shared_ptr histogram_stats_ = @@ -154,14 +168,21 @@ class MockStore : public Store { MOCK_METHOD2(deliverHistogramToSinks, void(const Histogram& histogram, uint64_t value)); MOCK_METHOD1(counter, Counter&(const std::string&)); + MOCK_METHOD1(counterx, Counter&(StatName)); MOCK_CONST_METHOD0(counters, std::vector()); MOCK_METHOD1(createScope_, Scope*(const std::string& name)); MOCK_METHOD1(gauge, Gauge&(const std::string&)); + MOCK_METHOD1(gaugex, Gauge&(StatName)); MOCK_CONST_METHOD0(gauges, std::vector()); MOCK_METHOD1(histogram, Histogram&(const std::string& name)); + MOCK_METHOD1(histogramx, Histogram&(StatName)); MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); + SymbolTable& symbolTable() override { return symbol_table_; } + const SymbolTable& symbolTable() const override { return symbol_table_; } + + SymbolTable symbol_table_; testing::NiceMock counter_; std::vector> histograms_; StatsOptionsImpl stats_options_; diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index b62a9e1aa4a2..45a6fb8371e9 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -164,6 +164,7 @@ class MockHost : public Host { testing::NiceMock cluster_; testing::NiceMock outlier_detector_; + Stats::SymbolTable symbol_table_; Stats::IsolatedStoreImpl stats_store_; HostStats stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}; }; diff --git a/test/server/hot_restart_impl_test.cc b/test/server/hot_restart_impl_test.cc index b9a2d6b5cea1..bbd3742d55ff 100644 --- a/test/server/hot_restart_impl_test.cc +++ b/test/server/hot_restart_impl_test.cc @@ -38,10 +38,11 @@ class HotRestartImplTest : public testing::Test { EXPECT_CALL(options_, statsOptions()).WillRepeatedly(ReturnRef(stats_options_)); // Test we match the correct stat with empty-slots before, after, or both. - hot_restart_.reset(new HotRestartImpl(options_)); + hot_restart_.reset(new HotRestartImpl(options_, symbol_table_)); hot_restart_->drainParentListeners(); } + Stats::SymbolTable symbol_table_; Api::MockOsSysCalls os_sys_calls_; TestThreadsafeSingletonInjector os_calls{&os_sys_calls_}; NiceMock options_; @@ -145,7 +146,7 @@ TEST_F(HotRestartImplTest, crossAlloc) { EXPECT_CALL(os_sys_calls_, mmap(_, _, _, _, _, _)) .WillOnce(Return(Api::SysCallPtrResult{buffer_.data(), 0})); EXPECT_CALL(os_sys_calls_, bind(_, _, _)); - HotRestartImpl hot_restart2(options_); + HotRestartImpl hot_restart2(options_, symbol_table_); Stats::RawStatData* stat1_prime = hot_restart2.alloc("stat1"); Stats::RawStatData* stat3_prime = hot_restart2.alloc("stat3"); Stats::RawStatData* stat5_prime = hot_restart2.alloc("stat5"); diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 80636eb0570e..ddac291093ea 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -1122,18 +1122,18 @@ TEST_P(AdminInstanceTest, PostRequest) { class PrometheusStatsFormatterTest : public testing::Test { protected: - PrometheusStatsFormatterTest() /*: alloc_(stats_options_)*/ {} + PrometheusStatsFormatterTest() : alloc_(symbol_table_) {} void addCounter(const std::string& name, std::vector cluster_tags) { - std::string tname = std::string(name); - counters_.push_back(alloc_.makeCounter(name, std::move(tname), std::move(cluster_tags))); + Stats::StatNameTempStorage storage(name, symbol_table_); + counters_.push_back(alloc_.makeCounter(storage.statName(), name, cluster_tags)); } void addGauge(const std::string& name, std::vector cluster_tags) { - std::string tname = std::string(name); - gauges_.push_back(alloc_.makeGauge(name, std::move(tname), std::move(cluster_tags))); + Stats::StatNameTempStorage storage(name, symbol_table_); + gauges_.push_back(alloc_.makeGauge(storage.statName(), name, cluster_tags)); } - Stats::StatsOptionsImpl stats_options_; + Stats::SymbolTable symbol_table_; Stats::HeapStatDataAllocator alloc_; std::vector counters_; std::vector gauges_; diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index a71ecdfe0711..d175af7dfb61 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -341,8 +341,7 @@ bool TestHeaderMapImpl::has(const LowerCaseString& key) { return get(key) != nul namespace Stats { MockedTestAllocator::MockedTestAllocator(const StatsOptions& stats_options) - : RawStatDataAllocator(symbol_table_), - alloc_(stats_options) { + : RawStatDataAllocator(symbol_table_), alloc_(stats_options) { ON_CALL(*this, alloc(_)).WillByDefault(Invoke([this](absl::string_view name) -> RawStatData* { return alloc_.alloc(name); })); diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 620a10469cd8..61cb45037105 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -407,8 +407,7 @@ namespace Stats { class TestAllocator : public RawStatDataAllocator { public: TestAllocator(const StatsOptions& stats_options) - : RawStatDataAllocator(symbol_table_), - stats_options_(stats_options) {} + : RawStatDataAllocator(symbol_table_), stats_options_(stats_options) {} ~TestAllocator() { EXPECT_TRUE(stats_.empty()); } RawStatData* alloc(absl::string_view name) override { From b3cad8a8a513646149513ab555c3e5de1a1214a1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 6 Nov 2018 20:27:18 -0500 Subject: [PATCH 004/106] format Signed-off-by: Joshua Marantz --- source/common/stats/histogram_impl.h | 2 +- source/common/stats/isolated_store_impl.cc | 10 ++- source/common/stats/isolated_store_impl.h | 2 - source/common/stats/metric_impl.h | 10 +-- source/common/stats/scope_prefixer.cc | 13 ++-- source/common/stats/scope_prefixer.h | 7 ++- .../common/stats/stat_data_allocator_impl.h | 4 +- source/common/stats/symbol_table_impl.h | 5 +- source/common/stats/thread_local_store.cc | 61 +++++++++---------- source/common/stats/thread_local_store.h | 22 +++---- source/common/upstream/upstream_impl.h | 5 +- .../extensions/stat_sinks/hystrix/hystrix.cc | 7 +-- source/server/hot_restart_impl.cc | 4 +- .../stats_sinks/hystrix/hystrix_test.cc | 3 +- test/mocks/stats/mocks.cc | 11 ++-- test/mocks/stats/mocks.h | 10 +-- 16 files changed, 81 insertions(+), 95 deletions(-) diff --git a/source/common/stats/histogram_impl.h b/source/common/stats/histogram_impl.h index 1a9928b0de8a..b3da0d133386 100644 --- a/source/common/stats/histogram_impl.h +++ b/source/common/stats/histogram_impl.h @@ -74,7 +74,7 @@ class HistogramImpl : public Histogram, public MetricImpl { */ class NullHistogramImpl : public Histogram, NullMetricImpl { public: - explicit NullHistogramImpl(SymbolTable& symbol_table): NullMetricImpl(symbol_table) {} + explicit NullHistogramImpl(SymbolTable& symbol_table) : NullMetricImpl(symbol_table) {} ~NullHistogramImpl() { MetricImpl::clear(); } void recordValue(uint64_t) override {} diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 31b7adbc0967..8dec6e96f0ac 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -14,17 +14,15 @@ namespace Envoy { namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() - : alloc_(symbol_table_), - counters_([this](StatName name) -> CounterSharedPtr { - return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), - std::vector()); + : alloc_(symbol_table_), counters_([this](StatName name) -> CounterSharedPtr { + return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), std::vector()); }), gauges_([this](StatName name) -> GaugeSharedPtr { return alloc_.makeGauge(name, name.toString(alloc_.symbolTable()), std::vector()); }), histograms_([this](StatName name) -> HistogramSharedPtr { - return std::make_shared( - name, *this, name.toString(alloc_.symbolTable()), std::vector()); + return std::make_shared(name, *this, name.toString(alloc_.symbolTable()), + std::vector()); }) {} 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 8af11350bc6c..325f1f41264f 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -92,8 +92,6 @@ class IsolatedStoreImpl : public Store { return histogramx(storage.statName()); } - - private: SymbolTable symbol_table_; HeapStatDataAllocator alloc_; diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index 3c9652c7ba53..05add1d947f1 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -47,10 +47,10 @@ class MetricImpl : public virtual Metric { }; class NullMetricImpl : public MetricImpl { - public: - explicit NullMetricImpl(SymbolTable& symbol_table) : - MetricImpl("", std::vector(), symbol_table), symbol_table_(symbol_table), - stat_name_storage_("", symbol_table) {} +public: + explicit NullMetricImpl(SymbolTable& symbol_table) + : MetricImpl("", std::vector(), symbol_table), symbol_table_(symbol_table), + stat_name_storage_("", symbol_table) {} ~NullMetricImpl() { stat_name_storage_.free(symbol_table_); } const SymbolTable& symbolTable() const override { return symbol_table_; } @@ -58,7 +58,7 @@ class NullMetricImpl : public MetricImpl { bool used() const override { return false; } StatName statName() const override { return stat_name_storage_.statName(); } - private: +private: SymbolTable& symbol_table_; StatNameStorage stat_name_storage_; }; diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 874fc11300bb..a233b1911f22 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -1,8 +1,9 @@ #include "common/stats/scope_prefixer.h" +#include "envoy/stats/scope.h" + #include "common/stats/symbol_table_impl.h" #include "common/stats/utility.h" -#include "envoy/stats/scope.h" namespace Envoy { namespace Stats { @@ -10,15 +11,13 @@ namespace Stats { // Variant of ScopePrefixer that owns the scope being prefixed, and will // delete it upon destruction. ScopePrefixer::ScopePrefixer(absl::string_view prefix, Scope* scope) - : prefix_(Utility::sanitizeStatsName(prefix), scope->symbolTable()), - scope_(scope), + : prefix_(Utility::sanitizeStatsName(prefix), scope->symbolTable()), scope_(scope), owns_scope_(true) {} // Variant of ScopePrefixer that references the scope being prefixed, but does // not own it. ScopePrefixer::ScopePrefixer(absl::string_view prefix, Scope& scope) - : prefix_(Utility::sanitizeStatsName(prefix), scope.symbolTable()), - scope_(&scope), + : prefix_(Utility::sanitizeStatsName(prefix), scope.symbolTable()), scope_(&scope), owns_scope_(false) {} ScopePrefixer::~ScopePrefixer() { @@ -29,8 +28,8 @@ ScopePrefixer::~ScopePrefixer() { } ScopePtr ScopePrefixer::createScope(const std::string& name) { - //StatNameStorage scope_stat_name(name, symbolTable()); // Takes a lock. - //StatNameStorage joiner(prefix_.statName(), scope_stat_name.statName()); + // StatNameStorage scope_stat_name(name, symbolTable()); // Takes a lock. + // StatNameStorage joiner(prefix_.statName(), scope_stat_name.statName()); return std::make_unique(prefix_.statName().toString(symbolTable()) + "." + name, *scope_); } diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index 6288d18d0a22..c8abcc548176 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -1,13 +1,14 @@ -#include "common/stats/symbol_table_impl.h" #include "envoy/stats/scope.h" +#include "common/stats/symbol_table_impl.h" + namespace Envoy { namespace Stats { // Implements a Scope that delegates to a passed-in scope, prefixing all names // prior to creation. class ScopePrefixer : public Scope { - public: +public: // Variant of ScopePrefixer that owns the scope being prefixed, and will // delete it upon destruction. ScopePrefixer(absl::string_view prefix, Scope* scope); @@ -42,7 +43,7 @@ class ScopePrefixer : public Scope { const SymbolTable& symbolTable() const override { return scope_->symbolTable(); } virtual SymbolTable& symbolTable() override { return scope_->symbolTable(); } - private: +private: StatNameStorage prefix_; Scope* scope_; const bool owns_scope_; diff --git a/source/common/stats/stat_data_allocator_impl.h b/source/common/stats/stat_data_allocator_impl.h index da0b0b3da590..e1354263f518 100644 --- a/source/common/stats/stat_data_allocator_impl.h +++ b/source/common/stats/stat_data_allocator_impl.h @@ -101,7 +101,7 @@ template class CounterImpl : public Counter, public MetricImpl */ class NullCounterImpl : public Counter, NullMetricImpl { public: - explicit NullCounterImpl(SymbolTable& symbol_table): NullMetricImpl(symbol_table) {} + explicit NullCounterImpl(SymbolTable& symbol_table) : NullMetricImpl(symbol_table) {} ~NullCounterImpl() { MetricImpl::clear(); } void add(uint64_t) override {} @@ -157,7 +157,7 @@ template class GaugeImpl : public Gauge, public MetricImpl { */ class NullGaugeImpl : public Gauge, NullMetricImpl { public: - explicit NullGaugeImpl(SymbolTable& symbol_table): NullMetricImpl(symbol_table) {} + explicit NullGaugeImpl(SymbolTable& symbol_table) : NullMetricImpl(symbol_table) {} ~NullGaugeImpl() { MetricImpl::clear(); } void add(uint64_t) override {} diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index c5ec564f0712..d3b615057294 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -341,18 +341,17 @@ class StatNameStorage { }; class StatNameTempStorage : public StatNameStorage { - public: +public: StatNameTempStorage(absl::string_view name, SymbolTable& table) : StatNameStorage(name, table), symbol_table_(table) {} StatNameTempStorage(StatName src, SymbolTable& table) : StatNameStorage(src, table), symbol_table_(table) {} ~StatNameTempStorage() { free(symbol_table_); } - private: +private: SymbolTable& symbol_table_; }; - // Helper class for constructing hash-tables with StatName keys. struct StatNameHash { size_t operator()(const StatName& a) const { return a.hash(); } diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 543fe14cf544..530ebf8133a8 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -29,11 +29,8 @@ ThreadLocalStoreImpl::ThreadLocalStoreImpl(const StatsOptions& stats_options, stats_matcher_(std::make_unique()), stats_overflow_("stats.overflow", alloc.symbolTable()), num_last_resort_stats_(default_scope_->counterx(stats_overflow_.statName())), - heap_allocator_(alloc.symbolTable()), - source_(*this), - null_counter_(alloc.symbolTable()), - null_gauge_(alloc.symbolTable()), - null_histogram_(alloc.symbolTable()) {} + heap_allocator_(alloc.symbolTable()), source_(*this), null_counter_(alloc.symbolTable()), + null_gauge_(alloc.symbolTable()), null_histogram_(alloc.symbolTable()) {} ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(shutting_down_); @@ -226,14 +223,14 @@ void ThreadLocalStoreImpl::ScopeImpl::extractTagsAndTruncate( // on the original, untruncated name so the extraction can complete properly, // even if the tag values are partially truncated. class TruncationExtraction { - public: +public: TruncationExtraction(ThreadLocalStoreImpl& tls, StatName name) : stat_name_(name) { std::string name_str = name.toString(tls.symbolTable()); tag_extracted_name_ = tls.tagProducer().produceTags(name_str, tags_); absl::string_view truncated_name = tls.truncateStatNameIfNeeded(name_str); if (truncated_name.size() < name_str.size()) { - truncated_name_storage_ = std::make_unique( - truncated_name, tls.symbolTable()); + truncated_name_storage_ = + std::make_unique(truncated_name, tls.symbolTable()); stat_name_ = truncated_name_storage_->statName(); } } @@ -242,7 +239,7 @@ class TruncationExtraction { const std::string& tagExtractedName() { return tag_extracted_name_; } StatName truncatedStatName() { return stat_name_; } - private: +private: std::vector tags_; std::string tag_extracted_name_; std::unique_ptr truncated_name_storage_; @@ -317,13 +314,13 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counterx(StatName name) { tls_cache = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].counters_; } - return safeMakeStat( - final_name.statName(), central_cache_.counters_, - [](StatDataAllocator& allocator, StatName name, absl::string_view tag_extracted_name, - const std::vector& tags) -> CounterSharedPtr { - return allocator.makeCounter(name, tag_extracted_name, tags); - }, - tls_cache); + return safeMakeStat(final_name.statName(), central_cache_.counters_, + [](StatDataAllocator& allocator, StatName name, + absl::string_view tag_extracted_name, + const std::vector& tags) -> CounterSharedPtr { + return allocator.makeCounter(name, tag_extracted_name, tags); + }, + tls_cache); } void ThreadLocalStoreImpl::ScopeImpl::deliverHistogramToSinks(const Histogram& histogram, @@ -363,13 +360,13 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugex(StatName name) { tls_cache = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].gauges_; } - return safeMakeStat( - final_name.statName(), central_cache_.gauges_, - [](StatDataAllocator& allocator, StatName name, absl::string_view tag_extracted_name, - const std::vector& tags) -> GaugeSharedPtr { - return allocator.makeGauge(name, tag_extracted_name, tags); - }, - tls_cache); + return safeMakeStat(final_name.statName(), central_cache_.gauges_, + [](StatDataAllocator& allocator, StatName name, + absl::string_view tag_extracted_name, + const std::vector& tags) -> GaugeSharedPtr { + return allocator.makeGauge(name, tag_extracted_name, tags); + }, + tls_cache); } Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramx(StatName name) { @@ -406,9 +403,9 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramx(StatName name) { central_ref = &p->second; } else { TruncationExtraction extraction(parent_, final_stat_name); - auto stat = std::make_shared( - extraction.truncatedStatName(), parent_, *this, extraction.tagExtractedName(), - extraction.tags()); + auto stat = + std::make_shared(extraction.truncatedStatName(), parent_, *this, + extraction.tagExtractedName(), extraction.tags()); central_ref = ¢ral_cache_.histograms_[stat->statName()]; *central_ref = stat; } @@ -437,10 +434,10 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(StatName name, } std::vector tags; - std::string tag_extracted_name = parent_.tagProducer().produceTags( - name.toString(symbolTable()), tags); - TlsHistogramSharedPtr hist_tls_ptr = std::make_shared( - name, tag_extracted_name, tags, symbolTable()); + std::string tag_extracted_name = + parent_.tagProducer().produceTags(name.toString(symbolTable()), tags); + TlsHistogramSharedPtr hist_tls_ptr = + std::make_shared(name, tag_extracted_name, tags, symbolTable()); parent.addTlsHistogram(hist_tls_ptr); @@ -480,8 +477,8 @@ void ThreadLocalHistogramImpl::merge(histogram_t* target) { hist_clear(*other_histogram); } -ParentHistogramImpl::ParentHistogramImpl(StatName name, Store& parent, - TlsScope& tls_scope, absl::string_view tag_extracted_name, +ParentHistogramImpl::ParentHistogramImpl(StatName name, Store& parent, TlsScope& tls_scope, + absl::string_view tag_extracted_name, const std::vector& tags) : MetricImpl(tag_extracted_name, tags, parent.symbolTable()), parent_(parent), tls_scope_(tls_scope), interval_histogram_(hist_alloc()), cumulative_histogram_(hist_alloc()), diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 1ab812ff74cd..86276e7a0418 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -227,10 +227,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo } template - using MakeStatFn = - std::function(StatDataAllocator&, StatName name, - absl::string_view tag_extracted_name, - const std::vector& tags)>; + using MakeStatFn = std::function(StatDataAllocator&, StatName name, + absl::string_view tag_extracted_name, + const std::vector& tags)>; /** * Makes a stat either by looking it up in the central cache, @@ -244,14 +243,13 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo * used if non-empty, or filled in if empty (and non-null). */ template - StatType& - safeMakeStat(StatName name, StatMap>& central_cache_map, - MakeStatFn make_stat, StatMap>* tls_cache); - - void extractTagsAndTruncate( - StatName& name, std::unique_ptr& truncated_name_storage, - std::vector& tags, - std::string& tag_extracted_name); + StatType& safeMakeStat(StatName name, StatMap>& central_cache_map, + MakeStatFn make_stat, + StatMap>* tls_cache); + + void extractTagsAndTruncate(StatName& name, + std::unique_ptr& truncated_name_storage, + std::vector& tags, std::string& tag_extracted_name); static std::atomic next_scope_id_; diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 906e458e220d..368978622fc0 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -77,9 +77,8 @@ class HostDescriptionImpl : virtual public HostDescription { Config::MetadataEnvoyLbKeys::get().CANARY) .bool_value()), metadata_(std::make_shared(metadata)), - locality_(locality), - stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), - POOL_GAUGE(stats_store_))}, + locality_(locality), stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), + POOL_GAUGE(stats_store_))}, priority_(priority) {} // Upstream::HostDescription diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index e6aad776c8f1..4191744624d1 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -330,10 +330,9 @@ void HystrixSink::flush(Stats::Source& source) { if (histogram->tagExtractedName() == "cluster.upstream_rq_time") { // TODO(mrice32): add an Envoy utility function to look up and return a tag for a metric. auto tags = histogram->tags(); - auto it = std::find_if(tags.begin(), tags.end(), - [](const Stats::Tag& tag) { - return (tag.name_ == Config::TagNames::get().CLUSTER_NAME); - }); + auto it = std::find_if(tags.begin(), tags.end(), [](const Stats::Tag& tag) { + return (tag.name_ == Config::TagNames::get().CLUSTER_NAME); + }); // Make sure we found the cluster name tag ASSERT(it != histogram->tags().end()); diff --git a/source/server/hot_restart_impl.cc b/source/server/hot_restart_impl.cc index 64e8c23d7f41..c50cf95fd354 100644 --- a/source/server/hot_restart_impl.cc +++ b/source/server/hot_restart_impl.cc @@ -121,8 +121,8 @@ std::string SharedMemory::version(uint64_t max_num_stats, } HotRestartImpl::HotRestartImpl(Options& options, Stats::SymbolTable& symbol_table) - : Stats::RawStatDataAllocator(symbol_table), - options_(options), stats_set_options_(blockMemHashOptions(options.maxStats())), + : Stats::RawStatDataAllocator(symbol_table), options_(options), + stats_set_options_(blockMemHashOptions(options.maxStats())), shmem_(SharedMemory::initialize( RawStatDataSet::numBytes(stats_set_options_, options_.statsOptions()), options_)), log_lock_(shmem_.log_lock_), access_log_lock_(shmem_.access_log_lock_), diff --git a/test/extensions/stats_sinks/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/hystrix/hystrix_test.cc index fba640b28bd9..392cdb3088d0 100644 --- a/test/extensions/stats_sinks/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/hystrix/hystrix_test.cc @@ -461,8 +461,7 @@ TEST_F(HystrixSinkTest, HistogramTest) { auto histogram = std::make_shared>(); histogram->name_ = "cluster." + cluster1_name_ + ".upstream_rq_time"; histogram->tag_extracted_name_ = "cluster.upstream_rq_time"; - histogram->tags_.emplace_back( - Stats::Tag{.name_ = Config::TagNames::get().CLUSTER_NAME, .value_ = cluster1_name_ }); + histogram->tags_.emplace_back(Stats::Tag{Config::TagNames::get().CLUSTER_NAME, cluster1_name_}); histogram->used_ = true; // Init with data such that the quantile value is equal to the quantile. diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 6eeeb61fdfc7..22ec895a02a8 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -14,12 +14,12 @@ namespace Envoy { namespace Stats { MockMetric::MockMetric() : symbol_table_(nullptr), name_(*this) { - //ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); - //ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); + // ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); + // ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); } MockMetric::MockMetric(SymbolTable& symbol_table) : symbol_table_(&symbol_table), name_(*this) { - //ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); - //ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); + // ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); + // ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); } MockMetric::~MockMetric() {} @@ -84,8 +84,7 @@ MockParentHistogram::MockParentHistogram() { ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); } -MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) - : MockMetric(symbol_table) { +MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { if (store_ != nullptr) { store_->deliverHistogramToSinks(*this, value); diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index db3fc72af878..6609f2c53565 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -23,7 +23,7 @@ namespace Envoy { namespace Stats { class MockMetric : public virtual Metric { - public: +public: explicit MockMetric(SymbolTable& symbol_table); MockMetric(); ~MockMetric(); @@ -32,7 +32,7 @@ class MockMetric : public virtual Metric { // do metric->name_ = "foo" even though names are more complex now. Note // that the statName is only populated if there is a symbol table. class MetricName { - public: + public: explicit MetricName(MockMetric& mock_metric) : mock_metric_(mock_metric) {} ~MetricName(); @@ -41,7 +41,7 @@ class MockMetric : public virtual Metric { std::string name() const { return name_; } StatName statName() const { return stat_name_storage_->statName(); } - private: + private: MockMetric& mock_metric_; std::string name_; std::unique_ptr stat_name_storage_; @@ -54,12 +54,12 @@ class MockMetric : public virtual Metric { // creates a deadlock in gmock and is an unintended use of mock functions. std::string name() const override { return name_.name(); } StatName statName() const override { return name_.statName(); } - //MOCK_CONST_METHOD0(tags, std::vector()); + // MOCK_CONST_METHOD0(tags, std::vector()); std::vector tags() const override { return tags_; } std::string tagExtractedName() const override { return tag_extracted_name_.empty() ? name() : tag_extracted_name_; } - //MOCK_CONST_METHOD0(tagExtractedName, std::string()); + // MOCK_CONST_METHOD0(tagExtractedName, std::string()); SymbolTable* symbol_table_; MetricName name_; From 524cf02cebcab2a0d34c8ae5373b51f649647835 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 6 Nov 2018 20:34:41 -0500 Subject: [PATCH 005/106] format Signed-off-by: Joshua Marantz --- source/exe/main_common.cc | 160 +++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index aa98a892e316..0d6d699d6b6a 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -60,114 +60,114 @@ MainCommonBase::MainCommonBase(OptionsImpl& options, Event::TimeSystem& time_sys if (restarter_.get() == nullptr) { restarter_.reset(new Server::HotRestartNopImpl(symbol_table_)); ======= - restarter_ = std::make_unique(options_); - } + restarter_ = std::make_unique(options_); + } #endif - if (restarter_ == nullptr) { - restarter_ = std::make_unique(); + if (restarter_ == nullptr) { + restarter_ = std::make_unique(); >>>>>>> master - } + } - tls_ = std::make_unique(); - Thread::BasicLockable& log_lock = restarter_->logLock(); - Thread::BasicLockable& access_log_lock = restarter_->accessLogLock(); - auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); - logging_context_ = - std::make_unique(options_.logLevel(), options_.logFormat(), log_lock); + tls_ = std::make_unique(); + Thread::BasicLockable& log_lock = restarter_->logLock(); + Thread::BasicLockable& access_log_lock = restarter_->accessLogLock(); + auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); + logging_context_ = + std::make_unique(options_.logLevel(), options_.logFormat(), log_lock); - configureComponentLogLevels(); + configureComponentLogLevels(); - stats_store_ = std::make_unique(options_.statsOptions(), - restarter_->statsAllocator()); + stats_store_ = std::make_unique(options_.statsOptions(), + restarter_->statsAllocator()); - server_ = std::make_unique( - options_, time_system, local_address, test_hooks, *restarter_, *stats_store_, - access_log_lock, component_factory, std::move(random_generator), *tls_); + server_ = std::make_unique( + options_, time_system, local_address, test_hooks, *restarter_, *stats_store_, + access_log_lock, component_factory, std::move(random_generator), *tls_); - break; - } + break; + } case Server::Mode::Validate: restarter_ = std::make_unique(symbol_table_); logging_context_ = std::make_unique(options_.logLevel(), options_.logFormat(), restarter_->logLock()); break; } -} + } // namespace Envoy -MainCommonBase::~MainCommonBase() { ares_library_cleanup(); } + MainCommonBase::~MainCommonBase() { ares_library_cleanup(); } -void MainCommonBase::configureComponentLogLevels() { - for (auto& component_log_level : options_.componentLogLevels()) { - Logger::Logger* logger_to_change = Logger::Registry::logger(component_log_level.first); - ASSERT(logger_to_change); - logger_to_change->setLevel(component_log_level.second); + void MainCommonBase::configureComponentLogLevels() { + for (auto& component_log_level : options_.componentLogLevels()) { + Logger::Logger* logger_to_change = Logger::Registry::logger(component_log_level.first); + ASSERT(logger_to_change); + logger_to_change->setLevel(component_log_level.second); + } } -} -bool MainCommonBase::run() { - switch (options_.mode()) { - case Server::Mode::Serve: - server_->run(); - return true; - case Server::Mode::Validate: { - auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); - return Server::validateConfig(options_, local_address, component_factory_); - } - case Server::Mode::InitOnly: - PERF_DUMP(); - return true; + bool MainCommonBase::run() { + switch (options_.mode()) { + case Server::Mode::Serve: + server_->run(); + return true; + case Server::Mode::Validate: { + auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); + return Server::validateConfig(options_, local_address, component_factory_); + } + case Server::Mode::InitOnly: + PERF_DUMP(); + return true; + } + NOT_REACHED_GCOVR_EXCL_LINE; } - NOT_REACHED_GCOVR_EXCL_LINE; -} -void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string_view method, - const AdminRequestFn& handler) { - std::string path_and_query_buf = std::string(path_and_query); - std::string method_buf = std::string(method); - server_->dispatcher().post([this, path_and_query_buf, method_buf, handler]() { - Http::HeaderMapImpl response_headers; - std::string body; - server_->admin().request(path_and_query_buf, method_buf, response_headers, body); - handler(response_headers, body); - }); -} + void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string_view method, + const AdminRequestFn& handler) { + std::string path_and_query_buf = std::string(path_and_query); + std::string method_buf = std::string(method); + server_->dispatcher().post([this, path_and_query_buf, method_buf, handler]() { + Http::HeaderMapImpl response_headers; + std::string body; + server_->admin().request(path_and_query_buf, method_buf, response_headers, body); + handler(response_headers, body); + }); + } -MainCommon::MainCommon(int argc, const char* const* argv) - : options_(argc, argv, &MainCommon::hotRestartVersion, spdlog::level::info), - base_(options_, real_time_system_, default_test_hooks_, prod_component_factory_, - std::make_unique()) {} + MainCommon::MainCommon(int argc, const char* const* argv) + : options_(argc, argv, &MainCommon::hotRestartVersion, spdlog::level::info), + base_(options_, real_time_system_, default_test_hooks_, prod_component_factory_, + std::make_unique()) {} -std::string MainCommon::hotRestartVersion(uint64_t max_num_stats, uint64_t max_stat_name_len, - bool hot_restart_enabled) { + std::string MainCommon::hotRestartVersion(uint64_t max_num_stats, uint64_t max_stat_name_len, + bool hot_restart_enabled) { #ifdef ENVOY_HOT_RESTART - if (hot_restart_enabled) { - return Server::HotRestartImpl::hotRestartVersion(max_num_stats, max_stat_name_len); - } + if (hot_restart_enabled) { + return Server::HotRestartImpl::hotRestartVersion(max_num_stats, max_stat_name_len); + } #else UNREFERENCED_PARAMETER(hot_restart_enabled); UNREFERENCED_PARAMETER(max_num_stats); UNREFERENCED_PARAMETER(max_stat_name_len); #endif - return "disabled"; -} + return "disabled"; + } -// Legacy implementation of main_common. -// -// TODO(jmarantz): Remove this when all callers are removed. At that time, MainCommonBase -// and MainCommon can be merged. The current theory is that only Google calls this. -int main_common(OptionsImpl& options) { - try { - Event::RealTimeSystem real_time_system_; - DefaultTestHooks default_test_hooks_; - ProdComponentFactory prod_component_factory_; - MainCommonBase main_common(options, real_time_system_, default_test_hooks_, - prod_component_factory_, - std::make_unique()); - return main_common.run() ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (EnvoyException& e) { - return EXIT_FAILURE; + // Legacy implementation of main_common. + // + // TODO(jmarantz): Remove this when all callers are removed. At that time, MainCommonBase + // and MainCommon can be merged. The current theory is that only Google calls this. + int main_common(OptionsImpl & options) { + try { + Event::RealTimeSystem real_time_system_; + DefaultTestHooks default_test_hooks_; + ProdComponentFactory prod_component_factory_; + MainCommonBase main_common(options, real_time_system_, default_test_hooks_, + prod_component_factory_, + std::make_unique()); + return main_common.run() ? EXIT_SUCCESS : EXIT_FAILURE; + } catch (EnvoyException& e) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } - return EXIT_SUCCESS; -} } // namespace Envoy From bb84b905178b65522e1eb84456fd57da7feb8404 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 6 Nov 2018 21:06:49 -0500 Subject: [PATCH 006/106] fix botched merge. Signed-off-by: Joshua Marantz --- source/exe/main_common.cc | 166 ++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 87 deletions(-) diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index 0d6d699d6b6a..c8488aab8e77 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -53,121 +53,113 @@ MainCommonBase::MainCommonBase(OptionsImpl& options, Event::TimeSystem& time_sys case Server::Mode::Serve: { #ifdef ENVOY_HOT_RESTART if (!options.hotRestartDisabled()) { -<<<<<<< HEAD - restarter_.reset(new Server::HotRestartImpl(options_, symbol_table_)); + restarter_ = std::make_unique(options_, symbol_table_); } #endif - if (restarter_.get() == nullptr) { - restarter_.reset(new Server::HotRestartNopImpl(symbol_table_)); -======= - restarter_ = std::make_unique(options_); - } -#endif - if (restarter_ == nullptr) { - restarter_ = std::make_unique(); ->>>>>>> master - } + if (restarter_ == nullptr) { + restarter_ = std::make_unique(symbol_table_); + } - tls_ = std::make_unique(); - Thread::BasicLockable& log_lock = restarter_->logLock(); - Thread::BasicLockable& access_log_lock = restarter_->accessLogLock(); - auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); - logging_context_ = - std::make_unique(options_.logLevel(), options_.logFormat(), log_lock); + tls_ = std::make_unique(); + Thread::BasicLockable& log_lock = restarter_->logLock(); + Thread::BasicLockable& access_log_lock = restarter_->accessLogLock(); + auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); + logging_context_ = + std::make_unique(options_.logLevel(), options_.logFormat(), log_lock); - configureComponentLogLevels(); + configureComponentLogLevels(); - stats_store_ = std::make_unique(options_.statsOptions(), - restarter_->statsAllocator()); + stats_store_ = std::make_unique(options_.statsOptions(), + restarter_->statsAllocator()); - server_ = std::make_unique( - options_, time_system, local_address, test_hooks, *restarter_, *stats_store_, - access_log_lock, component_factory, std::move(random_generator), *tls_); + server_ = std::make_unique( + options_, time_system, local_address, test_hooks, *restarter_, *stats_store_, + access_log_lock, component_factory, std::move(random_generator), *tls_); - break; - } + break; + } case Server::Mode::Validate: restarter_ = std::make_unique(symbol_table_); logging_context_ = std::make_unique(options_.logLevel(), options_.logFormat(), restarter_->logLock()); break; } - } // namespace Envoy +} - MainCommonBase::~MainCommonBase() { ares_library_cleanup(); } +MainCommonBase::~MainCommonBase() { ares_library_cleanup(); } - void MainCommonBase::configureComponentLogLevels() { - for (auto& component_log_level : options_.componentLogLevels()) { - Logger::Logger* logger_to_change = Logger::Registry::logger(component_log_level.first); - ASSERT(logger_to_change); - logger_to_change->setLevel(component_log_level.second); - } +void MainCommonBase::configureComponentLogLevels() { + for (auto& component_log_level : options_.componentLogLevels()) { + Logger::Logger* logger_to_change = Logger::Registry::logger(component_log_level.first); + ASSERT(logger_to_change); + logger_to_change->setLevel(component_log_level.second); } +} - bool MainCommonBase::run() { - switch (options_.mode()) { - case Server::Mode::Serve: - server_->run(); - return true; - case Server::Mode::Validate: { - auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); - return Server::validateConfig(options_, local_address, component_factory_); - } - case Server::Mode::InitOnly: - PERF_DUMP(); - return true; - } - NOT_REACHED_GCOVR_EXCL_LINE; +bool MainCommonBase::run() { + switch (options_.mode()) { + case Server::Mode::Serve: + server_->run(); + return true; + case Server::Mode::Validate: { + auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); + return Server::validateConfig(options_, local_address, component_factory_); } - - void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string_view method, - const AdminRequestFn& handler) { - std::string path_and_query_buf = std::string(path_and_query); - std::string method_buf = std::string(method); - server_->dispatcher().post([this, path_and_query_buf, method_buf, handler]() { - Http::HeaderMapImpl response_headers; - std::string body; - server_->admin().request(path_and_query_buf, method_buf, response_headers, body); - handler(response_headers, body); - }); + case Server::Mode::InitOnly: + PERF_DUMP(); + return true; } + NOT_REACHED_GCOVR_EXCL_LINE; +} - MainCommon::MainCommon(int argc, const char* const* argv) - : options_(argc, argv, &MainCommon::hotRestartVersion, spdlog::level::info), - base_(options_, real_time_system_, default_test_hooks_, prod_component_factory_, - std::make_unique()) {} +void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string_view method, + const AdminRequestFn& handler) { + std::string path_and_query_buf = std::string(path_and_query); + std::string method_buf = std::string(method); + server_->dispatcher().post([this, path_and_query_buf, method_buf, handler]() { + Http::HeaderMapImpl response_headers; + std::string body; + server_->admin().request(path_and_query_buf, method_buf, response_headers, body); + handler(response_headers, body); + }); +} + +MainCommon::MainCommon(int argc, const char* const* argv) + : options_(argc, argv, &MainCommon::hotRestartVersion, spdlog::level::info), + base_(options_, real_time_system_, default_test_hooks_, prod_component_factory_, + std::make_unique()) {} - std::string MainCommon::hotRestartVersion(uint64_t max_num_stats, uint64_t max_stat_name_len, - bool hot_restart_enabled) { +std::string MainCommon::hotRestartVersion(uint64_t max_num_stats, uint64_t max_stat_name_len, + bool hot_restart_enabled) { #ifdef ENVOY_HOT_RESTART - if (hot_restart_enabled) { - return Server::HotRestartImpl::hotRestartVersion(max_num_stats, max_stat_name_len); - } + if (hot_restart_enabled) { + return Server::HotRestartImpl::hotRestartVersion(max_num_stats, max_stat_name_len); + } #else UNREFERENCED_PARAMETER(hot_restart_enabled); UNREFERENCED_PARAMETER(max_num_stats); UNREFERENCED_PARAMETER(max_stat_name_len); #endif - return "disabled"; - } + return "disabled"; +} - // Legacy implementation of main_common. - // - // TODO(jmarantz): Remove this when all callers are removed. At that time, MainCommonBase - // and MainCommon can be merged. The current theory is that only Google calls this. - int main_common(OptionsImpl & options) { - try { - Event::RealTimeSystem real_time_system_; - DefaultTestHooks default_test_hooks_; - ProdComponentFactory prod_component_factory_; - MainCommonBase main_common(options, real_time_system_, default_test_hooks_, - prod_component_factory_, - std::make_unique()); - return main_common.run() ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (EnvoyException& e) { - return EXIT_FAILURE; - } - return EXIT_SUCCESS; +// Legacy implementation of main_common. +// +// TODO(jmarantz): Remove this when all callers are removed. At that time, MainCommonBase +// and MainCommon can be merged. The current theory is that only Google calls this. +int main_common(OptionsImpl& options) { + try { + Event::RealTimeSystem real_time_system_; + DefaultTestHooks default_test_hooks_; + ProdComponentFactory prod_component_factory_; + MainCommonBase main_common(options, real_time_system_, default_test_hooks_, + prod_component_factory_, + std::make_unique()); + return main_common.run() ? EXIT_SUCCESS : EXIT_FAILURE; + } catch (EnvoyException& e) { + return EXIT_FAILURE; } + return EXIT_SUCCESS; +} } // namespace Envoy From f9c04412471010549c91f78b2eb91fbcf89ca42c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 7 Nov 2018 16:12:10 -0500 Subject: [PATCH 007/106] perf test and tweaks Signed-off-by: Joshua Marantz --- include/envoy/stats/stats_matcher.h | 3 ++ source/common/stats/stats_matcher_impl.h | 8 ++- source/common/stats/thread_local_store.cc | 11 +++- source/common/stats/thread_local_store.h | 12 ++--- test/common/stats/stats_matcher_impl_test.cc | 51 ++++++++++++++++++- .../stats/thread_local_store_speed_test.cc | 14 ++++- test/common/stats/thread_local_store_test.cc | 4 +- 7 files changed, 85 insertions(+), 18 deletions(-) diff --git a/include/envoy/stats/stats_matcher.h b/include/envoy/stats/stats_matcher.h index b233644230e2..09516b165d33 100644 --- a/include/envoy/stats/stats_matcher.h +++ b/include/envoy/stats/stats_matcher.h @@ -19,6 +19,9 @@ class StatsMatcher { * @return bool true if that stat should not be instantiated. */ virtual bool rejects(const std::string& name) const PURE; + + virtual bool acceptsAll() const PURE; + virtual bool rejectsAll() const PURE; }; typedef std::unique_ptr StatsMatcherPtr; diff --git a/source/common/stats/stats_matcher_impl.h b/source/common/stats/stats_matcher_impl.h index a4b7a27fa82c..10658fd87e57 100644 --- a/source/common/stats/stats_matcher_impl.h +++ b/source/common/stats/stats_matcher_impl.h @@ -23,12 +23,10 @@ class StatsMatcherImpl : public StatsMatcher { // Default constructor simply allows everything. StatsMatcherImpl() : is_inclusive_(true) {} - /** - * Take a metric name and report whether or not it should be disallowed. - * @param the name of a Stats::Metric. - * @return bool true if that stat should not be instantiated. - */ + // StatsMatcher bool rejects(const std::string& name) const override; + bool acceptsAll() const override { return is_inclusive_ && matchers_.empty(); } + bool rejectsAll() const override { return !is_inclusive_ && matchers_.empty(); } private: // Bool indicating whether or not the StatsMatcher is including or excluding stats by default. See diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 530ebf8133a8..1072f8dec77c 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -303,8 +303,15 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counterx(StatName name) { // TODO(ambuc): If stats_matcher_ depends on regexes, this operation (on the hot path) could // become prohibitively expensive. Revisit this usage in the future. - if (parent_.stats_matcher_->rejects(final_name.statName().toString(symbolTable()))) { - return parent_.null_counter_; + // + // Also note that the elaboration of the stat-name into a string is expensive, + // so I think it might be better to move the matcher test until after caching, + // unless its acceptsAll/rejectsAll. + const StatsMatcher& matcher = *parent_.stats_matcher_; + if (!matcher.acceptsAll()) { + if (matcher.rejectsAll() || matcher.rejects(final_name.statName().toString(symbolTable()))) { + return parent_.null_counter_; + } } // We now find the TLS cache. This might remain null if we don't have TLS diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 86276e7a0418..a6ff06429a5b 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -31,7 +31,7 @@ class ThreadLocalHistogramImpl : public Histogram, public MetricImpl { public: ThreadLocalHistogramImpl(StatName name, absl::string_view tag_extracted_name, const std::vector& tags, SymbolTable& symbol_table); - ~ThreadLocalHistogramImpl(); + ~ThreadLocalHistogramImpl() override; void merge(histogram_t* target); @@ -64,7 +64,7 @@ class ThreadLocalHistogramImpl : public Histogram, public MetricImpl { SymbolTable& symbol_table_; }; -typedef std::shared_ptr TlsHistogramSharedPtr; +using TlsHistogramSharedPtr = std::shared_ptr; class TlsScope; @@ -75,7 +75,7 @@ class ParentHistogramImpl : public ParentHistogram, public MetricImpl { public: ParentHistogramImpl(StatName name, Store& parent, TlsScope& tlsScope, absl::string_view tag_extracted_name, const std::vector& tags); - ~ParentHistogramImpl(); + ~ParentHistogramImpl() override; void addTlsHistogram(const TlsHistogramSharedPtr& hist_ptr); bool used() const override; @@ -115,7 +115,7 @@ class ParentHistogramImpl : public ParentHistogram, public MetricImpl { StatNameStorage name_; }; -typedef std::shared_ptr ParentHistogramImplSharedPtr; +using ParentHistogramImplSharedPtr = std::shared_ptr; /** * Class used to create ThreadLocalHistogram in the scope. @@ -139,7 +139,7 @@ class TlsScope : public Scope { class ThreadLocalStoreImpl : Logger::Loggable, public StoreRoot { public: ThreadLocalStoreImpl(const Stats::StatsOptions& stats_options, StatDataAllocator& alloc); - ~ThreadLocalStoreImpl(); + ~ThreadLocalStoreImpl() override; // Stats::Scope Counter& counterx(StatName name) override { return default_scope_->counterx(name); } @@ -198,7 +198,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo struct ScopeImpl : public TlsScope { ScopeImpl(ThreadLocalStoreImpl& parent, const std::string& prefix); - ~ScopeImpl(); + ~ScopeImpl() override; // Stats::Scope Counter& counterx(StatName name) override; diff --git a/test/common/stats/stats_matcher_impl_test.cc b/test/common/stats/stats_matcher_impl_test.cc index 3ea99efd4192..1c9efb8aaabb 100644 --- a/test/common/stats/stats_matcher_impl_test.cc +++ b/test/common/stats/stats_matcher_impl_test.cc @@ -35,15 +35,18 @@ class StatsMatcherTest : public testing::Test { } } + std::unique_ptr stats_matcher_impl_; + private: envoy::config::metrics::v2::StatsConfig stats_config_; - std::unique_ptr stats_matcher_impl_; }; TEST_F(StatsMatcherTest, CheckDefault) { // With no set fields, everything should be allowed through. initMatcher(); expectAccepted({"foo", "bar", "foo.bar", "foo.bar.baz", "foobarbaz"}); + EXPECT_TRUE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Across-the-board matchers. @@ -53,6 +56,8 @@ TEST_F(StatsMatcherTest, CheckRejectAll) { rejectAll(true); initMatcher(); expectDenied({"foo", "bar", "foo.bar", "foo.bar.baz", "foobarbaz"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_TRUE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckNotRejectAll) { @@ -60,18 +65,26 @@ TEST_F(StatsMatcherTest, CheckNotRejectAll) { rejectAll(false); initMatcher(); expectAccepted({"foo", "bar", "foo.bar", "foo.bar.baz", "foobarbaz"}); + EXPECT_TRUE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckIncludeAll) { inclusionList()->set_regex(".*"); initMatcher(); expectAccepted({"foo", "bar", "foo.bar", "foo.bar.baz"}); + // It really does accept all, but we impl doesn't know it. + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + // Really does reject all, but we can't tell from API. + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckExcludeAll) { exclusionList()->set_regex(".*"); initMatcher(); expectDenied({"foo", "bar", "foo.bar", "foo.bar.baz"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Single exact matchers. @@ -82,6 +95,8 @@ TEST_F(StatsMatcherTest, CheckIncludeExact) { expectAccepted({"abc"}); expectDenied({"abcd", "abc.d", "d.abc", "dabc", "ab", "ac", "abcc", "Abc", "aBc", "abC", "abc.", ".abc", "ABC"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckExcludeExact) { @@ -90,6 +105,8 @@ TEST_F(StatsMatcherTest, CheckExcludeExact) { expectAccepted({"abcd", "abc.d", "d.abc", "dabc", "ab", "ac", "abcc", "Abc", "aBc", "abC", "abc.", ".abc", "ABC"}); expectDenied({"abc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Single prefix matchers. @@ -99,6 +116,8 @@ TEST_F(StatsMatcherTest, CheckIncludePrefix) { initMatcher(); expectAccepted({"abc", "abc.foo", "abcfoo"}); expectDenied({"ABC", "ABC.foo", "ABCfoo", "foo", "abb", "a.b.c", "_abc", "foo.abc", "fooabc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckExcludePrefix) { @@ -106,6 +125,8 @@ TEST_F(StatsMatcherTest, CheckExcludePrefix) { initMatcher(); expectAccepted({"ABC", "ABC.foo", "ABCfoo", "foo", "abb", "a.b.c", "_abc", "foo.abc", "fooabc"}); expectDenied({"abc", "abc.foo", "abcfoo"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Single suffix matchers. @@ -115,6 +136,8 @@ TEST_F(StatsMatcherTest, CheckIncludeSuffix) { initMatcher(); expectAccepted({"abc", "foo.abc", "fooabc"}); expectDenied({"ABC", "foo.ABC", "fooABC", "foo", "abb", "a.b.c", "abc_", "abc.foo", "abcfoo"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckExcludeSuffix) { @@ -122,6 +145,8 @@ TEST_F(StatsMatcherTest, CheckExcludeSuffix) { initMatcher(); expectAccepted({"ABC", "foo.ABC", "fooABC", "foo", "abb", "a.b.c", "abc_", "abc.foo", "abcfoo"}); expectDenied({"abc", "foo.abc", "fooabc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Single regex matchers. @@ -131,6 +156,8 @@ TEST_F(StatsMatcherTest, CheckIncludeRegex) { initMatcher(); expectAccepted({"envoy.matchers.requests", "stats.envoy.2xx", "regex.envoy.matchers"}); expectDenied({"foo", "Envoy", "EnvoyProxy"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckExcludeRegex) { @@ -138,6 +165,8 @@ TEST_F(StatsMatcherTest, CheckExcludeRegex) { initMatcher(); expectAccepted({"foo", "Envoy", "EnvoyProxy"}); expectDenied({"envoy.matchers.requests", "stats.envoy.2xx", "regex.envoy.matchers"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Multiple exact matchers. @@ -148,6 +177,8 @@ TEST_F(StatsMatcherTest, CheckMultipleIncludeExact) { initMatcher(); expectAccepted({"foo", "bar"}); expectDenied({"foobar", "barfoo", "fo", "ba", "foo.bar"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckMultipleExcludeExact) { @@ -156,6 +187,8 @@ TEST_F(StatsMatcherTest, CheckMultipleExcludeExact) { initMatcher(); expectAccepted({"foobar", "barfoo", "fo", "ba", "foo.bar"}); expectDenied({"foo", "bar"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Multiple prefix matchers. @@ -166,6 +199,8 @@ TEST_F(StatsMatcherTest, CheckMultipleIncludePrefix) { initMatcher(); expectAccepted({"foo", "foo.abc", "bar", "bar.abc"}); expectDenied({".foo", "abc.foo", "BAR", "_bar"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckMultipleExcludePrefix) { @@ -174,6 +209,8 @@ TEST_F(StatsMatcherTest, CheckMultipleExcludePrefix) { initMatcher(); expectAccepted({".foo", "abc.foo", "BAR", "_bar"}); expectDenied({"foo", "foo.abc", "bar", "bar.abc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Multiple suffix matchers. @@ -185,6 +222,8 @@ TEST_F(StatsMatcherTest, CheckMultipleIncludeSuffix) { expectAccepted( {"requests.for.spam", "requests.for.eggs", "spam", "eggs", "cannedspam", "fresheggs"}); expectDenied({"Spam", "EGGS", "spam_", "eggs_"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckMultipleExcludeSuffix) { @@ -194,6 +233,8 @@ TEST_F(StatsMatcherTest, CheckMultipleExcludeSuffix) { expectAccepted({"Spam", "EGGS", "spam_", "eggs_"}); expectDenied( {"requests.for.spam", "requests.for.eggs", "spam", "eggs", "cannedspam", "fresheggs"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Multiple regex matchers. @@ -204,6 +245,8 @@ TEST_F(StatsMatcherTest, CheckMultipleIncludeRegex) { initMatcher(); expectAccepted({"envoy.matchers.requests", "stats.absl.2xx", "absl.envoy.matchers"}); expectDenied({"Abseil", "EnvoyProxy"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckMultipleExcludeRegex) { @@ -212,6 +255,8 @@ TEST_F(StatsMatcherTest, CheckMultipleExcludeRegex) { initMatcher(); expectAccepted({"Abseil", "EnvoyProxy"}); expectDenied({"envoy.matchers.requests", "stats.absl.2xx", "absl.envoy.matchers"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } // Multiple prefix/suffix/regex matchers. @@ -226,6 +271,8 @@ TEST_F(StatsMatcherTest, CheckMultipleAssortedInclusionMatchers) { initMatcher(); expectAccepted({"envoy.matchers.requests", "requests.for.envoy", "envoyrequests", "regex"}); expectDenied({"requestsEnvoy", "EnvoyProxy", "foo", "regex_etc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } TEST_F(StatsMatcherTest, CheckMultipleAssortedExclusionMatchers) { @@ -235,6 +282,8 @@ TEST_F(StatsMatcherTest, CheckMultipleAssortedExclusionMatchers) { initMatcher(); expectAccepted({"requestsEnvoy", "EnvoyProxy", "foo", "regex_etc"}); expectDenied({"envoy.matchers.requests", "requests.for.envoy", "envoyrequests", "regex"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } } // namespace Stats diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 136476c1bd46..24144571bc71 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -21,9 +21,17 @@ class ThreadLocalStorePerf { public: ThreadLocalStorePerf() : heap_alloc_(symbol_table_), store_(options_, heap_alloc_) { store_.setTagProducer(std::make_unique(stats_config_)); + + Stats::TestUtil::forEachSampleStat( + 1000, [this](absl::string_view name) { + stat_names_.push_back(std::make_unique(name, symbol_table_)); + }); } ~ThreadLocalStorePerf() { + for (auto& stat_name_storage : stat_names_) { + stat_name_storage->free(symbol_table_); + } store_.shutdownThreading(); if (tls_) { tls_->shutdownGlobalThreading(); @@ -31,8 +39,9 @@ class ThreadLocalStorePerf { } void accessCounters() { - Stats::TestUtil::forEachSampleStat( - 1000, [this](absl::string_view name) { store_.counter(std::string(name)); }); + for (auto& stat_name_storage : stat_names_) { + store_.counterx(stat_name_storage->statName()); + } } void initThreading() { @@ -50,6 +59,7 @@ class ThreadLocalStorePerf { std::unique_ptr tls_; Stats::ThreadLocalStoreImpl store_; envoy::config::metrics::v2::StatsConfig stats_config_; + std::vector> stat_names_; }; } // namespace Envoy diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 0bc484746555..0130cce64831 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -668,7 +668,7 @@ TEST_F(HeapStatsThreadLocalStoreTest, MemoryWithoutTls) { 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); const size_t end_mem = Memory::Stats::totalCurrentlyAllocated(); EXPECT_LT(start_mem, end_mem); - EXPECT_LT(end_mem - start_mem, 28 * million); // actual value: 27203216 as of Oct 29, 2018 + EXPECT_LT(end_mem - start_mem, 13 * million); // actual value: 12443472 as of Nov 7, 2018 } TEST_F(HeapStatsThreadLocalStoreTest, MemoryWithTls) { @@ -691,7 +691,7 @@ TEST_F(HeapStatsThreadLocalStoreTest, MemoryWithTls) { 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); const size_t end_mem = Memory::Stats::totalCurrentlyAllocated(); EXPECT_LT(start_mem, end_mem); - EXPECT_LT(end_mem - start_mem, 31 * million); // actual value: 30482576 as of Oct 29, 2018 + EXPECT_LT(end_mem - start_mem, 16 * million); // actual value: 15722832 as of Nov 7, 2018 } TEST_F(StatsThreadLocalStoreTest, ShuttingDown) { From 7c5ac2f76e07966815b250d0c97289086664d8c7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 7 Nov 2018 16:41:48 -0500 Subject: [PATCH 008/106] formatting, comments, and tests. Signed-off-by: Joshua Marantz --- include/envoy/stats/stats_matcher.h | 7 ++++ source/common/stats/thread_local_store.cc | 42 +++++++++++-------- source/common/stats/thread_local_store.h | 1 + .../stats/thread_local_store_speed_test.cc | 7 ++-- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/include/envoy/stats/stats_matcher.h b/include/envoy/stats/stats_matcher.h index 09516b165d33..8b8d7ab6d69c 100644 --- a/include/envoy/stats/stats_matcher.h +++ b/include/envoy/stats/stats_matcher.h @@ -20,7 +20,14 @@ class StatsMatcher { */ virtual bool rejects(const std::string& name) const PURE; + /** + * @return bool whether StatsMatcher trivially accepts all stats. + */ virtual bool acceptsAll() const PURE; + + /** + * @return bool whether StatsMatcher trivially rejects all stats. + */ virtual bool rejectsAll() const PURE; }; diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 1072f8dec77c..7a079b592050 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -39,6 +39,23 @@ ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { stats_overflow_.free(symbolTable()); } +bool ThreadLocalStoreImpl::rejects(StatName stat_name) const { + // Don't both elaborating the StatName there are no pattern-based + // exclusions;/inclusions. + if (stats_matcher_->acceptsAll()) { + return false; + } + + // TODO(ambuc): If stats_matcher_ depends on regexes, this operation (on the + // hot path) could become prohibitively expensive. Revisit this usage in the + // future. + // + // Also note that the elaboration of the stat-name into a string is expensive, + // so I think it might be better to move the matcher test until after caching, + // unless its acceptsAll/rejectsAll. + return stats_matcher_->rejectsAll() || stats_matcher_->rejects(stat_name.toString(symbolTable())); +} + std::vector ThreadLocalStoreImpl::counters() const { // Handle de-dup due to overlapping scopes. std::vector ret; @@ -300,18 +317,10 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counterx(StatName name) { // strategy costs an extra hash lookup for each miss, but saves time // re-copying the string and significant memory overhead. StatNameJoiner final_name(prefix_.statName(), name); + StatName final_stat_name = final_name.statName(); - // TODO(ambuc): If stats_matcher_ depends on regexes, this operation (on the hot path) could - // become prohibitively expensive. Revisit this usage in the future. - // - // Also note that the elaboration of the stat-name into a string is expensive, - // so I think it might be better to move the matcher test until after caching, - // unless its acceptsAll/rejectsAll. - const StatsMatcher& matcher = *parent_.stats_matcher_; - if (!matcher.acceptsAll()) { - if (matcher.rejectsAll() || matcher.rejects(final_name.statName().toString(symbolTable()))) { - return parent_.null_counter_; - } + if (parent_.rejects(final_stat_name)) { + return parent_.null_counter_; } // We now find the TLS cache. This might remain null if we don't have TLS @@ -321,7 +330,7 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counterx(StatName name) { tls_cache = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].counters_; } - return safeMakeStat(final_name.statName(), central_cache_.counters_, + return safeMakeStat(final_stat_name, central_cache_.counters_, [](StatDataAllocator& allocator, StatName name, absl::string_view tag_extracted_name, const std::vector& tags) -> CounterSharedPtr { @@ -356,9 +365,9 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugex(StatName name) { // do a find() first, using tha if it succeeds. If it fails, then after we // construct the stat we can insert it into the required maps. StatNameJoiner final_name(prefix_.statName(), name); + StatName final_stat_name = final_name.statName(); - // See warning/comments in counter(). - if (parent_.stats_matcher_->rejects(final_name.statName().toString(symbolTable()))) { + if (parent_.rejects(final_stat_name)) { return parent_.null_gauge_; } @@ -367,7 +376,7 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugex(StatName name) { tls_cache = &parent_.tls_->getTyped().scope_cache_[this->scope_id_].gauges_; } - return safeMakeStat(final_name.statName(), central_cache_.gauges_, + return safeMakeStat(final_stat_name, central_cache_.gauges_, [](StatDataAllocator& allocator, StatName name, absl::string_view tag_extracted_name, const std::vector& tags) -> GaugeSharedPtr { @@ -388,8 +397,7 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramx(StatName name) { StatNameJoiner final_name(prefix_.statName(), name); StatName final_stat_name = final_name.statName(); - // See warning/comments in counterx(). - if (parent_.stats_matcher_->rejects(final_stat_name.toString(symbolTable()))) { + if (parent_.rejects(final_stat_name)) { return parent_.null_histogram_; } diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index a6ff06429a5b..fea04a9e289b 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -274,6 +274,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void clearScopeFromCaches(uint64_t scope_id); void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb mergeCb); + bool rejects(StatName name) const; const Stats::StatsOptions& stats_options_; StatDataAllocator& alloc_; diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 24144571bc71..6714a19ff291 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -22,10 +22,9 @@ class ThreadLocalStorePerf { ThreadLocalStorePerf() : heap_alloc_(symbol_table_), store_(options_, heap_alloc_) { store_.setTagProducer(std::make_unique(stats_config_)); - Stats::TestUtil::forEachSampleStat( - 1000, [this](absl::string_view name) { - stat_names_.push_back(std::make_unique(name, symbol_table_)); - }); + Stats::TestUtil::forEachSampleStat(1000, [this](absl::string_view name) { + stat_names_.push_back(std::make_unique(name, symbol_table_)); + }); } ~ThreadLocalStorePerf() { From bdbf34057f5933dad0cf032c7d6bf892edd08e49 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 08:59:37 -0500 Subject: [PATCH 009/106] use std::make_unique for clang-tidy Signed-off-by: Joshua Marantz --- source/common/stats/metric_impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 8681097e6602..9020610a4e0d 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -26,7 +26,7 @@ MetricImpl::MetricImpl(absl::string_view tag_extracted_name, const std::vector(total_size); uint8_t* p = &storage_[0]; *p++ = tags.size(); p += tag_extracted_stat_name.moveToStorage(p); From d70cb187ec4da3670496dc38917c952630850523 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 11:45:52 -0500 Subject: [PATCH 010/106] Make a class for Http::CodeUtility::chargeResponseTiming et al, plumb it in, and preconstruct strings. Signed-off-by: Joshua Marantz --- include/envoy/http/BUILD | 1 + include/envoy/http/codes.h | 53 ++++++++++ include/envoy/server/BUILD | 1 + include/envoy/server/filter_config.h | 7 ++ include/envoy/server/instance.h | 5 + include/envoy/upstream/cluster_manager.h | 3 +- source/common/http/async_client_impl.cc | 5 +- source/common/http/async_client_impl.h | 3 +- source/common/http/codes.cc | 100 +++++++++--------- source/common/http/codes.h | 80 +++++++------- source/common/router/router.cc | 82 +++++++------- source/common/router/router.h | 10 +- .../common/upstream/cluster_manager_impl.cc | 11 +- source/common/upstream/cluster_manager_impl.h | 7 +- .../filters/http/ext_authz/config.cc | 6 +- .../filters/http/ext_authz/ext_authz.cc | 22 ++-- .../filters/http/ext_authz/ext_authz.h | 8 +- .../filters/http/ratelimit/config.cc | 4 +- .../filters/http/ratelimit/ratelimit.cc | 22 ++-- .../filters/http/ratelimit/ratelimit.h | 8 +- source/server/BUILD | 1 + .../config_validation/cluster_manager.cc | 12 +-- .../config_validation/cluster_manager.h | 5 +- source/server/config_validation/server.h | 4 +- source/server/configuration_impl.cc | 2 +- source/server/listener_manager_impl.h | 1 + source/server/server.cc | 1 + source/server/server.h | 3 + .../grpc_client_integration_test_harness.h | 4 +- test/common/http/async_client_impl_test.cc | 4 +- test/common/http/codes_test.cc | 10 +- test/common/router/router_test.cc | 4 +- .../upstream/cluster_manager_impl_test.cc | 21 ++-- .../filters/http/ext_authz/ext_authz_test.cc | 11 +- .../filters/http/ratelimit/ratelimit_test.cc | 4 +- test/mocks/server/mocks.h | 7 +- test/mocks/upstream/mocks.h | 5 +- .../config_validation/cluster_manager_test.cc | 4 +- 38 files changed, 328 insertions(+), 213 deletions(-) diff --git a/include/envoy/http/BUILD b/include/envoy/http/BUILD index 203b5542636c..e248351ab112 100644 --- a/include/envoy/http/BUILD +++ b/include/envoy/http/BUILD @@ -31,6 +31,7 @@ envoy_cc_library( envoy_cc_library( name = "codes_interface", hdrs = ["codes.h"], + deps = ["//include/envoy/stats:stats_interface"], ) envoy_cc_library( diff --git a/include/envoy/http/codes.h b/include/envoy/http/codes.h index c92ec6554897..2a72f36a9107 100644 --- a/include/envoy/http/codes.h +++ b/include/envoy/http/codes.h @@ -1,5 +1,9 @@ #pragma once +#include + +#include "envoy/stats/scope.h" + namespace Envoy { namespace Http { @@ -73,5 +77,54 @@ enum class Code { // clang-format on }; +class CodeStats { +public: + virtual ~CodeStats() {} + + struct ResponseStatInfo { + Stats::Scope& global_scope_; + Stats::Scope& cluster_scope_; + const std::string& prefix_; + uint64_t response_status_code_; + bool internal_request_; + const std::string& request_vhost_name_; + const std::string& request_vcluster_name_; + const std::string& from_zone_; + const std::string& to_zone_; + bool upstream_canary_; + }; + + struct ResponseTimingInfo { + Stats::Scope& global_scope_; + Stats::Scope& cluster_scope_; + const std::string& prefix_; + std::chrono::milliseconds response_time_; + bool upstream_canary_; + bool internal_request_; + const std::string& request_vhost_name_; + const std::string& request_vcluster_name_; + const std::string& from_zone_; + const std::string& to_zone_; + }; + + /** + * Charge a simple response stat to an upstream. + */ + virtual void chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, + Code response_code) PURE; + + /** + * Charge a response stat to both agg counters (*xx) as well as code specific counters. This + * routine also looks for the x-envoy-upstream-canary header and if it is set, also charges + * canary stats. + */ + virtual void chargeResponseStat(const ResponseStatInfo& info) PURE; + + /** + * Charge a response timing to the various dynamic stat postfixes. + */ + virtual void chargeResponseTiming(const ResponseTimingInfo& info) PURE; +}; + } // namespace Http } // namespace Envoy diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index b2e8fb6079fd..3797c7e954d0 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -142,6 +142,7 @@ envoy_cc_library( deps = [ ":admin_interface", "//include/envoy/access_log:access_log_interface", + "//include/envoy/http:codes_interface", "//include/envoy/http:filter_interface", "//include/envoy/init:init_interface", "//include/envoy/json:json_object_interface", diff --git a/include/envoy/server/filter_config.h b/include/envoy/server/filter_config.h index 8cb1e337e055..c3179e1b7212 100644 --- a/include/envoy/server/filter_config.h +++ b/include/envoy/server/filter_config.h @@ -4,6 +4,7 @@ #include "envoy/access_log/access_log.h" #include "envoy/api/v2/core/base.pb.h" +#include "envoy/http/codes.h" #include "envoy/http/filter.h" #include "envoy/init/init.h" #include "envoy/json/json_object.h" @@ -140,6 +141,12 @@ class FactoryContext { * @return OverloadManager& the overload manager for the server. */ virtual OverloadManager& overloadManager() PURE; + + /** + * + * @return Http::CodeStats& a reference to the code stats. + */ + virtual Http::CodeStats& codeStats() PURE; }; class ListenerFactoryContext : public FactoryContext { diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 1c86da6ae1fe..02e88f51b7c6 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -213,6 +213,11 @@ class Instance { * @return the flush interval of stats sinks. */ virtual std::chrono::milliseconds statsFlushInterval() const PURE; + + /** + * @return Http::CodeStats the http response-code stats + */ + virtual Http::CodeStats& codeStats() PURE; }; } // namespace Server diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index c7e7aba180af..967711443bed 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -253,7 +253,8 @@ class ClusterManagerFactory { clusterManagerFromProto(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Server::Admin& admin) PURE; + AccessLog::AccessLogManager& log_manager, Server::Admin& admin, + Http::CodeStats& code_stats) PURE; /** * Allocate an HTTP connection pool for the host. Pools are separated by 'priority', diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index 64846751277f..533dd372ec07 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -36,10 +36,11 @@ AsyncClientImpl::AsyncClientImpl(Upstream::ClusterInfoConstSharedPtr cluster, const LocalInfo::LocalInfo& local_info, Upstream::ClusterManager& cm, Runtime::Loader& runtime, Runtime::RandomGenerator& random, - Router::ShadowWriterPtr&& shadow_writer) + Router::ShadowWriterPtr&& shadow_writer, + Http::CodeStats& code_stats) : cluster_(cluster), config_("http.async-client.", local_info, stats_store, cm, runtime, random, - std::move(shadow_writer), true, false, false, dispatcher.timeSystem()), + std::move(shadow_writer), true, false, false, dispatcher.timeSystem(), code_stats), dispatcher_(dispatcher) {} AsyncClientImpl::~AsyncClientImpl() { diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index d51ac48d2aa8..0ae225fe45b4 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -42,7 +42,8 @@ class AsyncClientImpl final : public AsyncClient { AsyncClientImpl(Upstream::ClusterInfoConstSharedPtr cluster, Stats::Store& stats_store, Event::Dispatcher& dispatcher, const LocalInfo::LocalInfo& local_info, Upstream::ClusterManager& cm, Runtime::Loader& runtime, - Runtime::RandomGenerator& random, Router::ShadowWriterPtr&& shadow_writer); + Runtime::RandomGenerator& random, Router::ShadowWriterPtr&& shadow_writer, + Http::CodeStats& code_stats); ~AsyncClientImpl(); // Http::AsyncClient diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 0f0562afe7ac..460bdee39d00 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -12,115 +12,115 @@ #include "common/http/headers.h" #include "common/http/utility.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/match.h" + namespace Envoy { namespace Http { -void CodeUtility::chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, - Code response_code) { +CodeStatsImpl::CodeStatsImpl() {} + +CodeStatsImpl::~CodeStatsImpl() {} + +void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, + Code response_code) { // Build a dynamic stat for the response code and increment it. - scope.counter(fmt::format("{}upstream_rq_completed", prefix)).inc(); - scope.counter(fmt::format("{}upstream_rq_{}", prefix, groupStringForResponseCode(response_code))) + scope.counter(absl::StrCat(prefix, upstream_rq_completed_)).inc(); + scope.counter(absl::StrCat(prefix, upstream_rq_, + CodeUtility::groupStringForResponseCode(response_code))) .inc(); - scope.counter(fmt::format("{}upstream_rq_{}", prefix, enumToInt(response_code))).inc(); + scope.counter(absl::StrCat(prefix, upstream_rq_, enumToInt(response_code))).inc(); } -void CodeUtility::chargeResponseStat(const ResponseStatInfo& info) { +void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { const uint64_t response_code = info.response_status_code_; chargeBasicResponseStat(info.cluster_scope_, info.prefix_, static_cast(response_code)); - std::string group_string = groupStringForResponseCode(static_cast(response_code)); + std::string group_string = + CodeUtility::groupStringForResponseCode(static_cast(response_code)); // If the response is from a canary, also create canary stats. if (info.upstream_canary_) { - info.cluster_scope_.counter(fmt::format("{}canary.upstream_rq_completed", info.prefix_)).inc(); - info.cluster_scope_.counter(fmt::format("{}canary.upstream_rq_{}", info.prefix_, group_string)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, canary_upstream_rq_completed_)).inc(); + info.cluster_scope_.counter(absl::StrCat(info.prefix_, canary_upstream_rq_, group_string)) .inc(); - info.cluster_scope_.counter(fmt::format("{}canary.upstream_rq_{}", info.prefix_, response_code)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, canary_upstream_rq_, response_code)) .inc(); } // Split stats into external vs. internal. if (info.internal_request_) { - info.cluster_scope_.counter(fmt::format("{}internal.upstream_rq_completed", info.prefix_)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_completed_)).inc(); + info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, group_string)) .inc(); - info.cluster_scope_ - .counter(fmt::format("{}internal.upstream_rq_{}", info.prefix_, group_string)) - .inc(); - info.cluster_scope_ - .counter(fmt::format("{}internal.upstream_rq_{}", info.prefix_, response_code)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, response_code)) .inc(); } else { - info.cluster_scope_.counter(fmt::format("{}external.upstream_rq_completed", info.prefix_)) - .inc(); - info.cluster_scope_ - .counter(fmt::format("{}external.upstream_rq_{}", info.prefix_, group_string)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_completed_)).inc(); + info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, group_string)) .inc(); - info.cluster_scope_ - .counter(fmt::format("{}external.upstream_rq_{}", info.prefix_, response_code)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, response_code)) .inc(); } // Handle request virtual cluster. if (!info.request_vcluster_name_.empty()) { info.global_scope_ - .counter(fmt::format("vhost.{}.vcluster.{}.upstream_rq_completed", info.request_vhost_name_, + .counter(fmt::format(vhost_vcluster_upstream_rq_completed_, info.request_vhost_name_, info.request_vcluster_name_)) .inc(); - info.global_scope_ - .counter(fmt::format("vhost.{}.vcluster.{}.upstream_rq_{}", info.request_vhost_name_, - info.request_vcluster_name_, group_string)) + info.global_scope_.counter(fmt::format(vhost_vcluster_upstream_rq_, info.request_vhost_name_, + info.request_vcluster_name_, group_string)) .inc(); - info.global_scope_ - .counter(fmt::format("vhost.{}.vcluster.{}.upstream_rq_{}", info.request_vhost_name_, - info.request_vcluster_name_, response_code)) + info.global_scope_.counter(fmt::format(vhost_vcluster_upstream_rq_, info.request_vhost_name_, + info.request_vcluster_name_, response_code)) .inc(); } // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { - info.cluster_scope_ - .counter(fmt::format("{}zone.{}.{}.upstream_rq_completed", info.prefix_, info.from_zone_, - info.to_zone_)) - .inc(); - info.cluster_scope_ - .counter(fmt::format("{}zone.{}.{}.upstream_rq_{}", info.prefix_, info.from_zone_, - info.to_zone_, group_string)) - .inc(); - info.cluster_scope_ - .counter(fmt::format("{}zone.{}.{}.upstream_rq_{}", info.prefix_, info.from_zone_, - info.to_zone_, response_code)) - .inc(); + info.cluster_scope_.counter(fmt::format( + zone_upstream_rq_completed_, info.prefix_, info.from_zone_, info.to_zone_)).inc(); + info.cluster_scope_.counter(fmt::format( + zone_upstream_rq_, info.prefix_, info.from_zone_, info.to_zone_, group_string)).inc(); + info.cluster_scope_.counter(fmt::format( + zone_upstream_rq_, info.prefix_, info.from_zone_, info.to_zone_, response_code)).inc(); } } -void CodeUtility::chargeResponseTiming(const ResponseTimingInfo& info) { - info.cluster_scope_.histogram(info.prefix_ + "upstream_rq_time") +void CodeStatsImpl::chargeResponseTiming(const ResponseTimingInfo& info) { + info.cluster_scope_.histogram(absl::StrCat(info.prefix_, upstream_rq_time_)) .recordValue(info.response_time_.count()); if (info.upstream_canary_) { - info.cluster_scope_.histogram(info.prefix_ + "canary.upstream_rq_time") + info.cluster_scope_.histogram(absl::StrCat(info.prefix_, canary_upstream_rq_time_)) .recordValue(info.response_time_.count()); } if (info.internal_request_) { - info.cluster_scope_.histogram(info.prefix_ + "internal.upstream_rq_time") + info.cluster_scope_.histogram(absl::StrCat(info.prefix_, internal_upstream_rq_time_)) .recordValue(info.response_time_.count()); } else { - info.cluster_scope_.histogram(info.prefix_ + "external.upstream_rq_time") + info.cluster_scope_.histogram(absl::StrCat(info.prefix_, external_upstream_rq_time_)) .recordValue(info.response_time_.count()); } if (!info.request_vcluster_name_.empty()) { info.global_scope_ - .histogram("vhost." + info.request_vhost_name_ + ".vcluster." + - info.request_vcluster_name_ + ".upstream_rq_time") + .histogram(absl::StrJoin({vhost_, info.request_vhost_name_, vcluster_, + info.request_vcluster_name_, upstream_rq_time_}, ".")) .recordValue(info.response_time_.count()); } // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { + std::string prefix_without_trailing_dot(info.prefix_); + if (absl::EndsWith(prefix_without_trailing_dot, ".")) { + prefix_without_trailing_dot.resize(prefix_without_trailing_dot.size() - 1); + } info.cluster_scope_ - .histogram(fmt::format("{}zone.{}.{}.upstream_rq_time", info.prefix_, info.from_zone_, - info.to_zone_)) + .histogram(absl::StrJoin({prefix_without_trailing_dot, zone_, info.from_zone_, + info.to_zone_, upstream_rq_time_}, ".")) .recordValue(info.response_time_.count()); } } diff --git a/source/common/http/codes.h b/source/common/http/codes.h index c621b361d6ea..bdcefd0a9cf6 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -11,55 +11,47 @@ namespace Envoy { namespace Http { +class CodeStatsImpl : public CodeStats { +public: + CodeStatsImpl(); + ~CodeStatsImpl() override; + + // CodeStats + void chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, + Code response_code) override; + void chargeResponseStat(const ResponseStatInfo& info) override; + void chargeResponseTiming(const ResponseTimingInfo& info) override; + + private: + std::string upstream_rq_completed_{"upstream_rq_completed"}; + std::string upstream_rq_{"upstream_rq_"}; + std::string canary_upstream_rq_completed_{"canary.upstream_rq_completed"}; + std::string canary_upstream_rq_{"canary.upstream_rq_"}; + std::string internal_upstream_rq_completed_{"internal.upstream_rq_completed"}; + std::string internal_upstream_rq_{"internal.upstream_rq_"}; + std::string internal_upstream_rq_time_{"internal.upstream_rq_time"}; + std::string external_upstream_rq_completed_{"external.upstream_rq_completed"}; + std::string external_upstream_rq_{"external.upstream_rq_"}; + std::string external_upstream_rq_time_{"external.upstream_rq_time"}; + std::string vhost_vcluster_upstream_rq_completed_{"vhost.{}.vcluster.{}.upstream_rq_completed"}; + std::string vhost_vcluster_upstream_rq_{"vhost.{}.vcluster.{}.upstream_rq_{}"}; + std::string zone_upstream_rq_completed_{"{}zone.{}.{}.upstream_rq_completed"}; + std::string zone_upstream_rq_{"{}zone.{}.{}.upstream_rq_{}"}; + std::string upstream_rq_time_{"upstream_rq_time"}; + std::string canary_upstream_rq_time_{"canary.upstream_rq_time"}; + std::string internal_rq_time_{"internal.upstream_rq_time"}; + std::string external_rq_time_{"external.upstream_rq_time"}; + std::string vhost_{"vhost"}; + std::string vcluster_{"vcluster"}; + std::string zone_{"zone"}; + std::string upstream_rq_time{"upstream_rq_time"}; +}; + /** * General utility routines for HTTP codes. */ class CodeUtility { public: - /** - * Charge a simple response stat to an upstream. - */ - static void chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, - Code response_code); - - struct ResponseStatInfo { - Stats::Scope& global_scope_; - Stats::Scope& cluster_scope_; - const std::string& prefix_; - uint64_t response_status_code_; - bool internal_request_; - const std::string& request_vhost_name_; - const std::string& request_vcluster_name_; - const std::string& from_zone_; - const std::string& to_zone_; - bool upstream_canary_; - }; - - /** - * Charge a response stat to both agg counters (*xx) as well as code specific counters. This - * routine also looks for the x-envoy-upstream-canary header and if it is set, also charges - * canary stats. - */ - static void chargeResponseStat(const ResponseStatInfo& info); - - struct ResponseTimingInfo { - Stats::Scope& global_scope_; - Stats::Scope& cluster_scope_; - const std::string& prefix_; - std::chrono::milliseconds response_time_; - bool upstream_canary_; - bool internal_request_; - const std::string& request_vhost_name_; - const std::string& request_vcluster_name_; - const std::string& from_zone_; - const std::string& to_zone_; - }; - - /** - * Charge a response timing to the various dynamic stat postfixes. - */ - static void chargeResponseTiming(const ResponseTimingInfo& info); - /** * Convert an HTTP response code to a descriptive string. * @param code supplies the code to convert. diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 0cb5a83db43d..3c26c98563d4 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -155,28 +155,28 @@ void Filter::chargeUpstreamCode(uint64_t response_status_code, const std::string zone_name = config_.local_info_.zoneName(); const std::string upstream_zone = upstreamZone(upstream_host); - Http::CodeUtility::ResponseStatInfo info{config_.scope_, - cluster_->statsScope(), - EMPTY_STRING, - response_status_code, - internal_request, - route_entry_->virtualHost().name(), - request_vcluster_ ? request_vcluster_->name() - : EMPTY_STRING, - zone_name, - upstream_zone, - is_canary}; - - Http::CodeUtility::chargeResponseStat(info); + Http::CodeStats::ResponseStatInfo info{config_.scope_, + cluster_->statsScope(), + EMPTY_STRING, + response_status_code, + internal_request, + route_entry_->virtualHost().name(), + request_vcluster_ ? request_vcluster_->name() + : EMPTY_STRING, + zone_name, + upstream_zone, + is_canary}; + + codeStats().chargeResponseStat(info); if (!alt_stat_prefix_.empty()) { - Http::CodeUtility::ResponseStatInfo info{config_.scope_, cluster_->statsScope(), - alt_stat_prefix_, response_status_code, - internal_request, EMPTY_STRING, - EMPTY_STRING, zone_name, - upstream_zone, is_canary}; + Http::CodeStats::ResponseStatInfo info{config_.scope_, cluster_->statsScope(), + alt_stat_prefix_, response_status_code, + internal_request, EMPTY_STRING, + EMPTY_STRING, zone_name, + upstream_zone, is_canary}; - Http::CodeUtility::chargeResponseStat(info); + codeStats().chargeResponseStat(info); } if (dropped) { @@ -624,8 +624,8 @@ void Filter::onUpstreamHeaders(const uint64_t response_code, Http::HeaderMapPtr& // upstream_request_. const auto upstream_host = upstream_request_->upstream_host_; if (retry_status == RetryStatus::Yes && setupRetry(end_stream)) { - Http::CodeUtility::chargeBasicResponseStat(cluster_->statsScope(), "retry.", - static_cast(response_code)); + codeStats().chargeBasicResponseStat(cluster_->statsScope(), "retry.", + static_cast(response_code)); upstream_host->stats().rq_error_.inc(); return; } else if (retry_status == RetryStatus::NoOverflow) { @@ -721,33 +721,33 @@ void Filter::onUpstreamComplete() { // TODO(mattklein123): Remove copy when G string compat issues are fixed. const std::string zone_name = config_.local_info_.zoneName(); - Http::CodeUtility::ResponseTimingInfo info{config_.scope_, + Http::CodeStats::ResponseTimingInfo info{config_.scope_, + cluster_->statsScope(), + EMPTY_STRING, + response_time, + upstream_request_->upstream_canary_, + internal_request, + route_entry_->virtualHost().name(), + request_vcluster_ ? request_vcluster_->name() + : EMPTY_STRING, + zone_name, + upstreamZone(upstream_request_->upstream_host_)}; + + codeStats().chargeResponseTiming(info); + + if (!alt_stat_prefix_.empty()) { + Http::CodeStats::ResponseTimingInfo info{config_.scope_, cluster_->statsScope(), - EMPTY_STRING, + alt_stat_prefix_, response_time, upstream_request_->upstream_canary_, internal_request, - route_entry_->virtualHost().name(), - request_vcluster_ ? request_vcluster_->name() - : EMPTY_STRING, + EMPTY_STRING, + EMPTY_STRING, zone_name, upstreamZone(upstream_request_->upstream_host_)}; - Http::CodeUtility::chargeResponseTiming(info); - - if (!alt_stat_prefix_.empty()) { - Http::CodeUtility::ResponseTimingInfo info{config_.scope_, - cluster_->statsScope(), - alt_stat_prefix_, - response_time, - upstream_request_->upstream_canary_, - internal_request, - EMPTY_STRING, - EMPTY_STRING, - zone_name, - upstreamZone(upstream_request_->upstream_host_)}; - - Http::CodeUtility::chargeResponseTiming(info); + codeStats().chargeResponseTiming(info); } } diff --git a/source/common/router/router.h b/source/common/router/router.h index 79438f24908c..ec08768ebef5 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -98,12 +98,12 @@ class FilterConfig { Stats::Scope& scope, Upstream::ClusterManager& cm, Runtime::Loader& runtime, Runtime::RandomGenerator& random, ShadowWriterPtr&& shadow_writer, bool emit_dynamic_stats, bool start_child_span, bool suppress_envoy_headers, - TimeSource& time_source) + TimeSource& time_source, Http::CodeStats& code_stats) : scope_(scope), local_info_(local_info), cm_(cm), runtime_(runtime), random_(random), stats_{ALL_ROUTER_STATS(POOL_COUNTER_PREFIX(scope, stat_prefix))}, emit_dynamic_stats_(emit_dynamic_stats), start_child_span_(start_child_span), - suppress_envoy_headers_(suppress_envoy_headers), shadow_writer_(std::move(shadow_writer)), - time_source_(time_source) {} + suppress_envoy_headers_(suppress_envoy_headers), code_stats_(code_stats), + shadow_writer_(std::move(shadow_writer)), time_source_(time_source) {} FilterConfig(const std::string& stat_prefix, Server::Configuration::FactoryContext& context, ShadowWriterPtr&& shadow_writer, @@ -112,7 +112,7 @@ class FilterConfig { context.runtime(), context.random(), std::move(shadow_writer), PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, dynamic_stats, true), config.start_child_span(), config.suppress_envoy_headers(), - context.timeSource()) { + context.timeSource(), context.codeStats()) { for (const auto& upstream_log : config.upstream_log()) { upstream_logs_.push_back(AccessLog::AccessLogFactory::fromProto(upstream_log, context)); } @@ -131,6 +131,7 @@ class FilterConfig { const bool start_child_span_; const bool suppress_envoy_headers_; std::list upstream_logs_; + Http::CodeStats& code_stats_; private: ShadowWriterPtr shadow_writer_; @@ -382,6 +383,7 @@ class Filter : Logger::Loggable, // and handle difference between gRPC and non-gRPC requests. void handleNon5xxResponseHeaders(const Http::HeaderMap& headers, bool end_stream); TimeSource& timeSource() { return config_.timeSource(); } + Http::CodeStats& codeStats() { return config_.code_stats_; } FilterConfig& config_; Http::StreamDecoderFilterCallbacks* callbacks_{}; diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 06ee71363572..140f848627cc 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -174,11 +174,11 @@ ClusterManagerImpl::ClusterManagerImpl(const envoy::config::bootstrap::v2::Boots const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin) + Server::Admin& admin, Http::CodeStats& code_stats) : factory_(factory), runtime_(runtime), stats_(stats), tls_(tls.allocateSlot()), random_(random), log_manager_(log_manager), bind_config_(bootstrap.cluster_manager().upstream_bind_config()), local_info_(local_info), - cm_stats_(generateStats(stats)), + cm_stats_(generateStats(stats)), code_stats_(code_stats), init_helper_([this](Cluster& cluster) { onClusterInit(cluster); }), config_tracker_entry_( admin.getConfigTracker().add("clusters", [this] { return dumpClusterConfigs(); })), @@ -1020,7 +1020,8 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::ClusterEntry( http_async_client_(cluster, parent.parent_.stats_, parent.thread_local_dispatcher_, parent.parent_.local_info_, parent.parent_, parent.parent_.runtime_, parent.parent_.random_, - Router::ShadowWriterPtr{new Router::ShadowWriterImpl(parent.parent_)}) { + Router::ShadowWriterPtr{new Router::ShadowWriterImpl(parent.parent_)}, + parent_.parent_.code_stats_) { priority_set_.getOrCreateHostSet(0); // TODO(mattklein123): Consider converting other LBs over to thread local. All of them could @@ -1170,10 +1171,10 @@ ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, - Server::Admin& admin) { + Server::Admin& admin, Http::CodeStats& code_stats) { return ClusterManagerPtr{new ClusterManagerImpl(bootstrap, *this, stats, tls, runtime, random, local_info, log_manager, main_thread_dispatcher_, - admin)}; + admin, code_stats)}; } Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool( diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 6e1087c7b410..df851ffc9e43 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -49,7 +49,8 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { clusterManagerFromProto(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Server::Admin& admin) override; + AccessLog::AccessLogManager& log_manager, Server::Admin& admin, + Http::CodeStats& code_stats) override; Http::ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority, Http::Protocol protocol, @@ -165,7 +166,8 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable(proto_config, context.localInfo(), context.scope(), - context.runtime(), context.clusterManager()); + const auto filter_config = std::make_shared( + proto_config, context.localInfo(), context.scope(), context.runtime(), + context.clusterManager(), context.codeStats()); if (proto_config.has_http_service()) { const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config.http_service().server_uri(), diff --git a/source/extensions/filters/http/ext_authz/ext_authz.cc b/source/extensions/filters/http/ext_authz/ext_authz.cc index e7c04fb4ca17..0840f0e9a10a 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.cc +++ b/source/extensions/filters/http/ext_authz/ext_authz.cc @@ -110,17 +110,17 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { case CheckStatus::Denied: cluster_->statsScope().counter("ext_authz.denied").inc(); - Http::CodeUtility::ResponseStatInfo info{config_->scope(), - cluster_->statsScope(), - EMPTY_STRING, - enumToInt(response->status_code), - true, - EMPTY_STRING, - EMPTY_STRING, - EMPTY_STRING, - EMPTY_STRING, - false}; - Http::CodeUtility::chargeResponseStat(info); + Http::CodeStats::ResponseStatInfo info{config_->scope(), + cluster_->statsScope(), + EMPTY_STRING, + enumToInt(response->status_code), + true, + EMPTY_STRING, + EMPTY_STRING, + EMPTY_STRING, + EMPTY_STRING, + false}; + config_->codeStats().chargeResponseStat(info); break; } diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index 02f4cd5ea03f..4f4607ad5bd2 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -37,7 +37,7 @@ class FilterConfig { public: FilterConfig(const envoy::config::filter::http::ext_authz::v2alpha::ExtAuthz& config, const LocalInfo::LocalInfo& local_info, Stats::Scope& scope, - Runtime::Loader& runtime, Upstream::ClusterManager& cm) + Runtime::Loader& runtime, Upstream::ClusterManager& cm, Http::CodeStats& code_stats) : local_info_(local_info), scope_(scope), runtime_(runtime), cm_(cm), cluster_name_(config.grpc_service().envoy_grpc().cluster_name()), allowed_authorization_headers_( @@ -45,7 +45,8 @@ class FilterConfig { allowed_request_headers_(toRequestHeaders(config.http_service().allowed_request_headers())), failure_mode_allow_(config.failure_mode_allow()), authorization_headers_to_add_( - toAuthorizationHeadersToAdd(config.http_service().authorization_headers_to_add())) {} + toAuthorizationHeadersToAdd(config.http_service().authorization_headers_to_add())), + code_stats_(code_stats) {} const LocalInfo::LocalInfo& localInfo() const { return local_info_; } Runtime::Loader& runtime() { return runtime_; } @@ -63,6 +64,8 @@ class FilterConfig { return authorization_headers_to_add_; } + Http::CodeStats& codeStats() { return code_stats_; } + private: static Http::LowerCaseStrUnorderedSet toRequestHeaders( const Protobuf::RepeatedPtrField& request_headers) { @@ -107,6 +110,7 @@ class FilterConfig { Http::LowerCaseStrUnorderedSet allowed_request_headers_; bool failure_mode_allow_; const Filters::Common::ExtAuthz::HeaderKeyValueVector authorization_headers_to_add_; + Http::CodeStats& code_stats_; }; typedef std::shared_ptr FilterConfigSharedPtr; diff --git a/source/extensions/filters/http/ratelimit/config.cc b/source/extensions/filters/http/ratelimit/config.cc index 7be912cb99a9..60bec8c35170 100644 --- a/source/extensions/filters/http/ratelimit/config.cc +++ b/source/extensions/filters/http/ratelimit/config.cc @@ -20,8 +20,8 @@ Http::FilterFactoryCb RateLimitFilterConfig::createFilterFactoryFromProtoTyped( const envoy::config::filter::http::rate_limit::v2::RateLimit& proto_config, const std::string&, Server::Configuration::FactoryContext& context) { ASSERT(!proto_config.domain().empty()); - FilterConfigSharedPtr filter_config( - new FilterConfig(proto_config, context.localInfo(), context.scope(), context.runtime())); + FilterConfigSharedPtr filter_config(new FilterConfig( + proto_config, context.localInfo(), context.scope(), context.runtime(), context.codeStats())); const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config, timeout, 20); return [filter_config, timeout_ms, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { diff --git a/source/extensions/filters/http/ratelimit/ratelimit.cc b/source/extensions/filters/http/ratelimit/ratelimit.cc index 27ec77d19519..b95a81f64d6c 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.cc +++ b/source/extensions/filters/http/ratelimit/ratelimit.cc @@ -126,17 +126,17 @@ void Filter::complete(RateLimit::LimitStatus status, Http::HeaderMapPtr&& header break; case RateLimit::LimitStatus::OverLimit: cluster_->statsScope().counter("ratelimit.over_limit").inc(); - Http::CodeUtility::ResponseStatInfo info{config_->scope(), - cluster_->statsScope(), - EMPTY_STRING, - enumToInt(Http::Code::TooManyRequests), - true, - EMPTY_STRING, - EMPTY_STRING, - EMPTY_STRING, - EMPTY_STRING, - false}; - Http::CodeUtility::chargeResponseStat(info); + Http::CodeStats::ResponseStatInfo info{config_->scope(), + cluster_->statsScope(), + EMPTY_STRING, + enumToInt(Http::Code::TooManyRequests), + true, + EMPTY_STRING, + EMPTY_STRING, + EMPTY_STRING, + EMPTY_STRING, + false}; + codeStats().chargeResponseStat(info); headers_to_add_->insertEnvoyRateLimited().value( Http::Headers::get().EnvoyRateLimitedValues.True); break; diff --git a/source/extensions/filters/http/ratelimit/ratelimit.h b/source/extensions/filters/http/ratelimit/ratelimit.h index 0eca636712fe..11abbb9d2ea9 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.h +++ b/source/extensions/filters/http/ratelimit/ratelimit.h @@ -33,7 +33,7 @@ class FilterConfig { public: FilterConfig(const envoy::config::filter::http::rate_limit::v2::RateLimit& config, const LocalInfo::LocalInfo& local_info, Stats::Scope& scope, - Runtime::Loader& runtime) + Runtime::Loader& runtime, Http::CodeStats& code_stats) : domain_(config.domain()), stage_(static_cast(config.stage())), request_type_(config.request_type().empty() ? stringToType("both") : stringToType(config.request_type())), @@ -42,7 +42,8 @@ class FilterConfig { rate_limited_grpc_status_( config.rate_limited_as_resource_exhausted() ? absl::make_optional(Grpc::Status::GrpcStatus::ResourceExhausted) - : absl::nullopt) {} + : absl::nullopt), + code_stats_(code_stats) {} const std::string& domain() const { return domain_; } const LocalInfo::LocalInfo& localInfo() const { return local_info_; } uint64_t stage() const { return stage_; } @@ -53,6 +54,7 @@ class FilterConfig { const absl::optional rateLimitedGrpcStatus() const { return rate_limited_grpc_status_; } + Http::CodeStats& codeStats() { return code_stats_; } private: static FilterRequestType stringToType(const std::string& request_type) { @@ -74,6 +76,7 @@ class FilterConfig { Runtime::Loader& runtime_; const bool failure_mode_deny_; const absl::optional rate_limited_grpc_status_; + Http::CodeStats& code_stats_; }; typedef std::shared_ptr FilterConfigSharedPtr; @@ -113,6 +116,7 @@ class Filter : public Http::StreamFilter, public RateLimit::RequestCallbacks { const Router::RouteEntry* route_entry, const Http::HeaderMap& headers) const; void addHeaders(Http::HeaderMap& headers); + Http::CodeStats& codeStats() { return config_->codeStats(); } enum class State { NotStarted, Calling, Complete, Responded }; diff --git a/source/server/BUILD b/source/server/BUILD index 4b36b4e38ef8..f6d92b61024a 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -304,6 +304,7 @@ envoy_cc_library( "//source/common/config:bootstrap_json_lib", "//source/common/config:utility_lib", "//source/common/grpc:async_client_manager_lib", + "//source/common/http:codes_lib", "//source/common/local_info:local_info_lib", "//source/common/memory:stats_lib", "//source/common/protobuf:utility_lib", diff --git a/source/server/config_validation/cluster_manager.cc b/source/server/config_validation/cluster_manager.cc index 27507b203fce..e9ea4c35dc2c 100644 --- a/source/server/config_validation/cluster_manager.cc +++ b/source/server/config_validation/cluster_manager.cc @@ -17,10 +17,10 @@ ClusterManagerPtr ValidationClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, - Server::Admin& admin) { - return ClusterManagerPtr{new ValidationClusterManager(bootstrap, *this, stats, tls, runtime, - random, local_info, log_manager, - main_thread_dispatcher_, admin)}; + Server::Admin& admin, Http::CodeStats& code_stats) { + return std::make_unique(bootstrap, *this, stats, tls, runtime, random, + local_info, log_manager, + main_thread_dispatcher_, admin, code_stats); } CdsApiPtr ValidationClusterManagerFactory::createCds( @@ -37,9 +37,9 @@ ValidationClusterManager::ValidationClusterManager( Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin) + Server::Admin& admin, Http::CodeStats& code_stats) : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, random, local_info, log_manager, - main_thread_dispatcher, admin), + main_thread_dispatcher, admin, code_stats), async_client_(main_thread_dispatcher.timeSystem()) {} Http::ConnectionPool::Instance* diff --git a/source/server/config_validation/cluster_manager.h b/source/server/config_validation/cluster_manager.h index 85bc6429dc7e..273e77425a15 100644 --- a/source/server/config_validation/cluster_manager.h +++ b/source/server/config_validation/cluster_manager.h @@ -28,7 +28,8 @@ class ValidationClusterManagerFactory : public ProdClusterManagerFactory { clusterManagerFromProto(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Server::Admin& admin) override; + AccessLog::AccessLogManager& log_manager, Server::Admin& admin, + Http::CodeStats& code_stats) override; // Delegates to ProdClusterManagerFactory::createCds, but discards the result and returns nullptr // unconditionally. @@ -47,7 +48,7 @@ class ValidationClusterManager : public ClusterManagerImpl { ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, Event::Dispatcher& dispatcher, - Server::Admin& admin); + Server::Admin& admin, Http::CodeStats& code_stats); Http::ConnectionPool::Instance* httpConnPoolForCluster(const std::string&, ResourcePriority, Http::Protocol, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 3812d7819784..69eadba2433c 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -97,6 +97,7 @@ class ValidationInstance : Logger::Loggable, const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } Event::TimeSystem& timeSystem() override { return time_system_; } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } + Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { return config_->statsFlushInterval(); @@ -159,7 +160,8 @@ class ValidationInstance : Logger::Loggable, std::unique_ptr listener_manager_; std::unique_ptr secret_manager_; std::unique_ptr overload_manager_; - Envoy::MutexTracer* mutex_tracer_; + MutexTracer* mutex_tracer_; + Http::CodeStatsImpl code_stats_; }; } // namespace Server diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 9d7da7bb5890..765db4b2a17d 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -56,7 +56,7 @@ void MainImpl::initialize(const envoy::config::bootstrap::v2::Bootstrap& bootstr ENVOY_LOG(info, "loading {} cluster(s)", bootstrap.static_resources().clusters().size()); cluster_manager_ = cluster_manager_factory.clusterManagerFromProto( bootstrap, server.stats(), server.threadLocal(), server.runtime(), server.random(), - server.localInfo(), server.accessLogManager(), server.admin()); + server.localInfo(), server.accessLogManager(), server.admin(), server.codeStats()); const auto& listeners = bootstrap.static_resources().listeners(); ENVOY_LOG(info, "loading {} listener(s)", listeners.size()); for (ssize_t i = 0; i < listeners.size(); i++) { diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 528c9361eed4..9c94bd6b6aa3 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -289,6 +289,7 @@ class ListenerImpl : public Network::ListenerConfig, ensureSocketOptions(); Network::Socket::appendOptions(listen_socket_options_, options); } + Http::CodeStats& codeStats() override { return parent_.server_.codeStats(); } // Network::DrainDecision bool drainClose() const override; diff --git a/source/server/server.cc b/source/server/server.cc index 530e0d3e0d8d..15a566d6d333 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -26,6 +26,7 @@ #include "common/config/bootstrap_json.h" #include "common/config/resources.h" #include "common/config/utility.h" +#include "common/http/codes.h" #include "common/local_info/local_info_impl.h" #include "common/memory/stats.h" #include "common/network/address_impl.h" diff --git a/source/server/server.h b/source/server/server.h index f46a65f3f343..1354013be080 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -19,6 +19,7 @@ #include "common/access_log/access_log_manager_impl.h" #include "common/common/logger_delegates.h" #include "common/grpc/async_client_manager_impl.h" +#include "common/http/codes.h" #include "common/runtime/runtime_impl.h" #include "common/secret/secret_manager_impl.h" #include "common/ssl/context_manager_impl.h" @@ -179,6 +180,7 @@ class InstanceImpl : Logger::Loggable, public Instance { ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } Event::TimeSystem& timeSystem() override { return time_system_; } + Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { return config_->statsFlushInterval(); @@ -202,6 +204,7 @@ class InstanceImpl : Logger::Loggable, public Instance { time_t original_start_time_; Stats::StoreRoot& stats_store_; std::unique_ptr server_stats_; + Http::CodeStatsImpl code_stats_; ThreadLocal::Instance& thread_local_; Api::ApiPtr api_; std::unique_ptr secret_manager_; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index ffe14eb12289..9703dcc6e84c 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -6,6 +6,7 @@ #include "common/grpc/async_client_impl.h" #include "common/grpc/google_async_client_impl.h" #include "common/http/async_client_impl.h" +#include "common/http/codes.h" #include "common/http/http2/conn_pool.h" #include "common/network/connection_impl.h" #include "common/network/raw_buffer_socket.h" @@ -272,7 +273,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { .WillRepeatedly(Return(http_conn_pool_.get())); http_async_client_ = std::make_unique( cluster_info_ptr_, *stats_store_, dispatcher_, local_info_, cm_, runtime_, random_, - std::move(shadow_writer_ptr_)); + std::move(shadow_writer_ptr_), code_stats_); EXPECT_CALL(cm_, httpAsyncClientForCluster(fake_cluster_name_)) .WillRepeatedly(ReturnRef(*http_async_client_)); EXPECT_CALL(cm_, get(fake_cluster_name_)).WillRepeatedly(Return(&thread_local_cluster_)); @@ -424,6 +425,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { NiceMock random_; Http::AsyncClientPtr http_async_client_; Http::ConnectionPool::InstancePtr http_conn_pool_; + Http::CodeStatsImpl code_stats_; envoy::api::v2::core::Locality host_locality_; Upstream::MockHost* mock_host_ = new NiceMock(); Upstream::MockHostDescription* mock_host_description_ = diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index bfbf55c4c8fe..9a02aebe93bc 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -5,6 +5,7 @@ #include "common/buffer/buffer_impl.h" #include "common/http/async_client_impl.h" +#include "common/http/codes.h" #include "common/http/headers.h" #include "common/http/utility.h" @@ -38,7 +39,7 @@ class AsyncClientImplTest : public testing::Test { AsyncClientImplTest() : client_(cm_.thread_local_cluster_.cluster_.info_, stats_store_, dispatcher_, local_info_, cm_, runtime_, random_, - Router::ShadowWriterPtr{new NiceMock()}) { + Router::ShadowWriterPtr{new NiceMock()}, code_stats_) { message_->headers().insertMethod().value(std::string("GET")); message_->headers().insertHost().value(std::string("host")); message_->headers().insertPath().value(std::string("/")); @@ -72,6 +73,7 @@ class AsyncClientImplTest : public testing::Test { NiceMock random_; Stats::IsolatedStoreImpl stats_store_; NiceMock local_info_; + Http::CodeStatsImpl code_stats_; AsyncClientImpl client_; }; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index a9127ee1d2d4..2ebb57ee435b 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -29,15 +29,16 @@ class CodeUtilityTest : public testing::Test { const std::string& request_vcluster_name = EMPTY_STRING, const std::string& from_az = EMPTY_STRING, const std::string& to_az = EMPTY_STRING) { - CodeUtility::ResponseStatInfo info{ + Http::CodeStats::ResponseStatInfo info{ global_store_, cluster_scope_, "prefix.", code, internal_request, request_vhost_name, request_vcluster_name, from_az, to_az, canary}; - CodeUtility::chargeResponseStat(info); + code_stats_.chargeResponseStat(info); } Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; + Http::CodeStatsImpl code_stats_; }; TEST_F(CodeUtilityTest, GroupStrings) { @@ -200,7 +201,7 @@ TEST(CodeUtilityResponseTimingTest, All) { Stats::MockStore global_store; Stats::MockStore cluster_scope; - CodeUtility::ResponseTimingInfo info{ + Http::CodeStats::ResponseTimingInfo info{ global_store, cluster_scope, "prefix.", std::chrono::milliseconds(5), true, true, "vhost_name", "req_vcluster_name", "from_az", "to_az"}; @@ -230,7 +231,8 @@ TEST(CodeUtilityResponseTimingTest, All) { EXPECT_CALL(cluster_scope, deliverHistogramToSinks( Property(&Stats::Metric::name, "prefix.zone.from_az.to_az.upstream_rq_time"), 5)); - CodeUtility::chargeResponseTiming(info); + Http::CodeStatsImpl code_stats; + code_stats.chargeResponseTiming(info); } } // namespace Http diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 7077eb39fdbd..77f83c1aef75 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -6,6 +6,7 @@ #include "common/common/empty_string.h" #include "common/config/metadata.h" #include "common/config/well_known_names.h" +#include "common/http/codes.h" #include "common/network/utility.h" #include "common/router/config_impl.h" #include "common/router/router.h" @@ -77,7 +78,7 @@ class RouterTestBase : public testing::Test { : shadow_writer_(new MockShadowWriter()), config_("test.", local_info_, stats_store_, cm_, runtime_, random_, ShadowWriterPtr{shadow_writer_}, true, start_child_span, suppress_envoy_headers, - test_time_.timeSystem()), + test_time_.timeSystem(), code_stats_), router_(config_) { router_.setDecoderFilterCallbacks(callbacks_); upstream_locality_.set_zone("to_az"); @@ -189,6 +190,7 @@ class RouterTestBase : public testing::Test { NiceMock runtime_; NiceMock random_; Http::ConnectionPool::MockCancellable cancellable_; + Http::CodeStatsImpl code_stats_; NiceMock callbacks_; MockShadowWriter* shadow_writer_; NiceMock local_info_; diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index d7ad715c59e2..034650576d25 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -7,6 +7,7 @@ #include "common/config/bootstrap_json.h" #include "common/config/utility.h" +#include "common/http/codes.h" #include "common/network/socket_option_impl.h" #include "common/network/utility.h" #include "common/ssl/context_manager_impl.h" @@ -92,19 +93,21 @@ class TestClusterManagerFactory : public ClusterManagerFactory { clusterManagerFromProto(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Server::Admin& admin) override { + AccessLog::AccessLogManager& log_manager, Server::Admin& admin, + Http::CodeStats& code_stats) override { return ClusterManagerPtr{clusterManagerFromProto_(bootstrap, stats, tls, runtime, random, - local_info, log_manager, admin)}; + local_info, log_manager, admin, code_stats)}; } Secret::SecretManager& secretManager() override { return secret_manager_; } - MOCK_METHOD8(clusterManagerFromProto_, + MOCK_METHOD9(clusterManagerFromProto_, ClusterManager*(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Server::Admin& admin)); + AccessLog::AccessLogManager& log_manager, Server::Admin& admin, + Http::CodeStats& code_stats)); MOCK_METHOD1(allocateConnPool_, Http::ConnectionPool::Instance*(HostConstSharedPtr host)); MOCK_METHOD1(allocateTcpConnPool_, Tcp::ConnectionPool::Instance*(HostConstSharedPtr host)); MOCK_METHOD5(clusterFromProto_, @@ -142,9 +145,9 @@ class TestClusterManagerImpl : public ClusterManagerImpl { Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, - MockLocalClusterUpdate& local_cluster_update) + MockLocalClusterUpdate& local_cluster_update, Http::CodeStats& code_stats) : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, random, local_info, log_manager, - main_thread_dispatcher, admin), + main_thread_dispatcher, admin, code_stats), local_cluster_update_(local_cluster_update) {} protected: @@ -169,7 +172,7 @@ class ClusterManagerImplTest : public testing::Test { void create(const envoy::config::bootstrap::v2::Bootstrap& bootstrap) { cluster_manager_ = std::make_unique( bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.random_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_); + factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, code_stats_); } void createWithLocalClusterUpdate(const bool enable_merge_window = true) { @@ -201,7 +204,8 @@ class ClusterManagerImplTest : public testing::Test { cluster_manager_ = std::make_unique( bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.random_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, local_cluster_update_); + factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, local_cluster_update_, + code_stats_); } void checkStats(uint64_t added, uint64_t modified, uint64_t removed, uint64_t active, @@ -241,6 +245,7 @@ class ClusterManagerImplTest : public testing::Test { NiceMock admin_; Event::SimulatedTimeSystem time_system_; MockLocalClusterUpdate local_cluster_update_; + Http::CodeStatsImpl code_stats_; }; envoy::config::bootstrap::v2::Bootstrap parseBootstrapFromJson(const std::string& json_string) { diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index 39543888c6ac..439c68192e65 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -8,6 +8,7 @@ #include "common/buffer/buffer_impl.h" #include "common/common/empty_string.h" +#include "common/http/codes.h" #include "common/http/headers.h" #include "common/json/json_loader.h" #include "common/network/address_impl.h" @@ -83,6 +84,11 @@ class HttpExtAuthzFilterTestBase { public: HttpExtAuthzFilterTestBase() {} + void initConfig(envoy::config::filter::http::ext_authz::v2alpha::ExtAuthz& proto_config) { + config_ = std::make_unique(proto_config, local_info_, stats_store_, runtime_, cm_, + code_stats_); + } + FilterConfigSharedPtr config_; Filters::Common::ExtAuthz::MockClient* client_; std::unique_ptr filter_; @@ -96,6 +102,7 @@ class HttpExtAuthzFilterTestBase { NiceMock local_info_; Network::Address::InstanceConstSharedPtr addr_; NiceMock connection_; + Http::CodeStatsImpl code_stats_; void prepareCheck() { ON_CALL(filter_callbacks_, connection()).WillByDefault(Return(&connection_)); @@ -111,7 +118,7 @@ class HttpExtAuthzFilterTest : public testing::Test, public HttpExtAuthzFilterTe void initialize(const std::string yaml) { envoy::config::filter::http::ext_authz::v2alpha::ExtAuthz proto_config{}; MessageUtil::loadFromYaml(yaml, proto_config); - config_.reset(new FilterConfig(proto_config, local_info_, stats_store_, runtime_, cm_)); + initConfig(proto_config); client_ = new Filters::Common::ExtAuthz::MockClient(); filter_ = std::make_unique(config_, Filters::Common::ExtAuthz::ClientPtr{client_}); @@ -134,7 +141,7 @@ class HttpExtAuthzFilterParamTest : public TestWithParam(config_, Filters::Common::ExtAuthz::ClientPtr{client_}); diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index c2753bc531d1..d100537fe5a5 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_test.cc @@ -5,6 +5,7 @@ #include "common/buffer/buffer_impl.h" #include "common/common/empty_string.h" #include "common/config/filter_json.h" +#include "common/http/codes.h" #include "common/http/headers.h" #include "extensions/filters/http/ratelimit/ratelimit.h" @@ -50,7 +51,7 @@ class HttpRateLimitFilterTest : public testing::Test { envoy::config::filter::http::rate_limit::v2::RateLimit proto_config{}; MessageUtil::loadFromYaml(yaml, proto_config); - config_.reset(new FilterConfig(proto_config, local_info_, stats_store_, runtime_)); + config_.reset(new FilterConfig(proto_config, local_info_, stats_store_, runtime_, code_stats_)); client_ = new RateLimit::MockClient(); filter_ = std::make_unique(config_, RateLimit::ClientPtr{client_}); @@ -88,6 +89,7 @@ class HttpRateLimitFilterTest : public testing::Test { NiceMock vh_rate_limit_; std::vector descriptor_{{{{"descriptor_key", "descriptor_value"}}}}; NiceMock local_info_; + Http::CodeStatsImpl code_stats_; }; TEST_F(HttpRateLimitFilterTest, BadConfig) { diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 5b17846fba5b..a050ddad2cbd 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -21,6 +21,7 @@ #include "envoy/stats/stats_options.h" #include "envoy/thread/thread.h" +#include "common/http/codes.h" #include "common/secret/secret_manager_impl.h" #include "common/ssl/context_manager_impl.h" @@ -344,10 +345,10 @@ class MockInstance : public Instance { MOCK_METHOD0(httpTracer, Tracing::HttpTracer&()); MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); - // MOCK_METHOD0(timeSystem, Event::TestTimeSystem&()); MOCK_CONST_METHOD0(statsFlushInterval, std::chrono::milliseconds()); Event::TestTimeSystem& timeSystem() override { return test_time_.timeSystem(); } + Http::CodeStats& codeStats() override { return code_stats_; } std::unique_ptr secret_manager_; testing::NiceMock thread_local_; @@ -373,6 +374,7 @@ class MockInstance : public Instance { testing::NiceMock listener_manager_; testing::NiceMock overload_manager_; Singleton::ManagerPtr singleton_manager_; + Http::CodeStatsImpl code_stats_; }; namespace Configuration { @@ -429,6 +431,8 @@ class MockFactoryContext : public FactoryContext { MOCK_METHOD0(timeSource, TimeSource&()); Event::SimulatedTimeSystem& timeSystem() { return time_system_; } + Http::CodeStats& codeStats() override { return code_stats_; } + testing::NiceMock access_log_manager_; testing::NiceMock cluster_manager_; testing::NiceMock dispatcher_; @@ -445,6 +449,7 @@ class MockFactoryContext : public FactoryContext { Stats::IsolatedStoreImpl listener_scope_; Event::SimulatedTimeSystem time_system_; testing::NiceMock overload_manager_; + Http::CodeStatsImpl code_stats_; }; class MockTransportSocketFactoryContext : public TransportSocketFactoryContext { diff --git a/test/mocks/upstream/mocks.h b/test/mocks/upstream/mocks.h index 458d3b7cab65..6c40647c6ee9 100644 --- a/test/mocks/upstream/mocks.h +++ b/test/mocks/upstream/mocks.h @@ -201,12 +201,13 @@ class MockClusterManagerFactory : public ClusterManagerFactory { Secret::MockSecretManager& secretManager() override { return secret_manager_; }; - MOCK_METHOD8(clusterManagerFromProto, + MOCK_METHOD9(clusterManagerFromProto, ClusterManagerPtr(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Server::Admin& admin)); + AccessLog::AccessLogManager& log_manager, Server::Admin& admin, + Http::CodeStats&)); MOCK_METHOD5(allocateConnPool, Http::ConnectionPool::InstancePtr( Event::Dispatcher& dispatcher, HostConstSharedPtr host, diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index f2a075723b93..650e8953bfd5 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -1,6 +1,7 @@ #include "envoy/upstream/resource_manager.h" #include "envoy/upstream/upstream.h" +#include "common/http/codes.h" #include "common/ssl/context_manager_impl.h" #include "server/config_validation/cluster_manager.h" @@ -40,8 +41,9 @@ TEST(ValidationClusterManagerTest, MockedMethods) { AccessLog::MockAccessLogManager log_manager; const envoy::config::bootstrap::v2::Bootstrap bootstrap; + Http::CodeStatsImpl code_stats; ClusterManagerPtr cluster_manager = factory.clusterManagerFromProto( - bootstrap, stats, tls, runtime, random, local_info, log_manager, admin); + bootstrap, stats, tls, runtime, random, local_info, log_manager, admin, code_stats); EXPECT_EQ(nullptr, cluster_manager->httpConnPoolForCluster("cluster", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); Host::CreateConnectionData data = cluster_manager->tcpConnForCluster("cluster", nullptr); From 9c49deca4262bbdba97a89cf658b87ca7230429c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 12:38:28 -0500 Subject: [PATCH 011/106] Use join() rather than fmt::format, and hold static strings in string_view rather than std::string. Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 64 +++++++++++++++++++++++-------------- source/common/http/codes.h | 46 +++++++++++++------------- 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 460bdee39d00..2dc3bc2aa385 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -7,14 +7,13 @@ #include "envoy/stats/scope.h" #include "common/common/enum_to_int.h" -#include "common/common/fmt.h" #include "common/common/utility.h" #include "common/http/headers.h" #include "common/http/utility.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" -#include "absl/strings/match.h" namespace Envoy { namespace Http { @@ -27,12 +26,17 @@ void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, const std::stri Code response_code) { // Build a dynamic stat for the response code and increment it. scope.counter(absl::StrCat(prefix, upstream_rq_completed_)).inc(); - scope.counter(absl::StrCat(prefix, upstream_rq_, - CodeUtility::groupStringForResponseCode(response_code))) + scope + .counter(absl::StrCat(prefix, upstream_rq_, + CodeUtility::groupStringForResponseCode(response_code))) .inc(); scope.counter(absl::StrCat(prefix, upstream_rq_, enumToInt(response_code))).inc(); } +std::string CodeStatsImpl::join(const std::vector& v) { + return absl::StrJoin(v, "."); +} + void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { const uint64_t response_code = info.response_status_code_; chargeBasicResponseStat(info.cluster_scope_, info.prefix_, static_cast(response_code)); @@ -67,25 +71,34 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { // Handle request virtual cluster. if (!info.request_vcluster_name_.empty()) { info.global_scope_ - .counter(fmt::format(vhost_vcluster_upstream_rq_completed_, info.request_vhost_name_, - info.request_vcluster_name_)) + .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, + upstream_rq_completed_})) .inc(); - info.global_scope_.counter(fmt::format(vhost_vcluster_upstream_rq_, info.request_vhost_name_, - info.request_vcluster_name_, group_string)) + info.global_scope_ + .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, + absl::StrCat(upstream_rq_, group_string)})) .inc(); - info.global_scope_.counter(fmt::format(vhost_vcluster_upstream_rq_, info.request_vhost_name_, - info.request_vcluster_name_, response_code)) + info.global_scope_ + .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, + absl::StrCat(upstream_rq_, response_code)})) .inc(); } // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { - info.cluster_scope_.counter(fmt::format( - zone_upstream_rq_completed_, info.prefix_, info.from_zone_, info.to_zone_)).inc(); - info.cluster_scope_.counter(fmt::format( - zone_upstream_rq_, info.prefix_, info.from_zone_, info.to_zone_, group_string)).inc(); - info.cluster_scope_.counter(fmt::format( - zone_upstream_rq_, info.prefix_, info.from_zone_, info.to_zone_, response_code)).inc(); + absl::string_view prefix_without_trailing_dot = stripTrailingDot(info.prefix_); + info.cluster_scope_ + .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, + upstream_rq_completed_})) + .inc(); + info.cluster_scope_ + .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, + absl::StrCat(upstream_rq_, group_string)})) + .inc(); + info.cluster_scope_ + .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, + absl::StrCat(upstream_rq_, response_code)})) + .inc(); } } @@ -107,24 +120,27 @@ void CodeStatsImpl::chargeResponseTiming(const ResponseTimingInfo& info) { if (!info.request_vcluster_name_.empty()) { info.global_scope_ - .histogram(absl::StrJoin({vhost_, info.request_vhost_name_, vcluster_, - info.request_vcluster_name_, upstream_rq_time_}, ".")) + .histogram(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, + upstream_rq_time_})) .recordValue(info.response_time_.count()); } // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { - std::string prefix_without_trailing_dot(info.prefix_); - if (absl::EndsWith(prefix_without_trailing_dot, ".")) { - prefix_without_trailing_dot.resize(prefix_without_trailing_dot.size() - 1); - } info.cluster_scope_ - .histogram(absl::StrJoin({prefix_without_trailing_dot, zone_, info.from_zone_, - info.to_zone_, upstream_rq_time_}, ".")) + .histogram(join({stripTrailingDot(info.prefix_), zone_, info.from_zone_, info.to_zone_, + upstream_rq_time_})) .recordValue(info.response_time_.count()); } } +absl::string_view CodeStatsImpl::stripTrailingDot(absl::string_view str) { + if (absl::EndsWith(str, ".")) { + str.remove_suffix(1); + } + return str; +} + std::string CodeUtility::groupStringForResponseCode(Code response_code) { if (CodeUtility::is2xx(enumToInt(response_code))) { return "2xx"; diff --git a/source/common/http/codes.h b/source/common/http/codes.h index bdcefd0a9cf6..f6b883bb9506 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -22,29 +22,29 @@ class CodeStatsImpl : public CodeStats { void chargeResponseStat(const ResponseStatInfo& info) override; void chargeResponseTiming(const ResponseTimingInfo& info) override; - private: - std::string upstream_rq_completed_{"upstream_rq_completed"}; - std::string upstream_rq_{"upstream_rq_"}; - std::string canary_upstream_rq_completed_{"canary.upstream_rq_completed"}; - std::string canary_upstream_rq_{"canary.upstream_rq_"}; - std::string internal_upstream_rq_completed_{"internal.upstream_rq_completed"}; - std::string internal_upstream_rq_{"internal.upstream_rq_"}; - std::string internal_upstream_rq_time_{"internal.upstream_rq_time"}; - std::string external_upstream_rq_completed_{"external.upstream_rq_completed"}; - std::string external_upstream_rq_{"external.upstream_rq_"}; - std::string external_upstream_rq_time_{"external.upstream_rq_time"}; - std::string vhost_vcluster_upstream_rq_completed_{"vhost.{}.vcluster.{}.upstream_rq_completed"}; - std::string vhost_vcluster_upstream_rq_{"vhost.{}.vcluster.{}.upstream_rq_{}"}; - std::string zone_upstream_rq_completed_{"{}zone.{}.{}.upstream_rq_completed"}; - std::string zone_upstream_rq_{"{}zone.{}.{}.upstream_rq_{}"}; - std::string upstream_rq_time_{"upstream_rq_time"}; - std::string canary_upstream_rq_time_{"canary.upstream_rq_time"}; - std::string internal_rq_time_{"internal.upstream_rq_time"}; - std::string external_rq_time_{"external.upstream_rq_time"}; - std::string vhost_{"vhost"}; - std::string vcluster_{"vcluster"}; - std::string zone_{"zone"}; - std::string upstream_rq_time{"upstream_rq_time"}; + static std::string join(const std::vector& v); + +private: + absl::string_view stripTrailingDot(absl::string_view prefix); + + absl::string_view canary_upstream_rq_completed_{"canary.upstream_rq_completed"}; + absl::string_view canary_upstream_rq_time_{"canary.upstream_rq_time"}; + absl::string_view canary_upstream_rq_{"canary.upstream_rq_"}; + absl::string_view external_rq_time_{"external.upstream_rq_time"}; + absl::string_view external_upstream_rq_completed_{"external.upstream_rq_completed"}; + absl::string_view external_upstream_rq_time_{"external.upstream_rq_time"}; + absl::string_view external_upstream_rq_{"external.upstream_rq_"}; + absl::string_view internal_rq_time_{"internal.upstream_rq_time"}; + absl::string_view internal_upstream_rq_completed_{"internal.upstream_rq_completed"}; + absl::string_view internal_upstream_rq_time_{"internal.upstream_rq_time"}; + absl::string_view internal_upstream_rq_{"internal.upstream_rq_"}; + absl::string_view upstream_rq_completed_{"upstream_rq_completed"}; + absl::string_view upstream_rq_time_{"upstream_rq_time"}; + absl::string_view upstream_rq_time{"upstream_rq_time"}; + absl::string_view upstream_rq_{"upstream_rq_"}; + absl::string_view vcluster_{"vcluster"}; + absl::string_view vhost_{"vhost"}; + absl::string_view zone_{"zone"}; }; /** From df7d7a14ce54785785dbf88636658e88ba07ea07 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 13:30:59 -0500 Subject: [PATCH 012/106] fix router test (empty prefix) and use const string_view for member vars. Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 13 ++++++++---- source/common/http/codes.h | 41 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 2dc3bc2aa385..258ac967d61d 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -33,10 +33,6 @@ void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, const std::stri scope.counter(absl::StrCat(prefix, upstream_rq_, enumToInt(response_code))).inc(); } -std::string CodeStatsImpl::join(const std::vector& v) { - return absl::StrJoin(v, "."); -} - void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { const uint64_t response_code = info.response_status_code_; chargeBasicResponseStat(info.cluster_scope_, info.prefix_, static_cast(response_code)); @@ -141,6 +137,15 @@ absl::string_view CodeStatsImpl::stripTrailingDot(absl::string_view str) { return str; } +std::string CodeStatsImpl::join(const std::vector& v) { + ASSERT(!v.empty()); + auto iter = v.begin(); + if (iter->empty()) { + ++iter; // Skip any initial empty prefix. + } + return absl::StrJoin(iter, v.end(), "."); +} + std::string CodeUtility::groupStringForResponseCode(Code response_code) { if (CodeUtility::is2xx(enumToInt(response_code))) { return "2xx"; diff --git a/source/common/http/codes.h b/source/common/http/codes.h index f6b883bb9506..244e68cf8c13 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -22,29 +22,28 @@ class CodeStatsImpl : public CodeStats { void chargeResponseStat(const ResponseStatInfo& info) override; void chargeResponseTiming(const ResponseTimingInfo& info) override; - static std::string join(const std::vector& v); - private: - absl::string_view stripTrailingDot(absl::string_view prefix); + static absl::string_view stripTrailingDot(absl::string_view prefix); + static std::string join(const std::vector& v); - absl::string_view canary_upstream_rq_completed_{"canary.upstream_rq_completed"}; - absl::string_view canary_upstream_rq_time_{"canary.upstream_rq_time"}; - absl::string_view canary_upstream_rq_{"canary.upstream_rq_"}; - absl::string_view external_rq_time_{"external.upstream_rq_time"}; - absl::string_view external_upstream_rq_completed_{"external.upstream_rq_completed"}; - absl::string_view external_upstream_rq_time_{"external.upstream_rq_time"}; - absl::string_view external_upstream_rq_{"external.upstream_rq_"}; - absl::string_view internal_rq_time_{"internal.upstream_rq_time"}; - absl::string_view internal_upstream_rq_completed_{"internal.upstream_rq_completed"}; - absl::string_view internal_upstream_rq_time_{"internal.upstream_rq_time"}; - absl::string_view internal_upstream_rq_{"internal.upstream_rq_"}; - absl::string_view upstream_rq_completed_{"upstream_rq_completed"}; - absl::string_view upstream_rq_time_{"upstream_rq_time"}; - absl::string_view upstream_rq_time{"upstream_rq_time"}; - absl::string_view upstream_rq_{"upstream_rq_"}; - absl::string_view vcluster_{"vcluster"}; - absl::string_view vhost_{"vhost"}; - absl::string_view zone_{"zone"}; + const absl::string_view canary_upstream_rq_completed_{"canary.upstream_rq_completed"}; + const absl::string_view canary_upstream_rq_time_{"canary.upstream_rq_time"}; + const absl::string_view canary_upstream_rq_{"canary.upstream_rq_"}; + const absl::string_view external_rq_time_{"external.upstream_rq_time"}; + const absl::string_view external_upstream_rq_completed_{"external.upstream_rq_completed"}; + const absl::string_view external_upstream_rq_time_{"external.upstream_rq_time"}; + const absl::string_view external_upstream_rq_{"external.upstream_rq_"}; + const absl::string_view internal_rq_time_{"internal.upstream_rq_time"}; + const absl::string_view internal_upstream_rq_completed_{"internal.upstream_rq_completed"}; + const absl::string_view internal_upstream_rq_time_{"internal.upstream_rq_time"}; + const absl::string_view internal_upstream_rq_{"internal.upstream_rq_"}; + const absl::string_view upstream_rq_completed_{"upstream_rq_completed"}; + const absl::string_view upstream_rq_time_{"upstream_rq_time"}; + const absl::string_view upstream_rq_time{"upstream_rq_time"}; + const absl::string_view upstream_rq_{"upstream_rq_"}; + const absl::string_view vcluster_{"vcluster"}; + const absl::string_view vhost_{"vhost"}; + const absl::string_view zone_{"zone"}; }; /** From da648b360197bc7777df7c200dcfe766d121e7d3 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 13:59:45 -0500 Subject: [PATCH 013/106] add speed-test. Signed-off-by: Joshua Marantz --- test/common/http/BUILD | 17 +++++ test/common/http/codes_speed_test.cc | 95 ++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 test/common/http/codes_speed_test.cc diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 31b826fe7db1..8f18b432d760 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -4,6 +4,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_cc_fuzz_test", "envoy_cc_test", + "envoy_cc_test_binary", "envoy_cc_test_library", "envoy_package", "envoy_proto_library", @@ -80,6 +81,22 @@ envoy_cc_test( ], ) +envoy_cc_test_binary( + name = "codes_speed_test", + srcs = ["codes_speed_test.cc"], + external_deps = [ + "benchmark", + ], + deps = [ + "//source/common/common:empty_string", + "//source/common/http:codes_lib", + "//source/common/http:header_map_lib", + "//source/common/stats:isolated_store_lib", + "//source/common/stats:stats_lib", + "//test/test_common:utility_lib", + ], +) + envoy_cc_test_library( name = "common_lib", srcs = ["common.cc"], diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc new file mode 100644 index 000000000000..110a41e5f842 --- /dev/null +++ b/test/common/http/codes_speed_test.cc @@ -0,0 +1,95 @@ +// Note: this should be run with --compilation_mode=opt, and would benefit from a +// quiescent system with disabled cstate power management. + +#include +#include +#include +#include + +#include "envoy/stats/stats.h" + +#include "common/common/empty_string.h" +#include "common/http/codes.h" +#include "common/http/header_map_impl.h" +#include "common/stats/isolated_store_impl.h" + +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "testing/base/public/benchmark.h" + +namespace Envoy { +namespace Http { + +class CodeUtilitySpeedTest { +public: + void addResponse(uint64_t code, bool canary, bool internal_request, + const std::string& request_vhost_name = EMPTY_STRING, + const std::string& request_vcluster_name = EMPTY_STRING, + const std::string& from_az = EMPTY_STRING, + const std::string& to_az = EMPTY_STRING) { + Http::CodeStats::ResponseStatInfo info{ + global_store_, cluster_scope_, "prefix.", code, internal_request, + request_vhost_name, request_vcluster_name, from_az, to_az, canary}; + + code_stats_.chargeResponseStat(info); + } + + void addResponses() { + addResponse(201, false, false); + addResponse(301, false, true); + addResponse(401, false, false); + addResponse(501, false, true); + addResponse(200, true, true); + addResponse(300, false, false); + addResponse(500, true, false); + addResponse(200, false, false, "test-vhost", "test-cluster"); + addResponse(200, false, false, "", "", "from_az", "to_az"); + } + + void responseTiming() { + Http::CodeStats::ResponseTimingInfo info{ + global_store_, cluster_scope_, "prefix.", std::chrono::milliseconds(5), + true, true, "vhost_name", "req_vcluster_name", + "from_az", "to_az"}; + code_stats_.chargeResponseTiming(info); + } + + Stats::IsolatedStoreImpl global_store_; + Stats::IsolatedStoreImpl cluster_scope_; + Http::CodeStatsImpl code_stats_; +}; + +} // namespace Http +} // namespace Envoy + +static void BM_AddResponses(benchmark::State& state) { + Envoy::Http::CodeUtilitySpeedTest context; + + for (auto _ : state) { + context.addResponses(); + } +} +BENCHMARK(BM_AddResponses); + +static void BM_ResponseTiming(benchmark::State& state) { + Envoy::Http::CodeUtilitySpeedTest context; + + for (auto _ : state) { + context.responseTiming(); + } +} +BENCHMARK(BM_ResponseTiming); + +// Boilerplate main(), which discovers benchmarks in the same file and runs them. +int main(int argc, char** argv) { + benchmark::Initialize(&argc, argv); + + Envoy::Event::Libevent::Global::initialize(); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) { + return 1; + } + benchmark::RunSpecifiedBenchmarks(); +} From ec66aaf2b8052387da4ad090336b1dd5810e582a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 14:01:50 -0500 Subject: [PATCH 014/106] Remove libraries not needed by speed test. Signed-off-by: Joshua Marantz --- test/common/http/BUILD | 2 -- test/common/http/codes_speed_test.cc | 13 +++---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 8f18b432d760..c59ef1da9c04 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -90,10 +90,8 @@ envoy_cc_test_binary( deps = [ "//source/common/common:empty_string", "//source/common/http:codes_lib", - "//source/common/http:header_map_lib", "//source/common/stats:isolated_store_lib", "//source/common/stats:stats_lib", - "//test/test_common:utility_lib", ], ) diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index 110a41e5f842..d59df3389ce0 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -10,14 +10,8 @@ #include "common/common/empty_string.h" #include "common/http/codes.h" -#include "common/http/header_map_impl.h" #include "common/stats/isolated_store_impl.h" -#include "test/test_common/utility.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - #include "testing/base/public/benchmark.h" namespace Envoy { @@ -51,9 +45,9 @@ class CodeUtilitySpeedTest { void responseTiming() { Http::CodeStats::ResponseTimingInfo info{ - global_store_, cluster_scope_, "prefix.", std::chrono::milliseconds(5), - true, true, "vhost_name", "req_vcluster_name", - "from_az", "to_az"}; + global_store_, cluster_scope_, "prefix.", std::chrono::milliseconds(5), + true, true, "vhost_name", "req_vcluster_name", + "from_az", "to_az"}; code_stats_.chargeResponseTiming(info); } @@ -87,7 +81,6 @@ BENCHMARK(BM_ResponseTiming); int main(int argc, char** argv) { benchmark::Initialize(&argc, argv); - Envoy::Event::Libevent::Global::initialize(); if (benchmark::ReportUnrecognizedArguments(argc, argv)) { return 1; } From b711b1bd768c1f67710b97432491989359c98954 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 17:26:38 -0500 Subject: [PATCH 015/106] checkpoint Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 83 +++++++++++++++++++++++++++++-------- source/common/http/codes.h | 62 ++++++++++++++++++--------- 2 files changed, 108 insertions(+), 37 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 258ac967d61d..fa88030fbeca 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -18,47 +18,82 @@ namespace Envoy { namespace Http { -CodeStatsImpl::CodeStatsImpl() {} +CodeStatsImpl::CodeStatsImpl(SymbolTable& symbol_table) + : symbol_table_(symbol_table), + canary_upstream_rq_completed_(makeStatName("canary.upstream_rq_completed")), + canary_upstream_rq_time_(makeStatName("canary.upstream_rq_time")), + canary_upstream_rq_(makeStatName("canary.upstream_rq_")), + external_rq_time_(makeStatName("external.upstream_rq_time")), + external_upstream_rq_completed_(makeStatName("external.upstream_rq_completed")), + external_upstream_rq_time_(makeStatName("external.upstream_rq_time")), + external_upstream_rq_(makeStatName("external.upstream_rq_")), + internal_rq_time_(makeStatName("internal.upstream_rq_time")), + internal_upstream_rq_completed_(makeStatName("internal.upstream_rq_completed")), + internal_upstream_rq_time_(makeStatName("internal.upstream_rq_time")), + internal_upstream_rq_(makeStatName("internal.upstream_rq_")), + upstream_rq_completed_(makeStatName("upstream_rq_completed")), + upstream_rq_time_(makeStatName("upstream_rq_time")), + upstream_rq_time(makeStatName("upstream_rq_time")), + upstream_rq_(makeStatName("upstream_rq_")), + vcluster_(makeStatName("vcluster")), + vhost_(makeStatName("vhost")), + zone_(makeStatName("zone")), + upstream_rq_2xx_(makeStatName("upstream_rq_2xx")), + upstream_rq_3xx_(makeStatName("upstream_rq_3xx")), + upstream_rq_4xx_(makeStatName("upstream_rq_4xx")), + upstream_rq_5xx_(makeStatName("upstream_rq_5xx")) { +} + +CodeStatsImpl::~CodeStatsImpl() { + for (StatNameStorage& stat_name_storage : storage_) { + stat_name_storage.free(symbol_table_); + } +} -CodeStatsImpl::~CodeStatsImpl() {} +Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { + storage_.push_back(StatNameStorage(name, symbol_table_)); + return storage_.back().statName(); +} void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, Code response_code) { + // TODO(jmarantz): consider passing prefix by StatName. + StatNameTempStorage prefix_storage(prefix, symbol_table_); + StatName prefix_stat_name = prefix_storage.statName(); + // Build a dynamic stat for the response code and increment it. - scope.counter(absl::StrCat(prefix, upstream_rq_completed_)).inc(); - scope - .counter(absl::StrCat(prefix, upstream_rq_, - CodeUtility::groupStringForResponseCode(response_code))) - .inc(); + scope.counter(StatNameJoiner(prefix_stat_name, upstream_rq_completed_).statName()).inc(); + scope.counter(StatNameJoiner(prefix_stat_name_, upstreamRqGroup(response_code))).inc(); scope.counter(absl::StrCat(prefix, upstream_rq_, enumToInt(response_code))).inc(); } void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { + StatNameTempStorage prefix_storage(prefix, symbol_table_); + StatName prefix_stat_name = prefix_storage.statName(); + const uint64_t response_code = info.response_status_code_; chargeBasicResponseStat(info.cluster_scope_, info.prefix_, static_cast(response_code)); - std::string group_string = - CodeUtility::groupStringForResponseCode(static_cast(response_code)); + absl::string_view prefix = stripTrailingDot(info.prefix_); // If the response is from a canary, also create canary stats. if (info.upstream_canary_) { - info.cluster_scope_.counter(absl::StrCat(info.prefix_, canary_upstream_rq_completed_)).inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, canary_upstream_rq_, group_string)) - .inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, canary_upstream_rq_, response_code)) + info.cluster_scope_.counter(join(prefix, canary_upstream_rq_completed_)).inc(); + info.cluster_scope_.counter(join(info.prefix, canary_, upstreamRqGroup(response_code)).inc(); + info.cluster_scope_.counter(prefix, canary_upstream_rq_, response_code)) .inc(); } // Split stats into external vs. internal. if (info.internal_request_) { info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_completed_)).inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, group_string)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, rc_group)) .inc(); info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, response_code)) .inc(); } else { info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_completed_)).inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, group_string)) + info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, rc_group)) .inc(); info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, response_code)) .inc(); @@ -72,7 +107,7 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { .inc(); info.global_scope_ .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, - absl::StrCat(upstream_rq_, group_string)})) + absl::StrCat(upstream_rq_, rc_group)})) .inc(); info.global_scope_ .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, @@ -82,14 +117,13 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { - absl::string_view prefix_without_trailing_dot = stripTrailingDot(info.prefix_); info.cluster_scope_ .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, upstream_rq_completed_})) .inc(); info.cluster_scope_ .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, - absl::StrCat(upstream_rq_, group_string)})) + absl::StrCat(upstream_rq_, rc_group)})) .inc(); info.cluster_scope_ .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, @@ -146,7 +180,20 @@ std::string CodeStatsImpl::join(const std::vector& v) { return absl::StrJoin(iter, v.end(), "."); } +StatName CodeStatsImpl::upstreamRq(Code response_code) { + switch (enumToInt(response_code) / 100) { + case 1: return response_code_1xx_; + case 2: return response_code_2xx_; + case 3: return response_code_3xx_; + case 4: return response_code_3xx_; + case 5: return response_code_3xx_; + } + return StatName(); +} + std::string CodeUtility::groupStringForResponseCode(Code response_code) { + // Note: this is only used in the unit test and in dynamo_filter.cc, which + // needs the same sort of symbloziation treatment we are doing here. if (CodeUtility::is2xx(enumToInt(response_code))) { return "2xx"; } else if (CodeUtility::is3xx(enumToInt(response_code))) { diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 244e68cf8c13..563fff86e313 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -13,7 +13,7 @@ namespace Http { class CodeStatsImpl : public CodeStats { public: - CodeStatsImpl(); + explicit CodeStatsImpl(Stats::SymbolTable& symbol_table); ~CodeStatsImpl() override; // CodeStats @@ -25,25 +25,49 @@ class CodeStatsImpl : public CodeStats { private: static absl::string_view stripTrailingDot(absl::string_view prefix); static std::string join(const std::vector& v); + Stats::StatName makeStatName(absl::string_view name); + Stats::StatName upstreamRqGroup(Code response_code) const; - const absl::string_view canary_upstream_rq_completed_{"canary.upstream_rq_completed"}; - const absl::string_view canary_upstream_rq_time_{"canary.upstream_rq_time"}; - const absl::string_view canary_upstream_rq_{"canary.upstream_rq_"}; - const absl::string_view external_rq_time_{"external.upstream_rq_time"}; - const absl::string_view external_upstream_rq_completed_{"external.upstream_rq_completed"}; - const absl::string_view external_upstream_rq_time_{"external.upstream_rq_time"}; - const absl::string_view external_upstream_rq_{"external.upstream_rq_"}; - const absl::string_view internal_rq_time_{"internal.upstream_rq_time"}; - const absl::string_view internal_upstream_rq_completed_{"internal.upstream_rq_completed"}; - const absl::string_view internal_upstream_rq_time_{"internal.upstream_rq_time"}; - const absl::string_view internal_upstream_rq_{"internal.upstream_rq_"}; - const absl::string_view upstream_rq_completed_{"upstream_rq_completed"}; - const absl::string_view upstream_rq_time_{"upstream_rq_time"}; - const absl::string_view upstream_rq_time{"upstream_rq_time"}; - const absl::string_view upstream_rq_{"upstream_rq_"}; - const absl::string_view vcluster_{"vcluster"}; - const absl::string_view vhost_{"vhost"}; - const absl::string_view zone_{"zone"}; + // We have to actively free the StatNameStorage with the symbol_table_, so + // it's easiest to accumulate the StatNameStorage objects in a vector, in + // addition to having discrete member variables. That saves having to + // enumerate the stat-names in both the member-variables listed below + // and the destructor. + // + // TODO(jmarantz): consider a new variant in stats_macros.h to enumerate stats + // names and manage their storage. + std::vector storage_; + + Stats::SymbolTable& symbol_table_; + Stats::StatName canary_upstream_rq_completed_; + Stats::StatName canary_upstream_rq_time_; + Stats::StatName external_rq_time_; + Stats::StatName external_upstream_rq_completed_; + Stats::StatName external_upstream_rq_time_; + Stats::StatName internal_rq_time_; + Stats::StatName internal_upstream_rq_completed_; + Stats::StatName internal_upstream_rq_time_; + Stats::StatName upstream_rq_completed_; + Stats::StatName upstream_rq_time_; + Stats::StatName upstream_rq_time; + Stats::StatName vcluster_; + Stats::StatName vhost_; + Stats::StatName zone_; + Stats::StatName response_code_2xx_; + Stats::StatName response_code_3xx_; + Stats::StatName response_code_4xx_; + Stats::StatName response_code_5xx_; + + // We keep several stats for each HTTP response codes, created lazily -- as + // most response-codes are never seen. We do a poor man's locking here by + // keeping them in an array. To minimize contention generally, we'll just have + // a r/w lock per response-code. + using LockedStatName = std::pair>; + constexpr MaxResponseCode = 600; + LockedStat[MaxResponseCode] canary_upstream_rq_; + LockedStat[MaxResponseCode] external_upstream_rq_; + LockedStat[MaxResponseCode] internal_upstream_rq_; + LockedStat[MaxResponseCode] upstream_rq_; }; /** From 772330922e50e4d3f0ee1608f4849a8db226c97a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Nov 2018 21:20:41 -0500 Subject: [PATCH 016/106] checkpoint Signed-off-by: Joshua Marantz --- include/envoy/http/codes.h | 3 + source/common/http/BUILD | 1 + source/common/http/codes.cc | 114 ++++++++++++++++++++++++------------ source/common/http/codes.h | 64 +++++++++++++++----- 4 files changed, 129 insertions(+), 53 deletions(-) diff --git a/include/envoy/http/codes.h b/include/envoy/http/codes.h index 2a72f36a9107..50116686bbde 100644 --- a/include/envoy/http/codes.h +++ b/include/envoy/http/codes.h @@ -3,6 +3,7 @@ #include #include "envoy/stats/scope.h" +#include "envoy/stats/symbol_table.h" namespace Envoy { namespace Http { @@ -112,6 +113,8 @@ class CodeStats { */ virtual void chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, Code response_code) PURE; + virtual void chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, + Code response_code) PURE; /** * Charge a response stat to both agg counters (*xx) as well as code specific counters. This diff --git a/source/common/http/BUILD b/source/common/http/BUILD index 38782e5a88e3..91e0af9bdf81 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -88,6 +88,7 @@ envoy_cc_library( "//include/envoy/stats:stats_interface", "//source/common/common:enum_to_int", "//source/common/common:utility_lib", + "//source/common/stats:symbol_table_lib", "@envoy_api//envoy/type:http_status_cc", ], ) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index fa88030fbeca..bb4f418709c9 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -18,91 +18,100 @@ namespace Envoy { namespace Http { -CodeStatsImpl::CodeStatsImpl(SymbolTable& symbol_table) +CodeStatsImpl::CodeStatsImpl(Stats::SymbolTable& symbol_table) : symbol_table_(symbol_table), + canary_(makeStatName("canary")), canary_upstream_rq_completed_(makeStatName("canary.upstream_rq_completed")), canary_upstream_rq_time_(makeStatName("canary.upstream_rq_time")), - canary_upstream_rq_(makeStatName("canary.upstream_rq_")), + external_(makeStatName("external")), external_rq_time_(makeStatName("external.upstream_rq_time")), external_upstream_rq_completed_(makeStatName("external.upstream_rq_completed")), external_upstream_rq_time_(makeStatName("external.upstream_rq_time")), - external_upstream_rq_(makeStatName("external.upstream_rq_")), + internal_(makeStatName("internal")), internal_rq_time_(makeStatName("internal.upstream_rq_time")), internal_upstream_rq_completed_(makeStatName("internal.upstream_rq_completed")), internal_upstream_rq_time_(makeStatName("internal.upstream_rq_time")), - internal_upstream_rq_(makeStatName("internal.upstream_rq_")), + upstream_rq_2xx_(makeStatName("upstream_rq_2xx")), + upstream_rq_3xx_(makeStatName("upstream_rq_3xx")), + upstream_rq_4xx_(makeStatName("upstream_rq_4xx")), + upstream_rq_5xx_(makeStatName("upstream_rq_5xx")), upstream_rq_completed_(makeStatName("upstream_rq_completed")), upstream_rq_time_(makeStatName("upstream_rq_time")), - upstream_rq_time(makeStatName("upstream_rq_time")), - upstream_rq_(makeStatName("upstream_rq_")), vcluster_(makeStatName("vcluster")), vhost_(makeStatName("vhost")), zone_(makeStatName("zone")), - upstream_rq_2xx_(makeStatName("upstream_rq_2xx")), - upstream_rq_3xx_(makeStatName("upstream_rq_3xx")), - upstream_rq_4xx_(makeStatName("upstream_rq_4xx")), - upstream_rq_5xx_(makeStatName("upstream_rq_5xx")) { -} + canary_upstream_rq_("canary_upstream_rq_", *this), + external_upstream_rq_("external_upstream_rq_", *this), + internal_upstream_rq_("internal_upstream_rq_", *this), + upstream_rq_("upstream_rq_rq_", *this) {} + CodeStatsImpl::~CodeStatsImpl() { - for (StatNameStorage& stat_name_storage : storage_) { + for (Stats::StatNameStorage& stat_name_storage : storage_) { stat_name_storage.free(symbol_table_); } } Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { - storage_.push_back(StatNameStorage(name, symbol_table_)); + storage_.push_back(Stats::StatNameStorage(name, symbol_table_)); return storage_.back().statName(); } -void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, +void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, Code response_code) { - // TODO(jmarantz): consider passing prefix by StatName. - StatNameTempStorage prefix_storage(prefix, symbol_table_); - StatName prefix_stat_name = prefix_storage.statName(); + using Join = Stats::StatNameJoiner; // Build a dynamic stat for the response code and increment it. - scope.counter(StatNameJoiner(prefix_stat_name, upstream_rq_completed_).statName()).inc(); - scope.counter(StatNameJoiner(prefix_stat_name_, upstreamRqGroup(response_code))).inc(); - scope.counter(absl::StrCat(prefix, upstream_rq_, enumToInt(response_code))).inc(); + scope.counterx(Join(prefix, upstream_rq_completed_).statName()).inc(); + scope.counterx(Join(prefix, upstreamRqGroup(response_code)).statName()).inc(); + scope.counterx(Join(prefix, upstream_rq_.statName(response_code)).statName()).inc(); +} + +void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, + Code response_code) { + Stats::StatNameTempStorage prefix_storage(stripTrailingDot(prefix), symbol_table_); + return chargeBasicResponseStat(scope, prefix_storage.statName(), response_code); } void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { - StatNameTempStorage prefix_storage(prefix, symbol_table_); - StatName prefix_stat_name = prefix_storage.statName(); + Stats::StatNameTempStorage prefix_storage(stripTrailingDot(info.prefix_), symbol_table_); + Stats::StatName prefix = prefix_storage.statName(); + Code code = static_cast(info.response_status_code_); - const uint64_t response_code = info.response_status_code_; - chargeBasicResponseStat(info.cluster_scope_, info.prefix_, static_cast(response_code)); + chargeBasicResponseStat(info.cluster_scope_, prefix, code); - absl::string_view prefix = stripTrailingDot(info.prefix_); + using Join = Stats::StatNameJoiner; // If the response is from a canary, also create canary stats. if (info.upstream_canary_) { - info.cluster_scope_.counter(join(prefix, canary_upstream_rq_completed_)).inc(); - info.cluster_scope_.counter(join(info.prefix, canary_, upstreamRqGroup(response_code)).inc(); - info.cluster_scope_.counter(prefix, canary_upstream_rq_, response_code)) + info.cluster_scope_.counterx(Join( + prefix, canary_upstream_rq_completed_).statName()) + .inc(); + info.cluster_scope_.counterx(Join( + {prefix, canary_, upstreamRqGroup(code)}).statName()) + .inc(); + info.cluster_scope_.counterx(Join({prefix, canary_, upstream_rq_.statName(code)}).statName()) .inc(); } // Split stats into external vs. internal. if (info.internal_request_) { - info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_completed_)).inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, rc_group)) - .inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, internal_upstream_rq_, response_code)) + info.cluster_scope_.counterx(Join(prefix, internal_upstream_rq_completed_).statName()).inc(); + info.cluster_scope_.counterx(Join({prefix, internal_, upstreamRqGroup(code)}).statName()) .inc(); + info.cluster_scope_.counterx(Join({prefix, internal_, upstream_rq_.statName(code)}) + .statName()).inc(); } else { - info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_completed_)).inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, rc_group)) - .inc(); - info.cluster_scope_.counter(absl::StrCat(info.prefix_, external_upstream_rq_, response_code)) + info.cluster_scope_.counterx(Join(prefix, external_upstream_rq_completed_).statName()).inc(); + info.cluster_scope_.counterx(Join({prefix, external_, upstreamRqGroup(code)}).statName()).inc(); + info.cluster_scope_.counterx(Join(prefix, external_, upstream_rq_.statName(code)).statName()) .inc(); } // Handle request virtual cluster. if (!info.request_vcluster_name_.empty()) { info.global_scope_ - .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, + .counter(Join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, upstream_rq_completed_})) .inc(); info.global_scope_ @@ -191,6 +200,37 @@ StatName CodeStatsImpl::upstreamRq(Code response_code) { return StatName(); } +CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { + for (auto p : rc_stat_name_map_) { + std::unique_ptr& storage = p->second; + storage->free(code_stats_.symbol_table_); + } +} + +Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { + { + absl::ReaderMutexLock lock(&mutex_); + auto p = rc_stat_name_map_.find(response_code); + if (p != rc_stat_name_map_.end()) { + return p->second; + } + } + + // Note -- another thread may swap in here and allocate the lock so we + // have to re-check after acquiring the write-lock. + + { + absl::MutexLock lock(&mutex_); + mutex_.WriterLock(); + std::unique_ptr& stat_name_storage = rc_stat_name_map_[response_code]; + if (stat_name_storage == nullptr) { + stat_name_storage = std::make_unique( + absl::StrCat(prefix_, enumToInt(reesponse_code))); + } + return stat_name_storage->statName(); + } +} + std::string CodeUtility::groupStringForResponseCode(Code response_code) { // Note: this is only used in the unit test and in dynamo_filter.cc, which // needs the same sort of symbloziation treatment we are doing here. diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 563fff86e313..c8cc50eabf56 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -8,6 +8,8 @@ #include "envoy/http/header_map.h" #include "envoy/stats/scope.h" +#include "common/stats/symbol_table_impl.h" + namespace Envoy { namespace Http { @@ -19,10 +21,41 @@ class CodeStatsImpl : public CodeStats { // CodeStats void chargeBasicResponseStat(Stats::Scope& scope, const std::string& prefix, Code response_code) override; + void chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, + Code response_code) override; void chargeResponseStat(const ResponseStatInfo& info) override; void chargeResponseTiming(const ResponseTimingInfo& info) override; private: + class RequestCodeGroup { + public: + RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats) + : code_stats_(code_stats), prefix_(std::string(prefix)) {} + ~RequestCodeGroup(); + + Stats::StatName statName(Code response_code); + + /* + using LockedStatName = std::pair>; + + // We'll cover known HTTP status codes in a mapped array, which we'll + // discover by calling CodeUtility::toString(). Of course the response-code + // can be any 64-bit integer as far as we can tell from this class, so + // we'll have a fallback flast hash map for those. + + constexpr MaxResponseCode = 600; + LockStatName[MaxResponseCode] locked_stat_names_; + */ + + private: + using RCStatNameMap = absl::flat_hash_map>; + + CodeStatsImpl& code_stats_; + std::string prefix_; + absl::Mutex mutex_; + RCStatNameMap rc_stat_name_map_ GUARDED_BY(mutex_); + }; + static absl::string_view stripTrailingDot(absl::string_view prefix); static std::string join(const std::vector& v); Stats::StatName makeStatName(absl::string_view name); @@ -39,35 +72,34 @@ class CodeStatsImpl : public CodeStats { std::vector storage_; Stats::SymbolTable& symbol_table_; + + Stats::StatName canary_; Stats::StatName canary_upstream_rq_completed_; Stats::StatName canary_upstream_rq_time_; + Stats::StatName external_; Stats::StatName external_rq_time_; Stats::StatName external_upstream_rq_completed_; Stats::StatName external_upstream_rq_time_; + Stats::StatName internal_; Stats::StatName internal_rq_time_; Stats::StatName internal_upstream_rq_completed_; Stats::StatName internal_upstream_rq_time_; + Stats::StatName upstream_; + Stats::StatName upstream_rq_2xx_; + Stats::StatName upstream_rq_3xx_; + Stats::StatName upstream_rq_4xx_; + Stats::StatName upstream_rq_5xx_; Stats::StatName upstream_rq_completed_; - Stats::StatName upstream_rq_time_; Stats::StatName upstream_rq_time; + Stats::StatName upstream_rq_time_; Stats::StatName vcluster_; Stats::StatName vhost_; Stats::StatName zone_; - Stats::StatName response_code_2xx_; - Stats::StatName response_code_3xx_; - Stats::StatName response_code_4xx_; - Stats::StatName response_code_5xx_; - - // We keep several stats for each HTTP response codes, created lazily -- as - // most response-codes are never seen. We do a poor man's locking here by - // keeping them in an array. To minimize contention generally, we'll just have - // a r/w lock per response-code. - using LockedStatName = std::pair>; - constexpr MaxResponseCode = 600; - LockedStat[MaxResponseCode] canary_upstream_rq_; - LockedStat[MaxResponseCode] external_upstream_rq_; - LockedStat[MaxResponseCode] internal_upstream_rq_; - LockedStat[MaxResponseCode] upstream_rq_; + + RequestCodeGroup canary_upstream_rq_; + RequestCodeGroup external_upstream_rq_; + RequestCodeGroup internal_upstream_rq_; + RequestCodeGroup upstream_rq_; }; /** From a892f70bd1daccd0852feaa940e3c7ed9b079b4e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 9 Nov 2018 00:35:02 -0500 Subject: [PATCH 017/106] partially symbolize http-response-code stats. Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 138 +++++++++++---------- source/common/http/codes.h | 10 +- source/common/stats/isolated_store_impl.cc | 9 +- source/common/stats/isolated_store_impl.h | 4 +- test/common/http/codes_speed_test.cc | 4 + test/common/http/codes_test.cc | 13 +- test/mocks/stats/mocks.cc | 6 +- test/mocks/stats/mocks.h | 11 +- 8 files changed, 109 insertions(+), 86 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index bb4f418709c9..b2485dbea78a 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -19,32 +19,24 @@ namespace Envoy { namespace Http { CodeStatsImpl::CodeStatsImpl(Stats::SymbolTable& symbol_table) - : symbol_table_(symbol_table), - canary_(makeStatName("canary")), - canary_upstream_rq_completed_(makeStatName("canary.upstream_rq_completed")), + : symbol_table_(symbol_table), canary_(makeStatName("canary")), canary_upstream_rq_time_(makeStatName("canary.upstream_rq_time")), external_(makeStatName("external")), external_rq_time_(makeStatName("external.upstream_rq_time")), - external_upstream_rq_completed_(makeStatName("external.upstream_rq_completed")), external_upstream_rq_time_(makeStatName("external.upstream_rq_time")), internal_(makeStatName("internal")), internal_rq_time_(makeStatName("internal.upstream_rq_time")), - internal_upstream_rq_completed_(makeStatName("internal.upstream_rq_completed")), internal_upstream_rq_time_(makeStatName("internal.upstream_rq_time")), upstream_rq_2xx_(makeStatName("upstream_rq_2xx")), upstream_rq_3xx_(makeStatName("upstream_rq_3xx")), upstream_rq_4xx_(makeStatName("upstream_rq_4xx")), upstream_rq_5xx_(makeStatName("upstream_rq_5xx")), upstream_rq_completed_(makeStatName("upstream_rq_completed")), - upstream_rq_time_(makeStatName("upstream_rq_time")), - vcluster_(makeStatName("vcluster")), - vhost_(makeStatName("vhost")), - zone_(makeStatName("zone")), + upstream_rq_time_(makeStatName("upstream_rq_time")), vcluster_(makeStatName("vcluster")), + vhost_(makeStatName("vhost")), zone_(makeStatName("zone")), canary_upstream_rq_("canary_upstream_rq_", *this), external_upstream_rq_("external_upstream_rq_", *this), - internal_upstream_rq_("internal_upstream_rq_", *this), - upstream_rq_("upstream_rq_rq_", *this) {} - + internal_upstream_rq_("internal_upstream_rq_", *this), upstream_rq_("upstream_rq_", *this) {} CodeStatsImpl::~CodeStatsImpl() { for (Stats::StatNameStorage& stat_name_storage : storage_) { @@ -59,7 +51,7 @@ Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, Code response_code) { - using Join = Stats::StatNameJoiner; + ASSERT(&scope.symbolTable() == &symbol_table_); // Build a dynamic stat for the response code and increment it. scope.counterx(Join(prefix, upstream_rq_completed_).statName()).inc(); @@ -78,97 +70,105 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { Stats::StatName prefix = prefix_storage.statName(); Code code = static_cast(info.response_status_code_); + ASSERT(&info.cluster_scope_.symbolTable() == &symbol_table_); chargeBasicResponseStat(info.cluster_scope_, prefix, code); - using Join = Stats::StatNameJoiner; + Stats::StatName rq_group = upstreamRqGroup(code); + Stats::StatName rq_code = upstream_rq_.statName(code); + + auto write_category = [this, prefix, rq_group, rq_code, &info](Stats::StatName category) { + info.cluster_scope_.counterx(Join({prefix, category, upstream_rq_completed_}).statName()).inc(); + info.cluster_scope_.counterx(Join({prefix, category, rq_group}).statName()).inc(); + info.cluster_scope_.counterx(Join({prefix, category, rq_code}).statName()).inc(); + }; // If the response is from a canary, also create canary stats. if (info.upstream_canary_) { - info.cluster_scope_.counterx(Join( - prefix, canary_upstream_rq_completed_).statName()) - .inc(); - info.cluster_scope_.counterx(Join( - {prefix, canary_, upstreamRqGroup(code)}).statName()) - .inc(); - info.cluster_scope_.counterx(Join({prefix, canary_, upstream_rq_.statName(code)}).statName()) - .inc(); + write_category(canary_); } // Split stats into external vs. internal. if (info.internal_request_) { - info.cluster_scope_.counterx(Join(prefix, internal_upstream_rq_completed_).statName()).inc(); - info.cluster_scope_.counterx(Join({prefix, internal_, upstreamRqGroup(code)}).statName()) - .inc(); - info.cluster_scope_.counterx(Join({prefix, internal_, upstream_rq_.statName(code)}) - .statName()).inc(); + write_category(internal_); } else { - info.cluster_scope_.counterx(Join(prefix, external_upstream_rq_completed_).statName()).inc(); - info.cluster_scope_.counterx(Join({prefix, external_, upstreamRqGroup(code)}).statName()).inc(); - info.cluster_scope_.counterx(Join(prefix, external_, upstream_rq_.statName(code)).statName()) - .inc(); + write_category(external_); } // Handle request virtual cluster. if (!info.request_vcluster_name_.empty()) { + Stats::StatNameTempStorage vhost_storage(info.request_vhost_name_, symbol_table_); + Stats::StatName vhost_name = vhost_storage.statName(); + Stats::StatNameTempStorage vcluster_storage(info.request_vcluster_name_, symbol_table_); + Stats::StatName vcluster_name = vcluster_storage.statName(); + info.global_scope_ - .counter(Join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, - upstream_rq_completed_})) + .counterx( + Join({vhost_, vhost_name, vcluster_, vcluster_name, upstream_rq_completed_}).statName()) .inc(); info.global_scope_ - .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, - absl::StrCat(upstream_rq_, rc_group)})) + .counterx(Join({vhost_, vhost_name, vcluster_, vcluster_name, rq_group}).statName()) .inc(); info.global_scope_ - .counter(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, - absl::StrCat(upstream_rq_, response_code)})) + .counterx(Join({vhost_, vhost_name, vcluster_, vcluster_name, rq_code}).statName()) .inc(); } // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { + Stats::StatNameTempStorage from_zone_storage(info.from_zone_, symbol_table_); + Stats::StatName from_zone = from_zone_storage.statName(); + Stats::StatNameTempStorage to_zone_storage(info.to_zone_, symbol_table_); + Stats::StatName to_zone = to_zone_storage.statName(); + info.cluster_scope_ - .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, - upstream_rq_completed_})) + .counterx(Join({prefix, zone_, from_zone, to_zone, upstream_rq_completed_}).statName()) .inc(); - info.cluster_scope_ - .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, - absl::StrCat(upstream_rq_, rc_group)})) + info.cluster_scope_.counterx(Join({prefix, zone_, from_zone, to_zone, rq_group}).statName()) .inc(); - info.cluster_scope_ - .counter(join({prefix_without_trailing_dot, zone_, info.from_zone_, info.to_zone_, - absl::StrCat(upstream_rq_, response_code)})) + info.cluster_scope_.counterx(Join({prefix, zone_, from_zone, to_zone, rq_code}).statName()) .inc(); } } void CodeStatsImpl::chargeResponseTiming(const ResponseTimingInfo& info) { - info.cluster_scope_.histogram(absl::StrCat(info.prefix_, upstream_rq_time_)) + Stats::StatNameTempStorage prefix_storage(stripTrailingDot(info.prefix_), symbol_table_); + Stats::StatName prefix = prefix_storage.statName(); + + info.cluster_scope_.histogramx(Join(prefix, upstream_rq_time_).statName()) .recordValue(info.response_time_.count()); if (info.upstream_canary_) { - info.cluster_scope_.histogram(absl::StrCat(info.prefix_, canary_upstream_rq_time_)) + info.cluster_scope_.histogramx(Join(prefix, canary_upstream_rq_time_).statName()) .recordValue(info.response_time_.count()); } if (info.internal_request_) { - info.cluster_scope_.histogram(absl::StrCat(info.prefix_, internal_upstream_rq_time_)) + info.cluster_scope_.histogramx(Join(prefix, internal_upstream_rq_time_).statName()) .recordValue(info.response_time_.count()); } else { - info.cluster_scope_.histogram(absl::StrCat(info.prefix_, external_upstream_rq_time_)) + info.cluster_scope_.histogramx(Join(prefix, external_upstream_rq_time_).statName()) .recordValue(info.response_time_.count()); } if (!info.request_vcluster_name_.empty()) { + Stats::StatNameTempStorage vhost_storage(info.request_vhost_name_, symbol_table_); + Stats::StatName vhost_name = vhost_storage.statName(); + Stats::StatNameTempStorage vcluster_storage(info.request_vcluster_name_, symbol_table_); + Stats::StatName vcluster_name = vcluster_storage.statName(); info.global_scope_ - .histogram(join({vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, - upstream_rq_time_})) + .histogramx( + Join({vhost_, vhost_name, vcluster_, vcluster_name, upstream_rq_time_}).statName()) .recordValue(info.response_time_.count()); } // Handle per zone stats. if (!info.from_zone_.empty() && !info.to_zone_.empty()) { + Stats::StatNameTempStorage from_zone_storage(info.from_zone_, symbol_table_); + Stats::StatName from_zone = from_zone_storage.statName(); + Stats::StatNameTempStorage to_zone_storage(info.to_zone_, symbol_table_); + Stats::StatName to_zone = to_zone_storage.statName(); + info.cluster_scope_ - .histogram(join({stripTrailingDot(info.prefix_), zone_, info.from_zone_, info.to_zone_, - upstream_rq_time_})) + .histogramx(Join({prefix, zone_, from_zone, to_zone, upstream_rq_time_}).statName()) .recordValue(info.response_time_.count()); } } @@ -189,20 +189,25 @@ std::string CodeStatsImpl::join(const std::vector& v) { return absl::StrJoin(iter, v.end(), "."); } -StatName CodeStatsImpl::upstreamRq(Code response_code) { +Stats::StatName CodeStatsImpl::upstreamRqGroup(Code response_code) const { switch (enumToInt(response_code) / 100) { - case 1: return response_code_1xx_; - case 2: return response_code_2xx_; - case 3: return response_code_3xx_; - case 4: return response_code_3xx_; - case 5: return response_code_3xx_; + case 1: + return upstream_rq_1xx_; + case 2: + return upstream_rq_2xx_; + case 3: + return upstream_rq_3xx_; + case 4: + return upstream_rq_4xx_; + case 5: + return upstream_rq_5xx_; } - return StatName(); + return Stats::StatName(); } CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { - for (auto p : rc_stat_name_map_) { - std::unique_ptr& storage = p->second; + for (auto& p : rc_stat_name_map_) { + std::unique_ptr& storage = p.second; storage->free(code_stats_.symbol_table_); } } @@ -212,7 +217,7 @@ Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { absl::ReaderMutexLock lock(&mutex_); auto p = rc_stat_name_map_.find(response_code); if (p != rc_stat_name_map_.end()) { - return p->second; + return p->second->statName(); } } @@ -221,11 +226,10 @@ Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { { absl::MutexLock lock(&mutex_); - mutex_.WriterLock(); - std::unique_ptr& stat_name_storage = rc_stat_name_map_[response_code]; + std::unique_ptr& stat_name_storage = rc_stat_name_map_[response_code]; if (stat_name_storage == nullptr) { - stat_name_storage = std::make_unique( - absl::StrCat(prefix_, enumToInt(reesponse_code))); + stat_name_storage = std::make_unique( + absl::StrCat(prefix_, enumToInt(response_code)), code_stats_.symbol_table_); } return stat_name_storage->statName(); } diff --git a/source/common/http/codes.h b/source/common/http/codes.h index c8cc50eabf56..be578c33df85 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -27,8 +27,10 @@ class CodeStatsImpl : public CodeStats { void chargeResponseTiming(const ResponseTimingInfo& info) override; private: + using Join = Stats::StatNameJoiner; + class RequestCodeGroup { - public: + public: RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats) : code_stats_(code_stats), prefix_(std::string(prefix)) {} ~RequestCodeGroup(); @@ -47,7 +49,7 @@ class CodeStatsImpl : public CodeStats { LockStatName[MaxResponseCode] locked_stat_names_; */ - private: + private: using RCStatNameMap = absl::flat_hash_map>; CodeStatsImpl& code_stats_; @@ -74,17 +76,15 @@ class CodeStatsImpl : public CodeStats { Stats::SymbolTable& symbol_table_; Stats::StatName canary_; - Stats::StatName canary_upstream_rq_completed_; Stats::StatName canary_upstream_rq_time_; Stats::StatName external_; Stats::StatName external_rq_time_; - Stats::StatName external_upstream_rq_completed_; Stats::StatName external_upstream_rq_time_; Stats::StatName internal_; Stats::StatName internal_rq_time_; - Stats::StatName internal_upstream_rq_completed_; Stats::StatName internal_upstream_rq_time_; Stats::StatName upstream_; + Stats::StatName upstream_rq_1xx_; Stats::StatName upstream_rq_2xx_; Stats::StatName upstream_rq_3xx_; Stats::StatName upstream_rq_4xx_; diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 8dec6e96f0ac..668f46c1ba42 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -13,8 +13,13 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() - : alloc_(symbol_table_), counters_([this](StatName name) -> CounterSharedPtr { +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(*new SymbolTable) { + owned_symbol_table_.reset(&symbol_table_); +} + +IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) + : symbol_table_(symbol_table), alloc_(symbol_table_), + counters_([this](StatName name) -> CounterSharedPtr { return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), std::vector()); }), gauges_([this](StatName name) -> GaugeSharedPtr { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 325f1f41264f..2c8fb9e36303 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -58,6 +58,7 @@ template class IsolatedStatsCache { class IsolatedStoreImpl : public Store { public: IsolatedStoreImpl(); + explicit IsolatedStoreImpl(SymbolTable& synbol_table); // Stats::Scope Counter& counterx(StatName name) override { return counters_.get(name); } @@ -93,7 +94,8 @@ class IsolatedStoreImpl : public Store { } private: - SymbolTable symbol_table_; + std::unique_ptr owned_symbol_table_; + SymbolTable& symbol_table_; HeapStatDataAllocator alloc_; IsolatedStatsCache counters_; IsolatedStatsCache gauges_; diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index d59df3389ce0..e32cb91d4bc6 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -19,6 +19,9 @@ namespace Http { class CodeUtilitySpeedTest { public: + CodeUtilitySpeedTest() + : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} + void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, const std::string& request_vcluster_name = EMPTY_STRING, @@ -51,6 +54,7 @@ class CodeUtilitySpeedTest { code_stats_.chargeResponseTiming(info); } + Stats::SymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index 2ebb57ee435b..1ef9529523eb 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -24,6 +24,9 @@ namespace Http { class CodeUtilityTest : public testing::Test { public: + CodeUtilityTest() + : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} + void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, const std::string& request_vcluster_name = EMPTY_STRING, @@ -36,6 +39,7 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } + Stats::SymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; @@ -197,9 +201,9 @@ TEST_F(CodeUtilityTest, PerZoneStats) { EXPECT_EQ(1U, cluster_scope_.counter("prefix.zone.from_az.to_az.upstream_rq_2xx").value()); } -TEST(CodeUtilityResponseTimingTest, All) { - Stats::MockStore global_store; - Stats::MockStore cluster_scope; +TEST_F(CodeUtilityTest, ResponseTimingTest) { + Stats::MockStore global_store(symbol_table_); + Stats::MockStore cluster_scope(symbol_table_); Http::CodeStats::ResponseTimingInfo info{ global_store, cluster_scope, "prefix.", std::chrono::milliseconds(5), @@ -231,8 +235,7 @@ TEST(CodeUtilityResponseTimingTest, All) { EXPECT_CALL(cluster_scope, deliverHistogramToSinks( Property(&Stats::Metric::name, "prefix.zone.from_az.to_az.upstream_rq_time"), 5)); - Http::CodeStatsImpl code_stats; - code_stats.chargeResponseTiming(info); + code_stats_.chargeResponseTiming(info); } } // namespace Http diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 22ec895a02a8..0c50db9f9a9c 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -107,9 +107,11 @@ MockSource::~MockSource() {} MockSink::MockSink() {} MockSink::~MockSink() {} -MockStore::MockStore() : counter_(symbol_table_) { +MockStore::MockStore() : MockStore(owned_symbol_table_) {} + +MockStore::MockStore(SymbolTable& symbol_table) + : symbol_table_(symbol_table), counter_(symbol_table_) { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); - ON_CALL(*this, counterx(_)).WillByDefault(ReturnRef(counter_)); ON_CALL(*this, histogram(_)).WillByDefault(Invoke([this](const std::string& name) -> Histogram& { auto* histogram = new NiceMock(symbol_table_); histogram->name_ = name; diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 6609f2c53565..b7b24c0179bf 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -161,6 +161,7 @@ class MockSink : public Sink { class MockStore : public Store { public: + explicit MockStore(SymbolTable& symbol_table); MockStore(); ~MockStore(); @@ -168,21 +169,23 @@ class MockStore : public Store { MOCK_METHOD2(deliverHistogramToSinks, void(const Histogram& histogram, uint64_t value)); MOCK_METHOD1(counter, Counter&(const std::string&)); - MOCK_METHOD1(counterx, Counter&(StatName)); MOCK_CONST_METHOD0(counters, std::vector()); MOCK_METHOD1(createScope_, Scope*(const std::string& name)); MOCK_METHOD1(gauge, Gauge&(const std::string&)); - MOCK_METHOD1(gaugex, Gauge&(StatName)); MOCK_CONST_METHOD0(gauges, std::vector()); MOCK_METHOD1(histogram, Histogram&(const std::string& name)); - MOCK_METHOD1(histogramx, Histogram&(StatName)); MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); + Counter& counterx(StatName name) { return counter(name.toString(symbol_table_)); } + Gauge& gaugex(StatName name) { return gauge(name.toString(symbol_table_)); } + Histogram& histogramx(StatName name) { return histogram(name.toString(symbol_table_)); } + SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& symbolTable() const override { return symbol_table_; } - SymbolTable symbol_table_; + SymbolTable owned_symbol_table_; + SymbolTable& symbol_table_; testing::NiceMock counter_; std::vector> histograms_; StatsOptionsImpl stats_options_; From 5fadb96bfd000b81045ee3aaa6b95a96e331f5d7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 11 Nov 2018 11:57:00 -0500 Subject: [PATCH 018/106] Virtualize SymbolTable (but not symbols or StatName) for use in mocks, to avoid plumbing one common SymbolTable through the object graph. Signed-off-by: Joshua Marantz --- include/envoy/server/instance.h | 5 + include/envoy/stats/symbol_table.h | 125 +++++++++++++++++- source/common/http/codes.cc | 15 ++- source/common/http/codes.h | 1 + source/common/stats/heap_stat_data.cc | 12 ++ source/common/stats/heap_stat_data.h | 4 + source/common/stats/isolated_store_impl.cc | 8 +- source/common/stats/isolated_store_impl.h | 9 +- source/common/stats/symbol_table_impl.cc | 26 ++-- source/common/stats/symbol_table_impl.h | 41 +++--- source/exe/main_common.h | 2 +- source/server/config_validation/server.cc | 3 +- source/server/config_validation/server.h | 1 + source/server/server.cc | 3 +- source/server/server.h | 1 + .../grpc_client_integration_test_harness.h | 5 +- test/common/http/async_client_impl_test.cc | 5 +- test/common/http/codes_speed_test.cc | 5 +- test/common/http/codes_test.cc | 9 +- test/common/router/router_test.cc | 4 +- test/common/stats/heap_stat_data_test.cc | 2 +- test/common/stats/source_impl_test.cc | 2 +- test/common/stats/symbol_table_impl_test.cc | 8 +- .../stats/thread_local_store_speed_test.cc | 2 +- test/common/stats/thread_local_store_test.cc | 2 +- .../upstream/cluster_manager_impl_test.cc | 4 +- .../filters/http/ext_authz/ext_authz_test.cc | 4 +- .../filters/http/ratelimit/ratelimit_test.cc | 4 +- .../common/statsd/udp_statsd_test.cc | 2 +- test/integration/server.cc | 7 +- test/mocks/server/mocks.cc | 6 +- test/mocks/server/mocks.h | 11 +- test/mocks/stats/mocks.cc | 50 ++++++- test/mocks/stats/mocks.h | 33 ++++- test/mocks/upstream/host.h | 5 +- .../config_validation/cluster_manager_test.cc | 3 +- test/server/hot_restart_impl_test.cc | 2 +- test/server/http/admin_test.cc | 2 +- test/test_common/utility.h | 4 +- 39 files changed, 338 insertions(+), 99 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 02e88f51b7c6..506789c4b539 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -209,6 +209,11 @@ class Instance { */ virtual Event::TimeSystem& timeSystem() PURE; + /** + * @return the statistics symbol table. + */ + virtual Stats::SymbolTable& symbolTable() PURE; + /** * @return the flush interval of stats sinks. */ diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index ceae58a366c5..729ee2801dd5 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -1,5 +1,7 @@ #pragma once +#include "envoy/common/pure.h" + #include "common/common/hash.h" namespace Envoy { @@ -8,7 +10,7 @@ namespace Stats { /** Efficient byte-encoded storage an array of tokens, which are typically < 127 */ using SymbolStorage = uint8_t[]; -// Interface for managing symbol tables. +class SymbolEncoding; class SymbolTable; /** @@ -78,19 +80,134 @@ class StatName { void debugPrint(); #endif + /** + * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). + */ + const uint8_t* data() const { return symbol_array_ + 2; } + protected: + /* friend SymbolTable; friend class StatNameTest; friend class StatNameJoiner; friend class StatNameStorage; + */ + + const uint8_t* symbol_array_; +}; + +/** + * SymbolTable manages a namespace optimized for stats, which are typically + * composed of arrays of "."-separated tokens, with a significant overlap + * between the tokens. Each token is mapped to a Symbol (uint32_t) and + * reference-counted so that no-longer-used symbols can be reclaimed. + * + * We use a uint8_t array to encode arrays of symbols in order to conserve + * space, as in practice the majority of token instances in stat names draw from + * a fairly small set of common names, typically less than 100. The format is + * somewhat similar to UTF-8, with a variable-length array of uint8_t. See the + * implementation for details. + * + * StatNameStorage can be used to manage memory for the byte-encoding. Not all + * StatNames are backed by StatNameStorage -- the storage may be inlined into + * another object such as HeapStatData. StaNameStorage is not fully RAII -- + * instead the owner must call free(SymbolTable&) explicitly before + * StatNameStorage is destructed. This saves 8 bytes of storage per stat, + * relative to holding a SymbolTable& in each StatNameStorage object. + * + * A StatName is a copyable and assignable reference to this storage. It does + * not own the storage or keep it alive via reference counts; the owner must + * ensure the backing store lives as long as the StatName. + * + * The underlying Symbol / SymbolVec data structures are private to the + * impl. One side effect of the non-monotonically-increasing symbol counter is + * that if a string is encoded, the resulting stat is destroyed, and then that + * same string is re-encoded, it may or may not encode to the same underlying + * symbol. + */ +class SymbolTable { +public: + virtual ~SymbolTable() {} /** - * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). + * Encodes a stat name using the symbol table, returning a SymbolEncoding. The + * SymbolEncoding is not intended for long-term storage, but is used to help + * allocate and StatName with the correct amount of storage. + * + * When a name is encoded, it bumps reference counts held in the table for + * each symbol. The caller is responsible for creating a StatName using this + * SymbolEncoding and ultimately disposing of it by calling + * StatName::free(). Otherwise the symbols will leak for the lifetime of the + * table, though they won't show up as a C++ leaks as the memory is still + * reachable from the SymolTable. + * + * @param name The name to encode. + * @return SymbolEncoding the encoded symbols. */ - const uint8_t* data() const { return symbol_array_ + 2; } + virtual SymbolEncoding encode(absl::string_view name) PURE; - const uint8_t* symbol_array_; + /** + * @return uint64_t the number of symbols in the symbol table. + */ + virtual uint64_t numSymbols() const PURE; + + /** + * Deterines 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; + + /** + * 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 symbol_vec the vector of symbols to be freed. + */ + virtual void free(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 symbol_vec the vector of symbols to be freed. + */ + virtual void incRefCount(StatName stat_name) 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 hard, since this + * should never happen, and we don't want to continue running with a corrupt stats set. + * + * @param symbol_vec the vector of symbols to decode. + * @return std::string the retrieved stat name. + */ + virtual std::string decode(const SymbolStorage symbol_vec, uint64_t size) const PURE; + +#ifndef ENVOY_CONFIG_COVERAGE + virtual void debugPrint() const PURE; +#endif + + virtual bool interoperable(const SymbolTable& other) const PURE; }; +using SharedSymbolTable = std::shared_ptr; + } // namespace Stats } // namespace Envoy diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index b2485dbea78a..10cbb4de0bcd 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -27,10 +27,12 @@ CodeStatsImpl::CodeStatsImpl(Stats::SymbolTable& symbol_table) internal_(makeStatName("internal")), internal_rq_time_(makeStatName("internal.upstream_rq_time")), internal_upstream_rq_time_(makeStatName("internal.upstream_rq_time")), + upstream_rq_1xx_(makeStatName("upstream_rq_1xx")), upstream_rq_2xx_(makeStatName("upstream_rq_2xx")), upstream_rq_3xx_(makeStatName("upstream_rq_3xx")), upstream_rq_4xx_(makeStatName("upstream_rq_4xx")), upstream_rq_5xx_(makeStatName("upstream_rq_5xx")), + upstream_rq_unknown_(makeStatName("upstream_rq_Unknown")), upstream_rq_completed_(makeStatName("upstream_rq_completed")), upstream_rq_time_(makeStatName("upstream_rq_time")), vcluster_(makeStatName("vcluster")), vhost_(makeStatName("vhost")), zone_(makeStatName("zone")), @@ -51,7 +53,8 @@ Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, Code response_code) { - ASSERT(&scope.symbolTable() == &symbol_table_); + ASSERT(scope.symbolTable().interoperable(symbol_table_)); + ASSERT(symbol_table_.interoperable(scope.symbolTable())); // Build a dynamic stat for the response code and increment it. scope.counterx(Join(prefix, upstream_rq_completed_).statName()).inc(); @@ -70,7 +73,9 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) { Stats::StatName prefix = prefix_storage.statName(); Code code = static_cast(info.response_status_code_); - ASSERT(&info.cluster_scope_.symbolTable() == &symbol_table_); + ASSERT(info.cluster_scope_.symbolTable().interoperable(symbol_table_)); + ASSERT(symbol_table_.interoperable(info.cluster_scope_.symbolTable())); + chargeBasicResponseStat(info.cluster_scope_, prefix, code); Stats::StatName rq_group = upstreamRqGroup(code); @@ -202,7 +207,7 @@ Stats::StatName CodeStatsImpl::upstreamRqGroup(Code response_code) const { case 5: return upstream_rq_5xx_; } - return Stats::StatName(); + return upstream_rq_unknown_; } CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { @@ -238,7 +243,9 @@ Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { std::string CodeUtility::groupStringForResponseCode(Code response_code) { // Note: this is only used in the unit test and in dynamo_filter.cc, which // needs the same sort of symbloziation treatment we are doing here. - if (CodeUtility::is2xx(enumToInt(response_code))) { + if (CodeUtility::is1xx(enumToInt(response_code))) { + return "1xx"; + } else if (CodeUtility::is2xx(enumToInt(response_code))) { return "2xx"; } else if (CodeUtility::is3xx(enumToInt(response_code))) { return "3xx"; diff --git a/source/common/http/codes.h b/source/common/http/codes.h index be578c33df85..7873519f2fe0 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -89,6 +89,7 @@ class CodeStatsImpl : public CodeStats { Stats::StatName upstream_rq_3xx_; Stats::StatName upstream_rq_4xx_; Stats::StatName upstream_rq_5xx_; + Stats::StatName upstream_rq_unknown_; Stats::StatName upstream_rq_completed_; Stats::StatName upstream_rq_time; Stats::StatName upstream_rq_time_; diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index 33ddabf6fc55..f0bdabe9b489 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -1,5 +1,7 @@ #include "common/stats/heap_stat_data.h" +#include + #include "common/common/lock_guard.h" #include "common/common/thread.h" #include "common/common/utility.h" @@ -54,6 +56,16 @@ void HeapStatDataAllocator::free(HeapStatData& data) { data.free(symbolTable()); } +#ifndef ENVOY_CONFIG_COVERAGE +void HeapStatDataAllocator::debugPrint() { + Thread::LockGuard lock(mutex_); + for (HeapStatData* heap_stat_data : stats_) { + std::cout << heap_stat_data->statName().toString(symbolTable()) << std::endl; + } + std::cout << std::flush; +} +#endif + template class StatDataAllocatorImpl; } // namespace Stats diff --git a/source/common/stats/heap_stat_data.h b/source/common/stats/heap_stat_data.h index 016826946d40..029c557d0f67 100644 --- a/source/common/stats/heap_stat_data.h +++ b/source/common/stats/heap_stat_data.h @@ -87,6 +87,10 @@ class HeapStatDataAllocator : public StatDataAllocatorImpl { tag_extracted_name, tags); } +#ifndef ENVOY_CONFIG_COVERAGE + void debugPrint(); +#endif + private: struct HeapStatHash { size_t operator()(const HeapStatData* a) const { return a->hash(); } diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 668f46c1ba42..9697c0dfada1 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -13,12 +13,10 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(*new SymbolTable) { - owned_symbol_table_.reset(&symbol_table_); -} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_shared()) {} -IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) - : symbol_table_(symbol_table), alloc_(symbol_table_), +IsolatedStoreImpl::IsolatedStoreImpl(const SharedSymbolTable& symbol_table) + : symbol_table_(symbol_table), alloc_(*symbol_table_), counters_([this](StatName name) -> CounterSharedPtr { return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), std::vector()); }), diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 2c8fb9e36303..a2ba33ce4129 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -58,7 +58,7 @@ template class IsolatedStatsCache { class IsolatedStoreImpl : public Store { public: IsolatedStoreImpl(); - explicit IsolatedStoreImpl(SymbolTable& synbol_table); + explicit IsolatedStoreImpl(const SharedSymbolTable& symbol_table); // Stats::Scope Counter& counterx(StatName name) override { return counters_.get(name); } @@ -70,8 +70,8 @@ class IsolatedStoreImpl : public Store { return histogram; } const Stats::StatsOptions& statsOptions() const override { return stats_options_; } - const SymbolTable& symbolTable() const override { return symbol_table_; } - virtual SymbolTable& symbolTable() override { return symbol_table_; } + const SymbolTable& symbolTable() const override { return *symbol_table_; } + virtual SymbolTable& symbolTable() override { return *symbol_table_; } // Stats::Store std::vector counters() const override { return counters_.toVector(); } @@ -94,8 +94,7 @@ class IsolatedStoreImpl : public Store { } private: - std::unique_ptr owned_symbol_table_; - SymbolTable& symbol_table_; + SharedSymbolTable symbol_table_; HeapStatDataAllocator alloc_; IsolatedStatsCache counters_; IsolatedStatsCache gauges_; diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 3da1c4fc9faf..da6424fe9a31 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -103,12 +103,12 @@ uint64_t SymbolEncoding::moveToStorage(SymbolStorage symbol_array) { return sz + 2; } -SymbolTable::SymbolTable() +SymbolTableImpl::SymbolTableImpl() // Have to be explicitly initialized, if we want to use the GUARDED_BY macro. : next_symbol_(0), monotonic_counter_(0) {} -SymbolTable::~SymbolTable() { - // To avoid leaks into the symbol table, we expect all StatNames to be freed. +SymbolTableImpl::~SymbolTableImpl() { + // 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 // tests. @@ -118,7 +118,7 @@ SymbolTable::~SymbolTable() { // 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. -SymbolEncoding SymbolTable::encode(const absl::string_view name) { +SymbolEncoding SymbolTableImpl::encode(const absl::string_view name) { SymbolEncoding encoding; if (name.empty()) { @@ -147,11 +147,11 @@ SymbolEncoding SymbolTable::encode(const absl::string_view name) { return encoding; } -std::string SymbolTable::decode(const SymbolStorage symbol_array, uint64_t size) const { - return decode(SymbolEncoding::decodeSymbols(symbol_array, size)); +std::string SymbolTableImpl::decode(const SymbolStorage symbol_array, uint64_t size) const { + return decodeSymbolVec(SymbolEncoding::decodeSymbols(symbol_array, size)); } -std::string SymbolTable::decode(const SymbolVec& symbols) const { +std::string SymbolTableImpl::decodeSymbolVec(const SymbolVec& symbols) const { std::vector name_tokens; name_tokens.reserve(symbols.size()); { @@ -164,7 +164,7 @@ std::string SymbolTable::decode(const SymbolVec& symbols) const { return absl::StrJoin(name_tokens, "."); } -void SymbolTable::adjustRefCount(StatName stat_name, int adjustment) { +void SymbolTableImpl::adjustRefCount(StatName stat_name, int adjustment) { // Before taking the lock, decode the array of symbols from the SymbolStorage. SymbolVec symbols = SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.numBytes()); @@ -188,7 +188,7 @@ void SymbolTable::adjustRefCount(StatName stat_name, int adjustment) { } } -Symbol SymbolTable::toSymbol(absl::string_view sv) EXCLUSIVE_LOCKS_REQUIRED(lock_) { +Symbol SymbolTableImpl::toSymbol(absl::string_view sv) EXCLUSIVE_LOCKS_REQUIRED(lock_) { Symbol result; auto encode_find = encode_map_.find(sv); // If the string segment doesn't already exist, @@ -214,14 +214,14 @@ Symbol SymbolTable::toSymbol(absl::string_view sv) EXCLUSIVE_LOCKS_REQUIRED(lock return result; } -absl::string_view SymbolTable::fromSymbol(const Symbol symbol) const +absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const EXCLUSIVE_LOCKS_REQUIRED(lock_) { auto search = decode_map_.find(symbol); ASSERT(search != decode_map_.end()); return search->second; } -void SymbolTable::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { +void SymbolTableImpl::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { if (pool_.empty()) { next_symbol_ = ++monotonic_counter_; } else { @@ -232,7 +232,7 @@ void SymbolTable::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { ASSERT(monotonic_counter_ != 0); } -bool SymbolTable::lessThan(const StatName& a, const StatName& b) const { +bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { // Constructing two temp vectors during lessThan is not strictly necessary. // If this becomes a performance bottleneck (e.g. during sorting), we could // provide an iterator-like interface for incrementally decoding the symbols @@ -249,7 +249,7 @@ bool SymbolTable::lessThan(const StatName& a, const StatName& b) const { } #ifndef ENVOY_CONFIG_COVERAGE -void SymbolTable::debugPrint() const { +void SymbolTableImpl::debugPrint() const { Thread::LockGuard lock(lock_); std::vector symbols; for (auto p : decode_map_) { diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index d3b615057294..a19831fd69ea 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -118,10 +118,10 @@ class SymbolEncoding { * same string is re-encoded, it may or may not encode to the same underlying * symbol. */ -class SymbolTable { +class SymbolTableImpl : public SymbolTable { public: - SymbolTable(); - ~SymbolTable(); + SymbolTableImpl(); + ~SymbolTableImpl() override; /** * Encodes a stat name using the symbol table, returning a SymbolEncoding. The @@ -138,12 +138,12 @@ class SymbolTable { * @param name The name to encode. * @return SymbolEncoding the encoded symbols. */ - SymbolEncoding encode(absl::string_view name); + SymbolEncoding encode(absl::string_view name) override; /** * @return uint64_t the number of symbols in the symbol table. */ - uint64_t numSymbols() const { + uint64_t numSymbols() const override { Thread::LockGuard lock(lock_); ASSERT(encode_map_.size() == decode_map_.size()); return encode_map_.size(); @@ -165,7 +165,7 @@ class SymbolTable { * @param b the second stat name * @return bool true if a lexically precedes b. */ - bool lessThan(const StatName& a, const StatName& b) const; + bool lessThan(const StatName& a, const StatName& b) const override; /** * Since SymbolTable does manual reference counting, a client of SymbolTable @@ -175,7 +175,7 @@ class SymbolTable { * * @param symbol_vec the vector of symbols to be freed. */ - void free(StatName stat_name) { adjustRefCount(stat_name, -1); } + void free(StatName stat_name) override { adjustRefCount(stat_name, -1); } /** * StatName backing-store can be managed by callers in a variety of ways @@ -186,12 +186,24 @@ class SymbolTable { * * @param symbol_vec the vector of symbols to be freed. */ - void incRefCount(StatName stat_name) { adjustRefCount(stat_name, 1); }; + void incRefCount(StatName stat_name) override { adjustRefCount(stat_name, 1); }; #ifndef ENVOY_CONFIG_COVERAGE - void debugPrint() const; + void debugPrint() const override; #endif + /** + * 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 hard, since this + * should never happen, and we don't want to continue running with a corrupt stats set. + * + * @param symbol_vec the vector of symbols to decode. + * @return std::string the retrieved stat name. + */ + std::string decode(const SymbolStorage symbol_vec, uint64_t size) const override; + + bool interoperable(const SymbolTable& other) const override { return &other == this; } + private: friend class StatName; friend class StatNameTest; @@ -204,16 +216,7 @@ class SymbolTable { // This must be called during both encode() and free(). mutable Thread::MutexBasicLockable lock_; - /** - * 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 hard, since this - * should never happen, and we don't want to continue running with a corrupt stats set. - * - * @param symbol_vec the vector of symbols to decode. - * @return std::string the retrieved stat name. - */ - std::string decode(const SymbolStorage symbol_vec, uint64_t size) const; - std::string decode(const SymbolVec& symbols) const; + std::string decodeSymbolVec(const SymbolVec& symbols) const; void adjustRefCount(StatName stat_name, int adjustment); diff --git a/source/exe/main_common.h b/source/exe/main_common.h index c1a25d7dae4d..8e5af57fad59 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -60,7 +60,7 @@ class MainCommonBase { protected: Envoy::OptionsImpl& options_; - Stats::SymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; Server::ComponentFactory& component_factory_; std::unique_ptr tls_; diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 30a483de3eb1..a7aff4c7b430 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -45,7 +45,8 @@ ValidationInstance::ValidationInstance(Options& options, Event::TimeSystem& time api_(new Api::ValidationImpl(options.fileFlushIntervalMsec())), dispatcher_(api_->allocateDispatcher(time_system)), singleton_manager_(new Singleton::ManagerImpl()), - access_log_manager_(*api_, *dispatcher_, access_log_lock, store), mutex_tracer_(nullptr) { + access_log_manager_(*api_, *dispatcher_, access_log_lock, store), mutex_tracer_(nullptr), + code_stats_(store.symbolTable()) { try { initialize(options, local_address, component_factory); } catch (const EnvoyException& e) { diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 69eadba2433c..61d491a41dde 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -96,6 +96,7 @@ class ValidationInstance : Logger::Loggable, ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } Event::TimeSystem& timeSystem() override { return time_system_; } + Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } Http::CodeStats& codeStats() override { return code_stats_; } diff --git a/source/server/server.cc b/source/server/server.cc index 34c7b6323bba..fc530386f02d 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -54,7 +54,8 @@ InstanceImpl::InstanceImpl(Options& options, Event::TimeSystem& time_system, ThreadLocal::Instance& tls) : shutdown_(false), options_(options), time_system_(time_system), restarter_(restarter), start_time_(time(nullptr)), original_start_time_(start_time_), stats_store_(store), - thread_local_(tls), api_(new Api::Impl(options.fileFlushIntervalMsec())), + code_stats_(store.symbolTable()), thread_local_(tls), + api_(new Api::Impl(options.fileFlushIntervalMsec())), secret_manager_(std::make_unique()), dispatcher_(api_->allocateDispatcher(time_system)), singleton_manager_(new Singleton::ManagerImpl()), diff --git a/source/server/server.h b/source/server/server.h index ea10cc69d8b2..5cb18c4287d4 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -181,6 +181,7 @@ class InstanceImpl : Logger::Loggable, public Instance { ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } Event::TimeSystem& timeSystem() override { return time_system_; } + Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 9703dcc6e84c..c66b66c2aec0 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -208,7 +208,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { public: GrpcClientIntegrationTest() : method_descriptor_(helloworld::Greeter::descriptor()->FindMethodByName("SayHello")), - dispatcher_(test_time_.timeSystem()) {} + dispatcher_(test_time_.timeSystem()), code_stats_(stats_store_->symbolTable()) {} virtual void initialize() { if (fake_upstream_ == nullptr) { @@ -402,7 +402,8 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { DangerousDeprecatedTestTime test_time_; Event::DispatcherImpl dispatcher_; DispatcherHelper dispatcher_helper_{dispatcher_}; - Stats::IsolatedStoreImpl* stats_store_ = new Stats::IsolatedStoreImpl(); + NiceMock* stats_store_ = + new NiceMock(); Stats::ScopeSharedPtr stats_scope_{stats_store_}; TestMetadata service_wide_initial_metadata_; #ifdef ENVOY_GOOGLE_GRPC diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 9a02aebe93bc..2af4a8d00ebe 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -37,7 +37,8 @@ namespace { class AsyncClientImplTest : public testing::Test { public: AsyncClientImplTest() - : client_(cm_.thread_local_cluster_.cluster_.info_, stats_store_, dispatcher_, local_info_, + : code_stats_(stats_store_.symbolTable()), + client_(cm_.thread_local_cluster_.cluster_.info_, stats_store_, dispatcher_, local_info_, cm_, runtime_, random_, Router::ShadowWriterPtr{new NiceMock()}, code_stats_) { message_->headers().insertMethod().value(std::string("GET")); @@ -71,7 +72,7 @@ class AsyncClientImplTest : public testing::Test { NiceMock dispatcher_; NiceMock runtime_; NiceMock random_; - Stats::IsolatedStoreImpl stats_store_; + Stats::MockIsolatedStatsStore stats_store_; NiceMock local_info_; Http::CodeStatsImpl code_stats_; AsyncClientImpl client_; diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index e32cb91d4bc6..fbde3e88a8fe 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -20,7 +20,8 @@ namespace Http { class CodeUtilitySpeedTest { public: CodeUtilitySpeedTest() - : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} + : symbol_table_(std::make_shared()), global_store_(symbol_table_), + cluster_scope_(symbol_table_), code_stats_(*symbol_table_) {} void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, @@ -54,7 +55,7 @@ class CodeUtilitySpeedTest { code_stats_.chargeResponseTiming(info); } - Stats::SymbolTable symbol_table_; + Stats::SharedSymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index 1ef9529523eb..539d7eeacccc 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -25,7 +25,8 @@ namespace Http { class CodeUtilityTest : public testing::Test { public: CodeUtilityTest() - : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} + : symbol_table_(std::make_shared()), global_store_(symbol_table_), + cluster_scope_(symbol_table_), code_stats_(*symbol_table_) {} void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, @@ -39,7 +40,7 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } - Stats::SymbolTable symbol_table_; + Stats::SharedSymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; @@ -202,8 +203,8 @@ TEST_F(CodeUtilityTest, PerZoneStats) { } TEST_F(CodeUtilityTest, ResponseTimingTest) { - Stats::MockStore global_store(symbol_table_); - Stats::MockStore cluster_scope(symbol_table_); + Stats::MockStore global_store(*symbol_table_); + Stats::MockStore cluster_scope(*symbol_table_); Http::CodeStats::ResponseTimingInfo info{ global_store, cluster_scope, "prefix.", std::chrono::milliseconds(5), diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 77f83c1aef75..27449f4cc7d3 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -75,7 +75,7 @@ class TestFilter : public Filter { class RouterTestBase : public testing::Test { public: RouterTestBase(bool start_child_span, bool suppress_envoy_headers) - : shadow_writer_(new MockShadowWriter()), + : code_stats_(stats_store_.symbolTable()), shadow_writer_(new MockShadowWriter()), config_("test.", local_info_, stats_store_, cm_, runtime_, random_, ShadowWriterPtr{shadow_writer_}, true, start_child_span, suppress_envoy_headers, test_time_.timeSystem(), code_stats_), @@ -185,7 +185,7 @@ class RouterTestBase : public testing::Test { DangerousDeprecatedTestTime test_time_; std::string upstream_zone_{"to_az"}; envoy::api::v2::core::Locality upstream_locality_; - Stats::IsolatedStoreImpl stats_store_; + NiceMock stats_store_; NiceMock cm_; NiceMock runtime_; NiceMock random_; diff --git a/test/common/stats/heap_stat_data_test.cc b/test/common/stats/heap_stat_data_test.cc index ee69a6cacb15..1b6516c53244 100644 --- a/test/common/stats/heap_stat_data_test.cc +++ b/test/common/stats/heap_stat_data_test.cc @@ -33,7 +33,7 @@ class HeapStatDataTest : public testing::Test { EXPECT_EQ(0, symbol_table_.numSymbols()); } - SymbolTable symbol_table_; + SymbolTableImpl symbol_table_; HeapStatDataAllocator alloc_; std::vector stat_name_storage_; }; diff --git a/test/common/stats/source_impl_test.cc b/test/common/stats/source_impl_test.cc index 1d8edf4da83b..d756b1e2ae25 100644 --- a/test/common/stats/source_impl_test.cc +++ b/test/common/stats/source_impl_test.cc @@ -23,7 +23,7 @@ TEST(SourceImplTest, Caching) { ON_CALL(store, histograms()).WillByDefault(ReturnPointee(&stored_histograms)); SourceImpl source(store); - SymbolTable symbol_table; + SymbolTableImpl symbol_table; // Once cached, new values should not be reflected by the return value. stored_counters.push_back(std::make_shared(symbol_table)); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index a044448bfeab..ceea9f9b6f65 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -27,7 +27,9 @@ class StatNameTest : public testing::Test { SymbolVec getSymbols(StatName stat_name) { return SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.numBytes()); } - std::string decodeSymbolVec(const SymbolVec& symbol_vec) { return table_.decode(symbol_vec); } + std::string decodeSymbolVec(const SymbolVec& symbol_vec) { + return table_.decodeSymbolVec(symbol_vec); + } Symbol monotonicCounter() { return table_.monotonicCounter(); } std::string encodeDecode(absl::string_view stat_name) { return makeStat(stat_name).toString(table_); @@ -40,7 +42,7 @@ class StatNameTest : public testing::Test { return stat_name_storage_.back().statName(); } - SymbolTable table_; + SymbolTableImpl table_; std::vector stat_name_storage_; }; @@ -337,7 +339,7 @@ TEST(SymbolTableTest, Memory) { string_mem_used = test_memory_usage(record_stat); } { - SymbolTable table; + SymbolTableImpl table; std::vector names; auto record_stat = [&names, &table](absl::string_view stat) { names.emplace_back(StatNameStorage(stat, table)); diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 6714a19ff291..2b2df9da2c1b 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -50,7 +50,7 @@ class ThreadLocalStorePerf { } private: - Stats::SymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; Stats::StatsOptionsImpl options_; Event::SimulatedTimeSystem time_system_; Stats::HeapStatDataAllocator heap_alloc_; diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 0130cce64831..79fca9637399 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -45,7 +45,7 @@ class StatsThreadLocalStoreTest : public testing::Test { store_->addSink(sink_); } - Stats::SymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; StatsOptionsImpl options_; diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 034650576d25..e0439099e5f6 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -167,7 +167,9 @@ envoy::config::bootstrap::v2::Bootstrap parseBootstrapFromV2Yaml(const std::stri class ClusterManagerImplTest : public testing::Test { public: - ClusterManagerImplTest() { factory_.dispatcher_.setTimeSystem(time_system_); } + ClusterManagerImplTest() : code_stats_(factory_.stats_.symbolTable()) { + factory_.dispatcher_.setTimeSystem(time_system_); + } void create(const envoy::config::bootstrap::v2::Bootstrap& bootstrap) { cluster_manager_ = std::make_unique( diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index 439c68192e65..8fed9c5ffa7b 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -82,7 +82,7 @@ TEST(HttpExtAuthzFilterConfigPerRouteTest, MergeConfig) { class HttpExtAuthzFilterTestBase { public: - HttpExtAuthzFilterTestBase() {} + HttpExtAuthzFilterTestBase() : code_stats_(stats_store_.symbolTable()) {} void initConfig(envoy::config::filter::http::ext_authz::v2alpha::ExtAuthz& proto_config) { config_ = std::make_unique(proto_config, local_info_, stats_store_, runtime_, cm_, @@ -96,7 +96,7 @@ class HttpExtAuthzFilterTestBase { Filters::Common::ExtAuthz::RequestCallbacks* request_callbacks_{}; Http::TestHeaderMapImpl request_headers_; Buffer::OwnedImpl data_; - Stats::IsolatedStoreImpl stats_store_; + NiceMock stats_store_; NiceMock runtime_; NiceMock cm_; NiceMock local_info_; diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index d100537fe5a5..2af376aff0bc 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_test.cc @@ -38,7 +38,7 @@ namespace RateLimitFilter { class HttpRateLimitFilterTest : public testing::Test { public: - HttpRateLimitFilterTest() { + HttpRateLimitFilterTest() : code_stats_(stats_store_.symbolTable()) { ON_CALL(runtime_.snapshot_, featureEnabled("ratelimit.http_filter_enabled", 100)) .WillByDefault(Return(true)); ON_CALL(runtime_.snapshot_, featureEnabled("ratelimit.http_filter_enforcing", 100)) @@ -83,7 +83,7 @@ class HttpRateLimitFilterTest : public testing::Test { Http::TestHeaderMapImpl response_headers_; Buffer::OwnedImpl data_; Buffer::OwnedImpl response_data_; - Stats::IsolatedStoreImpl stats_store_; + NiceMock stats_store_; NiceMock runtime_; NiceMock route_rate_limit_; NiceMock vh_rate_limit_; diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index f155acd8bba7..017a5211760e 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -178,7 +178,7 @@ TEST(UdpStatsdSinkTest, CheckActualStatsWithCustomPrefix) { } TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { - Stats::SymbolTable symbol_table; + Stats::SymbolTableImpl symbol_table; NiceMock source; auto writer_ptr = std::make_shared>(); NiceMock tls_; diff --git a/test/integration/server.cc b/test/integration/server.cc index 5fd4e0db9364..4191ea562023 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -132,7 +132,12 @@ void IntegrationTestServer::onWorkerListenerRemoved() { void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion version, bool deterministic) { OptionsImpl options(Server::createTestOptionsImpl(config_path_, "", version)); - Stats::SymbolTable symbol_table; + + // TODO(jmarantz): Sadly, we use a mock symbol table here, as plumbing the + // real symbol table through the mocking hierarchy -- which generally + // constructs hierarchies of objects with no context, is too daunting. I think + // the right thing to do is to avoid mocks in integration tests. + Stats::MockSymbolTable symbol_table; Server::HotRestartNopImpl restarter(symbol_table); Thread::MutexBasicLockable lock; diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index b8ebfc511ec7..12729a7a0359 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -118,7 +118,8 @@ MockWorker::~MockWorker() {} MockInstance::MockInstance() : secret_manager_(new Secret::SecretManagerImpl()), cluster_manager_(timeSystem()), - ssl_context_manager_(timeSystem()), singleton_manager_(new Singleton::ManagerImpl()) { + ssl_context_manager_(timeSystem()), singleton_manager_(new Singleton::ManagerImpl()), + code_stats_(stats_store_.symbolTable()) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); ON_CALL(*this, httpTracer()).WillByDefault(ReturnRef(http_tracer_)); @@ -157,7 +158,8 @@ MockMain::MockMain(int wd_miss, int wd_megamiss, int wd_kill, int wd_multikill) MockMain::~MockMain() {} -MockFactoryContext::MockFactoryContext() : singleton_manager_(new Singleton::ManagerImpl()) { +MockFactoryContext::MockFactoryContext() + : singleton_manager_(new Singleton::ManagerImpl()), code_stats_(scope_.symbolTable()) { ON_CALL(*this, accessLogManager()).WillByDefault(ReturnRef(access_log_manager_)); ON_CALL(*this, clusterManager()).WillByDefault(ReturnRef(cluster_manager_)); ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index ebcab09e0614..a64980ecc5a8 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -193,7 +193,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::StatDataAllocator&()); private: - Stats::SymbolTable symbol_table_; + Stats::MockSymbolTable symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::HeapStatDataAllocator stats_allocator_; @@ -349,11 +349,12 @@ class MockInstance : public Instance { MOCK_CONST_METHOD0(statsFlushInterval, std::chrono::milliseconds()); Event::TestTimeSystem& timeSystem() override { return test_time_.timeSystem(); } + Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } Http::CodeStats& codeStats() override { return code_stats_; } std::unique_ptr secret_manager_; testing::NiceMock thread_local_; - Stats::IsolatedStoreImpl stats_store_; + Stats::MockIsolatedStatsStore stats_store_; testing::NiceMock http_tracer_; std::shared_ptr> dns_resolver_{ new testing::NiceMock()}; @@ -430,8 +431,8 @@ class MockFactoryContext : public FactoryContext { MOCK_CONST_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_CONST_METHOD0(listenerMetadata, const envoy::api::v2::core::Metadata&()); MOCK_METHOD0(timeSource, TimeSource&()); - Event::SimulatedTimeSystem& timeSystem() { return time_system_; } + Event::SimulatedTimeSystem& timeSystem() { return time_system_; } Http::CodeStats& codeStats() override { return code_stats_; } testing::NiceMock access_log_manager_; @@ -443,11 +444,11 @@ class MockFactoryContext : public FactoryContext { testing::NiceMock local_info_; testing::NiceMock random_; testing::NiceMock runtime_loader_; - Stats::IsolatedStoreImpl scope_; + testing::NiceMock scope_; testing::NiceMock thread_local_; Singleton::ManagerPtr singleton_manager_; testing::NiceMock admin_; - Stats::IsolatedStoreImpl listener_scope_; + Stats::MockIsolatedStatsStore listener_scope_; Event::SimulatedTimeSystem time_system_; testing::NiceMock overload_manager_; Http::CodeStatsImpl code_stats_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 0c50db9f9a9c..b89eb8ace933 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -1,5 +1,7 @@ #include "mocks.h" +#include "common/stats/symbol_table_impl.h" + #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -123,7 +125,53 @@ MockStore::MockStore(SymbolTable& symbol_table) } MockStore::~MockStore() {} -MockIsolatedStatsStore::MockIsolatedStatsStore() {} +MockSymbolTable::MockSymbolTable() {} +MockSymbolTable::~MockSymbolTable() {} + +SymbolEncoding MockSymbolTable::encode(absl::string_view name) { + SymbolEncoding encoding; + if (name.empty()) { + return encoding; + } + std::vector tokens = absl::StrSplit(name, '.'); + for (absl::string_view token : tokens) { + encoding.addSymbol(static_cast(token.size())); + ; + for (char c : token) { + encoding.addSymbol(static_cast(c)); + } + } + return encoding; +} + +std::string MockSymbolTable::decode(const SymbolStorage symbol_vec, uint64_t size) const { + std::string out; + SymbolVec symbols = SymbolEncoding::decodeSymbols(symbol_vec, size); + for (size_t i = 0; i < symbols.size();) { + if (!out.empty()) { + out += "."; + } + size_t end = static_cast(symbols[i]) + i + 1; + while (++i < end) { + out += static_cast(symbols[i]); + } + } + return out; +} + +bool MockSymbolTable::lessThan(const StatName& a, const StatName& b) const { + return a.toString(*this) < b.toString(*this); +} + +uint64_t MockSymbolTable::numSymbols() const { return 0; } +void MockSymbolTable::free(StatName) {} +void MockSymbolTable::incRefCount(StatName) {} +#ifndef ENVOY_CONFIG_COVERAGE +void MockSymbolTable::debugPrint() const {} +#endif + +MockIsolatedStatsStore::MockIsolatedStatsStore() + : IsolatedStoreImpl(std::make_shared()) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() {} } // namespace Stats diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index b7b24c0179bf..a5c61ef37bb7 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -177,20 +177,45 @@ class MockStore : public Store { MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); - Counter& counterx(StatName name) { return counter(name.toString(symbol_table_)); } - Gauge& gaugex(StatName name) { return gauge(name.toString(symbol_table_)); } - Histogram& histogramx(StatName name) { return histogram(name.toString(symbol_table_)); } + Counter& counterx(StatName name) override { return counter(name.toString(symbol_table_)); } + Gauge& gaugex(StatName name) override { return gauge(name.toString(symbol_table_)); } + Histogram& histogramx(StatName name) override { return histogram(name.toString(symbol_table_)); } SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& symbolTable() const override { return symbol_table_; } - SymbolTable owned_symbol_table_; + SymbolTableImpl owned_symbol_table_; SymbolTable& symbol_table_; testing::NiceMock counter_; std::vector> histograms_; StatsOptionsImpl stats_options_; }; +// The MockSymbolTable behaves like SymbolTableImpl, except that you can +// have SymbolEncodings from multiple symbol tables interact. This is +// achieved with unity encoding. +// +// The reason this is desirable for tests is that it is very difficult to +// inject the right SymbolTable& everywhere it needs to go in the test +// infratsructure, since all the existing mocks have no context. +class MockSymbolTable : public SymbolTable { +public: + MockSymbolTable(); + ~MockSymbolTable(); + + SymbolEncoding encode(absl::string_view name) override; + uint64_t numSymbols() const override; + bool lessThan(const StatName& a, const StatName& b) const override; + void free(StatName stat_name) override; + void incRefCount(StatName stat_name) override; + std::string decode(const SymbolStorage symbol_vec, uint64_t size) const override; + bool interoperable(const SymbolTable&) const override { return true; } + +#ifndef ENVOY_CONFIG_COVERAGE + void debugPrint() const override; +#endif +}; + /** * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index 45a6fb8371e9..af2cda33d11a 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -94,7 +94,7 @@ class MockHostDescription : public HostDescription { testing::NiceMock outlier_detector_; testing::NiceMock health_checker_; testing::NiceMock cluster_; - Stats::IsolatedStoreImpl stats_store_; + testing::NiceMock stats_store_; HostStats stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}; }; @@ -164,8 +164,7 @@ class MockHost : public Host { testing::NiceMock cluster_; testing::NiceMock outlier_detector_; - Stats::SymbolTable symbol_table_; - Stats::IsolatedStoreImpl stats_store_; + NiceMock stats_store_; HostStats stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}; }; diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 650e8953bfd5..7268e1d1751a 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -41,7 +41,8 @@ TEST(ValidationClusterManagerTest, MockedMethods) { AccessLog::MockAccessLogManager log_manager; const envoy::config::bootstrap::v2::Bootstrap bootstrap; - Http::CodeStatsImpl code_stats; + Stats::SymbolTableImpl symbol_table; + Http::CodeStatsImpl code_stats(symbol_table); ClusterManagerPtr cluster_manager = factory.clusterManagerFromProto( bootstrap, stats, tls, runtime, random, local_info, log_manager, admin, code_stats); EXPECT_EQ(nullptr, cluster_manager->httpConnPoolForCluster("cluster", ResourcePriority::Default, diff --git a/test/server/hot_restart_impl_test.cc b/test/server/hot_restart_impl_test.cc index 1d5a365a3e39..610377a4a45e 100644 --- a/test/server/hot_restart_impl_test.cc +++ b/test/server/hot_restart_impl_test.cc @@ -44,7 +44,7 @@ class HotRestartImplTest : public testing::Test { hot_restart_->drainParentListeners(); } - Stats::SymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; Api::MockOsSysCalls os_sys_calls_; TestThreadsafeSingletonInjector os_calls{&os_sys_calls_}; NiceMock options_; diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 857d177d384d..926ce36458b9 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -1147,7 +1147,7 @@ class PrometheusStatsFormatterTest : public testing::Test { gauges_.push_back(alloc_.makeGauge(storage.statName(), name, cluster_tags)); } - Stats::SymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; Stats::HeapStatDataAllocator alloc_; std::vector counters_; std::vector gauges_; diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 61cb45037105..cd9ab268f5d8 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -436,7 +436,7 @@ class TestAllocator : public RawStatDataAllocator { private: static void freeAdapter(RawStatData* data) { ::free(data); } std::unordered_map> stats_; - SymbolTable symbol_table_; + SymbolTableImpl symbol_table_; const StatsOptions& stats_options_; }; @@ -448,7 +448,7 @@ class MockedTestAllocator : public RawStatDataAllocator { MOCK_METHOD1(alloc, RawStatData*(absl::string_view name)); MOCK_METHOD1(free, void(RawStatData& data)); - SymbolTable symbol_table_; + SymbolTableImpl symbol_table_; TestAllocator alloc_; }; From aaca9206d92ca79f517a25cd4c731c5837cbbac5 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 12 Nov 2018 08:21:13 -0500 Subject: [PATCH 019/106] Pre-allocate symbols for 200s and 404s, alloc file-system stats per file-system not per-file, and add histogramming of symbol-table encodes. Signed-off-by: Joshua Marantz --- include/envoy/api/api.h | 3 +-- .../common/access_log/access_log_manager_impl.cc | 2 +- .../common/access_log/access_log_manager_impl.h | 6 +++--- source/common/api/api_impl.cc | 11 +++++++---- source/common/api/api_impl.h | 8 +++++--- source/common/filesystem/filesystem_impl.cc | 5 ++--- source/common/filesystem/filesystem_impl.h | 4 ++-- source/common/http/codes.cc | 10 ++++++++++ source/common/http/codes.h | 8 +++++++- source/common/stats/symbol_table_impl.cc | 16 ++++++++++++++++ source/common/stats/symbol_table_impl.h | 6 ++++++ source/common/stats/thread_local_store.h | 1 + source/server/config_validation/api.cc | 5 +++-- source/server/config_validation/api.h | 2 +- source/server/config_validation/server.cc | 2 +- source/server/server.cc | 2 +- 16 files changed, 67 insertions(+), 24 deletions(-) diff --git a/include/envoy/api/api.h b/include/envoy/api/api.h index eece627e00ab..44da8b525a0c 100644 --- a/include/envoy/api/api.h +++ b/include/envoy/api/api.h @@ -35,8 +35,7 @@ class Api { */ virtual Filesystem::FileSharedPtr createFile(const std::string& path, Event::Dispatcher& dispatcher, - Thread::BasicLockable& lock, - Stats::Store& stats_store) PURE; + Thread::BasicLockable& lock) PURE; /** * @return bool whether a file exists and can be opened for read on disk. diff --git a/source/common/access_log/access_log_manager_impl.cc b/source/common/access_log/access_log_manager_impl.cc index 56933387b348..8993bda70a90 100644 --- a/source/common/access_log/access_log_manager_impl.cc +++ b/source/common/access_log/access_log_manager_impl.cc @@ -16,7 +16,7 @@ Filesystem::FileSharedPtr AccessLogManagerImpl::createAccessLog(const std::strin return access_logs_[file_name]; } - access_logs_[file_name] = api_.createFile(file_name, dispatcher_, lock_, stats_store_); + access_logs_[file_name] = api_.createFile(file_name, dispatcher_, lock_); return access_logs_[file_name]; } diff --git a/source/common/access_log/access_log_manager_impl.h b/source/common/access_log/access_log_manager_impl.h index 3c6c0208a2cd..5ab7e8872b49 100644 --- a/source/common/access_log/access_log_manager_impl.h +++ b/source/common/access_log/access_log_manager_impl.h @@ -12,8 +12,8 @@ namespace AccessLog { class AccessLogManagerImpl : public AccessLogManager { public: AccessLogManagerImpl(Api::Api& api, Event::Dispatcher& dispatcher, Thread::BasicLockable& lock, - Stats::Store& stats_store) - : api_(api), dispatcher_(dispatcher), lock_(lock), stats_store_(stats_store) {} + Stats::Store& /*stats_store*/) + : api_(api), dispatcher_(dispatcher), lock_(lock) /*, stats_store_(stats_store)*/ {} // AccessLog::AccessLogManager void reopen() override; @@ -23,7 +23,7 @@ class AccessLogManagerImpl : public AccessLogManager { Api::Api& api_; Event::Dispatcher& dispatcher_; Thread::BasicLockable& lock_; - Stats::Store& stats_store_; + //Stats::Store& stats_store_; std::unordered_map access_logs_; }; diff --git a/source/common/api/api_impl.cc b/source/common/api/api_impl.cc index 7e96e0f1a1a5..19ba20f0a281 100644 --- a/source/common/api/api_impl.cc +++ b/source/common/api/api_impl.cc @@ -13,12 +13,15 @@ Event::DispatcherPtr Impl::allocateDispatcher(Event::TimeSystem& time_system) { return Event::DispatcherPtr{new Event::DispatcherImpl(time_system)}; } -Impl::Impl(std::chrono::milliseconds file_flush_interval_msec) - : file_flush_interval_msec_(file_flush_interval_msec) {} +Impl::Impl(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& stats_store) + : file_flush_interval_msec_(file_flush_interval_msec), + stats_{FILESYSTEM_STATS(POOL_COUNTER_PREFIX(stats_store, "filesystem."), + POOL_GAUGE_PREFIX(stats_store, "filesystem."))} { +} Filesystem::FileSharedPtr Impl::createFile(const std::string& path, Event::Dispatcher& dispatcher, - Thread::BasicLockable& lock, Stats::Store& stats_store) { - return std::make_shared(path, dispatcher, lock, stats_store, + Thread::BasicLockable& lock) { + return std::make_shared(path, dispatcher, lock, stats_, file_flush_interval_msec_); } diff --git a/source/common/api/api_impl.h b/source/common/api/api_impl.h index ef4e84f4c9a8..116d27e64cc6 100644 --- a/source/common/api/api_impl.h +++ b/source/common/api/api_impl.h @@ -7,6 +7,8 @@ #include "envoy/event/timer.h" #include "envoy/filesystem/filesystem.h" +#include "common/filesystem/filesystem_impl.h" + namespace Envoy { namespace Api { @@ -15,18 +17,18 @@ namespace Api { */ class Impl : public Api::Api { public: - Impl(std::chrono::milliseconds file_flush_interval_msec); + Impl(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& stats_store); // Api::Api Event::DispatcherPtr allocateDispatcher(Event::TimeSystem& time_system) override; Filesystem::FileSharedPtr createFile(const std::string& path, Event::Dispatcher& dispatcher, - Thread::BasicLockable& lock, - Stats::Store& stats_store) override; + Thread::BasicLockable& lock) override; bool fileExists(const std::string& path) override; std::string fileReadToEnd(const std::string& path) override; private: std::chrono::milliseconds file_flush_interval_msec_; + FileSystemStats stats_; }; } // namespace Api diff --git a/source/common/filesystem/filesystem_impl.cc b/source/common/filesystem/filesystem_impl.cc index d0e2dea40dae..a7402cfe6f85 100644 --- a/source/common/filesystem/filesystem_impl.cc +++ b/source/common/filesystem/filesystem_impl.cc @@ -95,7 +95,7 @@ bool illegalPath(const std::string& path) { } FileImpl::FileImpl(const std::string& path, Event::Dispatcher& dispatcher, - Thread::BasicLockable& lock, Stats::Store& stats_store, + Thread::BasicLockable& lock, FileSystemStats& stats, std::chrono::milliseconds flush_interval_msec) : path_(path), file_lock_(lock), flush_timer_(dispatcher.createTimer([this]() -> void { stats_.flushed_by_timer_.inc(); @@ -103,8 +103,7 @@ FileImpl::FileImpl(const std::string& path, Event::Dispatcher& dispatcher, flush_timer_->enableTimer(flush_interval_msec_); })), os_sys_calls_(Api::OsSysCallsSingleton::get()), flush_interval_msec_(flush_interval_msec), - stats_{FILESYSTEM_STATS(POOL_COUNTER_PREFIX(stats_store, "filesystem."), - POOL_GAUGE_PREFIX(stats_store, "filesystem."))} { + stats_(stats) { open(); } diff --git a/source/common/filesystem/filesystem_impl.h b/source/common/filesystem/filesystem_impl.h index 0d1bb7a4116f..1768a2d4778a 100644 --- a/source/common/filesystem/filesystem_impl.h +++ b/source/common/filesystem/filesystem_impl.h @@ -82,7 +82,7 @@ bool illegalPath(const std::string& path); class FileImpl : public File { public: FileImpl(const std::string& path, Event::Dispatcher& dispatcher, Thread::BasicLockable& lock, - Stats::Store& stats_store, std::chrono::milliseconds flush_interval_msec); + FileSystemStats& stats_, std::chrono::milliseconds flush_interval_msec); ~FileImpl(); // Filesystem::File @@ -149,7 +149,7 @@ class FileImpl : public File { const std::chrono::milliseconds flush_interval_msec_; // Time interval buffer gets flushed no // matter if it reached the MIN_FLUSH_SIZE // or not. - FileSystemStats stats_; + FileSystemStats& stats_; }; } // namespace Filesystem diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 10cbb4de0bcd..19c1c81f466a 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -218,6 +218,16 @@ CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { } Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { + switch (response_code) { + case Code::OK: return upstream_rq_200_; + case Code::NotFound: return upstream_rq_404_; + default: break; + } + RELEASE_ASSERT(false, absl::StrCat("need to optimize code ", response_code)); + return makeStatName(response_code); +} + +Stats::StatName CodeStatsImpl::RequestCodeGroup::makeStatName(Code response_code) { { absl::ReaderMutexLock lock(&mutex_); auto p = rc_stat_name_map_.find(response_code); diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 7873519f2fe0..4e8a1d805aac 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -32,7 +32,9 @@ class CodeStatsImpl : public CodeStats { class RequestCodeGroup { public: RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats) - : code_stats_(code_stats), prefix_(std::string(prefix)) {} + : code_stats_(code_stats), prefix_(std::string(prefix)), + upstream_rq_200_(makeStatName(Code::OK)), + upstream_rq_404_(makeStatName(Code::NotFound)) {} ~RequestCodeGroup(); Stats::StatName statName(Code response_code); @@ -52,10 +54,14 @@ class CodeStatsImpl : public CodeStats { private: using RCStatNameMap = absl::flat_hash_map>; + Stats::StatName makeStatName(Code response_code); + CodeStatsImpl& code_stats_; std::string prefix_; absl::Mutex mutex_; RCStatNameMap rc_stat_name_map_ GUARDED_BY(mutex_); + Stats::StatName upstream_rq_200_; + Stats::StatName upstream_rq_404_; }; static absl::string_view stripTrailingDot(absl::string_view prefix); diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index da6424fe9a31..d1bbf955faf5 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -113,6 +113,19 @@ SymbolTableImpl::~SymbolTableImpl() { // is needed in production. But it would be good to ensure clean up during // tests. ASSERT(numSymbols() == 0); + +#ifdef TRACK_ENCODES + using CountNamePair = std::pair; + std::vector hist; + for (const auto& p : histogram_) { + hist.push_back(CountNamePair(p.second, p.first)); + } + std::sort(hist.begin(), hist.end()); + for (auto p : hist) { + std::cerr << absl::StrCat(p.first, "\t", p.second) << std::endl; + } + std::cerr << std::flush; +#endif } // TODO(ambuc): There is a possible performance optimization here for avoiding @@ -135,6 +148,9 @@ SymbolEncoding SymbolTableImpl::encode(const absl::string_view name) { // ref-counts in this. { Thread::LockGuard lock(lock_); +#ifdef TRACK_ENCODES + ++histogram_[std::string(name)]; +#endif for (absl::string_view token : tokens) { symbols.push_back(toSymbol(token)); } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index a19831fd69ea..3b69a4e94aca 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -261,6 +261,12 @@ class SymbolTableImpl : public SymbolTable { // TODO(ambuc): There might be an optimization here relating to storing ranges of freed symbols // using an Envoy::IntervalSet. std::stack pool_ GUARDED_BY(lock_); + + //#define TRACK_ENCODES +#ifdef TRACK_ENCODES + using Histogram = absl::flat_hash_map; + Histogram histogram_; +#endif }; /** diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index fea04a9e289b..bdc7af8d747b 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -214,6 +214,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo SymbolTable& symbolTable() override { return parent_.symbolTable(); } Counter& counter(const std::string& name) override { + //std::cerr << "counter(" << name << ")" << std::endl; StatNameTempStorage storage(name, symbolTable()); return counterx(storage.statName()); } diff --git a/source/server/config_validation/api.cc b/source/server/config_validation/api.cc index 227ed06f4aeb..7b65d5e3f8fd 100644 --- a/source/server/config_validation/api.cc +++ b/source/server/config_validation/api.cc @@ -5,8 +5,9 @@ namespace Envoy { namespace Api { -ValidationImpl::ValidationImpl(std::chrono::milliseconds file_flush_interval_msec) - : Impl(file_flush_interval_msec) {} +ValidationImpl::ValidationImpl(std::chrono::milliseconds file_flush_interval_msec, + Stats::Store& stats_store) + : Impl(file_flush_interval_msec, stats_store) {} Event::DispatcherPtr ValidationImpl::allocateDispatcher(Event::TimeSystem& time_system) { return Event::DispatcherPtr{new Event::ValidationDispatcher(time_system)}; diff --git a/source/server/config_validation/api.h b/source/server/config_validation/api.h index ef2f71dcbd85..c09cad87141c 100644 --- a/source/server/config_validation/api.h +++ b/source/server/config_validation/api.h @@ -15,7 +15,7 @@ namespace Api { */ class ValidationImpl : public Impl { public: - ValidationImpl(std::chrono::milliseconds file_flush_interval_msec); + ValidationImpl(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& stats_store); Event::DispatcherPtr allocateDispatcher(Event::TimeSystem&) override; }; diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index a7aff4c7b430..49f5bebbb2cd 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -42,7 +42,7 @@ ValidationInstance::ValidationInstance(Options& options, Event::TimeSystem& time Thread::BasicLockable& access_log_lock, ComponentFactory& component_factory) : options_(options), time_system_(time_system), stats_store_(store), - api_(new Api::ValidationImpl(options.fileFlushIntervalMsec())), + api_(new Api::ValidationImpl(options.fileFlushIntervalMsec(), store)), dispatcher_(api_->allocateDispatcher(time_system)), singleton_manager_(new Singleton::ManagerImpl()), access_log_manager_(*api_, *dispatcher_, access_log_lock, store), mutex_tracer_(nullptr), diff --git a/source/server/server.cc b/source/server/server.cc index fc530386f02d..7eefa62a397d 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -55,7 +55,7 @@ InstanceImpl::InstanceImpl(Options& options, Event::TimeSystem& time_system, : shutdown_(false), options_(options), time_system_(time_system), restarter_(restarter), start_time_(time(nullptr)), original_start_time_(start_time_), stats_store_(store), code_stats_(store.symbolTable()), thread_local_(tls), - api_(new Api::Impl(options.fileFlushIntervalMsec())), + api_(new Api::Impl(options.fileFlushIntervalMsec(), store)), secret_manager_(std::make_unique()), dispatcher_(api_->allocateDispatcher(time_system)), singleton_manager_(new Singleton::ManagerImpl()), From e25f7cdb01d41b2151e1ca8b66b5709e00bf3f66 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 13 Nov 2018 16:43:24 -0500 Subject: [PATCH 020/106] Finishing plumbing up Filesystem::Instance as an object via API and get all tests working. I believe this eliminates symbol-table lock contention except for a few handful of cases on startup, which do not persist during runtime (as everyone is taking read locks). Although there are hidden brief contentions on ref-count atomics but that's true with the stats themselves anyway. Those contentions are not tracked by the absl contention tracker. Signed-off-by: Joshua Marantz --- source/common/api/api_impl.cc | 8 +- source/common/api/api_impl.h | 3 +- source/common/filesystem/filesystem_impl.cc | 12 +++ source/common/filesystem/filesystem_impl.h | 17 ++++ source/common/http/codes.cc | 9 +- source/common/http/codes.h | 4 +- source/common/stats/symbol_table_impl.cc | 93 +++++++++++-------- source/common/stats/symbol_table_impl.h | 32 +++++-- source/common/stats/thread_local_store.cc | 34 +++++++ source/common/stats/thread_local_store.h | 18 +++- .../access_log_manager_impl_test.cc | 4 +- test/common/api/BUILD | 1 + test/common/api/api_impl_test.cc | 21 +++-- .../common/filesystem/filesystem_impl_test.cc | 79 ++++++++-------- test/integration/BUILD | 1 + test/integration/echo_integration_test.cc | 7 +- test/integration/fake_upstream.cc | 3 +- test/integration/http2_integration_test.cc | 8 +- test/integration/integration.cc | 7 +- test/integration/integration.h | 4 +- test/integration/integration_admin_test.cc | 6 +- test/integration/server.h | 1 + test/integration/utility.cc | 23 +++-- test/integration/utility.h | 7 +- test/mocks/api/mocks.cc | 2 +- test/mocks/api/mocks.h | 4 +- test/server/config_validation/BUILD | 1 + .../config_validation/dispatcher_test.cc | 5 +- 28 files changed, 279 insertions(+), 135 deletions(-) diff --git a/source/common/api/api_impl.cc b/source/common/api/api_impl.cc index 19ba20f0a281..d2e81a8e242a 100644 --- a/source/common/api/api_impl.cc +++ b/source/common/api/api_impl.cc @@ -14,15 +14,13 @@ Event::DispatcherPtr Impl::allocateDispatcher(Event::TimeSystem& time_system) { } Impl::Impl(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& stats_store) - : file_flush_interval_msec_(file_flush_interval_msec), - stats_{FILESYSTEM_STATS(POOL_COUNTER_PREFIX(stats_store, "filesystem."), - POOL_GAUGE_PREFIX(stats_store, "filesystem."))} { + : file_system_(file_flush_interval_msec, stats_store) { } Filesystem::FileSharedPtr Impl::createFile(const std::string& path, Event::Dispatcher& dispatcher, Thread::BasicLockable& lock) { - return std::make_shared(path, dispatcher, lock, stats_, - file_flush_interval_msec_); + + return file_system_.createFile(path, dispatcher, lock); } bool Impl::fileExists(const std::string& path) { return Filesystem::fileExists(path); } diff --git a/source/common/api/api_impl.h b/source/common/api/api_impl.h index 116d27e64cc6..7d9260a54563 100644 --- a/source/common/api/api_impl.h +++ b/source/common/api/api_impl.h @@ -27,8 +27,7 @@ class Impl : public Api::Api { std::string fileReadToEnd(const std::string& path) override; private: - std::chrono::milliseconds file_flush_interval_msec_; - FileSystemStats stats_; + Filesystem::Instance file_system_; }; } // namespace Api diff --git a/source/common/filesystem/filesystem_impl.cc b/source/common/filesystem/filesystem_impl.cc index a7402cfe6f85..27091b8a4e4c 100644 --- a/source/common/filesystem/filesystem_impl.cc +++ b/source/common/filesystem/filesystem_impl.cc @@ -94,6 +94,18 @@ bool illegalPath(const std::string& path) { } } +Instance::Instance(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& stats_store) + : file_flush_interval_msec_(file_flush_interval_msec), + file_stats_{FILESYSTEM_STATS(POOL_COUNTER_PREFIX(stats_store, "filesystem."), + POOL_GAUGE_PREFIX(stats_store, "filesystem."))} {} + +FileSharedPtr Instance::createFile(const std::string& path, Event::Dispatcher& dispatcher, + Thread::BasicLockable& lock, + std::chrono::milliseconds file_flush_interval_msec) { + return std::make_shared(path, dispatcher, lock, file_stats_, + file_flush_interval_msec); +}; + FileImpl::FileImpl(const std::string& path, Event::Dispatcher& dispatcher, Thread::BasicLockable& lock, FileSystemStats& stats, std::chrono::milliseconds flush_interval_msec) diff --git a/source/common/filesystem/filesystem_impl.h b/source/common/filesystem/filesystem_impl.h index 1768a2d4778a..e8bdc5b4a157 100644 --- a/source/common/filesystem/filesystem_impl.h +++ b/source/common/filesystem/filesystem_impl.h @@ -31,6 +31,23 @@ struct FileSystemStats { namespace Filesystem { +class Instance { + public: + explicit Instance(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& store); + + FileSharedPtr createFile(const std::string& path, Event::Dispatcher& dispatcher, + Thread::BasicLockable& lock, + std::chrono::milliseconds file_flush_interval_msec); + FileSharedPtr createFile(const std::string& path, Event::Dispatcher& dispatcher, + Thread::BasicLockable& lock) { + return createFile(path, dispatcher, lock, file_flush_interval_msec_); + } + + private: + std::chrono::milliseconds file_flush_interval_msec_; + FileSystemStats file_stats_; +}; + /** * @return bool whether a file exists on disk and can be opened for read. */ diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 19c1c81f466a..f7a782070b1e 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -219,11 +219,12 @@ CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { switch (response_code) { - case Code::OK: return upstream_rq_200_; - case Code::NotFound: return upstream_rq_404_; - default: break; + case Code::OK: return upstream_rq_200_; + case Code::NotFound: return upstream_rq_404_; + case Code::ServiceUnavailable: return upstream_rq_503_; + default: break; } - RELEASE_ASSERT(false, absl::StrCat("need to optimize code ", response_code)); + //RELEASE_ASSERT(false, absl::StrCat("need to optimize code ", response_code)); return makeStatName(response_code); } diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 4e8a1d805aac..8c8d1032877a 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -34,7 +34,8 @@ class CodeStatsImpl : public CodeStats { RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats) : code_stats_(code_stats), prefix_(std::string(prefix)), upstream_rq_200_(makeStatName(Code::OK)), - upstream_rq_404_(makeStatName(Code::NotFound)) {} + upstream_rq_404_(makeStatName(Code::NotFound)), + upstream_rq_503_(makeStatName(Code::ServiceUnavailable)) {} ~RequestCodeGroup(); Stats::StatName statName(Code response_code); @@ -62,6 +63,7 @@ class CodeStatsImpl : public CodeStats { RCStatNameMap rc_stat_name_map_ GUARDED_BY(mutex_); Stats::StatName upstream_rq_200_; Stats::StatName upstream_rq_404_; + Stats::StatName upstream_rq_503_; }; static absl::string_view stripTrailingDot(absl::string_view prefix); diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index d1bbf955faf5..7f40a0e47cfa 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -146,14 +146,11 @@ SymbolEncoding SymbolTableImpl::encode(const absl::string_view name) { // Now take the lock and populate the Symbol objects, which involves bumping // ref-counts in this. - { - Thread::LockGuard lock(lock_); #ifdef TRACK_ENCODES - ++histogram_[std::string(name)]; + ++histogram_[std::string(name)]; #endif - for (absl::string_view token : tokens) { - symbols.push_back(toSymbol(token)); - } + for (absl::string_view token : tokens) { + symbols.push_back(toSymbol(token)); } // Now efficiently encode the array of 32-bit symbols into a uint8_t array. @@ -172,7 +169,7 @@ std::string SymbolTableImpl::decodeSymbolVec(const SymbolVec& symbols) const { name_tokens.reserve(symbols.size()); { // Hold the lock only while decoding symbols. - Thread::LockGuard lock(lock_); + absl::ReaderMutexLock lock(&lock_); for (Symbol symbol : symbols) { name_tokens.push_back(fromSymbol(symbol)); } @@ -184,19 +181,19 @@ void SymbolTableImpl::adjustRefCount(StatName stat_name, int adjustment) { // Before taking the lock, decode the array of symbols from the SymbolStorage. SymbolVec symbols = SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.numBytes()); - Thread::LockGuard lock(lock_); + absl::MutexLock lock(&lock_); for (Symbol symbol : symbols) { auto decode_search = decode_map_.find(symbol); ASSERT(decode_search != decode_map_.end()); - auto encode_search = encode_map_.find(decode_search->second); + auto encode_search = encode_map_.find(decode_search->second.get()); ASSERT(encode_search != encode_map_.end()); - encode_search->second.ref_count_ += adjustment; + encode_search->second->ref_count_ += adjustment; // If that was the last remaining client usage of the symbol, erase the the current // mappings and add the now-unused symbol to the reuse pool. - if (encode_search->second.ref_count_ == 0) { + if (encode_search->second->ref_count_ == 0) { decode_map_.erase(decode_search); encode_map_.erase(encode_search); pool_.push(symbol); @@ -204,37 +201,58 @@ void SymbolTableImpl::adjustRefCount(StatName stat_name, int adjustment) { } } -Symbol SymbolTableImpl::toSymbol(absl::string_view sv) EXCLUSIVE_LOCKS_REQUIRED(lock_) { - Symbol result; - auto encode_find = encode_map_.find(sv); - // If the string segment doesn't already exist, - if (encode_find == encode_map_.end()) { - // We create the actual string, place it in the decode_map_, and then insert a string_view - // pointing to it in the encode_map_. This allows us to only store the string once. - std::string str = std::string(sv); +Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { + { + // First try to find the symbol with just a read-lock, so concurrent + // lookups for an already-allocated symbol do not conflict. + absl::ReaderMutexLock lock(&lock_); + auto encode_find = encode_map_.find(sv); + if (encode_find != encode_map_.end()) { + // If the insertion didn't take place, return the actual value at that location and up the + // refcount at that location + SharedSymbol& shared_symbol = *encode_find->second; + ++(shared_symbol.ref_count_); + return shared_symbol.symbol_; + } + } + // If the find() under read-lock failed, we need to release it and take a + // write-lock. Note that another thread may race to insert the symbol during + // this window of time with the lock released, so we need to check again + // under write-lock, which we do by proactively allocating the string and + // attempting to insert it into the encode_map. If that worked, we also + // write the decode-map, transfering the ownership of the string to the + // decode-map value. + absl::MutexLock lock(&lock_); + + // If the string segment doesn't already exist, create the actual string as a + // nul-terminated char-array and insert into encode_map_, and then insert a + // string_view pointing to it in the encode_map_. This allows us to only store + // the string once. + size_t size = sv.size() + 1; + std::unique_ptr str = std::make_unique(size); + StringUtil::strlcpy(str.get(), sv.data(), size); + + auto encode_insert = encode_map_.insert({str.get(), + std::make_unique(next_symbol_)}); + SharedSymbol& shared_symbol = *encode_insert.first->second; + + if (encode_insert.second) { auto decode_insert = decode_map_.insert({next_symbol_, std::move(str)}); ASSERT(decode_insert.second); - - auto encode_insert = encode_map_.insert({decode_insert.first->second, {next_symbol_, 1}}); - ASSERT(encode_insert.second); - - result = next_symbol_; newSymbol(); } else { - // If the insertion didn't take place, return the actual value at that location and up the - // refcount at that location - result = encode_find->second.symbol_; - ++(encode_find->second.ref_count_); + // If the insertion didn't take place, return the shared symbol, and bump the refcount. + ++(shared_symbol.ref_count_); } - return result; + return shared_symbol.symbol_; } absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const - EXCLUSIVE_LOCKS_REQUIRED(lock_) { + SHARED_LOCKS_REQUIRED(lock_) { auto search = decode_map_.find(symbol); ASSERT(search != decode_map_.end()); - return search->second; + return absl::string_view(search->second.get()); } void SymbolTableImpl::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { @@ -257,7 +275,7 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { SymbolVec bv = SymbolEncoding::decodeSymbols(b.data(), b.numBytes()); for (uint64_t i = 0, n = std::min(av.size(), bv.size()); i < n; ++i) { if (av[i] != bv[i]) { - Thread::LockGuard lock(lock_); + absl::ReaderMutexLock lock(&lock_); return fromSymbol(av[i]) < fromSymbol(bv[i]); } } @@ -266,16 +284,17 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { #ifndef ENVOY_CONFIG_COVERAGE void SymbolTableImpl::debugPrint() const { - Thread::LockGuard lock(lock_); + absl::ReaderMutexLock lock(&lock_); std::vector symbols; - for (auto p : decode_map_) { + for (const auto& p : decode_map_) { symbols.push_back(p.first); } std::sort(symbols.begin(), symbols.end()); for (Symbol symbol : symbols) { - const std::string& token = decode_map_.find(symbol)->second; - const SharedSymbol& shared_symbol = encode_map_.find(token)->second; - std::cout << symbol << ": '" << token << "' (" << shared_symbol.ref_count_ << ")" << std::endl; + const char* token = decode_map_.find(symbol)->second.get(); + const SharedSymbol& shared_symbol = *encode_map_.find(token)->second; + std::cout << symbol << ": '" << token << "' (" << shared_symbol.ref_count_ << ")" + << std::endl; } std::cout << std::flush; } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 3b69a4e94aca..7cd55b65a8f4 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -144,7 +144,7 @@ class SymbolTableImpl : public SymbolTable { * @return uint64_t the number of symbols in the symbol table. */ uint64_t numSymbols() const override { - Thread::LockGuard lock(lock_); + absl::ReaderMutexLock lock(&lock_); ASSERT(encode_map_.size() == decode_map_.size()); return encode_map_.size(); } @@ -209,12 +209,29 @@ class SymbolTableImpl : public SymbolTable { friend class StatNameTest; struct SharedSymbol { + SharedSymbol(Symbol symbol) : symbol_(symbol), ref_count_(1) {} + /* + SharedSymbol(const SharedSymbol& src) : symbol_(src.symbol_), ref_count_(1) { + ASSERT(src.ref_count_ == 1); + } + SharedSymbol& operator=(const SharedSymbol& src) { + if (&src != this) { + ASSERT(src.ref_count_ == 1); + symbol_ = src.symbol_; + ref_count_ = 1; + } + return *this; + } + */ + Symbol symbol_; - uint32_t ref_count_; + std::atomic ref_count_; + //uint32_t ref_count_; }; // This must be called during both encode() and free(). - mutable Thread::MutexBasicLockable lock_; + //mutable Thread::MutexBasicLockable lock_;] + mutable absl::Mutex lock_; std::string decodeSymbolVec(const SymbolVec& symbols) const; @@ -240,7 +257,7 @@ class SymbolTableImpl : public SymbolTable { void newSymbol(); Symbol monotonicCounter() { - Thread::LockGuard lock(lock_); + absl::ReaderMutexLock lock(&lock_); return monotonic_counter_; } @@ -254,8 +271,11 @@ class SymbolTableImpl : public SymbolTable { // Bimap implementation. // The encode map stores both the symbol and the ref count of that symbol. // Using absl::string_view lets us only store the complete string once, in the decode map. - std::unordered_map encode_map_ GUARDED_BY(lock_); - std::unordered_map decode_map_ GUARDED_BY(lock_); + using EncodeMap = absl::flat_hash_map, + StringViewHash>; + using DecodeMap = absl::flat_hash_map>; + EncodeMap encode_map_ GUARDED_BY(lock_); + DecodeMap decode_map_ GUARDED_BY(lock_); // Free pool of symbols for re-use. // TODO(ambuc): There might be an optimization here relating to storing ranges of freed symbols diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 7a079b592050..c2007ad50eea 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -39,6 +39,40 @@ ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { stats_overflow_.free(symbolTable()); } +void ThreadLocalStoreImpl::setStatsMatcher(StatsMatcherPtr&& stats_matcher) { + stats_matcher_ = std::move(stats_matcher); + if (stats_matcher_->acceptsAll()) { + return; + } + + // The Filesystem and potentially other stat-registering objects are + // constructed prior to the stat-matcher, and those add stats + // in the default_scope. There should be no requests, so there will + // be no copies in TLS caches. + Thread::LockGuard lock(lock_); + for (ScopeImpl* scope : scopes_) { + removeRejectedStats(scope->central_cache_.counters_, deleted_counters_); + removeRejectedStats(scope->central_cache_.gauges_, deleted_gauges_); + removeRejectedStats(scope->central_cache_.histograms_, deleted_histograms_); + } +} + +template void ThreadLocalStoreImpl::removeRejectedStats( + StatMapClass& map, StatListClass& list) { + std::vector remove_list; + for (auto& stat : map) { + if (rejects(stat.first)) { + remove_list.push_back(stat.first); + } + } + for (StatName stat_name : remove_list) { + auto p = map.find(stat_name); + ASSERT(p != map.end()); + list.push_back(p->second); // Save SharedPtr to the list to avoid invalidating refs to stat. + map.erase(p); + } +} + bool ThreadLocalStoreImpl::rejects(StatName stat_name) const { // Don't both elaborating the StatName there are no pattern-based // exclusions;/inclusions. diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index bdc7af8d747b..897011107eba 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -166,9 +166,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void setTagProducer(TagProducerPtr&& tag_producer) override { tag_producer_ = std::move(tag_producer); } - void setStatsMatcher(StatsMatcherPtr&& stats_matcher) override { - stats_matcher_ = std::move(stats_matcher); - } + void setStatsMatcher(StatsMatcherPtr&& stats_matcher) override; void initializeThreading(Event::Dispatcher& main_thread_dispatcher, ThreadLocal::Instance& tls) override; void shutdownThreading() override; @@ -276,6 +274,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb mergeCb); bool rejects(StatName name) const; + template void removeRejectedStats( + StatMapClass& map, StatListClass& list); const Stats::StatsOptions& stats_options_; StatDataAllocator& alloc_; @@ -297,6 +297,18 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo NullCounterImpl null_counter_; NullGaugeImpl null_gauge_; NullHistogramImpl null_histogram_; + + // Retain storage for deleted stats; these are no longer in maps because the + // matcher-pattern was established after they were created. Since the stats + // are held by reference in code that expects them to be there, we can't + // actually delete the stats. + // + // It seems like it would be better to have each client that expects a stat + // to exist to hold it as (e.g.) a CounterSharedPtr rather than a Counter& + // but that would be fairly complex to change. + std::vector deleted_counters_; + std::vector deleted_gauges_; + std::vector deleted_histograms_; }; } // namespace Stats diff --git a/test/common/access_log/access_log_manager_impl_test.cc b/test/common/access_log/access_log_manager_impl_test.cc index d4b644ad6db7..b7d23e81be80 100644 --- a/test/common/access_log/access_log_manager_impl_test.cc +++ b/test/common/access_log/access_log_manager_impl_test.cc @@ -26,9 +26,9 @@ TEST(AccessLogManagerImpl, reopenAllFiles) { std::shared_ptr log1(new Filesystem::MockFile()); std::shared_ptr log2(new Filesystem::MockFile()); AccessLogManagerImpl access_log_manager(api, dispatcher, lock, stats_store); - EXPECT_CALL(api, createFile("foo", _, _, _)).WillOnce(Return(log1)); + EXPECT_CALL(api, createFile("foo", _, _)).WillOnce(Return(log1)); access_log_manager.createAccessLog("foo"); - EXPECT_CALL(api, createFile("bar", _, _, _)).WillOnce(Return(log2)); + EXPECT_CALL(api, createFile("bar", _, _)).WillOnce(Return(log2)); access_log_manager.createAccessLog("bar"); // Make sure that getting the access log with the same name returns the same underlying file. diff --git a/test/common/api/BUILD b/test/common/api/BUILD index 0144845cbf77..bb81e6900c84 100644 --- a/test/common/api/BUILD +++ b/test/common/api/BUILD @@ -13,6 +13,7 @@ envoy_cc_test( srcs = ["api_impl_test.cc"], deps = [ "//source/common/api:api_lib", + "//source/common/stats:isolated_store_lib", "//test/test_common:environment_lib", ], ) diff --git a/test/common/api/api_impl_test.cc b/test/common/api/api_impl_test.cc index f06330d2bd1d..6e64d747330c 100644 --- a/test/common/api/api_impl_test.cc +++ b/test/common/api/api_impl_test.cc @@ -2,6 +2,7 @@ #include #include "common/api/api_impl.h" +#include "common/stats/isolated_store_impl.h" #include "test/test_common/environment.h" @@ -10,20 +11,24 @@ namespace Envoy { namespace Api { -TEST(ApiImplTest, readFileToEnd) { - Impl api(std::chrono::milliseconds(10000)); +class ApiImplTest : public testing::Test { + protected: + ApiImplTest() : api_(std::chrono::milliseconds(10000), store_) {} + Stats::IsolatedStoreImpl store_; + Impl api_; +}; + +TEST_F(ApiImplTest, readFileToEnd) { const std::string data = "test read To End\nWith new lines."; const std::string file_path = TestEnvironment::writeStringToFileForTest("test_api_envoy", data); - EXPECT_EQ(data, api.fileReadToEnd(file_path)); + EXPECT_EQ(data, api_.fileReadToEnd(file_path)); } -TEST(ApiImplTest, fileExists) { - Impl api(std::chrono::milliseconds(10000)); - - EXPECT_TRUE(api.fileExists("/dev/null")); - EXPECT_FALSE(api.fileExists("/dev/blahblahblah")); +TEST_F(ApiImplTest, fileExists) { + EXPECT_TRUE(api_.fileExists("/dev/null")); + EXPECT_FALSE(api_.fileExists("/dev/blahblahblah")); } } // namespace Api diff --git a/test/common/filesystem/filesystem_impl_test.cc b/test/common/filesystem/filesystem_impl_test.cc index 2a1c706dce47..a9198f407492 100644 --- a/test/common/filesystem/filesystem_impl_test.cc +++ b/test/common/filesystem/filesystem_impl_test.cc @@ -28,13 +28,20 @@ using testing::Throw; namespace Envoy { -TEST(FileSystemImpl, BadFile) { +class FileSystemImplTest : public testing::Test { + protected: + FileSystemImplTest() : file_system_(std::chrono::milliseconds(10000), stats_store_) {} + + const std::chrono::milliseconds timeout_40ms_{40}; + Stats::IsolatedStoreImpl stats_store_; + Filesystem::Instance file_system_; +}; + +TEST_F(FileSystemImplTest, BadFile) { Event::MockDispatcher dispatcher; Thread::MutexBasicLockable lock; - Stats::IsolatedStoreImpl store; EXPECT_CALL(dispatcher, createTimer_(_)); - EXPECT_THROW(Filesystem::FileImpl("", dispatcher, lock, store, std::chrono::milliseconds(10000)), - EnvoyException); + EXPECT_THROW(file_system_.createFile("", dispatcher, lock), EnvoyException); } TEST(FileSystemImpl, fileExists) { @@ -104,19 +111,19 @@ TEST(FilesystemImpl, IllegalPath) { EXPECT_TRUE(Filesystem::illegalPath("/_some_non_existent_file")); } -TEST(FileSystemImpl, flushToLogFilePeriodically) { +TEST_F(FileSystemImplTest, flushToLogFilePeriodically) { NiceMock dispatcher; NiceMock* timer = new NiceMock(&dispatcher); Thread::MutexBasicLockable mutex; - Stats::IsolatedStoreImpl stats_store; NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); EXPECT_CALL(os_sys_calls, open_(_, _, _)).WillOnce(Return(5)); - Filesystem::FileImpl file("", dispatcher, mutex, stats_store, std::chrono::milliseconds(40)); + Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, + timeout_40ms_); - EXPECT_CALL(*timer, enableTimer(std::chrono::milliseconds(40))); + EXPECT_CALL(*timer, enableTimer(timeout_40ms_)); EXPECT_CALL(os_sys_calls, write_(_, _, _)) .WillOnce(Invoke([](int fd, const void* buffer, size_t num_bytes) -> ssize_t { std::string written = std::string(reinterpret_cast(buffer), num_bytes); @@ -126,7 +133,7 @@ TEST(FileSystemImpl, flushToLogFilePeriodically) { return num_bytes; })); - file.write("test"); + file->write("test"); { Thread::LockGuard lock(os_sys_calls.write_mutex_); @@ -145,8 +152,8 @@ TEST(FileSystemImpl, flushToLogFilePeriodically) { })); // make sure timer is re-enabled on callback call - file.write("test2"); - EXPECT_CALL(*timer, enableTimer(std::chrono::milliseconds(40))); + file->write("test2"); + EXPECT_CALL(*timer, enableTimer(timeout_40ms_)); timer->callback_(); { @@ -157,27 +164,26 @@ TEST(FileSystemImpl, flushToLogFilePeriodically) { } } -TEST(FileSystemImpl, flushToLogFileOnDemand) { +TEST_F(FileSystemImplTest, flushToLogFileOnDemand) { NiceMock dispatcher; NiceMock* timer = new NiceMock(&dispatcher); Thread::MutexBasicLockable mutex; - Stats::IsolatedStoreImpl stats_store; NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); EXPECT_CALL(os_sys_calls, open_(_, _, _)).WillOnce(Return(5)); - Filesystem::FileImpl file("", dispatcher, mutex, stats_store, std::chrono::milliseconds(40)); + Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, timeout_40ms_); - EXPECT_CALL(*timer, enableTimer(std::chrono::milliseconds(40))); + EXPECT_CALL(*timer, enableTimer(timeout_40ms_)); // The first write to a given file will start the flush thread, which can flush // immediately (race on whether it will or not). So do a write and flush to // get that state out of the way, then test that small writes don't trigger a flush. EXPECT_CALL(os_sys_calls, write_(_, _, _)) .WillOnce(Invoke([](int, const void*, size_t num_bytes) -> ssize_t { return num_bytes; })); - file.write("prime-it"); - file.flush(); + file->write("prime-it"); + file->flush(); uint32_t expected_writes = 1; { Thread::LockGuard lock(os_sys_calls.write_mutex_); @@ -193,14 +199,14 @@ TEST(FileSystemImpl, flushToLogFileOnDemand) { return num_bytes; })); - file.write("test"); + file->write("test"); { Thread::LockGuard lock(os_sys_calls.write_mutex_); EXPECT_EQ(expected_writes, os_sys_calls.num_writes_); } - file.flush(); + file->flush(); expected_writes++; { Thread::LockGuard lock(os_sys_calls.write_mutex_); @@ -217,8 +223,8 @@ TEST(FileSystemImpl, flushToLogFileOnDemand) { })); // make sure timer is re-enabled on callback call - file.write("test2"); - EXPECT_CALL(*timer, enableTimer(std::chrono::milliseconds(40))); + file->write("test2"); + EXPECT_CALL(*timer, enableTimer(timeout_40ms_)); timer->callback_(); expected_writes++; @@ -230,18 +236,17 @@ TEST(FileSystemImpl, flushToLogFileOnDemand) { } } -TEST(FileSystemImpl, reopenFile) { +TEST_F(FileSystemImplTest, reopenFile) { NiceMock dispatcher; NiceMock* timer = new NiceMock(&dispatcher); Thread::MutexBasicLockable mutex; - Stats::IsolatedStoreImpl stats_store; NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); Sequence sq; EXPECT_CALL(os_sys_calls, open_(_, _, _)).InSequence(sq).WillOnce(Return(5)); - Filesystem::FileImpl file("", dispatcher, mutex, stats_store, std::chrono::milliseconds(40)); + Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, timeout_40ms_); EXPECT_CALL(os_sys_calls, write_(_, _, _)) .InSequence(sq) @@ -253,7 +258,7 @@ TEST(FileSystemImpl, reopenFile) { return num_bytes; })); - file.write("before"); + file->write("before"); timer->callback_(); { @@ -278,8 +283,8 @@ TEST(FileSystemImpl, reopenFile) { EXPECT_CALL(os_sys_calls, close(10)).InSequence(sq); - file.reopen(); - file.write("reopened"); + file->reopen(); + file->write("reopened"); timer->callback_(); { @@ -290,7 +295,7 @@ TEST(FileSystemImpl, reopenFile) { } } -TEST(FilesystemImpl, reopenThrows) { +TEST_F(FileSystemImplTest, reopenThrows) { NiceMock dispatcher; NiceMock* timer = new NiceMock(&dispatcher); @@ -310,11 +315,11 @@ TEST(FilesystemImpl, reopenThrows) { Sequence sq; EXPECT_CALL(os_sys_calls, open_(_, _, _)).InSequence(sq).WillOnce(Return(5)); - Filesystem::FileImpl file("", dispatcher, mutex, stats_store, std::chrono::milliseconds(40)); + Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, timeout_40ms_); EXPECT_CALL(os_sys_calls, close(5)).InSequence(sq); EXPECT_CALL(os_sys_calls, open_(_, _, _)).InSequence(sq).WillOnce(Return(-1)); - file.write("test write"); + file->write("test write"); timer->callback_(); { Thread::LockGuard lock(os_sys_calls.write_mutex_); @@ -322,9 +327,9 @@ TEST(FilesystemImpl, reopenThrows) { os_sys_calls.write_event_.wait(os_sys_calls.write_mutex_); } } - file.reopen(); + file->reopen(); - file.write("this is to force reopen"); + file->write("this is to force reopen"); timer->callback_(); { @@ -335,18 +340,18 @@ TEST(FilesystemImpl, reopenThrows) { } // write call should not cause any exceptions - file.write("random data"); + file->write("random data"); timer->callback_(); } -TEST(FilesystemImpl, bigDataChunkShouldBeFlushedWithoutTimer) { +TEST_F(FileSystemImplTest, bigDataChunkShouldBeFlushedWithoutTimer) { NiceMock dispatcher; Thread::MutexBasicLockable mutex; Stats::IsolatedStoreImpl stats_store; NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); - Filesystem::FileImpl file("", dispatcher, mutex, stats_store, std::chrono::milliseconds(40)); + Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, timeout_40ms_); EXPECT_CALL(os_sys_calls, write_(_, _, _)) .WillOnce(Invoke([](int fd, const void* buffer, size_t num_bytes) -> ssize_t { @@ -359,7 +364,7 @@ TEST(FilesystemImpl, bigDataChunkShouldBeFlushedWithoutTimer) { return num_bytes; })); - file.write("a"); + file->write("a"); { Thread::LockGuard lock(os_sys_calls.write_mutex_); @@ -382,7 +387,7 @@ TEST(FilesystemImpl, bigDataChunkShouldBeFlushedWithoutTimer) { })); std::string big_string(1024 * 64 + 1, 'b'); - file.write(big_string); + file->write(big_string); { Thread::LockGuard lock(os_sys_calls.write_mutex_); diff --git a/test/integration/BUILD b/test/integration/BUILD index 9f70902f2888..6792b4e24c58 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -304,6 +304,7 @@ envoy_cc_test_library( "//test/config:utility_lib", "//test/mocks/buffer:buffer_mocks", "//test/mocks/server:server_mocks", + "//test/mocks/stats:stats_mocks", "//test/mocks/upstream:upstream_mocks", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", diff --git a/test/integration/echo_integration_test.cc b/test/integration/echo_integration_test.cc index c23dd42c25f3..c5bb013d8902 100644 --- a/test/integration/echo_integration_test.cc +++ b/test/integration/echo_integration_test.cc @@ -55,7 +55,7 @@ TEST_P(EchoIntegrationTest, Hello) { response.append(data.toString()); connection.close(); }, - version_); + version_, stats_store_); connection.run(); EXPECT_EQ("hello", response); @@ -104,7 +104,7 @@ TEST_P(EchoIntegrationTest, AddRemoveListener) { response.append(data.toString()); connection.close(); }, - version_); + version_, stats_store_); connection.run(); EXPECT_EQ("hello", response); @@ -129,7 +129,8 @@ TEST_P(EchoIntegrationTest, AddRemoveListener) { for (int i = 0; i < 10; ++i) { RawConnectionDriver connection2( new_listener_port, buffer, - [&](Network::ClientConnection&, const Buffer::Instance&) -> void { FAIL(); }, version_); + [&](Network::ClientConnection&, const Buffer::Instance&) -> void { FAIL(); }, version_, + stats_store_); while (connection2.connecting()) { // Don't busy loop, but OS X often needs a moment to decide this connection isn't happening. timeSystem().sleep(std::chrono::milliseconds(10)); diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 38f46fc532a5..92206e5412c0 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -364,7 +364,8 @@ FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket_factory, Network::SocketPtr&& listen_socket, FakeHttpConnection::Type type, Event::TestTimeSystem& time_system, bool enable_half_close) - : http_type_(type), socket_(std::move(listen_socket)), api_(new Api::Impl(milliseconds(10000))), + : http_type_(type), socket_(std::move(listen_socket)), + api_(new Api::Impl(milliseconds(10000), stats_store_)), time_system_(time_system), dispatcher_(api_->allocateDispatcher(time_system_)), handler_(new Server::ConnectionHandlerImpl(ENVOY_LOGGER(), *dispatcher_)), allow_unexpected_disconnects_(false), enable_half_close_(enable_half_close), listener_(*this), diff --git a/test/integration/http2_integration_test.cc b/test/integration/http2_integration_test.cc index 3c4dd561b407..e0ab532b707b 100644 --- a/test/integration/http2_integration_test.cc +++ b/test/integration/http2_integration_test.cc @@ -145,7 +145,7 @@ TEST_P(Http2IntegrationTest, BadMagic) { [&](Network::ClientConnection&, const Buffer::Instance& data) -> void { response.append(data.toString()); }, - version_); + version_, stats_store_); connection.run(); EXPECT_EQ("", response); @@ -160,7 +160,7 @@ TEST_P(Http2IntegrationTest, BadFrame) { [&](Network::ClientConnection&, const Buffer::Instance& data) -> void { response.append(data.toString()); }, - version_); + version_, stats_store_); connection.run(); EXPECT_TRUE(response.find("SETTINGS expected") != std::string::npos); @@ -405,7 +405,7 @@ TEST_P(Http2IntegrationTest, DelayedCloseAfterBadFrame) { response.append(data.toString()); connection.dispatcher().exit(); }, - version_); + version_, stats_store_); connection.run(); EXPECT_THAT(response, HasSubstr("SETTINGS expected")); @@ -436,7 +436,7 @@ TEST_P(Http2IntegrationTest, DelayedCloseDisabled) { response.append(data.toString()); connection.dispatcher().exit(); }, - version_); + version_, stats_store_); connection.run(); EXPECT_THAT(response, HasSubstr("SETTINGS expected")); diff --git a/test/integration/integration.cc b/test/integration/integration.cc index 51bca91c4458..a2314fdbab0a 100644 --- a/test/integration/integration.cc +++ b/test/integration/integration.cc @@ -213,10 +213,11 @@ void IntegrationTcpClient::ConnectionCallbacks::onEvent(Network::ConnectionEvent BaseIntegrationTest::BaseIntegrationTest(Network::Address::IpVersion version, TestTimeSystemPtr time_system, const std::string& config) - : api_(new Api::Impl(std::chrono::milliseconds(10000))), - mock_buffer_factory_(new NiceMock), time_system_(std::move(time_system)), + : mock_buffer_factory_(new NiceMock), + time_system_(std::move(time_system)), dispatcher_(new Event::DispatcherImpl(*time_system_, Buffer::WatermarkFactoryPtr{mock_buffer_factory_})), + api_(new Api::Impl(std::chrono::milliseconds(10000), stats_store_)), version_(version), config_helper_(version, config), default_log_level_(TestEnvironment::getOptions().logLevel()) { // This is a hack, but there are situations where we disconnect fake upstream connections and @@ -390,7 +391,7 @@ void BaseIntegrationTest::sendRawHttpAndWaitForResponse(int port, const char* ra client.close(Network::ConnectionCloseType::NoFlush); } }, - version_); + version_, stats_store_);; connection.run(); } diff --git a/test/integration/integration.h b/test/integration/integration.h index f551a9c1bade..1bb7dffceb45 100644 --- a/test/integration/integration.h +++ b/test/integration/integration.h @@ -12,6 +12,7 @@ #include "test/integration/server.h" #include "test/integration/utility.h" #include "test/mocks/buffer/mocks.h" +#include "test/mocks/stats/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/printers.h" #include "test/test_common/simulated_time_system.h" @@ -183,13 +184,14 @@ class BaseIntegrationTest : Logger::Loggable { Event::TestTimeSystem& timeSystem() { return *time_system_; } - Api::ApiPtr api_; MockBufferFactory* mock_buffer_factory_; // Will point to the dispatcher's factory. private: TestTimeSystemPtr time_system_; public: Event::DispatcherPtr dispatcher_; + Stats::MockIsolatedStatsStore stats_store_; + Api::ApiPtr api_; /** * Open a connection to Envoy, send a series of bytes, and return the diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 639098fa7eb4..dedb809e0e41 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -463,7 +463,8 @@ class StatsMatcherIntegrationTest } void makeRequest() { response_ = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats", "", - downstreamProtocol(), version_); + downstreamProtocol(), version_, + "host", "", test_server_->statsStore()); ASSERT_TRUE(response_->complete()); EXPECT_STREQ("200", response_->headers().Status()->value().c_str()); } @@ -519,8 +520,7 @@ TEST_P(StatsMatcherIntegrationTest, IncludeExact) { "listener_manager.listener_create_success"); initialize(); makeRequest(); - EXPECT_THAT(response_->body(), - testing::Eq("listener_manager.listener_create_success: 1\nstats.overflow: 0\n")); + EXPECT_EQ(response_->body(), "listener_manager.listener_create_success: 1\n"); } } // namespace Envoy diff --git a/test/integration/server.h b/test/integration/server.h index 33c6d7cb5dfc..d6630b627fd6 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -205,6 +205,7 @@ class IntegrationTestServer : Logger::Loggable, ~IntegrationTestServer(); Server::TestDrainManager& drainManager() { return *drain_manager_; } + Stats::Store* statsStore() { return stat_store_; } Server::InstanceImpl& server() { RELEASE_ASSERT(server_ != nullptr, ""); return *server_; diff --git a/test/integration/utility.cc b/test/integration/utility.cc index ef4166830c89..5ecd88eb8545 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -21,6 +21,7 @@ #include "test/mocks/upstream/mocks.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" +#include "test/mocks/stats/mocks.h" #include "test/test_common/utility.h" namespace Envoy { @@ -59,11 +60,16 @@ BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, - const std::string& host, const std::string& content_type) { - - Api::Impl api(std::chrono::milliseconds(9000)); + const std::string& host, const std::string& content_type, + Stats::Store* stats_store) { + + NiceMock mock_stats_store; + //if (stats_store == nullptr) { + stats_store = &mock_stats_store; + //} + Api::Impl api(std::chrono::milliseconds(9000), *stats_store); Event::DispatcherPtr dispatcher(api.allocateDispatcher(evil_singleton_test_time_.timeSystem())); - std::shared_ptr cluster{new NiceMock()}; + auto cluster = std::make_shared>(); Upstream::HostDescriptionConstSharedPtr host_description{ Upstream::makeTestHostDescription(cluster, "tcp://127.0.0.1:80")}; Http::CodecClientProd client( @@ -100,16 +106,17 @@ BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest(uint32_t port, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, Network::Address::IpVersion ip_version, const std::string& host, - const std::string& content_type) { + const std::string& content_type, Stats::Store* stats_store) { auto addr = Network::Utility::resolveUrl( fmt::format("tcp://{}:{}", Network::Test::getLoopbackAddressUrlString(ip_version), port)); - return makeSingleRequest(addr, method, url, body, type, host, content_type); + return makeSingleRequest(addr, method, url, body, type, host, content_type, stats_store); } RawConnectionDriver::RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, ReadCallback data_callback, - Network::Address::IpVersion version) { - api_ = std::make_unique(std::chrono::milliseconds(10000)); + Network::Address::IpVersion version, + Stats::Store& stats_store) { + api_ = std::make_unique(std::chrono::milliseconds(10000), stats_store); dispatcher_ = api_->allocateDispatcher(IntegrationUtil::evil_singleton_test_time_.timeSystem()); callbacks_ = std::make_unique(); client_ = dispatcher_->createClientConnection( diff --git a/test/integration/utility.h b/test/integration/utility.h index 2740b87463fc..83151d0cc7c1 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -60,7 +60,7 @@ class RawConnectionDriver { typedef std::function ReadCallback; RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, ReadCallback data_callback, - Network::Address::IpVersion version); + Network::Address::IpVersion version, Stats::Store& stat_store); ~RawConnectionDriver(); const Network::Connection& connection() { return *client_; } bool connecting() { return callbacks_->connecting_; } @@ -124,7 +124,8 @@ class IntegrationUtil { static BufferingStreamDecoderPtr makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, - const std::string& host = "host", const std::string& content_type = ""); + const std::string& host = "host", const std::string& content_type = "", + Stats::Store* stats_store = nullptr); /** * Make a new connection, issues a request, and then disconnect when the request is complete. @@ -143,7 +144,7 @@ class IntegrationUtil { makeSingleRequest(uint32_t port, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, Network::Address::IpVersion ip_version, const std::string& host = "host", - const std::string& content_type = ""); + const std::string& content_type = "", Stats::Store* stats_store = nullptr); // TODO(jmarantz): this should be injectable. static DangerousDeprecatedTestTime evil_singleton_test_time_; diff --git a/test/mocks/api/mocks.cc b/test/mocks/api/mocks.cc index 2a577efed115..626a791d723c 100644 --- a/test/mocks/api/mocks.cc +++ b/test/mocks/api/mocks.cc @@ -12,7 +12,7 @@ using testing::Return; namespace Envoy { namespace Api { -MockApi::MockApi() { ON_CALL(*this, createFile(_, _, _, _)).WillByDefault(Return(file_)); } +MockApi::MockApi() { ON_CALL(*this, createFile(_, _, _)).WillByDefault(Return(file_)); } MockApi::~MockApi() {} diff --git a/test/mocks/api/mocks.h b/test/mocks/api/mocks.h index 4f0ae32a9bdf..d033b68b2c66 100644 --- a/test/mocks/api/mocks.h +++ b/test/mocks/api/mocks.h @@ -30,9 +30,9 @@ class MockApi : public Api { } MOCK_METHOD1(allocateDispatcher_, Event::Dispatcher*(Event::TimeSystem&)); - MOCK_METHOD4(createFile, + MOCK_METHOD3(createFile, Filesystem::FileSharedPtr(const std::string& path, Event::Dispatcher& dispatcher, - Thread::BasicLockable& lock, Stats::Store& stats_store)); + Thread::BasicLockable& lock)); MOCK_METHOD1(fileExists, bool(const std::string& path)); MOCK_METHOD1(fileReadToEnd, std::string(const std::string& path)); diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD index 2777f1ddfcf5..1f5e4c5fbfa4 100644 --- a/test/server/config_validation/BUILD +++ b/test/server/config_validation/BUILD @@ -72,6 +72,7 @@ envoy_cc_test( srcs = ["dispatcher_test.cc"], deps = [ "//source/common/event:libevent_lib", + "//source/common/stats:isolated_store_lib", "//source/server/config_validation:api_lib", "//source/server/config_validation:dns_lib", "//test/test_common:environment_lib", diff --git a/test/server/config_validation/dispatcher_test.cc b/test/server/config_validation/dispatcher_test.cc index 8f9757bb7e72..840b518c94ff 100644 --- a/test/server/config_validation/dispatcher_test.cc +++ b/test/server/config_validation/dispatcher_test.cc @@ -4,6 +4,7 @@ #include "common/event/libevent.h" #include "common/network/address_impl.h" #include "common/network/utility.h" +#include "common/stats/isolated_store_impl.h" #include "server/config_validation/api.h" @@ -22,12 +23,14 @@ class ConfigValidation : public ::testing::TestWithParam(std::chrono::milliseconds(1000)); + validation_ = std::make_unique(std::chrono::milliseconds(1000), + stats_store_); dispatcher_ = validation_->allocateDispatcher(test_time_.timeSystem()); } DangerousDeprecatedTestTime test_time_; Event::DispatcherPtr dispatcher_; + Stats::IsolatedStoreImpl stats_store_; private: // Using config validation API. From ae0d3458f9160f5f0332bb7427587c50f291583e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 13 Nov 2018 17:36:46 -0500 Subject: [PATCH 021/106] format Signed-off-by: Joshua Marantz --- source/common/access_log/access_log_manager_impl.h | 2 +- source/common/api/api_impl.cc | 3 +-- source/common/filesystem/filesystem_impl.h | 4 ++-- source/common/http/codes.cc | 14 +++++++++----- source/common/http/codes.h | 3 +-- source/common/stats/symbol_table_impl.cc | 9 ++++----- source/common/stats/symbol_table_impl.h | 8 ++++---- source/common/stats/thread_local_store.cc | 6 +++--- source/common/stats/thread_local_store.h | 6 +++--- test/common/api/api_impl_test.cc | 2 +- test/common/filesystem/filesystem_impl_test.cc | 5 ++--- test/integration/fake_upstream.cc | 4 ++-- test/integration/integration.cc | 10 +++++----- test/integration/integration_admin_test.cc | 4 ++-- test/integration/utility.cc | 14 ++++++-------- test/server/config_validation/dispatcher_test.cc | 4 ++-- 16 files changed, 48 insertions(+), 50 deletions(-) diff --git a/source/common/access_log/access_log_manager_impl.h b/source/common/access_log/access_log_manager_impl.h index 5ab7e8872b49..080646f38a37 100644 --- a/source/common/access_log/access_log_manager_impl.h +++ b/source/common/access_log/access_log_manager_impl.h @@ -23,7 +23,7 @@ class AccessLogManagerImpl : public AccessLogManager { Api::Api& api_; Event::Dispatcher& dispatcher_; Thread::BasicLockable& lock_; - //Stats::Store& stats_store_; + // Stats::Store& stats_store_; std::unordered_map access_logs_; }; diff --git a/source/common/api/api_impl.cc b/source/common/api/api_impl.cc index d2e81a8e242a..a15feb6ea39f 100644 --- a/source/common/api/api_impl.cc +++ b/source/common/api/api_impl.cc @@ -14,8 +14,7 @@ Event::DispatcherPtr Impl::allocateDispatcher(Event::TimeSystem& time_system) { } Impl::Impl(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& stats_store) - : file_system_(file_flush_interval_msec, stats_store) { -} + : file_system_(file_flush_interval_msec, stats_store) {} Filesystem::FileSharedPtr Impl::createFile(const std::string& path, Event::Dispatcher& dispatcher, Thread::BasicLockable& lock) { diff --git a/source/common/filesystem/filesystem_impl.h b/source/common/filesystem/filesystem_impl.h index e8bdc5b4a157..2b43b4a263ba 100644 --- a/source/common/filesystem/filesystem_impl.h +++ b/source/common/filesystem/filesystem_impl.h @@ -32,7 +32,7 @@ struct FileSystemStats { namespace Filesystem { class Instance { - public: +public: explicit Instance(std::chrono::milliseconds file_flush_interval_msec, Stats::Store& store); FileSharedPtr createFile(const std::string& path, Event::Dispatcher& dispatcher, @@ -43,7 +43,7 @@ class Instance { return createFile(path, dispatcher, lock, file_flush_interval_msec_); } - private: +private: std::chrono::milliseconds file_flush_interval_msec_; FileSystemStats file_stats_; }; diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index f7a782070b1e..57173d743326 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -219,12 +219,16 @@ CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { switch (response_code) { - case Code::OK: return upstream_rq_200_; - case Code::NotFound: return upstream_rq_404_; - case Code::ServiceUnavailable: return upstream_rq_503_; - default: break; + case Code::OK: + return upstream_rq_200_; + case Code::NotFound: + return upstream_rq_404_; + case Code::ServiceUnavailable: + return upstream_rq_503_; + default: + break; } - //RELEASE_ASSERT(false, absl::StrCat("need to optimize code ", response_code)); + // RELEASE_ASSERT(false, absl::StrCat("need to optimize code ", response_code)); return makeStatName(response_code); } diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 8c8d1032877a..95b5754209be 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -33,8 +33,7 @@ class CodeStatsImpl : public CodeStats { public: RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats) : code_stats_(code_stats), prefix_(std::string(prefix)), - upstream_rq_200_(makeStatName(Code::OK)), - upstream_rq_404_(makeStatName(Code::NotFound)), + upstream_rq_200_(makeStatName(Code::OK)), upstream_rq_404_(makeStatName(Code::NotFound)), upstream_rq_503_(makeStatName(Code::ServiceUnavailable)) {} ~RequestCodeGroup(); diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 7f40a0e47cfa..ea5a5d853fd4 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -221,7 +221,7 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { // this window of time with the lock released, so we need to check again // under write-lock, which we do by proactively allocating the string and // attempting to insert it into the encode_map. If that worked, we also - // write the decode-map, transfering the ownership of the string to the + // write the decode-map, transferring the ownership of the string to the // decode-map value. absl::MutexLock lock(&lock_); @@ -233,8 +233,8 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { std::unique_ptr str = std::make_unique(size); StringUtil::strlcpy(str.get(), sv.data(), size); - auto encode_insert = encode_map_.insert({str.get(), - std::make_unique(next_symbol_)}); + auto encode_insert = + encode_map_.insert({str.get(), std::make_unique(next_symbol_)}); SharedSymbol& shared_symbol = *encode_insert.first->second; if (encode_insert.second) { @@ -293,8 +293,7 @@ void SymbolTableImpl::debugPrint() const { for (Symbol symbol : symbols) { const char* token = decode_map_.find(symbol)->second.get(); const SharedSymbol& shared_symbol = *encode_map_.find(token)->second; - std::cout << symbol << ": '" << token << "' (" << shared_symbol.ref_count_ << ")" - << std::endl; + std::cout << symbol << ": '" << token << "' (" << shared_symbol.ref_count_ << ")" << std::endl; } std::cout << std::flush; } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 7cd55b65a8f4..dd27b8066285 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -226,11 +226,11 @@ class SymbolTableImpl : public SymbolTable { Symbol symbol_; std::atomic ref_count_; - //uint32_t ref_count_; + // uint32_t ref_count_; }; // This must be called during both encode() and free(). - //mutable Thread::MutexBasicLockable lock_;] + // mutable Thread::MutexBasicLockable lock_;] mutable absl::Mutex lock_; std::string decodeSymbolVec(const SymbolVec& symbols) const; @@ -271,8 +271,8 @@ class SymbolTableImpl : public SymbolTable { // Bimap implementation. // The encode map stores both the symbol and the ref count of that symbol. // Using absl::string_view lets us only store the complete string once, in the decode map. - using EncodeMap = absl::flat_hash_map, - StringViewHash>; + using EncodeMap = + absl::flat_hash_map, StringViewHash>; using DecodeMap = absl::flat_hash_map>; EncodeMap encode_map_ GUARDED_BY(lock_); DecodeMap decode_map_ GUARDED_BY(lock_); diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index c2007ad50eea..18ed27d1f5f8 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -57,8 +57,8 @@ void ThreadLocalStoreImpl::setStatsMatcher(StatsMatcherPtr&& stats_matcher) { } } -template void ThreadLocalStoreImpl::removeRejectedStats( - StatMapClass& map, StatListClass& list) { +template +void ThreadLocalStoreImpl::removeRejectedStats(StatMapClass& map, StatListClass& list) { std::vector remove_list; for (auto& stat : map) { if (rejects(stat.first)) { @@ -68,7 +68,7 @@ template void ThreadLocalStoreImpl::rem for (StatName stat_name : remove_list) { auto p = map.find(stat_name); ASSERT(p != map.end()); - list.push_back(p->second); // Save SharedPtr to the list to avoid invalidating refs to stat. + list.push_back(p->second); // Save SharedPtr to the list to avoid invalidating refs to stat. map.erase(p); } } diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 897011107eba..4c3b0c6b98be 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -212,7 +212,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo SymbolTable& symbolTable() override { return parent_.symbolTable(); } Counter& counter(const std::string& name) override { - //std::cerr << "counter(" << name << ")" << std::endl; + // std::cerr << "counter(" << name << ")" << std::endl; StatNameTempStorage storage(name, symbolTable()); return counterx(storage.statName()); } @@ -274,8 +274,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb mergeCb); bool rejects(StatName name) const; - template void removeRejectedStats( - StatMapClass& map, StatListClass& list); + template + void removeRejectedStats(StatMapClass& map, StatListClass& list); const Stats::StatsOptions& stats_options_; StatDataAllocator& alloc_; diff --git a/test/common/api/api_impl_test.cc b/test/common/api/api_impl_test.cc index 6e64d747330c..0fe29955b7aa 100644 --- a/test/common/api/api_impl_test.cc +++ b/test/common/api/api_impl_test.cc @@ -12,7 +12,7 @@ namespace Envoy { namespace Api { class ApiImplTest : public testing::Test { - protected: +protected: ApiImplTest() : api_(std::chrono::milliseconds(10000), store_) {} Stats::IsolatedStoreImpl store_; diff --git a/test/common/filesystem/filesystem_impl_test.cc b/test/common/filesystem/filesystem_impl_test.cc index a9198f407492..6d5f08eefade 100644 --- a/test/common/filesystem/filesystem_impl_test.cc +++ b/test/common/filesystem/filesystem_impl_test.cc @@ -29,7 +29,7 @@ using testing::Throw; namespace Envoy { class FileSystemImplTest : public testing::Test { - protected: +protected: FileSystemImplTest() : file_system_(std::chrono::milliseconds(10000), stats_store_) {} const std::chrono::milliseconds timeout_40ms_{40}; @@ -120,8 +120,7 @@ TEST_F(FileSystemImplTest, flushToLogFilePeriodically) { TestThreadsafeSingletonInjector os_calls(&os_sys_calls); EXPECT_CALL(os_sys_calls, open_(_, _, _)).WillOnce(Return(5)); - Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, - timeout_40ms_); + Filesystem::FileSharedPtr file = file_system_.createFile("", dispatcher, mutex, timeout_40ms_); EXPECT_CALL(*timer, enableTimer(timeout_40ms_)); EXPECT_CALL(os_sys_calls, write_(_, _, _)) diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 92206e5412c0..90db0e138110 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -365,8 +365,8 @@ FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket Network::SocketPtr&& listen_socket, FakeHttpConnection::Type type, Event::TestTimeSystem& time_system, bool enable_half_close) : http_type_(type), socket_(std::move(listen_socket)), - api_(new Api::Impl(milliseconds(10000), stats_store_)), - time_system_(time_system), dispatcher_(api_->allocateDispatcher(time_system_)), + api_(new Api::Impl(milliseconds(10000), stats_store_)), time_system_(time_system), + dispatcher_(api_->allocateDispatcher(time_system_)), handler_(new Server::ConnectionHandlerImpl(ENVOY_LOGGER(), *dispatcher_)), allow_unexpected_disconnects_(false), enable_half_close_(enable_half_close), listener_(*this), filter_chain_(Network::Test::createEmptyFilterChain(std::move(transport_socket_factory))) { diff --git a/test/integration/integration.cc b/test/integration/integration.cc index a2314fdbab0a..7f799dfe2a86 100644 --- a/test/integration/integration.cc +++ b/test/integration/integration.cc @@ -213,12 +213,11 @@ void IntegrationTcpClient::ConnectionCallbacks::onEvent(Network::ConnectionEvent BaseIntegrationTest::BaseIntegrationTest(Network::Address::IpVersion version, TestTimeSystemPtr time_system, const std::string& config) - : mock_buffer_factory_(new NiceMock), - time_system_(std::move(time_system)), + : mock_buffer_factory_(new NiceMock), time_system_(std::move(time_system)), dispatcher_(new Event::DispatcherImpl(*time_system_, Buffer::WatermarkFactoryPtr{mock_buffer_factory_})), - api_(new Api::Impl(std::chrono::milliseconds(10000), stats_store_)), - version_(version), config_helper_(version, config), + api_(new Api::Impl(std::chrono::milliseconds(10000), stats_store_)), version_(version), + config_helper_(version, config), default_log_level_(TestEnvironment::getOptions().logLevel()) { // This is a hack, but there are situations where we disconnect fake upstream connections and // then we expect the server connection pool to get the disconnect before the next test starts. @@ -391,7 +390,8 @@ void BaseIntegrationTest::sendRawHttpAndWaitForResponse(int port, const char* ra client.close(Network::ConnectionCloseType::NoFlush); } }, - version_, stats_store_);; + version_, stats_store_); + ; connection.run(); } diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index dedb809e0e41..9af1eea01ab7 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -463,8 +463,8 @@ class StatsMatcherIntegrationTest } void makeRequest() { response_ = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats", "", - downstreamProtocol(), version_, - "host", "", test_server_->statsStore()); + downstreamProtocol(), version_, "host", "", + test_server_->statsStore()); ASSERT_TRUE(response_->complete()); EXPECT_STREQ("200", response_->headers().Status()->value().c_str()); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 5ecd88eb8545..68461dcb7034 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -18,10 +18,10 @@ #include "common/upstream/upstream_impl.h" #include "test/common/upstream/utility.h" +#include "test/mocks/stats/mocks.h" #include "test/mocks/upstream/mocks.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" -#include "test/mocks/stats/mocks.h" #include "test/test_common/utility.h" namespace Envoy { @@ -56,15 +56,13 @@ void BufferingStreamDecoder::onResetStream(Http::StreamResetReason) { ADD_FAILUR DangerousDeprecatedTestTime IntegrationUtil::evil_singleton_test_time_; -BufferingStreamDecoderPtr -IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, - const std::string& method, const std::string& url, - const std::string& body, Http::CodecClient::Type type, - const std::string& host, const std::string& content_type, - Stats::Store* stats_store) { +BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest( + const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, + const std::string& url, const std::string& body, Http::CodecClient::Type type, + const std::string& host, const std::string& content_type, Stats::Store* stats_store) { NiceMock mock_stats_store; - //if (stats_store == nullptr) { + // if (stats_store == nullptr) { stats_store = &mock_stats_store; //} Api::Impl api(std::chrono::milliseconds(9000), *stats_store); diff --git a/test/server/config_validation/dispatcher_test.cc b/test/server/config_validation/dispatcher_test.cc index 840b518c94ff..9fb7cac578dc 100644 --- a/test/server/config_validation/dispatcher_test.cc +++ b/test/server/config_validation/dispatcher_test.cc @@ -23,8 +23,8 @@ class ConfigValidation : public ::testing::TestWithParam(std::chrono::milliseconds(1000), - stats_store_); + validation_ = + std::make_unique(std::chrono::milliseconds(1000), stats_store_); dispatcher_ = validation_->allocateDispatcher(test_time_.timeSystem()); } From 739bf0659abe566fe159ce121df547307f91d717 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 14 Nov 2018 16:09:43 -0500 Subject: [PATCH 022/106] revert API change - make an IsolatedStoreImpl in the RawConnectionDriver instead. Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 11 +++++-- source/common/stats/isolated_store_impl.h | 10 +++--- test/common/http/codes_speed_test.cc | 5 ++- test/common/http/codes_test.cc | 9 +++-- test/common/stats/isolated_store_impl_test.cc | 33 ++++++++++--------- test/integration/echo_integration_test.cc | 7 ++-- test/integration/http2_integration_test.cc | 8 ++--- test/integration/integration.cc | 6 ++-- test/integration/integration.h | 7 ++-- test/integration/integration_admin_test.cc | 3 +- test/integration/server.h | 2 ++ test/integration/utility.cc | 26 +++++++-------- test/integration/utility.h | 9 ++--- test/mocks/stats/mocks.cc | 4 ++- 14 files changed, 74 insertions(+), 66 deletions(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 9697c0dfada1..e6d0179a7f14 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -13,10 +13,15 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_shared()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} -IsolatedStoreImpl::IsolatedStoreImpl(const SharedSymbolTable& symbol_table) - : symbol_table_(symbol_table), alloc_(*symbol_table_), +IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) + : IsolatedStoreImpl(*symbol_table) { + symbol_table_storage_ = std::move(symbol_table); +} + +IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) + : symbol_table_(symbol_table), alloc_(symbol_table_), counters_([this](StatName name) -> CounterSharedPtr { return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), std::vector()); }), diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index a2ba33ce4129..27b4589f5b64 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -58,7 +58,8 @@ template class IsolatedStatsCache { class IsolatedStoreImpl : public Store { public: IsolatedStoreImpl(); - explicit IsolatedStoreImpl(const SharedSymbolTable& symbol_table); + explicit IsolatedStoreImpl(std::unique_ptr symbol_table); + explicit IsolatedStoreImpl(SymbolTable& symbol_table); // Stats::Scope Counter& counterx(StatName name) override { return counters_.get(name); } @@ -70,8 +71,8 @@ class IsolatedStoreImpl : public Store { return histogram; } const Stats::StatsOptions& statsOptions() const override { return stats_options_; } - const SymbolTable& symbolTable() const override { return *symbol_table_; } - virtual SymbolTable& symbolTable() override { return *symbol_table_; } + const SymbolTable& symbolTable() const override { return symbol_table_; } + virtual SymbolTable& symbolTable() override { return symbol_table_; } // Stats::Store std::vector counters() const override { return counters_.toVector(); } @@ -94,7 +95,8 @@ class IsolatedStoreImpl : public Store { } private: - SharedSymbolTable symbol_table_; + std::unique_ptr symbol_table_storage_; + SymbolTable& symbol_table_; HeapStatDataAllocator alloc_; IsolatedStatsCache counters_; IsolatedStatsCache gauges_; diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index fbde3e88a8fe..592b1da5f0d3 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -20,8 +20,7 @@ namespace Http { class CodeUtilitySpeedTest { public: CodeUtilitySpeedTest() - : symbol_table_(std::make_shared()), global_store_(symbol_table_), - cluster_scope_(symbol_table_), code_stats_(*symbol_table_) {} + : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, @@ -55,7 +54,7 @@ class CodeUtilitySpeedTest { code_stats_.chargeResponseTiming(info); } - Stats::SharedSymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index 539d7eeacccc..41e5d19b2f67 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -25,8 +25,7 @@ namespace Http { class CodeUtilityTest : public testing::Test { public: CodeUtilityTest() - : symbol_table_(std::make_shared()), global_store_(symbol_table_), - cluster_scope_(symbol_table_), code_stats_(*symbol_table_) {} + : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, @@ -40,7 +39,7 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } - Stats::SharedSymbolTable symbol_table_; + Stats::SymbolTableImpl symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; @@ -203,8 +202,8 @@ TEST_F(CodeUtilityTest, PerZoneStats) { } TEST_F(CodeUtilityTest, ResponseTimingTest) { - Stats::MockStore global_store(*symbol_table_); - Stats::MockStore cluster_scope(*symbol_table_); + Stats::MockStore global_store(symbol_table_); + Stats::MockStore cluster_scope(symbol_table_); Http::CodeStats::ResponseTimingInfo info{ global_store, cluster_scope, "prefix.", std::chrono::milliseconds(5), diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index b2aaa80693fd..8d165ead0143 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -11,11 +11,14 @@ namespace Envoy { namespace Stats { -TEST(StatsIsolatedStoreImplTest, All) { - IsolatedStoreImpl store; +class StatsIsolatedStoreImplTest: public testing::Test { + protected: + IsolatedStoreImpl store_; +}; - ScopePtr scope1 = store.createScope("scope1."); - Counter& c1 = store.counter("c1"); +TEST_F(StatsIsolatedStoreImplTest, All) { + ScopePtr scope1 = store_.createScope("scope1."); + Counter& c1 = store_.counter("c1"); Counter& c2 = scope1->counter("c2"); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); @@ -24,7 +27,7 @@ TEST(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, c1.tags().size()); EXPECT_EQ(0, c1.tags().size()); - Gauge& g1 = store.gauge("g1"); + Gauge& g1 = store_.gauge("g1"); Gauge& g2 = scope1->gauge("g2"); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); @@ -33,7 +36,7 @@ TEST(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g1.tags().size()); - Histogram& h1 = store.histogram("h1"); + Histogram& h1 = store_.histogram("h1"); Histogram& h2 = scope1->histogram("h2"); scope1->deliverHistogramToSinks(h2, 0); EXPECT_EQ("h1", h1.name()); @@ -52,16 +55,15 @@ TEST(StatsIsolatedStoreImplTest, All) { ScopePtr scope3 = scope1->createScope(std::string("foo:\0:.", 7)); EXPECT_EQ("scope1.foo___.bar", scope3->counter("bar").name()); - EXPECT_EQ(4UL, store.counters().size()); - EXPECT_EQ(2UL, store.gauges().size()); + EXPECT_EQ(4UL, store_.counters().size()); + EXPECT_EQ(2UL, store_.gauges().size()); } -TEST(StatsIsolatedStoreImplTest, LongStatName) { - IsolatedStoreImpl store; +TEST_F(StatsIsolatedStoreImplTest, LongStatName) { Stats::StatsOptionsImpl stats_options; const std::string long_string(stats_options.maxNameLength() + 1, 'A'); - ScopePtr scope = store.createScope("scope."); + ScopePtr scope = store_.createScope("scope."); Counter& counter = scope->counter(long_string); EXPECT_EQ(absl::StrCat("scope.", long_string), counter.name()); } @@ -80,11 +82,10 @@ struct TestStats { ALL_TEST_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT) }; -TEST(StatsMacros, All) { - IsolatedStoreImpl stats_store; - TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(stats_store, "test."), - POOL_GAUGE_PREFIX(stats_store, "test."), - POOL_HISTOGRAM_PREFIX(stats_store, "test."))}; +TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { + TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(store_, "test."), + POOL_GAUGE_PREFIX(store_, "test."), + POOL_HISTOGRAM_PREFIX(store_, "test."))}; Counter& counter = test_stats.test_counter_; EXPECT_EQ("test.test_counter", counter.name()); diff --git a/test/integration/echo_integration_test.cc b/test/integration/echo_integration_test.cc index c5bb013d8902..c23dd42c25f3 100644 --- a/test/integration/echo_integration_test.cc +++ b/test/integration/echo_integration_test.cc @@ -55,7 +55,7 @@ TEST_P(EchoIntegrationTest, Hello) { response.append(data.toString()); connection.close(); }, - version_, stats_store_); + version_); connection.run(); EXPECT_EQ("hello", response); @@ -104,7 +104,7 @@ TEST_P(EchoIntegrationTest, AddRemoveListener) { response.append(data.toString()); connection.close(); }, - version_, stats_store_); + version_); connection.run(); EXPECT_EQ("hello", response); @@ -129,8 +129,7 @@ TEST_P(EchoIntegrationTest, AddRemoveListener) { for (int i = 0; i < 10; ++i) { RawConnectionDriver connection2( new_listener_port, buffer, - [&](Network::ClientConnection&, const Buffer::Instance&) -> void { FAIL(); }, version_, - stats_store_); + [&](Network::ClientConnection&, const Buffer::Instance&) -> void { FAIL(); }, version_); while (connection2.connecting()) { // Don't busy loop, but OS X often needs a moment to decide this connection isn't happening. timeSystem().sleep(std::chrono::milliseconds(10)); diff --git a/test/integration/http2_integration_test.cc b/test/integration/http2_integration_test.cc index e0ab532b707b..3c4dd561b407 100644 --- a/test/integration/http2_integration_test.cc +++ b/test/integration/http2_integration_test.cc @@ -145,7 +145,7 @@ TEST_P(Http2IntegrationTest, BadMagic) { [&](Network::ClientConnection&, const Buffer::Instance& data) -> void { response.append(data.toString()); }, - version_, stats_store_); + version_); connection.run(); EXPECT_EQ("", response); @@ -160,7 +160,7 @@ TEST_P(Http2IntegrationTest, BadFrame) { [&](Network::ClientConnection&, const Buffer::Instance& data) -> void { response.append(data.toString()); }, - version_, stats_store_); + version_); connection.run(); EXPECT_TRUE(response.find("SETTINGS expected") != std::string::npos); @@ -405,7 +405,7 @@ TEST_P(Http2IntegrationTest, DelayedCloseAfterBadFrame) { response.append(data.toString()); connection.dispatcher().exit(); }, - version_, stats_store_); + version_); connection.run(); EXPECT_THAT(response, HasSubstr("SETTINGS expected")); @@ -436,7 +436,7 @@ TEST_P(Http2IntegrationTest, DelayedCloseDisabled) { response.append(data.toString()); connection.dispatcher().exit(); }, - version_, stats_store_); + version_); connection.run(); EXPECT_THAT(response, HasSubstr("SETTINGS expected")); diff --git a/test/integration/integration.cc b/test/integration/integration.cc index 7f799dfe2a86..31822b8b88aa 100644 --- a/test/integration/integration.cc +++ b/test/integration/integration.cc @@ -216,7 +216,7 @@ BaseIntegrationTest::BaseIntegrationTest(Network::Address::IpVersion version, : mock_buffer_factory_(new NiceMock), time_system_(std::move(time_system)), dispatcher_(new Event::DispatcherImpl(*time_system_, Buffer::WatermarkFactoryPtr{mock_buffer_factory_})), - api_(new Api::Impl(std::chrono::milliseconds(10000), stats_store_)), version_(version), + /*api_(new Api::Impl(std::chrono::milliseconds(10000), stats_store_))*,*/ version_(version), config_helper_(version, config), default_log_level_(TestEnvironment::getOptions().logLevel()) { // This is a hack, but there are situations where we disconnect fake upstream connections and @@ -351,6 +351,7 @@ void BaseIntegrationTest::createGeneratedApiTestServer(const std::string& bootst test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); registerTestServerPorts(port_names); } + //stats_ = std::make_unique(test_server_->stats().symbolTable()); } void BaseIntegrationTest::createApiTestServer(const ApiFilesystemConfig& api_filesystem_config, @@ -390,8 +391,7 @@ void BaseIntegrationTest::sendRawHttpAndWaitForResponse(int port, const char* ra client.close(Network::ConnectionCloseType::NoFlush); } }, - version_, stats_store_); - ; + version_); connection.run(); } diff --git a/test/integration/integration.h b/test/integration/integration.h index 1bb7dffceb45..0f63e6ac59df 100644 --- a/test/integration/integration.h +++ b/test/integration/integration.h @@ -183,15 +183,16 @@ class BaseIntegrationTest : Logger::Loggable { const std::vector& port_names); Event::TestTimeSystem& timeSystem() { return *time_system_; } + //Stats::Store& stats() { return *stats_; } MockBufferFactory* mock_buffer_factory_; // Will point to the dispatcher's factory. + private: TestTimeSystemPtr time_system_; public: Event::DispatcherPtr dispatcher_; - Stats::MockIsolatedStatsStore stats_store_; - Api::ApiPtr api_; + //Api::ApiPtr api_; /** * Open a connection to Envoy, send a series of bytes, and return the @@ -222,6 +223,8 @@ class BaseIntegrationTest : Logger::Loggable { uint32_t fake_upstreams_count_{1}; spdlog::level::level_enum default_log_level_; IntegrationTestServerPtr test_server_; + //std::unique_ptr stats_; + // A map of keys to port names. Generally the names are pulled from the v2 listener name // but if a listener is created via ADS, it will be from whatever key is used with registerPort. TestEnvironment::PortMap port_map_; diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 9af1eea01ab7..0e45ed8a9757 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -463,8 +463,7 @@ class StatsMatcherIntegrationTest } void makeRequest() { response_ = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats", "", - downstreamProtocol(), version_, "host", "", - test_server_->statsStore()); + downstreamProtocol(), version_, "host", ""); ASSERT_TRUE(response_->complete()); EXPECT_STREQ("200", response_->headers().Status()->value().c_str()); } diff --git a/test/integration/server.h b/test/integration/server.h index d6630b627fd6..86770849bc4e 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -267,6 +267,8 @@ class IntegrationTestServer : Logger::Loggable, return Server::InstanceUtil::createRuntime(server, config); } + Stats::Store& stats() { return server_->stats(); } + protected: IntegrationTestServer(Event::TestTimeSystem& time_system, const std::string& config_path) : time_system_(time_system), config_path_(config_path) {} diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 68461dcb7034..26de9bc40eb3 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -18,7 +18,6 @@ #include "common/upstream/upstream_impl.h" #include "test/common/upstream/utility.h" -#include "test/mocks/stats/mocks.h" #include "test/mocks/upstream/mocks.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" @@ -56,16 +55,14 @@ void BufferingStreamDecoder::onResetStream(Http::StreamResetReason) { ADD_FAILUR DangerousDeprecatedTestTime IntegrationUtil::evil_singleton_test_time_; -BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest( - const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, - const std::string& url, const std::string& body, Http::CodecClient::Type type, - const std::string& host, const std::string& content_type, Stats::Store* stats_store) { +BufferingStreamDecoderPtr +IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, + const std::string& method, const std::string& url, + const std::string& body, Http::CodecClient::Type type, + const std::string& host, const std::string& content_type) { - NiceMock mock_stats_store; - // if (stats_store == nullptr) { - stats_store = &mock_stats_store; - //} - Api::Impl api(std::chrono::milliseconds(9000), *stats_store); + Stats::IsolatedStoreImpl stats_store; + Api::Impl api(std::chrono::milliseconds(9000), stats_store); Event::DispatcherPtr dispatcher(api.allocateDispatcher(evil_singleton_test_time_.timeSystem())); auto cluster = std::make_shared>(); Upstream::HostDescriptionConstSharedPtr host_description{ @@ -104,17 +101,16 @@ BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest(uint32_t port, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, Network::Address::IpVersion ip_version, const std::string& host, - const std::string& content_type, Stats::Store* stats_store) { + const std::string& content_type) { auto addr = Network::Utility::resolveUrl( fmt::format("tcp://{}:{}", Network::Test::getLoopbackAddressUrlString(ip_version), port)); - return makeSingleRequest(addr, method, url, body, type, host, content_type, stats_store); + return makeSingleRequest(addr, method, url, body, type, host, content_type); } RawConnectionDriver::RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, ReadCallback data_callback, - Network::Address::IpVersion version, - Stats::Store& stats_store) { - api_ = std::make_unique(std::chrono::milliseconds(10000), stats_store); + Network::Address::IpVersion version) { + api_ = std::make_unique(std::chrono::milliseconds(10000), stats_store_); dispatcher_ = api_->allocateDispatcher(IntegrationUtil::evil_singleton_test_time_.timeSystem()); callbacks_ = std::make_unique(); client_ = dispatcher_->createClientConnection( diff --git a/test/integration/utility.h b/test/integration/utility.h index 83151d0cc7c1..35235d16a75d 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -13,6 +13,7 @@ #include "common/common/assert.h" #include "common/common/utility.h" #include "common/http/codec_client.h" +#include "common/stats/isolated_store_impl.h" #include "test/test_common/printers.h" #include "test/test_common/test_time.h" @@ -60,7 +61,7 @@ class RawConnectionDriver { typedef std::function ReadCallback; RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, ReadCallback data_callback, - Network::Address::IpVersion version, Stats::Store& stat_store); + Network::Address::IpVersion version); ~RawConnectionDriver(); const Network::Connection& connection() { return *client_; } bool connecting() { return callbacks_->connecting_; } @@ -98,6 +99,7 @@ class RawConnectionDriver { Network::ConnectionEvent last_connection_event_; }; + Stats::IsolatedStoreImpl stats_store_; Api::ApiPtr api_; Event::DispatcherPtr dispatcher_; std::unique_ptr callbacks_; @@ -124,8 +126,7 @@ class IntegrationUtil { static BufferingStreamDecoderPtr makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, - const std::string& host = "host", const std::string& content_type = "", - Stats::Store* stats_store = nullptr); + const std::string& host = "host", const std::string& content_type = ""); /** * Make a new connection, issues a request, and then disconnect when the request is complete. @@ -144,7 +145,7 @@ class IntegrationUtil { makeSingleRequest(uint32_t port, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, Network::Address::IpVersion ip_version, const std::string& host = "host", - const std::string& content_type = "", Stats::Store* stats_store = nullptr); + const std::string& content_type = ""); // TODO(jmarantz): this should be injectable. static DangerousDeprecatedTestTime evil_singleton_test_time_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index b89eb8ace933..fc3ed4924393 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -1,3 +1,5 @@ +#include + #include "mocks.h" #include "common/stats/symbol_table_impl.h" @@ -171,7 +173,7 @@ void MockSymbolTable::debugPrint() const {} #endif MockIsolatedStatsStore::MockIsolatedStatsStore() - : IsolatedStoreImpl(std::make_shared()) {} + : IsolatedStoreImpl(std::make_unique()) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() {} } // namespace Stats From 83366508c37a6b0e7dcb492ddcf0600d528f7659 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 15 Nov 2018 20:17:04 -0500 Subject: [PATCH 023/106] force the SymbolTable backing MockIsolatedStore to be a singleton. Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 6 ++ source/common/stats/isolated_store_impl.h | 5 ++ test/mocks/stats/mocks.cc | 68 +++++++++------------- test/mocks/stats/mocks.h | 49 +++++++++++----- 4 files changed, 72 insertions(+), 56 deletions(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index e6d0179a7f14..c2d6152d07cc 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -37,5 +37,11 @@ ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { return std::make_unique(name, *this); } +void IsolatedStoreImpl::clear() { + counters_.clear(); + gauges_.clear(); + histograms_.clear(); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 27b4589f5b64..2809e46d6a2b 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -50,6 +50,9 @@ template class IsolatedStatsCache { return vec; } + void clear() { stats_.clear(); } + + private: StatNameHashMap> stats_; Allocator alloc_; @@ -94,6 +97,8 @@ class IsolatedStoreImpl : public Store { return histogramx(storage.statName()); } + void clear(); + private: std::unique_ptr symbol_table_storage_; SymbolTable& symbol_table_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index fc3ed4924393..d5517c9f7df7 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -127,54 +127,42 @@ MockStore::MockStore(SymbolTable& symbol_table) } MockStore::~MockStore() {} -MockSymbolTable::MockSymbolTable() {} -MockSymbolTable::~MockSymbolTable() {} - -SymbolEncoding MockSymbolTable::encode(absl::string_view name) { - SymbolEncoding encoding; - if (name.empty()) { - return encoding; - } - std::vector tokens = absl::StrSplit(name, '.'); - for (absl::string_view token : tokens) { - encoding.addSymbol(static_cast(token.size())); - ; - for (char c : token) { - encoding.addSymbol(static_cast(c)); - } +SymbolTableSingleton* SymbolTableSingleton::singleton_ = nullptr; + +SymbolTableSingleton::SymbolTableSingleton(Thread::MutexBasicLockable& mutex) + : mutex_(mutex), ref_count_(1) {} + +SymbolTableSingleton& SymbolTableSingleton::get() { + static Thread::MutexBasicLockable* mutex = new Thread::MutexBasicLockable; + Thread::LockGuard lock(*mutex); + if (singleton_ == nullptr) { + singleton_ = new SymbolTableSingleton(*mutex); + } else { + ++singleton_->ref_count_; } - return encoding; + return *singleton_; } -std::string MockSymbolTable::decode(const SymbolStorage symbol_vec, uint64_t size) const { - std::string out; - SymbolVec symbols = SymbolEncoding::decodeSymbols(symbol_vec, size); - for (size_t i = 0; i < symbols.size();) { - if (!out.empty()) { - out += "."; - } - size_t end = static_cast(symbols[i]) + i + 1; - while (++i < end) { - out += static_cast(symbols[i]); - } +void SymbolTableSingleton::release() { + Thread::LockGuard lock(mutex_); + ASSERT(singleton_ != nullptr); + ASSERT(singleton_ == this); + if (--singleton_->ref_count_ == 0) { + singleton_ = nullptr; + delete this; } - return out; } -bool MockSymbolTable::lessThan(const StatName& a, const StatName& b) const { - return a.toString(*this) < b.toString(*this); -} +MockSymbolTable::MockSymbolTable() : singleton_(SymbolTableSingleton::get()) {} -uint64_t MockSymbolTable::numSymbols() const { return 0; } -void MockSymbolTable::free(StatName) {} -void MockSymbolTable::incRefCount(StatName) {} -#ifndef ENVOY_CONFIG_COVERAGE -void MockSymbolTable::debugPrint() const {} -#endif +MockSymbolTable::~MockSymbolTable() { + singleton_.release(); +} -MockIsolatedStatsStore::MockIsolatedStatsStore() - : IsolatedStoreImpl(std::make_unique()) {} -MockIsolatedStatsStore::~MockIsolatedStatsStore() {} +MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(symbol_table_) {} +MockIsolatedStatsStore::~MockIsolatedStatsStore() { + IsolatedStoreImpl::clear(); +} } // namespace Stats } // namespace Envoy diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index a5c61ef37bb7..61b1c68837b3 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -191,29 +191,43 @@ class MockStore : public Store { StatsOptionsImpl stats_options_; }; -// The MockSymbolTable behaves like SymbolTableImpl, except that you can -// have SymbolEncodings from multiple symbol tables interact. This is -// achieved with unity encoding. -// -// The reason this is desirable for tests is that it is very difficult to -// inject the right SymbolTable& everywhere it needs to go in the test -// infratsructure, since all the existing mocks have no context. +class SymbolTableSingleton : public SymbolTableImpl { + public: + static SymbolTableSingleton& get(); + void release(); + + private: + SymbolTableSingleton(Thread::MutexBasicLockable& mutex); + static SymbolTableSingleton* singleton_; + Thread::MutexBasicLockable& mutex_; // Lock guards don't work as mutex outlives object. + uint64_t ref_count_; +}; + class MockSymbolTable : public SymbolTable { -public: + public: MockSymbolTable(); ~MockSymbolTable(); - SymbolEncoding encode(absl::string_view name) override; - uint64_t numSymbols() const override; - bool lessThan(const StatName& a, const StatName& b) const override; - void free(StatName stat_name) override; - void incRefCount(StatName stat_name) override; - std::string decode(const SymbolStorage symbol_vec, uint64_t size) const override; - bool interoperable(const SymbolTable&) const override { return true; } + SymbolEncoding encode(absl::string_view name) override { return singleton_.encode(name); } + uint64_t numSymbols() const override { return singleton_.numSymbols(); } + bool lessThan(const StatName& a, const StatName& b) const override { + return singleton_.lessThan(a, b); + } + void free(StatName stat_name) override { singleton_.free(stat_name); } + void incRefCount(StatName stat_name) override { singleton_.incRefCount(stat_name); } + std::string decode(const SymbolStorage symbol_vec, uint64_t size) const override { + return singleton_.decode(symbol_vec, size); + } + bool interoperable(const SymbolTable& other) const override { + return dynamic_cast(&other) != nullptr; + } #ifndef ENVOY_CONFIG_COVERAGE - void debugPrint() const override; + void debugPrint() const override { singleton_.debugPrint(); } #endif + + private: + SymbolTableSingleton& singleton_; }; /** @@ -226,6 +240,9 @@ class MockIsolatedStatsStore : public IsolatedStoreImpl { ~MockIsolatedStatsStore(); MOCK_METHOD2(deliverHistogramToSinks, void(const Histogram& histogram, uint64_t value)); + + private: + MockSymbolTable symbol_table_; }; } // namespace Stats From c17cca9a55cdaa28fc5433691da632b92d9f4235 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 15 Nov 2018 20:19:32 -0500 Subject: [PATCH 024/106] format Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.h | 1 - test/common/stats/isolated_store_impl_test.cc | 4 ++-- test/integration/integration.cc | 2 +- test/integration/integration.h | 6 +++--- test/mocks/stats/mocks.cc | 12 ++++-------- test/mocks/stats/mocks.h | 12 ++++++------ 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 2809e46d6a2b..459cd76dcdcf 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -52,7 +52,6 @@ template class IsolatedStatsCache { void clear() { stats_.clear(); } - private: StatNameHashMap> stats_; Allocator alloc_; diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index 8d165ead0143..05e63c815761 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -11,8 +11,8 @@ namespace Envoy { namespace Stats { -class StatsIsolatedStoreImplTest: public testing::Test { - protected: +class StatsIsolatedStoreImplTest : public testing::Test { +protected: IsolatedStoreImpl store_; }; diff --git a/test/integration/integration.cc b/test/integration/integration.cc index 31822b8b88aa..cf6dea180620 100644 --- a/test/integration/integration.cc +++ b/test/integration/integration.cc @@ -351,7 +351,7 @@ void BaseIntegrationTest::createGeneratedApiTestServer(const std::string& bootst test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); registerTestServerPorts(port_names); } - //stats_ = std::make_unique(test_server_->stats().symbolTable()); + // stats_ = std::make_unique(test_server_->stats().symbolTable()); } void BaseIntegrationTest::createApiTestServer(const ApiFilesystemConfig& api_filesystem_config, diff --git a/test/integration/integration.h b/test/integration/integration.h index 0f63e6ac59df..76fb31f51bf4 100644 --- a/test/integration/integration.h +++ b/test/integration/integration.h @@ -183,7 +183,7 @@ class BaseIntegrationTest : Logger::Loggable { const std::vector& port_names); Event::TestTimeSystem& timeSystem() { return *time_system_; } - //Stats::Store& stats() { return *stats_; } + // Stats::Store& stats() { return *stats_; } MockBufferFactory* mock_buffer_factory_; // Will point to the dispatcher's factory. @@ -192,7 +192,7 @@ class BaseIntegrationTest : Logger::Loggable { public: Event::DispatcherPtr dispatcher_; - //Api::ApiPtr api_; + // Api::ApiPtr api_; /** * Open a connection to Envoy, send a series of bytes, and return the @@ -223,7 +223,7 @@ class BaseIntegrationTest : Logger::Loggable { uint32_t fake_upstreams_count_{1}; spdlog::level::level_enum default_log_level_; IntegrationTestServerPtr test_server_; - //std::unique_ptr stats_; + // std::unique_ptr stats_; // A map of keys to port names. Generally the names are pulled from the v2 listener name // but if a listener is created via ADS, it will be from whatever key is used with registerPort. diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index d5517c9f7df7..531ae5253e05 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -1,7 +1,7 @@ -#include - #include "mocks.h" +#include + #include "common/stats/symbol_table_impl.h" #include "gmock/gmock.h" @@ -155,14 +155,10 @@ void SymbolTableSingleton::release() { MockSymbolTable::MockSymbolTable() : singleton_(SymbolTableSingleton::get()) {} -MockSymbolTable::~MockSymbolTable() { - singleton_.release(); -} +MockSymbolTable::~MockSymbolTable() { singleton_.release(); } MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(symbol_table_) {} -MockIsolatedStatsStore::~MockIsolatedStatsStore() { - IsolatedStoreImpl::clear(); -} +MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } } // namespace Stats } // namespace Envoy diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 61b1c68837b3..124ca40f9a66 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -192,19 +192,19 @@ class MockStore : public Store { }; class SymbolTableSingleton : public SymbolTableImpl { - public: +public: static SymbolTableSingleton& get(); void release(); - private: +private: SymbolTableSingleton(Thread::MutexBasicLockable& mutex); static SymbolTableSingleton* singleton_; - Thread::MutexBasicLockable& mutex_; // Lock guards don't work as mutex outlives object. + Thread::MutexBasicLockable& mutex_; // Lock guards don't work as mutex outlives object. uint64_t ref_count_; }; class MockSymbolTable : public SymbolTable { - public: +public: MockSymbolTable(); ~MockSymbolTable(); @@ -226,7 +226,7 @@ class MockSymbolTable : public SymbolTable { void debugPrint() const override { singleton_.debugPrint(); } #endif - private: +private: SymbolTableSingleton& singleton_; }; @@ -241,7 +241,7 @@ class MockIsolatedStatsStore : public IsolatedStoreImpl { MOCK_METHOD2(deliverHistogramToSinks, void(const Histogram& histogram, uint64_t value)); - private: +private: MockSymbolTable symbol_table_; }; From 04cbc9c43970deb565c1fa7042a3b0e7c1b49c71 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 16 Nov 2018 12:16:07 -0500 Subject: [PATCH 025/106] comment out singleton symbol-table usage in MockIsolatedStatsStore. Signed-off-by: Joshua Marantz --- test/mocks/stats/mocks.cc | 3 ++- test/mocks/stats/mocks.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 531ae5253e05..835439a56d3d 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -157,7 +157,8 @@ MockSymbolTable::MockSymbolTable() : singleton_(SymbolTableSingleton::get()) {} MockSymbolTable::~MockSymbolTable() { singleton_.release(); } -MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(symbol_table_) {} +//MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(symbol_table_) {} +MockIsolatedStatsStore::MockIsolatedStatsStore() {} MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } } // namespace Stats diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 124ca40f9a66..5fc85e2d9071 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -242,7 +242,7 @@ class MockIsolatedStatsStore : public IsolatedStoreImpl { MOCK_METHOD2(deliverHistogramToSinks, void(const Histogram& histogram, uint64_t value)); private: - MockSymbolTable symbol_table_; + //MockSymbolTable symbol_table_; }; } // namespace Stats From 101e9130450526f4d84b2519688c2fbc4a23a357 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 21 Nov 2018 12:14:03 -0500 Subject: [PATCH 026/106] use globals Signed-off-by: Joshua Marantz --- ci/run_clang_tidy.sh | 10 +++- test/BUILD | 1 + test/common/http/conn_manager_impl_test.cc | 3 +- test/common/network/connection_impl_test.cc | 1 - test/common/tracing/http_tracer_impl_test.cc | 3 +- .../network/thrift_proxy/decoder_test.cc | 3 +- .../thrift_proxy/route_matcher_test.cc | 1 - .../network/thrift_proxy/router_test.cc | 6 +-- .../thrift_proxy/thrift_object_impl_test.cc | 3 +- .../common/ot/opentracing_driver_impl_test.cc | 4 +- .../datadog/datadog_tracer_impl_test.cc | 3 +- .../dynamic_opentracing_driver_impl_test.cc | 4 +- .../lightstep/lightstep_tracer_impl_test.cc | 3 +- .../tracers/zipkin/zipkin_tracer_impl_test.cc | 3 +- .../alts/tsi_frame_protector_test.cc | 3 +- .../alts/tsi_handshaker_test.cc | 3 +- test/integration/server.cc | 6 +-- test/mocks/server/mocks.cc | 2 +- test/mocks/server/mocks.h | 2 +- test/mocks/stats/BUILD | 1 + test/mocks/stats/mocks.cc | 34 +------------- test/mocks/stats/mocks.h | 47 ++----------------- test/test_common/BUILD | 18 +++++++ test/test_runner.h | 12 ++++- tools/check_format.py | 2 + tools/check_format_test_helper.py | 2 + 26 files changed, 66 insertions(+), 114 deletions(-) diff --git a/ci/run_clang_tidy.sh b/ci/run_clang_tidy.sh index 2cc20142023e..e9df93bfcb65 100755 --- a/ci/run_clang_tidy.sh +++ b/ci/run_clang_tidy.sh @@ -16,14 +16,20 @@ echo "build ${BAZEL_BUILD_OPTIONS}" >> .bazelrc mv ./compile_commands.json "${ENVOY_SRCDIR}/compile_commands.json" cd "${ENVOY_SRCDIR}" +# Do not run incremental clang-tidy on check_format testdata files. +function exclude_testdata() { + grep -v tools/testdata/check_format/ +} + if [[ "${RUN_FULL_CLANG_TIDY}" == 1 ]]; then echo "Running full clang-tidy..." run-clang-tidy-7 elif [[ -z "${CIRCLE_PR_NUMBER}" && "$CIRCLE_BRANCH" == "master" ]]; then echo "On master branch, running clang-tidy-diff against previous commit..." - git diff HEAD^ | clang-tidy-diff-7.py -p 1 + git diff HEAD^ | exclude_testdata | clang-tidy-diff-7.py -p 1 else echo "Running clang-tidy-diff against master branch..." git fetch https://github.com/envoyproxy/envoy.git master - git diff $(git merge-base HEAD FETCH_HEAD)..HEAD | clang-tidy-diff-7.py -p 1 + git diff $(git merge-base HEAD FETCH_HEAD)..HEAD | exclude_testdata | \ + clang-tidy-diff-7.py -p 1 fi diff --git a/test/BUILD b/test/BUILD index f7044560db8f..d1e28211c9e4 100644 --- a/test/BUILD +++ b/test/BUILD @@ -29,6 +29,7 @@ envoy_cc_test_library( "//source/common/event:libevent_lib", "//test/mocks/access_log:access_log_mocks", "//test/test_common:environment_lib", + "//test/test_common:global_lib", "//test/test_common:printers_lib", ] + select({ "//bazel:disable_signal_trace": [], diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index b04ddcf26407..e7bd2a26276c 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -55,12 +55,11 @@ using testing::Ref; using testing::Return; using testing::ReturnRef; using testing::Sequence; -using testing::Test; namespace Envoy { namespace Http { -class HttpConnectionManagerImplTest : public Test, public ConnectionManagerConfig { +class HttpConnectionManagerImplTest : public testing::Test, public ConnectionManagerConfig { public: struct RouteConfigProvider : public Router::RouteConfigProvider { RouteConfigProvider(TimeSource& time_source) : time_source_(time_source) {} diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index a176458e250f..fe6ea3a67206 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -36,7 +36,6 @@ using testing::Return; using testing::SaveArg; using testing::Sequence; using testing::StrictMock; -using testing::Test; namespace Envoy { namespace Network { diff --git a/test/common/tracing/http_tracer_impl_test.cc b/test/common/tracing/http_tracer_impl_test.cc index c05ebf0dac08..99e07658df81 100644 --- a/test/common/tracing/http_tracer_impl_test.cc +++ b/test/common/tracing/http_tracer_impl_test.cc @@ -29,7 +29,6 @@ using testing::NiceMock; using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; -using testing::Test; namespace Envoy { namespace Tracing { @@ -324,7 +323,7 @@ TEST(HttpNullTracerTest, BasicFunctionality) { EXPECT_NE(nullptr, span_ptr->spawnChild(config, "foo", SystemTime())); } -class HttpTracerImplTest : public Test { +class HttpTracerImplTest : public testing::Test { public: HttpTracerImplTest() { driver_ = new MockDriver(); diff --git a/test/extensions/filters/network/thrift_proxy/decoder_test.cc b/test/extensions/filters/network/thrift_proxy/decoder_test.cc index b7abc2b6d702..2808a799e84b 100644 --- a/test/extensions/filters/network/thrift_proxy/decoder_test.cc +++ b/test/extensions/filters/network/thrift_proxy/decoder_test.cc @@ -26,7 +26,6 @@ using testing::Return; using testing::ReturnRef; using testing::SetArgReferee; using testing::StrictMock; -using testing::Test; using testing::TestParamInfo; using testing::TestWithParam; using testing::Values; @@ -206,7 +205,7 @@ INSTANTIATE_TEST_CASE_P(NonValueProtocolStates, DecoderStateMachineNonValueTest, ProtocolState::SetBegin, ProtocolState::SetEnd), protoStateParamToString); -class DecoderStateMachineTest : public DecoderStateMachineTestBase, public Test {}; +class DecoderStateMachineTest : public DecoderStateMachineTestBase, public testing::Test {}; class DecoderStateMachineValueTest : public DecoderStateMachineTestBase, public TestWithParam {}; diff --git a/test/extensions/filters/network/thrift_proxy/route_matcher_test.cc b/test/extensions/filters/network/thrift_proxy/route_matcher_test.cc index ffc9093de722..e4cbff2dcc93 100644 --- a/test/extensions/filters/network/thrift_proxy/route_matcher_test.cc +++ b/test/extensions/filters/network/thrift_proxy/route_matcher_test.cc @@ -10,7 +10,6 @@ #include "gtest/gtest.h" using testing::_; -using testing::Test; namespace Envoy { namespace Extensions { diff --git a/test/extensions/filters/network/thrift_proxy/router_test.cc b/test/extensions/filters/network/thrift_proxy/router_test.cc index d35809d97f67..de2d084c5708 100644 --- a/test/extensions/filters/network/thrift_proxy/router_test.cc +++ b/test/extensions/filters/network/thrift_proxy/router_test.cc @@ -29,7 +29,6 @@ using testing::NiceMock; using testing::Ref; using testing::Return; using testing::ReturnRef; -using testing::Test; using testing::TestWithParam; using testing::Values; @@ -330,14 +329,12 @@ class ThriftRouterTestBase { NiceMock upstream_connection_; }; -class ThriftRouterTest : public ThriftRouterTestBase, public Test { +class ThriftRouterTest : public ThriftRouterTestBase, public testing::Test { public: - ThriftRouterTest() {} }; class ThriftRouterFieldTypeTest : public ThriftRouterTestBase, public TestWithParam { public: - ThriftRouterFieldTypeTest() {} }; INSTANTIATE_TEST_CASE_P(PrimitiveFieldTypes, ThriftRouterFieldTypeTest, @@ -347,7 +344,6 @@ INSTANTIATE_TEST_CASE_P(PrimitiveFieldTypes, ThriftRouterFieldTypeTest, class ThriftRouterContainerTest : public ThriftRouterTestBase, public TestWithParam { public: - ThriftRouterContainerTest() {} }; INSTANTIATE_TEST_CASE_P(ContainerFieldTypes, ThriftRouterContainerTest, diff --git a/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc b/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc index 3e8d12403dcd..79df66bc1b30 100644 --- a/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc +++ b/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc @@ -18,7 +18,6 @@ using testing::NiceMock; using testing::Ref; using testing::Return; using testing::ReturnRef; -using testing::Test; using testing::TestWithParam; using testing::Values; @@ -144,7 +143,7 @@ class ThriftObjectImplTestBase { Buffer::OwnedImpl buffer_; }; -class ThriftObjectImplTest : public ThriftObjectImplTestBase, public Test {}; +class ThriftObjectImplTest : public ThriftObjectImplTestBase, public testing::Test {}; // Test parsing an empty struct (just a stop field). TEST_F(ThriftObjectImplTest, ParseEmptyStruct) { diff --git a/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc b/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc index 5172d9d0aeb6..c826495f54bc 100644 --- a/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc +++ b/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc @@ -11,8 +11,6 @@ #include "opentracing/mocktracer/in_memory_recorder.h" #include "opentracing/mocktracer/tracer.h" -using testing::Test; - namespace Envoy { namespace Extensions { namespace Tracers { @@ -46,7 +44,7 @@ class TestDriver : public OpenTracingDriver { std::shared_ptr tracer_; }; -class OpenTracingDriverTest : public Test { +class OpenTracingDriverTest : public testing::Test { public: void setupValidDriver(OpenTracingDriver::PropagationMode propagation_mode = diff --git a/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc b/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc index 6900e56b3ed8..f06ec0c34fca 100644 --- a/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc +++ b/test/extensions/tracers/datadog/datadog_tracer_impl_test.cc @@ -32,14 +32,13 @@ using testing::Invoke; using testing::NiceMock; using testing::Return; using testing::ReturnRef; -using testing::Test; namespace Envoy { namespace Extensions { namespace Tracers { namespace Datadog { -class DatadogDriverTest : public Test { +class DatadogDriverTest : public testing::Test { public: void setup(envoy::config::trace::v2::DatadogConfig& datadog_config, bool init_timer) { ON_CALL(cm_, httpAsyncClientForCluster("fake_cluster")) diff --git a/test/extensions/tracers/dynamic_ot/dynamic_opentracing_driver_impl_test.cc b/test/extensions/tracers/dynamic_ot/dynamic_opentracing_driver_impl_test.cc index 9232518de202..3e75240289bc 100644 --- a/test/extensions/tracers/dynamic_ot/dynamic_opentracing_driver_impl_test.cc +++ b/test/extensions/tracers/dynamic_ot/dynamic_opentracing_driver_impl_test.cc @@ -13,14 +13,12 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -using testing::Test; - namespace Envoy { namespace Extensions { namespace Tracers { namespace DynamicOt { -class DynamicOpenTracingDriverTest : public Test { +class DynamicOpenTracingDriverTest : public testing::Test { public: void setup(const std::string& library, const std::string& tracer_config) { driver_ = std::make_unique(stats_, library, tracer_config); diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 8bd1ce10218a..8e87727d132d 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -33,14 +33,13 @@ using testing::Invoke; using testing::NiceMock; using testing::Return; using testing::ReturnRef; -using testing::Test; namespace Envoy { namespace Extensions { namespace Tracers { namespace Lightstep { -class LightStepDriverTest : public Test { +class LightStepDriverTest : public testing::Test { public: void setup(envoy::config::trace::v2::LightstepConfig& lightstep_config, bool init_timer, Common::Ot::OpenTracingDriver::PropagationMode propagation_mode = diff --git a/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc b/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc index f1ae5bf60f49..18d9eb4540a9 100644 --- a/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc +++ b/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc @@ -31,14 +31,13 @@ using testing::Invoke; using testing::NiceMock; using testing::Return; using testing::ReturnRef; -using testing::Test; namespace Envoy { namespace Extensions { namespace Tracers { namespace Zipkin { -class ZipkinDriverTest : public Test { +class ZipkinDriverTest : public testing::Test { public: ZipkinDriverTest() : time_source_(test_time_.timeSystem()) {} diff --git a/test/extensions/transport_sockets/alts/tsi_frame_protector_test.cc b/test/extensions/transport_sockets/alts/tsi_frame_protector_test.cc index d98217a9f537..724638b3d4a1 100644 --- a/test/extensions/transport_sockets/alts/tsi_frame_protector_test.cc +++ b/test/extensions/transport_sockets/alts/tsi_frame_protector_test.cc @@ -16,14 +16,13 @@ using testing::InSequence; using testing::Invoke; using testing::NiceMock; using testing::SaveArg; -using testing::Test; using namespace std::string_literals; /** * Test with fake frame protector. The protected frame header is 4 byte length (little endian, * include header itself) and following the body. */ -class TsiFrameProtectorTest : public Test { +class TsiFrameProtectorTest : public testing::Test { public: TsiFrameProtectorTest() : raw_frame_protector_(tsi_create_fake_frame_protector(nullptr)), diff --git a/test/extensions/transport_sockets/alts/tsi_handshaker_test.cc b/test/extensions/transport_sockets/alts/tsi_handshaker_test.cc index 611479298277..75ff19c0f32e 100644 --- a/test/extensions/transport_sockets/alts/tsi_handshaker_test.cc +++ b/test/extensions/transport_sockets/alts/tsi_handshaker_test.cc @@ -16,7 +16,6 @@ using testing::InSequence; using testing::Invoke; using testing::NiceMock; using testing::SaveArg; -using testing::Test; class MockTsiHandshakerCallbacks : public TsiHandshakerCallbacks { public: @@ -33,7 +32,7 @@ class MockTsiHandshakerCallbacks : public TsiHandshakerCallbacks { } }; -class TsiHandshakerTest : public Test { +class TsiHandshakerTest : public testing::Test { public: TsiHandshakerTest() : server_handshaker_({tsi_create_fake_handshaker(0)}, dispatcher_), diff --git a/test/integration/server.cc b/test/integration/server.cc index 4191ea562023..0bb335e8615e 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -137,12 +137,12 @@ void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion vers // real symbol table through the mocking hierarchy -- which generally // constructs hierarchies of objects with no context, is too daunting. I think // the right thing to do is to avoid mocks in integration tests. - Stats::MockSymbolTable symbol_table; - Server::HotRestartNopImpl restarter(symbol_table); + Test::Global symbol_table; + Server::HotRestartNopImpl restarter(*symbol_table); Thread::MutexBasicLockable lock; ThreadLocal::InstanceImpl tls; - Stats::HeapStatDataAllocator stats_allocator(symbol_table); + Stats::HeapStatDataAllocator stats_allocator(*symbol_table); Stats::StatsOptionsImpl stats_options; Stats::ThreadLocalStoreImpl stats_store(stats_options, stats_allocator); stat_store_ = &stats_store; diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index 12729a7a0359..c6ea7b595d45 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -66,7 +66,7 @@ MockGuardDog::MockGuardDog() : watch_dog_(new NiceMock()) { } MockGuardDog::~MockGuardDog() {} -MockHotRestart::MockHotRestart() : stats_allocator_(symbol_table_) { +MockHotRestart::MockHotRestart() : stats_allocator_(symbol_table_.get()) { ON_CALL(*this, logLock()).WillByDefault(ReturnRef(log_lock_)); ON_CALL(*this, accessLogLock()).WillByDefault(ReturnRef(access_log_lock_)); ON_CALL(*this, statsAllocator()).WillByDefault(ReturnRef(stats_allocator_)); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index a64980ecc5a8..fff9c994bb9f 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -193,7 +193,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::StatDataAllocator&()); private: - Stats::MockSymbolTable symbol_table_; + Test::Global symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::HeapStatDataAllocator stats_allocator_; diff --git a/test/mocks/stats/BUILD b/test/mocks/stats/BUILD index 73cda48a2b41..ddb104e96124 100644 --- a/test/mocks/stats/BUILD +++ b/test/mocks/stats/BUILD @@ -21,5 +21,6 @@ envoy_cc_mock( "//source/common/stats:isolated_store_lib", "//source/common/stats:stats_lib", "//test/mocks:common_lib", + "//test/test_common:global_lib", ], ) diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 835439a56d3d..29b8726bc390 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -127,38 +127,8 @@ MockStore::MockStore(SymbolTable& symbol_table) } MockStore::~MockStore() {} -SymbolTableSingleton* SymbolTableSingleton::singleton_ = nullptr; - -SymbolTableSingleton::SymbolTableSingleton(Thread::MutexBasicLockable& mutex) - : mutex_(mutex), ref_count_(1) {} - -SymbolTableSingleton& SymbolTableSingleton::get() { - static Thread::MutexBasicLockable* mutex = new Thread::MutexBasicLockable; - Thread::LockGuard lock(*mutex); - if (singleton_ == nullptr) { - singleton_ = new SymbolTableSingleton(*mutex); - } else { - ++singleton_->ref_count_; - } - return *singleton_; -} - -void SymbolTableSingleton::release() { - Thread::LockGuard lock(mutex_); - ASSERT(singleton_ != nullptr); - ASSERT(singleton_ == this); - if (--singleton_->ref_count_ == 0) { - singleton_ = nullptr; - delete this; - } -} - -MockSymbolTable::MockSymbolTable() : singleton_(SymbolTableSingleton::get()) {} - -MockSymbolTable::~MockSymbolTable() { singleton_.release(); } - -//MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(symbol_table_) {} -MockIsolatedStatsStore::MockIsolatedStatsStore() {} +MockIsolatedStatsStore::MockIsolatedStatsStore() + : IsolatedStoreImpl(Test::Global::get()) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } } // namespace Stats diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 5fc85e2d9071..77dd93590372 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -17,6 +17,8 @@ #include "common/stats/histogram_impl.h" #include "common/stats/isolated_store_impl.h" +#include "test/test_common/global.h" + #include "gmock/gmock.h" namespace Envoy { @@ -191,58 +193,17 @@ class MockStore : public Store { StatsOptionsImpl stats_options_; }; -class SymbolTableSingleton : public SymbolTableImpl { -public: - static SymbolTableSingleton& get(); - void release(); - -private: - SymbolTableSingleton(Thread::MutexBasicLockable& mutex); - static SymbolTableSingleton* singleton_; - Thread::MutexBasicLockable& mutex_; // Lock guards don't work as mutex outlives object. - uint64_t ref_count_; -}; - -class MockSymbolTable : public SymbolTable { -public: - MockSymbolTable(); - ~MockSymbolTable(); - - SymbolEncoding encode(absl::string_view name) override { return singleton_.encode(name); } - uint64_t numSymbols() const override { return singleton_.numSymbols(); } - bool lessThan(const StatName& a, const StatName& b) const override { - return singleton_.lessThan(a, b); - } - void free(StatName stat_name) override { singleton_.free(stat_name); } - void incRefCount(StatName stat_name) override { singleton_.incRefCount(stat_name); } - std::string decode(const SymbolStorage symbol_vec, uint64_t size) const override { - return singleton_.decode(symbol_vec, size); - } - bool interoperable(const SymbolTable& other) const override { - return dynamic_cast(&other) != nullptr; - } - -#ifndef ENVOY_CONFIG_COVERAGE - void debugPrint() const override { singleton_.debugPrint(); } -#endif - -private: - SymbolTableSingleton& singleton_; -}; - /** * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : public IsolatedStoreImpl { +class MockIsolatedStatsStore : private Test::Global, + public IsolatedStoreImpl { public: MockIsolatedStatsStore(); ~MockIsolatedStatsStore(); MOCK_METHOD2(deliverHistogramToSinks, void(const Histogram& histogram, uint64_t value)); - -private: - //MockSymbolTable symbol_table_; }; } // namespace Stats diff --git a/test/test_common/BUILD b/test/test_common/BUILD index 84593f9108f3..0d77934a2ffc 100644 --- a/test/test_common/BUILD +++ b/test/test_common/BUILD @@ -131,6 +131,24 @@ envoy_cc_library( ], ) +envoy_cc_test_library( + name = "global_lib", + srcs = ["global.cc"], + hdrs = ["global.h"], + deps = [ + "//source/common/common:assert_lib", + "//source/common/common:thread_lib", + ], +) + +envoy_cc_test( + name = "global_test", + srcs = ["global_test.cc"], + deps = [ + ":global_lib", + ], +) + envoy_cc_test_library( name = "test_time_lib", srcs = ["test_time.cc"], diff --git a/test/test_runner.h b/test/test_runner.h index 77cd8389867c..1c4a003e623d 100644 --- a/test/test_runner.h +++ b/test/test_runner.h @@ -7,6 +7,7 @@ #include "test/mocks/access_log/mocks.h" #include "test/test_common/environment.h" +#include "test/test_common/global.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -41,7 +42,16 @@ class TestRunner { file_logger = std::make_unique( TestEnvironment::getOptions().logPath(), access_log_manager, Logger::Registry::getSink()); } - return RUN_ALL_TESTS(); + int exit_status = RUN_ALL_TESTS(); + + // Check that all singletons have been destroyed. + std::string active_singletons = Test::Globals::describeActiveSingletons(); + if (!active_singletons.empty()) { + std::cerr << "\n\nFAIL: Active singletons exist:\n" << active_singletons << std::endl; + exit_status = EXIT_FAILURE; + } + + return exit_status; } }; } // namespace Envoy diff --git a/tools/check_format.py b/tools/check_format.py index e10ba2595a92..d94ad2e26e6c 100755 --- a/tools/check_format.py +++ b/tools/check_format.py @@ -286,6 +286,8 @@ def checkSourceLine(line, file_path, reportError): if ' ?: ' in line: # The ?: operator is non-standard, it is a GCC extension reportError("Don't use the '?:' operator, it is a non-standard GCC extension") + if line.startswith('using testing::Test;'): + reportError("Don't use 'using testing::Test;, elaborate the type instead") def checkBuildLine(line, file_path, reportError): diff --git a/tools/check_format_test_helper.py b/tools/check_format_test_helper.py index 99d541b75113..7cf24a49a6d8 100755 --- a/tools/check_format_test_helper.py +++ b/tools/check_format_test_helper.py @@ -190,6 +190,8 @@ def checkFileExpectingOK(filename): errors += checkUnfixableError("attribute_packed.cc", "Don't use __attribute__((packed))") errors += checkUnfixableError("designated_initializers.cc", "Don't use designated initializers") errors += checkUnfixableError("elvis_operator.cc", "Don't use the '?:' operator") + errors += checkUnfixableError("testing_test.cc", + "Don't use 'using testing::Test;, elaborate the type instead") # The following files have errors that can be automatically fixed. errors += checkAndFixError("over_enthusiastic_spaces.cc", From 09d1b6e73d6c226d3dbdb51a3de231eb37a255d9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 21 Nov 2018 12:17:58 -0500 Subject: [PATCH 027/106] merge in globals Signed-off-by: Joshua Marantz --- test/test_common/global.cc | 26 +++++++ test/test_common/global.h | 128 ++++++++++++++++++++++++++++++++ test/test_common/global_test.cc | 46 ++++++++++++ tools/testdata/testing_test.cc | 5 ++ 4 files changed, 205 insertions(+) create mode 100644 test/test_common/global.cc create mode 100644 test/test_common/global.h create mode 100644 test/test_common/global_test.cc create mode 100644 tools/testdata/testing_test.cc diff --git a/test/test_common/global.cc b/test/test_common/global.cc new file mode 100644 index 000000000000..4c827922ee91 --- /dev/null +++ b/test/test_common/global.cc @@ -0,0 +1,26 @@ +#include "test/test_common/global.h" + +#include "common/common/assert.h" + +namespace Envoy { +namespace Test { + +Globals& Globals::instance() { + static Globals* globals = new Globals; + return *globals; +} + +std::string Globals::describeActiveSingletonsHelper() { + std::string ret; + Thread::ReleasableLockGuard map_lock(map_mutex_); + for (auto& p : singleton_map_) { + SingletonSharedPtr singleton = p.second.lock(); + if (singleton != nullptr) { + absl::StrAppend(&ret, "Unexpected active singleton: ", p.first, "\n"); + } + } + return ret; +} + +} // namespace Test +} // namespace Envoy diff --git a/test/test_common/global.h b/test/test_common/global.h new file mode 100644 index 000000000000..c00f847667f0 --- /dev/null +++ b/test/test_common/global.h @@ -0,0 +1,128 @@ +#pragma once + +#include "common/common/lock_guard.h" +#include "common/common/thread.h" + +#include "absl/container/flat_hash_map.h" + +namespace Envoy { +namespace Test { + +/** + * Helper class for managing Globals. + * + * This class is instantiated as a process-scoped singleton. It manages a map + * from type-name to weak_ptr. That map accumulates + * over a process lifetime and never shrinks. However, the weak_ptr will + * generally be cleared after each test, as all shared_ptr references held in + * Global instances are destroyed. This way, each unit-test gets a fresh + * start. + */ +class Globals { +public: + /** + * Walks through all global singletons and ensures that none of them are + * active. No singletons should be allocaed at the end of unit tests, so + * this is called at the end of Envoy::TestRunner::RunTests(). + * + * @return std::string empty string if quiescent, otherwise newline-separated + * error messages. + */ + static std::string describeActiveSingletons() { + return instance().describeActiveSingletonsHelper(); + } + + /** + * Manages Singleton objects that are cleaned up after all references are + * dropped. This class must not be templatized because as a map value where + * every singleton in the map represents a different type. Instead we + * templatize the ptr() and ref() methods. + */ + struct Singleton { + virtual ~Singleton() = default; + virtual void* ptrHelper() PURE; + template Type* ptr() { return static_cast(ptrHelper()); } + template Type& ref() { return *ptr(); } + }; + using SingletonSharedPtr = std::shared_ptr; + + /** + * @return Type a singleton instance of Type. T must be default-constructible. + */ + template static SingletonSharedPtr get() { return instance().getHelper(); } + + ~Globals() = delete; // GlobalHeler is constructed once and never destroyed. + +private: + /** + * Templatized derived class of Singleton which holds the Type object and is + * responsible for deleting it using the correct destructor. + */ + template struct TypedSingleton : public Singleton { + ~TypedSingleton() override = default; + void* ptrHelper() override { return ptr_.get(); } + + private: + std::unique_ptr ptr_{std::make_unique()}; + }; + + Globals() = default; // Construct via Globals::instance(). + + /** + * @return Globals& a singleton for Globals. + */ + static Globals& instance(); + + template SingletonSharedPtr getHelper() { + Thread::LockGuard map_lock(map_mutex_); + std::weak_ptr& weak_singleton_ref = singleton_map_[typeid(Type).name()]; + SingletonSharedPtr singleton = weak_singleton_ref.lock(); + + if (singleton == nullptr) { + singleton = std::make_shared>(); + weak_singleton_ref = singleton; + } + return singleton; + } + + std::string describeActiveSingletonsHelper(); + + Thread::MutexBasicLockable map_mutex_; + absl::flat_hash_map> singleton_map_ GUARDED_BY(map_mutex_); +}; + +/** + * Helps manage classes that need to be instantiated once per server. In + * production they must be be plumbed through call/class hierarchy, but + * in test-code the zero-arg-constructor Mock pattern makes this impractical. + * Instead we use self-cleaning singletons. + * + * Say for example you need a FooImpl plumbed through the system. In production + * code you must propagate a FooImpl through constructors to provide access + * where needed. For tests, everywhere a common FooImpl is required, + * instantiate: + * + * Global foo; + * + * You can ghen access the singleton FooImpl via foo.get(). The underlying + * FooImpl is ref-counted, and when the last TestGlobal is freed, the singleton + * FooImpl will be destructed and the singleton pointer nulled. + * + * The templated type must have a zero-arg constructor. Templatizing this on an + * int will compile, but will be hard to use as the memory will be uninitialized + * and you will not know when instantiating it whether it needs to be + * initialized. + */ +template class Global { +public: + Global() : singleton_(Globals::get()) {} + Type& get() { return singleton_->ref(); } + Type* operator->() { return singleton_->ptr(); } + Type& operator*() { return singleton_->ref(); } + +private: + Globals::SingletonSharedPtr singleton_; +}; + +} // namespace Test +} // namespace Envoy diff --git a/test/test_common/global_test.cc b/test/test_common/global_test.cc new file mode 100644 index 000000000000..bd6bb5674660 --- /dev/null +++ b/test/test_common/global_test.cc @@ -0,0 +1,46 @@ +#include +#include + +#include "test/test_common/global.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Test { + +class GlobalTest : public testing::Test { +protected: +}; + +TEST_F(GlobalTest, SingletonStringAndVector) { + { + Global s1; + Global> v1; + EXPECT_EQ("", *s1); + *s1 = "foo"; + EXPECT_TRUE(v1->empty()); + v1->push_back(42); + + Global s2; + Global> v2; + EXPECT_EQ("foo", *s2); + ASSERT_EQ(1, v2->size()); + EXPECT_EQ(42, (*v2)[0]); + } + + // The system is now quiescent, having dropped all references to the globals. + EXPECT_EQ("", Globals::describeActiveSingletons()); + + // After the globals went out of scope, referencing them again we start + // from clean objects; + Global s3; + Global> v3; + EXPECT_EQ("", *s3); + EXPECT_TRUE(v3->empty()); + + // With s3 and v3 on the stack, there are active singletons. + EXPECT_NE("", Globals::describeActiveSingletons()); +} + +} // namespace Test +} // namespace Envoy diff --git a/tools/testdata/testing_test.cc b/tools/testdata/testing_test.cc new file mode 100644 index 000000000000..187deb462125 --- /dev/null +++ b/tools/testdata/testing_test.cc @@ -0,0 +1,5 @@ +namespace Envoy { + +using testing::Test; + +} // namespace Envoy From cee54fe16c70e39e2a5628e01cd995268ac19c20 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 21 Nov 2018 12:19:52 -0500 Subject: [PATCH 028/106] move file placed in wrong dir Signed-off-by: Joshua Marantz --- tools/testdata/testing_test.cc | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 tools/testdata/testing_test.cc diff --git a/tools/testdata/testing_test.cc b/tools/testdata/testing_test.cc deleted file mode 100644 index 187deb462125..000000000000 --- a/tools/testdata/testing_test.cc +++ /dev/null @@ -1,5 +0,0 @@ -namespace Envoy { - -using testing::Test; - -} // namespace Envoy From 6883d6d1d8aed93e51e8bb0b86424dbcdd131ff6 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 21 Nov 2018 12:25:36 -0500 Subject: [PATCH 029/106] add missing file. Signed-off-by: Joshua Marantz --- tools/testdata/check_format/testing_test.cc | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tools/testdata/check_format/testing_test.cc diff --git a/tools/testdata/check_format/testing_test.cc b/tools/testdata/check_format/testing_test.cc new file mode 100644 index 000000000000..187deb462125 --- /dev/null +++ b/tools/testdata/check_format/testing_test.cc @@ -0,0 +1,5 @@ +namespace Envoy { + +using testing::Test; + +} // namespace Envoy From ac03dd9376256a767751e5d125be9641a2162211 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 29 Nov 2018 21:30:34 -0500 Subject: [PATCH 030/106] All tests working again. Signed-off-by: Joshua Marantz --- source/common/api/api_impl.h | 2 -- source/common/stats/raw_stat_data.cc | 4 ++-- source/common/stats/raw_stat_data.h | 2 +- source/common/stats/thread_local_store.h | 2 +- .../common/upstream/cluster_manager_impl.cc | 15 ++++++------- source/common/upstream/cluster_manager_impl.h | 4 ++-- .../config_validation/cluster_manager.cc | 7 +++---- test/common/stats/thread_local_store_test.cc | 21 ++++++++++--------- .../upstream/cluster_manager_impl_test.cc | 4 ++-- 9 files changed, 28 insertions(+), 33 deletions(-) diff --git a/source/common/api/api_impl.h b/source/common/api/api_impl.h index 6c786605df00..69cdaf79fef4 100644 --- a/source/common/api/api_impl.h +++ b/source/common/api/api_impl.h @@ -10,8 +10,6 @@ #include "common/filesystem/filesystem_impl.h" -#include "common/filesystem/filesystem_impl.h" - namespace Envoy { namespace Api { diff --git a/source/common/stats/raw_stat_data.cc b/source/common/stats/raw_stat_data.cc index 249354c66487..cf2d141fcb68 100644 --- a/source/common/stats/raw_stat_data.cc +++ b/source/common/stats/raw_stat_data.cc @@ -42,8 +42,8 @@ void RawStatData::initialize(absl::string_view key, const StatsOptions& stats_op if (key.size() > stats_options.maxNameLength()) { ENVOY_LOG_MISC( warn, - "Statistic '{}' is too long with {} characters, it will be truncated to {} characters", - key, key.size(), stats_options.maxNameLength()); + "Statistic '{}' is too long with {} characters, it will be truncated to {} characters", key, + key.size(), stats_options.maxNameLength()); key = key.substr(0, stats_options.maxNameLength()); } ref_count_ = 1; diff --git a/source/common/stats/raw_stat_data.h b/source/common/stats/raw_stat_data.h index fc660ef4a45e..428a80ebe93a 100644 --- a/source/common/stats/raw_stat_data.h +++ b/source/common/stats/raw_stat_data.h @@ -116,7 +116,7 @@ class RawStatDataAllocator : public StatDataAllocatorImpl { options_(options) {} ~RawStatDataAllocator(); - RawStatData* alloc(absl::string_view name); + virtual RawStatData* alloc(absl::string_view name); // Virtual only for mocking. void free(Stats::RawStatData& data) override; RawStatData* allocStatName(StatName stat_name) { return alloc(stat_name.toString(symbolTable())); diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 97369b0429f2..2374155bb273 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -274,7 +274,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb mergeCb); bool rejects(StatName name) const; - //absl::string_view truncateStatNameIfNeeded(absl::string_view name); + // absl::string_view truncateStatNameIfNeeded(absl::string_view name); template void removeRejectedStats(StatMapClass& map, StatListClass& list); diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 650f99988b8d..af5edbcf778d 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -167,15 +167,12 @@ void ClusterManagerInitHelper::setInitializedCb(std::function callback) } } -ClusterManagerImpl::ClusterManagerImpl(const envoy::config::bootstrap::v2::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, - ThreadLocal::Instance& tls, Runtime::Loader& runtime, - Runtime::RandomGenerator& random, - const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, - Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin, Api::Api& api, - Http::CodeStats& code_stats) +ClusterManagerImpl::ClusterManagerImpl( + const envoy::config::bootstrap::v2::Bootstrap& bootstrap, ClusterManagerFactory& factory, + Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, + Runtime::RandomGenerator& random, const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, + Server::Admin& admin, Api::Api& api, Http::CodeStats& code_stats) : factory_(factory), runtime_(runtime), stats_(stats), tls_(tls.allocateSlot()), random_(random), log_manager_(log_manager), bind_config_(bootstrap.cluster_manager().upstream_bind_config()), local_info_(local_info), diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index edc74a8795b2..f3e8d104f198 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -171,8 +171,8 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggablecounter("stats.overflow").value()); - // The name will be truncated, so we won't be able to find it with the entire name. - EXPECT_EQ(nullptr, TestUtility::findCounter(*store_, name_1).get()); + // Truncation occurs in the underlying representation, but the by-name lookups + // are all based on the untruncated name. + EXPECT_NE(nullptr, TestUtility::findCounter(*store_, name_1).get()); - // But we can find it based on the expected truncation. - EXPECT_NE(nullptr, TestUtility::findCounter(*store_, name_1.substr(0, max_name_length)).get()); + // Outside the stats system, no Enovy code can see the truncated view, so + // lookups for truncated names will fail. + EXPECT_EQ(nullptr, TestUtility::findCounter(*store_, name_1.substr(0, max_name_length)).get()); // The same should be true with heap allocation, which occurs when the default // allocator fails. @@ -451,11 +453,11 @@ TEST_F(StatsThreadLocalStoreTest, HotRestartTruncation) { EXPECT_CALL(*alloc_, alloc(_)).WillOnce(Return(nullptr)); store_->counter(name_2); - // Same deal: the name will be truncated, so we won't be able to find it with the entire name. - EXPECT_EQ(nullptr, TestUtility::findCounter(*store_, name_1).get()); + // Same deal: the name will be truncated, but we find it with the entire name. + EXPECT_NE(nullptr, TestUtility::findCounter(*store_, name_1).get()); - // But we can find it based on the expected truncation. - EXPECT_NE(nullptr, TestUtility::findCounter(*store_, name_1.substr(0, max_name_length)).get()); + // But we can't find it based on the truncation -- that name is not visible at the API. + EXPECT_EQ(nullptr, TestUtility::findCounter(*store_, name_1.substr(0, max_name_length)).get()); // Now the stats have overflowed. EXPECT_EQ(1UL, store_->counter("stats.overflow").value()); @@ -923,8 +925,7 @@ TEST_F(HistogramTest, BasicHistogramUsed) { class TruncatingAllocTest : public HeapStatsThreadLocalStoreTest { protected: TruncatingAllocTest() - : test_alloc_(options_, symbol_table_), - long_name_(options_.maxNameLength() + 1, 'A') {} + : test_alloc_(options_, symbol_table_), long_name_(options_.maxNameLength() + 1, 'A') {} void SetUp() override { store_ = std::make_unique(options_, test_alloc_); diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 3c88d1825406..04efe44c285a 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -173,8 +173,8 @@ envoy::config::bootstrap::v2::Bootstrap parseBootstrapFromV2Yaml(const std::stri class ClusterManagerImplTest : public testing::Test { public: - ClusterManagerImplTest() : api_(Api::createApiForTest(stats_store_)), - code_stats_(factory_.stats_.symbolTable()) { + ClusterManagerImplTest() + : api_(Api::createApiForTest(stats_store_)), code_stats_(factory_.stats_.symbolTable()) { factory_.dispatcher_.setTimeSystem(time_system_); } From 0a2fbe86141c03f3e6c7753f2256144ae65b34d9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 29 Nov 2018 21:33:13 -0500 Subject: [PATCH 031/106] format Signed-off-by: Joshua Marantz --- source/common/stats/thread_local_store.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 81dba5360afb..df9f5b8e4a48 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -307,10 +307,10 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( central_ref = &(p->second); } else { TagExtraction extraction(parent_, name); - //std::shared_ptr stat = make_stat(parent_.alloc_, extraction.truncatedStatName(), - //extraction.tagExtractedName(), extraction.tags()); - std::shared_ptr stat = make_stat(parent_.alloc_, name, - extraction.tagExtractedName(), extraction.tags()); + // std::shared_ptr stat = make_stat(parent_.alloc_, extraction.truncatedStatName(), + // extraction.tagExtractedName(), extraction.tags()); + std::shared_ptr stat = + make_stat(parent_.alloc_, name, extraction.tagExtractedName(), extraction.tags()); if (stat == nullptr) { parent_.num_last_resort_stats_.inc(); stat = make_stat(parent_.heap_allocator_, name, extraction.tagExtractedName(), From 4bb7c0deefc175a81bd1937e27136ce68c3015ee Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 6 Dec 2018 14:29:41 -0500 Subject: [PATCH 032/106] format Signed-off-by: Joshua Marantz --- include/envoy/server/instance.h | 2 +- source/common/router/router.cc | 3 ++- source/common/router/router.h | 2 +- source/common/upstream/cluster_manager_impl.h | 2 +- source/server/config_validation/server.h | 2 +- source/server/listener_manager_impl.h | 2 +- source/server/server.cc | 2 +- source/server/server.h | 2 +- test/common/http/codes_test.cc | 2 +- test/common/router/router_test.cc | 3 +-- test/common/upstream/cluster_manager_impl_test.cc | 3 +-- test/mocks/server/mocks.cc | 3 +-- test/mocks/server/mocks.h | 2 +- 13 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index ab20658d7271..c065df9e2161 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -216,7 +216,7 @@ class Instance { /** * @return Http::CodeStats the http response-code stats */ - //virtual Http::CodeStats& codeStats() PURE; + // virtual Http::CodeStats& codeStats() PURE; }; } // namespace Server diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 51f872d66afb..e187f71bd739 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -167,7 +167,8 @@ void Filter::chargeUpstreamCode(uint64_t response_status_code, response_status_code, internal_request, route_entry_->virtualHost().name(), - request_vcluster_ ? request_vcluster_->name() : EMPTY_STRING, + request_vcluster_ ? request_vcluster_->name() + : EMPTY_STRING, zone_name, upstream_zone, is_canary}; diff --git a/source/common/router/router.h b/source/common/router/router.h index 1f171384c2fc..87eb79eab329 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -22,10 +22,10 @@ #include "common/common/hash.h" #include "common/common/hex.h" #include "common/common/logger.h" -#include "common/stats/symbol_table_impl.h" #include "common/config/well_known_names.h" #include "common/http/utility.h" #include "common/router/config_impl.h" +#include "common/stats/symbol_table_impl.h" #include "common/stream_info/stream_info_impl.h" #include "common/upstream/load_balancer_impl.h" diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 9f6c3ef8da77..07f6737ad8e9 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -439,7 +439,7 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable, Event::TimeSystem& timeSystem() override { return time_system_; } Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } - //Http::CodeStats& codeStats() override { return code_stats_; } + // Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { return config_.statsFlushInterval(); diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index dd1de50ecf25..825d2f6a9eae 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -288,7 +288,7 @@ class ListenerImpl : public Network::ListenerConfig, ensureSocketOptions(); Network::Socket::appendOptions(listen_socket_options_, options); } - //Http::CodeStats& codeStats() override { return parent_.server_.codeStats(); } + // Http::CodeStats& codeStats() override { return parent_.server_.codeStats(); } // Network::DrainDecision bool drainClose() const override; diff --git a/source/server/server.cc b/source/server/server.cc index 8df707ebbae5..0343fc6741b8 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -65,7 +65,7 @@ InstanceImpl::InstanceImpl(Options& options, Event::TimeSystem& time_system, dns_resolver_(dispatcher_->createDnsResolver({})), access_log_manager_(*api_, *dispatcher_, access_log_lock), terminated_(false), mutex_tracer_(options.mutexTracingEnabled() ? &Envoy::MutexTracerImpl::getOrCreateTracer() - : nullptr), + : nullptr), http_context_(store.symbolTable()) { try { diff --git a/source/server/server.h b/source/server/server.h index f2d5a3d9aff4..a6f7c0fc9ba9 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -181,7 +181,7 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } Event::TimeSystem& timeSystem() override { return time_system_; } Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } - //Http::CodeStats& codeStats() override { return code_stats_; } + // Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { return config_.statsFlushInterval(); diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index f6c269cba201..e6e5ed6a0670 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -271,7 +271,7 @@ TEST_F(CodeStatsTest, StripTrailingDot) { TEST_F(CodeStatsTest, Join) { EXPECT_EQ("hello.world", join({"hello", "world"})); EXPECT_EQ("hello.world", join({"", "hello", "world"})); // leading empty token ignored. - //EXPECT_EQ("hello.", join({"hello", ""})); // trailing empty token not ignored. + // EXPECT_EQ("hello.", join({"hello", ""})); // trailing empty token not ignored. EXPECT_EQ("hello", join({"hello"})); EXPECT_EQ("", join({""})); } diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 7e1138decad5..8259f1e7a381 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -75,8 +75,7 @@ class TestFilter : public Filter { class RouterTestBase : public testing::Test { public: RouterTestBase(bool start_child_span, bool suppress_envoy_headers) - : http_context_(stats_store_.symbolTable()), - shadow_writer_(new MockShadowWriter()), + : http_context_(stats_store_.symbolTable()), shadow_writer_(new MockShadowWriter()), config_("test.", local_info_, stats_store_, cm_, runtime_, random_, ShadowWriterPtr{shadow_writer_}, true, start_child_span, suppress_envoy_headers, test_time_.timeSystem(), http_context_), diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index c0a0d64965eb..10a224f28afb 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -172,8 +172,7 @@ envoy::config::bootstrap::v2::Bootstrap parseBootstrapFromV2Yaml(const std::stri class ClusterManagerImplTest : public testing::Test { public: ClusterManagerImplTest() - : api_(Api::createApiForTest(stats_store_)), - http_context_(factory_.stats_.symbolTable()) { + : api_(Api::createApiForTest(stats_store_)), http_context_(factory_.stats_.symbolTable()) { factory_.dispatcher_.setTimeSystem(time_system_); } diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index dd4e501482a9..2f2cf26b58f8 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -162,8 +162,7 @@ MockMain::MockMain(int wd_miss, int wd_megamiss, int wd_kill, int wd_multikill) MockMain::~MockMain() {} MockFactoryContext::MockFactoryContext() - : singleton_manager_(new Singleton::ManagerImpl()), - http_context_(scope_.symbolTable()) { + : singleton_manager_(new Singleton::ManagerImpl()), http_context_(scope_.symbolTable()) { ON_CALL(*this, accessLogManager()).WillByDefault(ReturnRef(access_log_manager_)); ON_CALL(*this, clusterManager()).WillByDefault(ReturnRef(cluster_manager_)); ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 923786e44b1c..02b8befda8dd 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -420,7 +420,7 @@ class MockFactoryContext : public FactoryContext { MOCK_METHOD0(timeSource, TimeSource&()); Event::SimulatedTimeSystem& timeSystem() { return time_system_; } - //Http::CodeStats& codeStats() override { return code_stats_; } + // Http::CodeStats& codeStats() override { return code_stats_; } Http::Context& httpContext() override { return http_context_; } From b7acdd56ed1442442908ba63f3f1561c2a8a3081 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 6 Dec 2018 14:48:07 -0500 Subject: [PATCH 033/106] prune dead code. Signed-off-by: Joshua Marantz --- include/envoy/server/instance.h | 5 ----- source/common/http/codes.cc | 18 ------------------ source/common/http/codes.h | 1 - 3 files changed, 24 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index c065df9e2161..0ac38eed2f8e 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -212,11 +212,6 @@ class Instance { * @return the flush interval of stats sinks. */ virtual std::chrono::milliseconds statsFlushInterval() const PURE; - - /** - * @return Http::CodeStats the http response-code stats - */ - // virtual Http::CodeStats& codeStats() PURE; }; } // namespace Server diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 8cc84954c890..89fd3595f92e 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -62,13 +62,6 @@ void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName scope.counterx(Join(prefix, upstream_rq_.statName(response_code)).statName()).inc(); } -/* -void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, - Code response_code) const { - return chargeBasicResponseStat(scope, prefix, response_code); -} -*/ - void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) const { Stats::StatNameTempStorage prefix_storage(stripTrailingDot(info.prefix_), symbol_table_); Stats::StatName prefix = prefix_storage.statName(); @@ -186,17 +179,6 @@ absl::string_view CodeStatsImpl::stripTrailingDot(absl::string_view str) { return str; } -std::string CodeStatsImpl::join(const std::vector& v) { - if (v.empty()) { - return ""; - } - auto iter = v.begin(); - if (iter->empty()) { - ++iter; // Skip any initial empty prefix. - } - return absl::StrJoin(iter, v.end(), "."); -} - Stats::StatName CodeStatsImpl::upstreamRqGroup(Code response_code) const { switch (enumToInt(response_code) / 100) { case 1: diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 03d5a1ec4004..ee49f0ed486a 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -66,7 +66,6 @@ class CodeStatsImpl : public CodeStats { }; static absl::string_view stripTrailingDot(absl::string_view prefix); - static std::string join(const std::vector& v); Stats::StatName makeStatName(absl::string_view name); Stats::StatName upstreamRqGroup(Code response_code) const; From 4014dde0b8ab79c29bbec23d7572f02dfa6a4428 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 17 Dec 2018 19:06:48 -0500 Subject: [PATCH 034/106] Remove the symbol-table interface; just call symbol table directly. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 206 +---------------- source/common/http/codes.cc | 7 +- source/common/stats/heap_stat_data.cc | 2 +- source/common/stats/isolated_store_impl.cc | 2 +- source/common/stats/metric_impl.cc | 12 +- source/common/stats/symbol_table_impl.cc | 119 +++++----- source/common/stats/symbol_table_impl.h | 216 +++++++++++------- source/exe/main_common.h | 2 +- test/common/http/codes_speed_test.cc | 2 +- test/common/http/codes_test.cc | 4 +- .../http/conn_manager_impl_fuzz_test.cc | 2 +- test/common/stats/BUILD | 1 + test/common/stats/heap_stat_data_test.cc | 2 +- test/common/stats/raw_stat_data_test.cc | 2 +- test/common/stats/source_impl_test.cc | 2 +- test/common/stats/symbol_table_impl_test.cc | 104 ++++++++- .../stats/thread_local_store_speed_test.cc | 2 +- test/common/stats/thread_local_store_test.cc | 6 +- .../common/statsd/udp_statsd_test.cc | 2 +- test/integration/server.cc | 4 +- test/mocks/server/mocks.h | 2 +- test/mocks/stats/mocks.cc | 2 +- test/mocks/stats/mocks.h | 4 +- .../config_validation/cluster_manager_test.cc | 2 +- test/server/hot_restart_impl_test.cc | 2 +- test/server/http/admin_test.cc | 4 +- test/test_common/utility.h | 2 +- 27 files changed, 332 insertions(+), 385 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 729ee2801dd5..3f8152a6c859 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -1,213 +1,13 @@ #pragma once -#include "envoy/common/pure.h" - -#include "common/common/hash.h" - namespace Envoy { namespace Stats { -/** Efficient byte-encoded storage an array of tokens, which are typically < 127 */ -using SymbolStorage = uint8_t[]; +// Interface for referencing a stat name. +class StatName; -class SymbolEncoding; +// Interface for managing symbol tables. class SymbolTable; -/** - * Efficiently represents a stat name using a variable-length array of uint8_t. - * This class does not own the backing store for this array; the backing-store - * can be held in StatNameStorage, or it can be packed more tightly into another - * object. - * - * For large numbers of clusters, there are a huge number of StatNames so - * avoiding extra per-stat pointers has a significant memory impact. - */ -class StatName { -public: - // Constructs a StatName object directly referencing the storage of another - // StatName. - explicit StatName(const SymbolStorage symbol_array) : symbol_array_(symbol_array) {} - - // Constructs an empty StatName object. - StatName() : symbol_array_(nullptr) {} - - // Constructs a StatName object with new storage, which must be of size - // src.numBytesIncludingLenggth(). This is used in the a flow where we first - // construct a StatName for lookup in a cache, and then on a miss need/ to - // store the data directly. - StatName(const StatName& src, SymbolStorage memory); - - std::string toString(const SymbolTable& table) const; - - /** - * Note that this hash function will return a different hash than that of - * the elaborated string. - * - * @return uint64_t a hash of the underlying representation. - */ - uint64_t hash() const { - const char* cdata = reinterpret_cast(data()); - return HashUtil::xxHash64(absl::string_view(cdata, numBytes())); - } - - bool operator==(const StatName& rhs) const { - const uint64_t sz = numBytes(); - return sz == rhs.numBytes() && memcmp(data(), rhs.data(), sz * sizeof(uint8_t)) == 0; - } - bool operator!=(const StatName& rhs) const { return !(*this == rhs); } - - /** - * @return uint64_t the number of bytes in the symbol array, excluding the two-byte - * overhead for the size itself. - */ - uint64_t numBytes() const { - return symbol_array_[0] | (static_cast(symbol_array_[1]) << 8); - } - - const uint8_t* symbolArray() const { return symbol_array_; } - - /** - * @return uint64_t the number of bytes in the symbol array, including the two-byte - * overhead for the size itself. - */ - uint64_t numBytesIncludingLength() const { return numBytes() + 2; } - - void copyToStorage(SymbolStorage storage) { - memcpy(storage, symbol_array_, numBytesIncludingLength()); - } - -#ifndef ENVOY_CONFIG_COVERAGE - void debugPrint(); -#endif - - /** - * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). - */ - const uint8_t* data() const { return symbol_array_ + 2; } - -protected: - /* - friend SymbolTable; - friend class StatNameTest; - friend class StatNameJoiner; - friend class StatNameStorage; - */ - - const uint8_t* symbol_array_; -}; - -/** - * SymbolTable manages a namespace optimized for stats, which are typically - * composed of arrays of "."-separated tokens, with a significant overlap - * between the tokens. Each token is mapped to a Symbol (uint32_t) and - * reference-counted so that no-longer-used symbols can be reclaimed. - * - * We use a uint8_t array to encode arrays of symbols in order to conserve - * space, as in practice the majority of token instances in stat names draw from - * a fairly small set of common names, typically less than 100. The format is - * somewhat similar to UTF-8, with a variable-length array of uint8_t. See the - * implementation for details. - * - * StatNameStorage can be used to manage memory for the byte-encoding. Not all - * StatNames are backed by StatNameStorage -- the storage may be inlined into - * another object such as HeapStatData. StaNameStorage is not fully RAII -- - * instead the owner must call free(SymbolTable&) explicitly before - * StatNameStorage is destructed. This saves 8 bytes of storage per stat, - * relative to holding a SymbolTable& in each StatNameStorage object. - * - * A StatName is a copyable and assignable reference to this storage. It does - * not own the storage or keep it alive via reference counts; the owner must - * ensure the backing store lives as long as the StatName. - * - * The underlying Symbol / SymbolVec data structures are private to the - * impl. One side effect of the non-monotonically-increasing symbol counter is - * that if a string is encoded, the resulting stat is destroyed, and then that - * same string is re-encoded, it may or may not encode to the same underlying - * symbol. - */ -class SymbolTable { -public: - virtual ~SymbolTable() {} - - /** - * Encodes a stat name using the symbol table, returning a SymbolEncoding. The - * SymbolEncoding is not intended for long-term storage, but is used to help - * allocate and StatName with the correct amount of storage. - * - * When a name is encoded, it bumps reference counts held in the table for - * each symbol. The caller is responsible for creating a StatName using this - * SymbolEncoding and ultimately disposing of it by calling - * StatName::free(). Otherwise the symbols will leak for the lifetime of the - * table, though they won't show up as a C++ leaks as the memory is still - * reachable from the SymolTable. - * - * @param name The name to encode. - * @return SymbolEncoding the encoded symbols. - */ - virtual SymbolEncoding encode(absl::string_view name) PURE; - - /** - * @return uint64_t the number of symbols in the symbol table. - */ - virtual uint64_t numSymbols() const PURE; - - /** - * Deterines 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; - - /** - * 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 symbol_vec the vector of symbols to be freed. - */ - virtual void free(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 symbol_vec the vector of symbols to be freed. - */ - virtual void incRefCount(StatName stat_name) 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 hard, since this - * should never happen, and we don't want to continue running with a corrupt stats set. - * - * @param symbol_vec the vector of symbols to decode. - * @return std::string the retrieved stat name. - */ - virtual std::string decode(const SymbolStorage symbol_vec, uint64_t size) const PURE; - -#ifndef ENVOY_CONFIG_COVERAGE - virtual void debugPrint() const PURE; -#endif - - virtual bool interoperable(const SymbolTable& other) const PURE; -}; - -using SharedSymbolTable = std::shared_ptr; - } // namespace Stats } // namespace Envoy diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 89fd3595f92e..9036a94f313b 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -53,8 +53,7 @@ Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, Code response_code) const { - ASSERT(scope.symbolTable().interoperable(symbol_table_)); - ASSERT(symbol_table_.interoperable(scope.symbolTable())); + ASSERT(&symbol_table_ == &scope.symbolTable()); // Build a dynamic stat for the response code and increment it. scope.counterx(Join(prefix, upstream_rq_completed_).statName()).inc(); @@ -67,9 +66,7 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) const { Stats::StatName prefix = prefix_storage.statName(); Code code = static_cast(info.response_status_code_); - ASSERT(info.cluster_scope_.symbolTable().interoperable(symbol_table_)); - ASSERT(symbol_table_.interoperable(info.cluster_scope_.symbolTable())); - + ASSERT(&info.cluster_scope_.symbolTable() == &symbol_table_); chargeBasicResponseStat(info.cluster_scope_, prefix, code); Stats::StatName rq_group = upstreamRqGroup(code); diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index f0bdabe9b489..796755b703ac 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -12,7 +12,7 @@ namespace Stats { HeapStatDataAllocator::~HeapStatDataAllocator() { ASSERT(stats_.empty()); } HeapStatData* HeapStatData::alloc(StatName stat_name, SymbolTable& symbol_table) { - void* memory = ::malloc(sizeof(HeapStatData) + stat_name.numBytesIncludingLength()); + void* memory = ::malloc(sizeof(HeapStatData) + stat_name.size()); ASSERT(memory); symbol_table.incRefCount(stat_name); return new (memory) HeapStatData(stat_name); diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index c2d6152d07cc..4cea30b08c16 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -13,7 +13,7 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 9020610a4e0d..8ade71a858a8 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -41,15 +41,15 @@ void MetricImpl::clear() { SymbolTable& symbol_table = symbolTable(); uint8_t* p = &storage_[0]; uint32_t num_tags = *p++; - p += tagExtractedStatName().numBytesIncludingLength(); + p += tagExtractedStatName().size(); symbol_table.free(tagExtractedStatName()); for (size_t i = 0; i < num_tags; ++i) { Tag tag; StatName name(p); - p += name.numBytesIncludingLength(); + p += name.size(); symbol_table.free(name); StatName value(p); - p += value.numBytesIncludingLength(); + p += value.size(); symbol_table.free(value); } storage_.reset(); @@ -71,7 +71,7 @@ StatName MetricImpl::tagExtractedStatName() const { return StatName(&storage_[1] std::vector MetricImpl::tags() const { uint8_t* p = &storage_[0]; uint32_t num_tags = *p++; - p += tagExtractedStatName().numBytesIncludingLength(); + p += tagExtractedStatName().size(); std::vector tags; tags.reserve(num_tags); @@ -81,10 +81,10 @@ std::vector MetricImpl::tags() const { Tag tag; StatName name(p); tag.name_ = name.toString(symbol_table); - p += name.numBytesIncludingLength(); + p += name.size(); StatName value(p); tag.value_ = value.toString(symbol_table); - p += value.numBytesIncludingLength(); + p += value.size(); tags.emplace_back(tag); } return tags; diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index ea5a5d853fd4..a2135cecc156 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -13,26 +13,25 @@ namespace Stats { static const uint32_t SpilloverMask = 0x80; static const uint32_t Low7Bits = 0x7f; -StatName::StatName(const StatName& src, SymbolStorage memory) : symbol_array_(memory) { - memcpy(memory, src.symbolArray(), src.numBytesIncludingLength()); - // src.symbol_array_ = nullptr; // transfers ownership. +StatName::StatName(const StatName& src, SymbolStorage memory) : size_and_data_(memory) { + memcpy(memory, src.size_and_data_, src.size()); } std::string StatName::toString(const SymbolTable& table) const { - return table.decode(data(), numBytes()); + return table.decode(data(), dataSize()); } #ifndef ENVOY_CONFIG_COVERAGE void StatName::debugPrint() { - if (symbol_array_ == nullptr) { + if (size_and_data_ == nullptr) { std::cout << "Null StatName" << std::endl << std::flush; } else { - uint64_t nbytes = numBytes(); - std::cout << "numBytes=" << nbytes << ":"; + uint64_t nbytes = dataSize(); + std::cout << "dataSize=" << nbytes << ":"; for (uint64_t i = 0; i < nbytes; ++i) { std::cout << " " << static_cast(data()[i]); } - SymbolVec encoding = SymbolEncoding::decodeSymbols(data(), numBytes()); + SymbolVec encoding = SymbolEncoding::decodeSymbols(data(), dataSize()); std::cout << ", numSymbols=" << encoding.size() << ":"; for (Symbol symbol : encoding) { std::cout << " " << symbol; @@ -87,7 +86,7 @@ SymbolVec SymbolEncoding::decodeSymbols(const SymbolStorage array, uint64_t size // There is no guarantee that bytes will be aligned, so we can't cast to a // uint16_t* and assign, but must individually copy the bytes. static inline uint8_t* saveLengthToBytesReturningNext(uint64_t length, uint8_t* bytes) { - ASSERT(length < 65536); + ASSERT(length < StatNameMaxSize); *bytes++ = length & 0xff; *bytes++ = length >> 8; return bytes; @@ -100,38 +99,25 @@ uint64_t SymbolEncoding::moveToStorage(SymbolStorage symbol_array) { memcpy(symbol_array, vec_.data(), sz * sizeof(uint8_t)); } vec_.clear(); // Logically transfer ownership, enabling empty assert on destruct. - return sz + 2; + return sz + StatNameSizeEncodingBytes; } -SymbolTableImpl::SymbolTableImpl() +SymbolTable::SymbolTable() // Have to be explicitly initialized, if we want to use the GUARDED_BY macro. : next_symbol_(0), monotonic_counter_(0) {} -SymbolTableImpl::~SymbolTableImpl() { - // to avoid leaks into the symbol table, we expect all StatNames to be freed. +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 // tests. ASSERT(numSymbols() == 0); - -#ifdef TRACK_ENCODES - using CountNamePair = std::pair; - std::vector hist; - for (const auto& p : histogram_) { - hist.push_back(CountNamePair(p.second, p.first)); - } - std::sort(hist.begin(), hist.end()); - for (auto p : hist) { - std::cerr << absl::StrCat(p.first, "\t", p.second) << std::endl; - } - std::cerr << std::flush; -#endif } // 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. -SymbolEncoding SymbolTableImpl::encode(const absl::string_view name) { +SymbolEncoding SymbolTable::encode(const absl::string_view name) { SymbolEncoding encoding; if (name.empty()) { @@ -146,9 +132,6 @@ SymbolEncoding SymbolTableImpl::encode(const absl::string_view name) { // Now take the lock and populate the Symbol objects, which involves bumping // ref-counts in this. -#ifdef TRACK_ENCODES - ++histogram_[std::string(name)]; -#endif for (absl::string_view token : tokens) { symbols.push_back(toSymbol(token)); } @@ -160,11 +143,11 @@ SymbolEncoding SymbolTableImpl::encode(const absl::string_view name) { return encoding; } -std::string SymbolTableImpl::decode(const SymbolStorage symbol_array, uint64_t size) const { +std::string SymbolTable::decode(const SymbolStorage symbol_array, uint64_t size) const { return decodeSymbolVec(SymbolEncoding::decodeSymbols(symbol_array, size)); } -std::string SymbolTableImpl::decodeSymbolVec(const SymbolVec& symbols) const { +std::string SymbolTable::decodeSymbolVec(const SymbolVec& symbols) const { std::vector name_tokens; name_tokens.reserve(symbols.size()); { @@ -177,11 +160,27 @@ std::string SymbolTableImpl::decodeSymbolVec(const SymbolVec& symbols) const { return absl::StrJoin(name_tokens, "."); } -void SymbolTableImpl::adjustRefCount(StatName stat_name, int adjustment) { +void SymbolTable::incRefCount(const StatName& stat_name) { // Before taking the lock, decode the array of symbols from the SymbolStorage. - SymbolVec symbols = SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.numBytes()); + SymbolVec symbols = SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.dataSize()); - absl::MutexLock lock(&lock_); + absl::ReaderMutexLock lock(&lock_); + for (Symbol symbol : symbols) { + auto decode_search = decode_map_.find(symbol); + ASSERT(decode_search != decode_map_.end()); + + auto encode_search = encode_map_.find(decode_search->second.get()); + ASSERT(encode_search != encode_map_.end()); + + ++encode_search->second->ref_count_; + } +} + +void SymbolTable::free(const StatName& stat_name) { + // Before taking the lock, decode the array of symbols from the SymbolStorage. + SymbolVec symbols = SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.dataSize()); + + absl::MutexLock lock(&lock_); // Takes write-lock as we may mutate decode_map_ and encode_map_. for (Symbol symbol : symbols) { auto decode_search = decode_map_.find(symbol); ASSERT(decode_search != decode_map_.end()); @@ -189,7 +188,7 @@ void SymbolTableImpl::adjustRefCount(StatName stat_name, int adjustment) { auto encode_search = encode_map_.find(decode_search->second.get()); ASSERT(encode_search != encode_map_.end()); - encode_search->second->ref_count_ += adjustment; + --encode_search->second->ref_count_; // If that was the last remaining client usage of the symbol, erase the the current // mappings and add the now-unused symbol to the reuse pool. @@ -201,15 +200,15 @@ void SymbolTableImpl::adjustRefCount(StatName stat_name, int adjustment) { } } -Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { +Symbol SymbolTable::toSymbol(absl::string_view sv) { { // First try to find the symbol with just a read-lock, so concurrent - // lookups for an already-allocated symbol do not conflict. + // lookups for an already-allocated symbol do not contend. absl::ReaderMutexLock lock(&lock_); auto encode_find = encode_map_.find(sv); if (encode_find != encode_map_.end()) { - // If the insertion didn't take place, return the actual value at that location and up the - // refcount at that location + // Increment the refcount of the already existing symbol. Note that the + // ref_count_ is atomic to allow incrementing it under read-lock. SharedSymbol& shared_symbol = *encode_find->second; ++(shared_symbol.ref_count_); return shared_symbol.symbol_; @@ -238,24 +237,34 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) { SharedSymbol& shared_symbol = *encode_insert.first->second; if (encode_insert.second) { + // The insertion took place. auto decode_insert = decode_map_.insert({next_symbol_, std::move(str)}); ASSERT(decode_insert.second); newSymbol(); } else { - // If the insertion didn't take place, return the shared symbol, and bump the refcount. + // If the insertion didn't take place -- due to another thread racing to + // insert the same symbmol after we drop the read-lock above -- we can + // return the shared symbol, but we must bump the refcount. ++(shared_symbol.ref_count_); + + // Note: this condition is hard to hit in tests as it requires a tight race + // between multiple threads concurrently creating the same symbol. + // Uncommenting this line can help rapidly determine coverage during + // development. StatNameTest.RacingSymbolCreation hits this occasionally + // when testing with optimization, and frequently with fastbuild and debug. + // + // std::cerr << "Covered insertion race" << std::endl; } return shared_symbol.symbol_; } -absl::string_view SymbolTableImpl::fromSymbol(const Symbol symbol) const - SHARED_LOCKS_REQUIRED(lock_) { +absl::string_view SymbolTable::fromSymbol(const Symbol symbol) const SHARED_LOCKS_REQUIRED(lock_) { auto search = decode_map_.find(symbol); - ASSERT(search != decode_map_.end()); + RELEASE_ASSERT(search != decode_map_.end(), "no such symbol"); return absl::string_view(search->second.get()); } -void SymbolTableImpl::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { +void SymbolTable::newSymbol() EXCLUSIVE_LOCKS_REQUIRED(lock_) { if (pool_.empty()) { next_symbol_ = ++monotonic_counter_; } else { @@ -266,13 +275,13 @@ void SymbolTableImpl::newSymbol() 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 { // Constructing two temp vectors during lessThan is not strictly necessary. // If this becomes a performance bottleneck (e.g. during sorting), we could // provide an iterator-like interface for incrementally decoding the symbols // without allocating memory. - SymbolVec av = SymbolEncoding::decodeSymbols(a.data(), a.numBytes()); - SymbolVec bv = SymbolEncoding::decodeSymbols(b.data(), b.numBytes()); + SymbolVec av = SymbolEncoding::decodeSymbols(a.data(), a.dataSize()); + SymbolVec bv = SymbolEncoding::decodeSymbols(b.data(), b.dataSize()); for (uint64_t i = 0, n = std::min(av.size(), bv.size()); i < n; ++i) { if (av[i] != bv[i]) { absl::ReaderMutexLock lock(&lock_); @@ -283,7 +292,7 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { } #ifndef ENVOY_CONFIG_COVERAGE -void SymbolTableImpl::debugPrint() const { +void SymbolTable::debugPrint() const { absl::ReaderMutexLock lock(&lock_); std::vector symbols; for (const auto& p : decode_map_) { @@ -306,7 +315,7 @@ StatNameStorage::StatNameStorage(absl::string_view name, SymbolTable& table) { } StatNameStorage::StatNameStorage(StatName src, SymbolTable& table) { - uint64_t size = src.numBytesIncludingLength(); + uint64_t size = src.size(); bytes_ = std::make_unique(size); src.copyToStorage(bytes_.get()); table.incRefCount(statName()); @@ -326,8 +335,8 @@ void StatNameStorage::free(SymbolTable& table) { } StatNameJoiner::StatNameJoiner(StatName a, StatName b) { - const uint64_t a_size = a.numBytes(); - const uint64_t b_size = b.numBytes(); + const uint64_t a_size = a.dataSize(); + const uint64_t b_size = b.dataSize(); uint8_t* const p = alloc(a_size + b_size); memcpy(p, a.data(), a_size); memcpy(p + a_size, b.data(), b_size); @@ -336,18 +345,18 @@ StatNameJoiner::StatNameJoiner(StatName a, StatName b) { StatNameJoiner::StatNameJoiner(const std::vector& stat_names) { uint64_t num_bytes = 0; for (StatName stat_name : stat_names) { - num_bytes += stat_name.numBytes(); + num_bytes += stat_name.dataSize(); } uint8_t* p = alloc(num_bytes); for (StatName stat_name : stat_names) { - num_bytes = stat_name.numBytes(); + num_bytes = stat_name.dataSize(); memcpy(p, stat_name.data(), num_bytes); p += num_bytes; } } uint8_t* StatNameJoiner::alloc(uint64_t num_bytes) { - bytes_ = std::make_unique(num_bytes + 2); // +2 bytes for the length up to 64k. + bytes_ = std::make_unique(num_bytes + StatNameSizeEncodingBytes); return saveLengthToBytesReturningNext(num_bytes, bytes_.get()); } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index dd27b8066285..f7e07c8a912d 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -28,14 +28,24 @@ namespace Stats { /** A Symbol represents a string-token with a small index. */ using Symbol = uint32_t; -/** Efficient byte-encoded storage an array of tokens, which are typically < 127 */ +/** + * 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. + */ using SymbolStorage = uint8_t[]; +/** + * We encode the byte-size of a StatName as its first two bytes. + */ +constexpr uint64_t StatNameSizeEncodingBytes = 2; +constexpr uint64_t StatNameMaxSize = 1 << (8 * StatNameSizeEncodingBytes); // 65536 + /** Transient representations of a vector of 32-bit symbols */ using SymbolVec = std::vector; -class StatName; - /** * Represents an 8-bit encoding of a vector of symbols, used as a transient * representation during encoding and prior to retained allocation. @@ -67,7 +77,7 @@ class SymbolEncoding { * Returns the number of bytes required to represent StatName as a uint8_t * array, including the encoded size. */ - uint64_t bytesRequired() const { return size() + 2 /* size encoded as 2 bytes */; } + uint64_t bytesRequired() const { return size() + StatNameSizeEncodingBytes; } /** * Returns the number of uint8_t entries we collected while adding symbols. @@ -118,10 +128,10 @@ class SymbolEncoding { * same string is re-encoded, it may or may not encode to the same underlying * symbol. */ -class SymbolTableImpl : public SymbolTable { +class SymbolTable { public: - SymbolTableImpl(); - ~SymbolTableImpl() override; + SymbolTable(); + ~SymbolTable(); /** * Encodes a stat name using the symbol table, returning a SymbolEncoding. The @@ -138,12 +148,12 @@ class SymbolTableImpl : public SymbolTable { * @param name The name to encode. * @return SymbolEncoding the encoded symbols. */ - SymbolEncoding encode(absl::string_view name) override; + SymbolEncoding encode(absl::string_view name); /** * @return uint64_t the number of symbols in the symbol table. */ - uint64_t numSymbols() const override { + uint64_t numSymbols() const { absl::ReaderMutexLock lock(&lock_); ASSERT(encode_map_.size() == decode_map_.size()); return encode_map_.size(); @@ -165,7 +175,7 @@ class SymbolTableImpl : public SymbolTable { * @param b the second stat name * @return bool true if a lexically precedes b. */ - bool lessThan(const StatName& a, const StatName& b) const override; + bool lessThan(const StatName& a, const StatName& b) const; /** * Since SymbolTable does manual reference counting, a client of SymbolTable @@ -175,7 +185,7 @@ class SymbolTableImpl : public SymbolTable { * * @param symbol_vec the vector of symbols to be freed. */ - void free(StatName stat_name) override { adjustRefCount(stat_name, -1); } + void free(const StatName& stat_name); /** * StatName backing-store can be managed by callers in a variety of ways @@ -186,23 +196,25 @@ class SymbolTableImpl : public SymbolTable { * * @param symbol_vec the vector of symbols to be freed. */ - void incRefCount(StatName stat_name) override { adjustRefCount(stat_name, 1); }; + void incRefCount(const StatName& stat_name); #ifndef ENVOY_CONFIG_COVERAGE - void debugPrint() const override; + // It is convenient when debugging to be able to print the state of the table, + // but this code is not hit during tests ordinarily, and is not needed in + // production code. + void debugPrint() const; #endif /** - * 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 hard, since this - * should never happen, and we don't want to continue running with a corrupt stats set. + * 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, since this + * should never happen, and we don't want to continue running with a corrupt + * stats set. * * @param symbol_vec the vector of symbols to decode. * @return std::string the retrieved stat name. */ - std::string decode(const SymbolStorage symbol_vec, uint64_t size) const override; - - bool interoperable(const SymbolTable& other) const override { return &other == this; } + std::string decode(const SymbolStorage symbol_vec, uint64_t size) const; private: friend class StatName; @@ -210,33 +222,16 @@ class SymbolTableImpl : public SymbolTable { struct SharedSymbol { SharedSymbol(Symbol symbol) : symbol_(symbol), ref_count_(1) {} - /* - SharedSymbol(const SharedSymbol& src) : symbol_(src.symbol_), ref_count_(1) { - ASSERT(src.ref_count_ == 1); - } - SharedSymbol& operator=(const SharedSymbol& src) { - if (&src != this) { - ASSERT(src.ref_count_ == 1); - symbol_ = src.symbol_; - ref_count_ = 1; - } - return *this; - } - */ Symbol symbol_; std::atomic ref_count_; - // uint32_t ref_count_; }; - // This must be called during both encode() and free(). - // mutable Thread::MutexBasicLockable lock_;] + // This must be held during both encode() and free(). mutable absl::Mutex lock_; std::string decodeSymbolVec(const SymbolVec& symbols) const; - void adjustRefCount(StatName stat_name, int adjustment); - /** * Convenience function for encode(), symbolizing one string segment at a time. * @@ -281,43 +276,6 @@ class SymbolTableImpl : public SymbolTable { // TODO(ambuc): There might be an optimization here relating to storing ranges of freed symbols // using an Envoy::IntervalSet. std::stack pool_ GUARDED_BY(lock_); - - //#define TRACK_ENCODES -#ifdef TRACK_ENCODES - using Histogram = absl::flat_hash_map; - Histogram histogram_; -#endif -}; - -/** - * 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 elaborted form, and does not require locking the SymbolTable. - * - * The caveat is that this representation does not bump reference counts on - * for 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 acessingm the - * SymbolTable or, in particular, taking its lock. - */ -class StatNameJoiner { -public: - StatNameJoiner(StatName a, StatName b); - StatNameJoiner(const std::vector& stat_names); - - /** - * @return StatName a reference to the joined StatName. - */ - StatName statName() const { return StatName(bytes_.get()); } - -private: - uint8_t* alloc(uint64_t num_bytes); - - std::unique_ptr bytes_; }; /** @@ -356,16 +314,114 @@ class StatNameStorage { /** * @return StatName a reference to the owned storage. */ - StatName statName() const { return StatName(bytes_.get()); } + inline StatName statName() const; + +private: + std::unique_ptr bytes_; +}; + +/** + * Efficiently represents a stat name using a variable-length array of uint8_t. + * This class does not own the backing store for this array; the backing-store + * can be held in StatNameStorage, or it can be packed more tightly into another + * object. + * + * When Envoy is configured with a large numbers of clusters, there are a huge + * number of StatNames, so avoiding extra per-stat pointers has a significant + * memory impact. + */ +class StatName { +public: + // Constructs a StatName object directly referencing the storage of another + // StatName. + explicit StatName(const SymbolStorage size_and_data) : size_and_data_(size_and_data) {} + + // Constructs an empty StatName object. + StatName() : size_and_data_(nullptr) {} - /* - template T append(StatName suffix, std::function f) { - StatNameStorage joiner(statName(), suffix); - f(joiner.statName()); + // Constructs a StatName object with new storage, which must be of size + // src.size(). This is used in the a flow where we first construct a StatName + // for lookup in a cache, and then on a miss need to store the data directly. + StatName(const StatName& src, SymbolStorage memory); + + std::string toString(const SymbolTable& table) const; + + /** + * Note that this hash function will return a different hash than that of + * the elaborated string. + * + * @return uint64_t a hash of the underlying representation. + */ + uint64_t hash() const { + const char* cdata = reinterpret_cast(data()); + return HashUtil::xxHash64(absl::string_view(cdata, dataSize())); + } + + bool operator==(const StatName& rhs) const { + const uint64_t sz = dataSize(); + return sz == rhs.dataSize() && memcmp(data(), rhs.data(), sz * sizeof(uint8_t)) == 0; } - */ + bool operator!=(const StatName& rhs) const { return !(*this == rhs); } + + /** + * @return uint64_t the number of bytes in the symbol array, excluding the two-byte + * overhead for the size itself. + */ + uint64_t dataSize() const { + return size_and_data_[0] | (static_cast(size_and_data_[1]) << 8); + } + + /** + * @return uint64_t the number of bytes in the symbol array, including the two-byte + * overhead for the size itself. + */ + uint64_t size() const { return dataSize() + StatNameSizeEncodingBytes; } + + void copyToStorage(SymbolStorage storage) { memcpy(storage, size_and_data_, size()); } + +#ifndef ENVOY_CONFIG_COVERAGE + void debugPrint(); +#endif + + /** + * @return uint8_t* A pointer to the first byte of data (skipping over size bytes). + */ + const uint8_t* data() const { return size_and_data_ + StatNameSizeEncodingBytes; } + +private: + const uint8_t* size_and_data_; +}; + +StatName StatNameStorage::statName() const { return StatName(bytes_.get()); } + +/** + * 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 elaborted form, and does not require locking the SymbolTable. + * + * The caveat is that this representation does not bump reference counts on + * for 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 acessingm the + * SymbolTable or, in particular, taking its lock. + */ +class StatNameJoiner { +public: + StatNameJoiner(StatName a, StatName b); + StatNameJoiner(const std::vector& stat_names); + + /** + * @return StatName a reference to the joined StatName. + */ + StatName statName() const { return StatName(bytes_.get()); } private: + uint8_t* alloc(uint64_t num_bytes); + std::unique_ptr bytes_; }; diff --git a/source/exe/main_common.h b/source/exe/main_common.h index fa40be73f240..25bc87c0fa01 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -62,7 +62,7 @@ class MainCommonBase { protected: Envoy::OptionsImpl& options_; - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; Server::ComponentFactory& component_factory_; Thread::ThreadFactory& thread_factory_; diff --git a/test/common/http/codes_speed_test.cc b/test/common/http/codes_speed_test.cc index 592b1da5f0d3..e32cb91d4bc6 100644 --- a/test/common/http/codes_speed_test.cc +++ b/test/common/http/codes_speed_test.cc @@ -54,7 +54,7 @@ class CodeUtilitySpeedTest { code_stats_.chargeResponseTiming(info); } - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index e6e5ed6a0670..cfa3a045441a 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -39,7 +39,7 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; @@ -257,7 +257,7 @@ class CodeStatsTest : public testing::Test { return CodeStatsImpl::Join(stat_name_vec).statName().toString(symbol_table_); } - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; CodeStatsImpl code_stats_; }; diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 6f2e9f3380ba..ce5ec0ee8733 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -383,7 +383,7 @@ DEFINE_PROTO_FUZZER(const test::common::http::ConnManagerImplTestCase& input) { FuzzConfig config; NiceMock drain_close; NiceMock random; - Stats::SymbolTableImpl symbol_table; + Stats::SymbolTable symbol_table; Http::ContextImpl http_context(symbol_table); NiceMock runtime; NiceMock local_info; diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 76d74d1a05cf..bc910b3c50da 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -72,6 +72,7 @@ envoy_cc_test( srcs = ["symbol_table_impl_test.cc"], deps = [ ":stat_test_utility_lib", + "//source/common/common:mutex_tracer_lib", "//source/common/memory:stats_lib", "//source/common/stats:symbol_table_lib", "//test/mocks/stats:stats_mocks", diff --git a/test/common/stats/heap_stat_data_test.cc b/test/common/stats/heap_stat_data_test.cc index a0a124360f81..d71f8eb3b125 100644 --- a/test/common/stats/heap_stat_data_test.cc +++ b/test/common/stats/heap_stat_data_test.cc @@ -33,7 +33,7 @@ class HeapStatDataTest : public testing::Test { EXPECT_EQ(0, symbol_table_.numSymbols()); } - SymbolTableImpl symbol_table_; + SymbolTable symbol_table_; HeapStatDataAllocator alloc_; std::vector stat_name_storage_; }; diff --git a/test/common/stats/raw_stat_data_test.cc b/test/common/stats/raw_stat_data_test.cc index 0840d2ed286d..5669a9838ff0 100644 --- a/test/common/stats/raw_stat_data_test.cc +++ b/test/common/stats/raw_stat_data_test.cc @@ -16,7 +16,7 @@ class RawStatDataTest : public testing::Test { RawStatDataTest() : allocator_(stats_options_, symbol_table_) {} StatsOptionsImpl stats_options_; - SymbolTableImpl symbol_table_; + SymbolTable symbol_table_; TestAllocator allocator_; // This is RawStatDataAllocator with some size settings. }; diff --git a/test/common/stats/source_impl_test.cc b/test/common/stats/source_impl_test.cc index d756b1e2ae25..1d8edf4da83b 100644 --- a/test/common/stats/source_impl_test.cc +++ b/test/common/stats/source_impl_test.cc @@ -23,7 +23,7 @@ TEST(SourceImplTest, Caching) { ON_CALL(store, histograms()).WillByDefault(ReturnPointee(&stored_histograms)); SourceImpl source(store); - SymbolTableImpl symbol_table; + SymbolTable symbol_table; // Once cached, new values should not be reflected by the return value. stored_counters.push_back(std::make_shared(symbol_table)); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index ceea9f9b6f65..10f70e755f66 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -1,5 +1,6 @@ #include +#include "common/common/mutex_tracer_impl.h" #include "common/memory/stats.h" #include "common/stats/symbol_table_impl.h" @@ -7,6 +8,7 @@ #include "test/test_common/logging.h" #include "test/test_common/utility.h" +#include "absl/synchronization/blocking_counter.h" #include "gtest/gtest.h" namespace Envoy { @@ -25,7 +27,7 @@ class StatNameTest : public testing::Test { } SymbolVec getSymbols(StatName stat_name) { - return SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.numBytes()); + return SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.dataSize()); } std::string decodeSymbolVec(const SymbolVec& symbol_vec) { return table_.decodeSymbolVec(symbol_vec); @@ -42,7 +44,7 @@ class StatNameTest : public testing::Test { return stat_name_storage_.back().statName(); } - SymbolTableImpl table_; + SymbolTable table_; std::vector stat_name_storage_; }; @@ -56,8 +58,8 @@ TEST_F(StatNameTest, TestArbitrarySymbolRoundtrip) { } } -TEST_F(StatNameTest, TestMillionSymbolsRoundtrip) { - for (int i = 0; i < 1 * 1000 * 1000; ++i) { +TEST_F(StatNameTest, Test100kSymbolsRoundtrip) { + for (int i = 0; i < 100 * 1000; ++i) { const std::string stat_name = absl::StrCat("symbol_", i); EXPECT_EQ(stat_name, encodeDecode(stat_name)); } @@ -314,6 +316,92 @@ TEST_F(StatNameTest, JoinAllEmpty) { EXPECT_EQ("", joiner.statName().toString(table_)); } +namespace { + +// This class functions like absl::Notification except the usage of SignalAll() +// appears to trigger tighter simultaneous wakeups in multiple threads. Note +// that the synchronization mechanism in +// https://github.com/abseil/abseil-cpp/blob/master/absl/synchronization/notification.h +// has timing properties that do not seem to trigger the race condition in +// SymbolTable::toSymbol() where the find() under read-lock fails, but by the +// time the insert() under write-lock occurs, the symbol has been added by +// another thread. +class Notifier { +public: + Notifier() : cond_(false) {} + + void notify() { + absl::MutexLock lock(&mutex_); + cond_ = true; + cond_var_.SignalAll(); + } + + void wait() { + absl::MutexLock lock(&mutex_); + while (!cond_) { + cond_var_.Wait(&mutex_); + } + } + +private: + absl::Mutex mutex_; + bool cond_ GUARDED_BY(mutex_); + absl::CondVar cond_var_; +}; + +} // namespace + +TEST_F(StatNameTest, RacingSymbolCreation) { + Thread::ThreadFactory& thread_factory = Thread::threadFactoryForTest(); + MutexTracerImpl& mutex_tracer = MutexTracerImpl::getOrCreateTracer(); + + // Make 100 threads, each of which will race to encode an overlapping set of + // symbols, triggering corner-cases in SymbolTable::toSymbol. + constexpr int num_threads = 100; + std::vector threads; + threads.reserve(num_threads); + Notifier creation, access, wait; + absl::BlockingCounter creates(num_threads), accesses(num_threads); + for (int i = 0; i < num_threads; ++i) { + threads.push_back( + thread_factory.createThread([this, i, &creation, &access, &wait, &creates, &accesses]() { + // Rotate between 20 different symbols to try to get some + // contention. Based on a logging print statement in + // SymbolTable::toSymbol(), this appears to trigger creation-races, + // even when compiled with optimization. + std::string stat_name_string = absl::StrCat("symbol", i % 20); + + // Block each thread on waking up a common condition variable, + // so we make it likely to race on creation. + creation.wait(); + StatNameTempStorage initial(stat_name_string, table_); + creates.DecrementCount(); + + access.wait(); + StatNameTempStorage second(stat_name_string, table_); + accesses.DecrementCount(); + + wait.wait(); + })); + } + creation.notify(); + creates.Wait(); + + int64_t create_contentions = mutex_tracer.numContentions(); + std::cerr << "Number of contentions: " << create_contentions << std::endl; + + // But when we access the already-existing symbols, we guarantee that no + // further mutex contentions occur. + access.notify(); + accesses.Wait(); + EXPECT_EQ(create_contentions, mutex_tracer.numContentions()); + + wait.notify(); + for (auto& thread : threads) { + thread->join(); + } +} + // Tests the memory savings realized from using symbol tables with 1k clusters. This // test shows the memory drops from almost 8M to less than 2M. TEST(SymbolTableTest, Memory) { @@ -339,7 +427,7 @@ TEST(SymbolTableTest, Memory) { string_mem_used = test_memory_usage(record_stat); } { - SymbolTableImpl table; + SymbolTable table; std::vector names; auto record_stat = [&names, &table](absl::string_view stat) { names.emplace_back(StatNameStorage(stat, table)); @@ -356,12 +444,8 @@ TEST(SymbolTableTest, Memory) { std::cerr << "SymbolTableTest.Memory comparison skipped due to malloc-stats returning 0." << std::endl; } else { - // In manual tests, string memory used 7759488 in this example, and - // symbol-table mem used 1739672. Setting the benchmark at 7759488/4 = - // 1939872, which should allow for some slop and platform dependence - // in the allocation library. - EXPECT_LT(symbol_table_mem_used, string_mem_used / 4); + EXPECT_LT(symbol_table_mem_used, 1740000); // Dec 16, 2018: 1734552 bytes. } } diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 2b2df9da2c1b..6714a19ff291 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -50,7 +50,7 @@ class ThreadLocalStorePerf { } private: - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; Stats::StatsOptionsImpl options_; Event::SimulatedTimeSystem time_system_; Stats::HeapStatDataAllocator heap_alloc_; diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 55f1dc8d4c29..8f9c7de0022d 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -45,7 +45,7 @@ class StatsThreadLocalStoreTest : public testing::Test { store_->addSink(sink_); } - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; StatsOptionsImpl options_; @@ -161,7 +161,7 @@ class HistogramTest : public testing::Test { MOCK_METHOD1(alloc, RawStatData*(const std::string& name)); MOCK_METHOD1(free, void(RawStatData& data)); - SymbolTableImpl symbol_table_; + SymbolTable symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; StatsOptionsImpl options_; @@ -932,7 +932,7 @@ class TruncatingAllocTest : public HeapStatsThreadLocalStoreTest { // Do not call superclass SetUp. } - SymbolTableImpl symbol_table_; + SymbolTable symbol_table_; TestAllocator test_alloc_; std::string long_name_; }; diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index 017a5211760e..f155acd8bba7 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -178,7 +178,7 @@ TEST(UdpStatsdSinkTest, CheckActualStatsWithCustomPrefix) { } TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { - Stats::SymbolTableImpl symbol_table; + Stats::SymbolTable symbol_table; NiceMock source; auto writer_ptr = std::make_shared>(); NiceMock tls_; diff --git a/test/integration/server.cc b/test/integration/server.cc index 114a5809460f..d19a46b24b0d 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -138,7 +138,7 @@ void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion vers // real symbol table through the mocking hierarchy -- which generally // constructs hierarchies of objects with no context, is too daunting. I think // the right thing to do is to avoid mocks in integration tests. - Test::Global symbol_table; + Test::Global symbol_table; Server::HotRestartNopImpl restarter(*symbol_table); Thread::MutexBasicLockable lock; @@ -165,7 +165,7 @@ void IntegrationTestServerImpl::createAndRunEnvoyServer( Network::Address::InstanceConstSharedPtr local_address, TestHooks& hooks, Thread::BasicLockable& access_log_lock, Server::ComponentFactory& component_factory, Runtime::RandomGeneratorPtr&& random_generator) { - Stats::SymbolTableImpl symbol_table; + Stats::SymbolTable symbol_table; Server::HotRestartNopImpl restarter(symbol_table); ThreadLocal::InstanceImpl tls; Stats::HeapStatDataAllocator stats_allocator(symbol_table); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index c918e44005d5..b6de207e191b 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -194,7 +194,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::StatDataAllocator&()); private: - Test::Global symbol_table_; + Test::Global symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::HeapStatDataAllocator stats_allocator_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 29b8726bc390..a5b4240fe566 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -128,7 +128,7 @@ MockStore::MockStore(SymbolTable& symbol_table) MockStore::~MockStore() {} MockIsolatedStatsStore::MockIsolatedStatsStore() - : IsolatedStoreImpl(Test::Global::get()) {} + : IsolatedStoreImpl(Test::Global::get()) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } } // namespace Stats diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 77dd93590372..b6048b14e5e2 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -186,7 +186,7 @@ class MockStore : public Store { SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& symbolTable() const override { return symbol_table_; } - SymbolTableImpl owned_symbol_table_; + SymbolTable owned_symbol_table_; SymbolTable& symbol_table_; testing::NiceMock counter_; std::vector> histograms_; @@ -197,7 +197,7 @@ class MockStore : public Store { * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : private Test::Global, +class MockIsolatedStatsStore : private Test::Global, public IsolatedStoreImpl { public: MockIsolatedStatsStore(); diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index c01b280b280f..2f29edbb7efe 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -44,7 +44,7 @@ TEST(ValidationClusterManagerTest, MockedMethods) { AccessLog::MockAccessLogManager log_manager; const envoy::config::bootstrap::v2::Bootstrap bootstrap; - Stats::SymbolTableImpl symbol_table; + Stats::SymbolTable symbol_table; ClusterManagerPtr cluster_manager = factory.clusterManagerFromProto( bootstrap, stats_store, tls, runtime, random, local_info, log_manager, admin); EXPECT_EQ(nullptr, cluster_manager->httpConnPoolForCluster("cluster", ResourcePriority::Default, diff --git a/test/server/hot_restart_impl_test.cc b/test/server/hot_restart_impl_test.cc index 2c3bad09831d..3fb42d0c155f 100644 --- a/test/server/hot_restart_impl_test.cc +++ b/test/server/hot_restart_impl_test.cc @@ -44,7 +44,7 @@ class HotRestartImplTest : public testing::Test { hot_restart_->drainParentListeners(); } - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; Api::MockOsSysCalls os_sys_calls_; TestThreadsafeSingletonInjector os_calls{&os_sys_calls_}; NiceMock options_; diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 76e9970da258..e7fc2c0dd766 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -62,7 +62,7 @@ class AdminStatsTest : public testing::TestWithParam main_thread_dispatcher_; NiceMock tls_; Stats::StatsOptionsImpl options_; @@ -1196,7 +1196,7 @@ class PrometheusStatsFormatterTest : public testing::Test { gauges_.push_back(alloc_.makeGauge(storage.statName(), name, cluster_tags)); } - Stats::SymbolTableImpl symbol_table_; + Stats::SymbolTable symbol_table_; Stats::HeapStatDataAllocator alloc_; std::vector counters_; std::vector gauges_; diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 19123b30f745..8a311a99ea12 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -434,7 +434,7 @@ class TestAllocator : public RawStatDataAllocator { ~TestAllocator() { EXPECT_EQ(0, hash_set_.size()); } private: - SymbolTableImpl symbol_table_; + SymbolTable symbol_table_; Thread::MutexBasicLockable mutex_; TestBlockMemoryHashSetOptions block_hash_options_; std::unique_ptr block_memory_; From 346e9e503f38282d2c6fc7ae8594190cb1a732db Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 17 Dec 2018 21:19:23 -0500 Subject: [PATCH 035/106] move toString to SymbolTable. Signed-off-by: Joshua Marantz --- source/common/stats/heap_stat_data.cc | 2 +- source/common/stats/isolated_store_impl.cc | 12 ++++----- source/common/stats/metric_impl.cc | 6 ++--- source/common/stats/metric_impl.h | 2 +- source/common/stats/raw_stat_data.h | 2 +- source/common/stats/scope_prefixer.cc | 2 +- source/common/stats/symbol_table_impl.cc | 8 ++---- source/common/stats/symbol_table_impl.h | 6 ++--- source/common/stats/thread_local_store.cc | 6 ++--- source/common/stats/thread_local_store.h | 2 +- test/common/http/codes_test.cc | 2 +- test/common/stats/symbol_table_impl_test.cc | 30 ++++++++++----------- test/mocks/stats/mocks.h | 6 ++--- 13 files changed, 40 insertions(+), 46 deletions(-) diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index 796755b703ac..9ccca38aafd8 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -60,7 +60,7 @@ void HeapStatDataAllocator::free(HeapStatData& data) { void HeapStatDataAllocator::debugPrint() { Thread::LockGuard lock(mutex_); for (HeapStatData* heap_stat_data : stats_) { - std::cout << heap_stat_data->statName().toString(symbolTable()) << std::endl; + std::cout << symbolTable().toString(heap_stat_data->statName()) << std::endl; } std::cout << std::flush; } diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 4cea30b08c16..6307c8fb220a 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -23,14 +23,14 @@ IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) : symbol_table_(symbol_table), alloc_(symbol_table_), counters_([this](StatName name) -> CounterSharedPtr { - return alloc_.makeCounter(name, name.toString(alloc_.symbolTable()), std::vector()); - }), + return alloc_.makeCounter(name, alloc_.symbolTable().toString(name), std::vector()); + }), gauges_([this](StatName name) -> GaugeSharedPtr { - return alloc_.makeGauge(name, name.toString(alloc_.symbolTable()), std::vector()); - }), + return alloc_.makeGauge(name, alloc_.symbolTable().toString(name), std::vector()); + }), histograms_([this](StatName name) -> HistogramSharedPtr { - return std::make_shared(name, *this, name.toString(alloc_.symbolTable()), - std::vector()); + return std::make_shared(name, *this, alloc_.symbolTable().toString(name), + std::vector()); }) {} ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 8ade71a858a8..81976dc5abc4 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -63,7 +63,7 @@ MetricImpl::~MetricImpl() { } std::string MetricImpl::tagExtractedName() const { - return tagExtractedStatName().toString(symbolTable()); + return symbolTable().toString(tagExtractedStatName()); } StatName MetricImpl::tagExtractedStatName() const { return StatName(&storage_[1]); } @@ -80,10 +80,10 @@ std::vector MetricImpl::tags() const { for (size_t i = 0; i < num_tags; ++i) { Tag tag; StatName name(p); - tag.name_ = name.toString(symbol_table); + tag.name_ = symbol_table.toString(name); p += name.size(); StatName value(p); - tag.value_ = value.toString(symbol_table); + tag.value_ = symbol_table.toString(value); p += value.size(); tags.emplace_back(tag); } diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index 05add1d947f1..ce04f40a1d58 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -26,7 +26,7 @@ class MetricImpl : public virtual Metric { SymbolTable& symbol_table); ~MetricImpl(); - std::string name() const override { return statName().toString(symbolTable()); } + std::string name() const override { return symbolTable().toString(statName()); } std::string tagExtractedName() const override; std::vector tags() const override; diff --git a/source/common/stats/raw_stat_data.h b/source/common/stats/raw_stat_data.h index 428a80ebe93a..3c4461f0f417 100644 --- a/source/common/stats/raw_stat_data.h +++ b/source/common/stats/raw_stat_data.h @@ -119,7 +119,7 @@ class RawStatDataAllocator : public StatDataAllocatorImpl { virtual RawStatData* alloc(absl::string_view name); // Virtual only for mocking. void free(Stats::RawStatData& data) override; RawStatData* allocStatName(StatName stat_name) { - return alloc(stat_name.toString(symbolTable())); + return alloc(symbolTable().toString(stat_name)); } // StatDataAllocator diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index a233b1911f22..bde5b999be2c 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -30,7 +30,7 @@ ScopePrefixer::~ScopePrefixer() { ScopePtr ScopePrefixer::createScope(const std::string& name) { // StatNameStorage scope_stat_name(name, symbolTable()); // Takes a lock. // StatNameStorage joiner(prefix_.statName(), scope_stat_name.statName()); - return std::make_unique(prefix_.statName().toString(symbolTable()) + "." + name, + return std::make_unique(symbolTable().toString(prefix_.statName()) + "." + name, *scope_); } diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index a2135cecc156..2902bbd70736 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -17,10 +17,6 @@ StatName::StatName(const StatName& src, SymbolStorage memory) : size_and_data_(m memcpy(memory, src.size_and_data_, src.size()); } -std::string StatName::toString(const SymbolTable& table) const { - return table.decode(data(), dataSize()); -} - #ifndef ENVOY_CONFIG_COVERAGE void StatName::debugPrint() { if (size_and_data_ == nullptr) { @@ -143,8 +139,8 @@ SymbolEncoding SymbolTable::encode(const absl::string_view name) { return encoding; } -std::string SymbolTable::decode(const SymbolStorage symbol_array, uint64_t size) const { - return decodeSymbolVec(SymbolEncoding::decodeSymbols(symbol_array, size)); +std::string SymbolTable::toString(const StatName& stat_name) const { + return decodeSymbolVec(SymbolEncoding::decodeSymbols(stat_name.data(), stat_name.dataSize())); } std::string SymbolTable::decodeSymbolVec(const SymbolVec& symbols) const { diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index f7e07c8a912d..627a8878ab13 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -211,10 +211,10 @@ class SymbolTable { * should never happen, and we don't want to continue running with a corrupt * stats set. * - * @param symbol_vec the vector of symbols to decode. + * @param stat_name symbol_vec the vector of symbols to decode. * @return std::string the retrieved stat name. */ - std::string decode(const SymbolStorage symbol_vec, uint64_t size) const; + std::string toString(const StatName& stat_name) const; private: friend class StatName; @@ -344,8 +344,6 @@ class StatName { // for lookup in a cache, and then on a miss need to store the data directly. StatName(const StatName& src, SymbolStorage memory); - std::string toString(const SymbolTable& table) const; - /** * Note that this hash function will return a different hash than that of * the elaborated string. diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index df9f5b8e4a48..1794c13b0e65 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -87,7 +87,7 @@ bool ThreadLocalStoreImpl::rejects(StatName stat_name) const { // Also note that the elaboration of the stat-name into a string is expensive, // so I think it might be better to move the matcher test until after caching, // unless its acceptsAll/rejectsAll. - return stats_matcher_->rejectsAll() || stats_matcher_->rejects(stat_name.toString(symbolTable())); + return stats_matcher_->rejectsAll() || stats_matcher_->rejects(symbolTable().toString(stat_name)); } std::vector ThreadLocalStoreImpl::counters() const { @@ -273,7 +273,7 @@ void ThreadLocalStoreImpl::ScopeImpl::extractTagsAndTruncate( class TagExtraction { public: TagExtraction(ThreadLocalStoreImpl& tls, StatName name) { - std::string name_str = name.toString(tls.symbolTable()); + std::string name_str = tls.symbolTable().toString(name); tag_extracted_name_ = tls.tagProducer().produceTags(name_str, tags_); } @@ -473,7 +473,7 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(StatName name, std::vector tags; std::string tag_extracted_name = - parent_.tagProducer().produceTags(name.toString(symbolTable()), tags); + parent_.tagProducer().produceTags(symbolTable().toString(name), tags); TlsHistogramSharedPtr hist_tls_ptr = std::make_shared(name, tag_extracted_name, tags, symbolTable()); diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 2374155bb273..51368eb27ee0 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -206,7 +206,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Histogram& tlsHistogram(StatName name, ParentHistogramImpl& parent) override; const Stats::StatsOptions& statsOptions() const override { return parent_.statsOptions(); } ScopePtr createScope(const std::string& name) override { - return parent_.createScope(prefix_.statName().toString(symbolTable()) + "." + name); + return parent_.createScope(symbolTable().toString(prefix_.statName()) + "." + name); } const SymbolTable& symbolTable() const override { return parent_.symbolTable(); } SymbolTable& symbolTable() override { return parent_.symbolTable(); } diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index cfa3a045441a..55bf79f79921 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -254,7 +254,7 @@ class CodeStatsTest : public testing::Test { storage_vec.emplace_back(std::make_unique(str, symbol_table_)); stat_name_vec.push_back(storage_vec.back()->statName()); } - return CodeStatsImpl::Join(stat_name_vec).statName().toString(symbol_table_); + return symbol_table_.toString(CodeStatsImpl::Join(stat_name_vec).statName()); } Stats::SymbolTable symbol_table_; diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 10f70e755f66..729e8f832834 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -34,7 +34,7 @@ class StatNameTest : public testing::Test { } Symbol monotonicCounter() { return table_.monotonicCounter(); } std::string encodeDecode(absl::string_view stat_name) { - return makeStat(stat_name).toString(table_); + return table_.toString(makeStat(stat_name)); } StatNameStorage makeStatStorage(absl::string_view name) { return StatNameStorage(name, table_); } @@ -83,8 +83,8 @@ TEST_F(StatNameTest, TestSuccessfulDecode) { std::string stat_name = "foo.bar.baz"; StatName stat_name_1(makeStat(stat_name)); StatName stat_name_2(makeStat(stat_name)); - EXPECT_EQ(stat_name_1.toString(table_), stat_name_2.toString(table_)); - EXPECT_EQ(stat_name_1.toString(table_), stat_name); + EXPECT_EQ(table_.toString(stat_name_1), table_.toString(stat_name_2)); + EXPECT_EQ(table_.toString(stat_name_1), stat_name); } TEST_F(StatNameTest, TestBadDecodes) { @@ -108,7 +108,7 @@ TEST_F(StatNameTest, TestBadDecodes) { TEST_F(StatNameTest, TestDifferentStats) { StatName stat_name_1(makeStat("foo.bar")); StatName stat_name_2(makeStat("bar.foo")); - EXPECT_NE(stat_name_1.toString(table_), stat_name_2.toString(table_)); + EXPECT_NE(table_.toString(stat_name_1), table_.toString(stat_name_2)); EXPECT_NE(stat_name_1, stat_name_2); } @@ -234,8 +234,8 @@ TEST_F(StatNameTest, StoringWithoutStatNameStorage) { StatName hello(storage.get()); StatName goodbye(storage.get() + goodbye_offset); - EXPECT_EQ("hello.world", hello.toString(table_)); - EXPECT_EQ("goodbye.world", goodbye.toString(table_)); + EXPECT_EQ("hello.world", table_.toString(hello)); + EXPECT_EQ("goodbye.world", table_.toString(goodbye)); // If we don't explicitly call free() on the the StatName objects the // SymbolTable will assert on destruction. @@ -273,47 +273,47 @@ TEST_F(StatNameTest, Sort) { TEST_F(StatNameTest, Concat2) { StatNameJoiner joiner(makeStat("a.b"), makeStat("c.d")); - EXPECT_EQ("a.b.c.d", joiner.statName().toString(table_)); + EXPECT_EQ("a.b.c.d", table_.toString(joiner.statName())); } TEST_F(StatNameTest, ConcatFirstEmpty) { StatNameJoiner joiner(makeStat(""), makeStat("c.d")); - EXPECT_EQ("c.d", joiner.statName().toString(table_)); + EXPECT_EQ("c.d", table_.toString(joiner.statName())); } TEST_F(StatNameTest, ConcatSecondEmpty) { StatNameJoiner joiner(makeStat("a.b"), makeStat("")); - EXPECT_EQ("a.b", joiner.statName().toString(table_)); + EXPECT_EQ("a.b", table_.toString(joiner.statName())); } TEST_F(StatNameTest, ConcatAllEmpty) { StatNameJoiner joiner(makeStat(""), makeStat("")); - EXPECT_EQ("", joiner.statName().toString(table_)); + EXPECT_EQ("", table_.toString(joiner.statName())); } TEST_F(StatNameTest, Join3) { StatNameJoiner joiner({makeStat("a.b"), makeStat("c.d"), makeStat("e.f")}); - EXPECT_EQ("a.b.c.d.e.f", joiner.statName().toString(table_)); + EXPECT_EQ("a.b.c.d.e.f", table_.toString(joiner.statName())); } TEST_F(StatNameTest, Join3FirstEmpty) { StatNameJoiner joiner({makeStat(""), makeStat("c.d"), makeStat("e.f")}); - EXPECT_EQ("c.d.e.f", joiner.statName().toString(table_)); + EXPECT_EQ("c.d.e.f", table_.toString(joiner.statName())); } TEST_F(StatNameTest, Join3SecondEmpty) { StatNameJoiner joiner({makeStat("a.b"), makeStat(""), makeStat("e.f")}); - EXPECT_EQ("a.b.e.f", joiner.statName().toString(table_)); + EXPECT_EQ("a.b.e.f", table_.toString(joiner.statName())); } TEST_F(StatNameTest, Join3ThirdEmpty) { StatNameJoiner joiner({makeStat("a.b"), makeStat("c.d"), makeStat("")}); - EXPECT_EQ("a.b.c.d", joiner.statName().toString(table_)); + EXPECT_EQ("a.b.c.d", table_.toString(joiner.statName())); } TEST_F(StatNameTest, JoinAllEmpty) { StatNameJoiner joiner({makeStat(""), makeStat(""), makeStat("")}); - EXPECT_EQ("", joiner.statName().toString(table_)); + EXPECT_EQ("", table_.toString(joiner.statName())); } namespace { diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index b6048b14e5e2..c1b00e1cc619 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -179,9 +179,9 @@ class MockStore : public Store { MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); - Counter& counterx(StatName name) override { return counter(name.toString(symbol_table_)); } - Gauge& gaugex(StatName name) override { return gauge(name.toString(symbol_table_)); } - Histogram& histogramx(StatName name) override { return histogram(name.toString(symbol_table_)); } + Counter& counterx(StatName name) override { return counter(symbol_table_.toString(name)); } + Gauge& gaugex(StatName name) override { return gauge(symbol_table_.toString(name)); } + Histogram& histogramx(StatName name) override { return histogram(symbol_table_.toString(name)); } SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& symbolTable() const override { return symbol_table_; } From 889f8197a4f06a9e7faa36d46e7285c79f3c3cbf Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 24 Dec 2018 23:27:41 -0500 Subject: [PATCH 036/106] format Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 10 +++++----- test/common/stats/thread_local_store_speed_test.cc | 5 +++-- test/mocks/server/mocks.cc | 2 +- test/mocks/stats/mocks.h | 3 +-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 6307c8fb220a..1651bdd787fd 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -23,14 +23,14 @@ IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) : symbol_table_(symbol_table), alloc_(symbol_table_), counters_([this](StatName name) -> CounterSharedPtr { - return alloc_.makeCounter(name, alloc_.symbolTable().toString(name), std::vector()); - }), + return alloc_.makeCounter(name, alloc_.symbolTable().toString(name), std::vector()); + }), gauges_([this](StatName name) -> GaugeSharedPtr { return alloc_.makeGauge(name, alloc_.symbolTable().toString(name), std::vector()); - }), + }), histograms_([this](StatName name) -> HistogramSharedPtr { - return std::make_shared(name, *this, alloc_.symbolTable().toString(name), - std::vector()); + return std::make_shared(name, *this, alloc_.symbolTable().toString(name), + std::vector()); }) {} ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 5664e427eb83..5a6abcebc9a9 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -20,8 +20,9 @@ namespace Envoy { class ThreadLocalStorePerf { public: - ThreadLocalStorePerf() : heap_alloc_(symbol_table_), - store_(options_, heap_alloc_), api_(Api::createApiForTest(store_)) { + ThreadLocalStorePerf() + : heap_alloc_(symbol_table_), store_(options_, heap_alloc_), + api_(Api::createApiForTest(store_)) { store_.setTagProducer(std::make_unique(stats_config_)); Stats::TestUtil::forEachSampleStat(1000, [this](absl::string_view name) { diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index f581d3ddb3b5..273e32159ef0 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -122,7 +122,7 @@ MockWorker::~MockWorker() {} MockInstance::MockInstance() : secret_manager_(new Secret::SecretManagerImpl()), cluster_manager_(timeSystem()), ssl_context_manager_(timeSystem()), singleton_manager_(new Singleton::ManagerImpl( - Thread::threadFactoryForTest().currentThreadId())), + Thread::threadFactoryForTest().currentThreadId())), http_context_(stats_store_.symbolTable()) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index c1b00e1cc619..3983d4428ebd 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -197,8 +197,7 @@ class MockStore : public Store { * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : private Test::Global, - public IsolatedStoreImpl { +class MockIsolatedStatsStore : private Test::Global, public IsolatedStoreImpl { public: MockIsolatedStatsStore(); ~MockIsolatedStatsStore(); From cc87f9217894db46febec2b76d3d696e6bb3fe51 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 25 Dec 2018 16:42:21 -0500 Subject: [PATCH 037/106] fix tsan error (from a stale version of a test due to a botched git merge). Signed-off-by: Joshua Marantz --- test/common/stats/symbol_table_impl_test.cc | 86 --------------------- 1 file changed, 86 deletions(-) diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 7ef678cbf1e6..990be0a3bf9d 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -316,92 +316,6 @@ TEST_F(StatNameTest, JoinAllEmpty) { EXPECT_EQ("", table_.toString(joiner.statName())); } -namespace { - -// This class functions like absl::Notification except the usage of SignalAll() -// appears to trigger tighter simultaneous wakeups in multiple threads. Note -// that the synchronization mechanism in -// https://github.com/abseil/abseil-cpp/blob/master/absl/synchronization/notification.h -// has timing properties that do not seem to trigger the race condition in -// SymbolTable::toSymbol() where the find() under read-lock fails, but by the -// time the insert() under write-lock occurs, the symbol has been added by -// another thread. -class Notifier { -public: - Notifier() : cond_(false) {} - - void notify() { - absl::MutexLock lock(&mutex_); - cond_ = true; - cond_var_.SignalAll(); - } - - void wait() { - absl::MutexLock lock(&mutex_); - while (!cond_) { - cond_var_.Wait(&mutex_); - } - } - -private: - absl::Mutex mutex_; - bool cond_ GUARDED_BY(mutex_); - absl::CondVar cond_var_; -}; - -} // namespace - -TEST_F(StatNameTest, RacingSymbolCreation) { - Thread::ThreadFactory& thread_factory = Thread::threadFactoryForTest(); - MutexTracerImpl& mutex_tracer = MutexTracerImpl::getOrCreateTracer(); - - // Make 100 threads, each of which will race to encode an overlapping set of - // symbols, triggering corner-cases in SymbolTable::toSymbol. - constexpr int num_threads = 100; - std::vector threads; - threads.reserve(num_threads); - Notifier creation, access, wait; - absl::BlockingCounter creates(num_threads), accesses(num_threads); - for (int i = 0; i < num_threads; ++i) { - threads.push_back( - thread_factory.createThread([this, i, &creation, &access, &wait, &creates, &accesses]() { - // Rotate between 20 different symbols to try to get some - // contention. Based on a logging print statement in - // SymbolTable::toSymbol(), this appears to trigger creation-races, - // even when compiled with optimization. - std::string stat_name_string = absl::StrCat("symbol", i % 20); - - // Block each thread on waking up a common condition variable, - // so we make it likely to race on creation. - creation.wait(); - StatNameTempStorage initial(stat_name_string, table_); - creates.DecrementCount(); - - access.wait(); - StatNameTempStorage second(stat_name_string, table_); - accesses.DecrementCount(); - - wait.wait(); - })); - } - creation.notify(); - creates.Wait(); - - int64_t create_contentions = mutex_tracer.numContentions(); - std::cerr << "Number of contentions: " << create_contentions << std::endl; - - // But when we access the already-existing symbols, we guarantee that no - // further mutex contentions occur. - access.notify(); - accesses.Wait(); - EXPECT_EQ(create_contentions, mutex_tracer.numContentions()); - - wait.notify(); - for (auto& thread : threads) { - thread->join(); - } -} - TEST_F(StatNameTest, MutexContentionOnExistingSymbols) { Thread::ThreadFactory& thread_factory = Thread::threadFactoryForTest(); MutexTracerImpl& mutex_tracer = MutexTracerImpl::getOrCreateTracer(); From f81f794d0bfa3ccd33c75add76bd78aedfe554bc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 27 Dec 2018 11:33:13 -0500 Subject: [PATCH 038/106] Re-order member variables in the hope of improving test flakes due to lifetime issues. Signed-off-by: Joshua Marantz --- source/common/router/router.h | 2 +- test/common/http/async_client_impl_test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/common/router/router.h b/source/common/router/router.h index 87eb79eab329..cdc6b938fbb0 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -149,7 +149,7 @@ class Filter : Logger::Loggable, public Upstream::LoadBalancerContextBase { public: explicit Filter(FilterConfig& config); - ~Filter(); + ~Filter() override; // Http::StreamFilterBase void onDestroy() override; diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 8ba69c990141..c05aa5d896a5 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -63,6 +63,7 @@ class AsyncClientImplTest : public testing::Test { } MessagePtr message_{new RequestMessageImpl()}; + Stats::MockIsolatedStatsStore stats_store_; MockAsyncClientCallbacks callbacks_; MockAsyncClientStreamCallbacks stream_callbacks_; NiceMock cm_; @@ -72,7 +73,6 @@ class AsyncClientImplTest : public testing::Test { NiceMock dispatcher_; NiceMock runtime_; NiceMock random_; - Stats::MockIsolatedStatsStore stats_store_; NiceMock local_info_; Http::ContextImpl http_context_; AsyncClientImpl client_; From 5fe26f5b987465269efdd744dee68305548c0c22 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Jan 2019 11:25:42 -0500 Subject: [PATCH 039/106] Move the aggregated stat-name storage mechanism out of MetricImpl and into its own SymbolTable helper class. Signed-off-by: Joshua Marantz --- source/common/stats/metric_impl.cc | 104 +++++++++-------------- source/common/stats/metric_impl.h | 2 +- source/common/stats/symbol_table_impl.cc | 51 +++++++++++ source/common/stats/symbol_table_impl.h | 12 +++ 4 files changed, 105 insertions(+), 64 deletions(-) diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 81976dc5abc4..7177b331ca62 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -7,86 +7,64 @@ namespace Envoy { namespace Stats { +MetricImpl::~MetricImpl() { + // The storage must be cleaned by a subclass of MetricImpl in its + // destructor, because the symbol-table is owned by the subclass. + // Simply call MetricImpl::clear() in the subclass dtor. + ASSERT(!stat_names_.populated()); +} + MetricImpl::MetricImpl(absl::string_view tag_extracted_name, const std::vector& tags, SymbolTable& symbol_table) { - ASSERT(tags.size() < 256); - // Encode all the names and tags into transient storage so we can count the // required bytes. - SymbolEncoding tag_extracted_stat_name = symbol_table.encode(tag_extracted_name); - size_t total_size = 1 /* for tags.size() */ + tag_extracted_stat_name.bytesRequired(); - std::vector> sym_vecs; - sym_vecs.resize(tags.size()); + std::vector names; + names.resize(1 /* tag_extracted_name */ + 2 * tags.size()); + names[0] = tag_extracted_name; int index = 0; - for (auto tag : tags) { - auto& vecs = sym_vecs[index++]; - SymbolEncoding x = symbol_table.encode(tag.name_); - vecs.first.swap(x); - SymbolEncoding y = symbol_table.encode(tag.value_); - vecs.second.swap(y); - total_size += vecs.first.bytesRequired() + vecs.second.bytesRequired(); - } - storage_ = std::make_unique(total_size); - uint8_t* p = &storage_[0]; - *p++ = tags.size(); - p += tag_extracted_stat_name.moveToStorage(p); - for (auto& sym_vec_pair : sym_vecs) { - p += sym_vec_pair.first.moveToStorage(p); - p += sym_vec_pair.second.moveToStorage(p); - } - ASSERT(p == &storage_[0] + total_size); -} - -void MetricImpl::clear() { - SymbolTable& symbol_table = symbolTable(); - uint8_t* p = &storage_[0]; - uint32_t num_tags = *p++; - p += tagExtractedStatName().size(); - symbol_table.free(tagExtractedStatName()); - for (size_t i = 0; i < num_tags; ++i) { - Tag tag; - StatName name(p); - p += name.size(); - symbol_table.free(name); - StatName value(p); - p += value.size(); - symbol_table.free(value); + for (auto& tag : tags) { + names[++index] = tag.name_; + names[++index] = tag.value_; } - storage_.reset(); + stat_names_.populate(names, symbol_table); } -MetricImpl::~MetricImpl() { - // The storage must be cleaned by a subclass of MetricImpl in its - // destructor, because the symbol-table is owned by the subclass. - // Simply call MetricImpl::clear() in the subclass dtor. - ASSERT(storage_ == nullptr); -} +void MetricImpl::clear() { stat_names_.clear(symbolTable()); } std::string MetricImpl::tagExtractedName() const { return symbolTable().toString(tagExtractedStatName()); } -StatName MetricImpl::tagExtractedStatName() const { return StatName(&storage_[1]); } +StatName MetricImpl::tagExtractedStatName() const { + StatName stat_name; + stat_names_.foreach ([&stat_name](StatName s) -> bool { + stat_name = s; + return false; + }); + return stat_name; +} std::vector MetricImpl::tags() const { - uint8_t* p = &storage_[0]; - uint32_t num_tags = *p++; - p += tagExtractedStatName().size(); - std::vector tags; - tags.reserve(num_tags); + enum { TagExtractedName, Name, Value } state; + Tag tag; const SymbolTable& symbol_table = symbolTable(); - - for (size_t i = 0; i < num_tags; ++i) { - Tag tag; - StatName name(p); - tag.name_ = symbol_table.toString(name); - p += name.size(); - StatName value(p); - tag.value_ = symbol_table.toString(value); - p += value.size(); - tags.emplace_back(tag); - } + stat_names_.foreach ([&tags, &state, &tag, &symbol_table](StatName stat_name) -> bool { + switch (state) { + case TagExtractedName: + state = Name; + break; + case Name: + tag.name_ = symbol_table.toString(stat_name); + state = Value; + break; + case Value: + tag.value_ = symbol_table.toString(stat_name); + tags.emplace_back(tag); + state = Name; + } + return true; + }); return tags; } diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index ce04f40a1d58..314118f6f95e 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -43,7 +43,7 @@ class MetricImpl : public virtual Metric { private: StatName tagExtractedStatName() const; - std::unique_ptr storage_; + StatNameList stat_names_; }; class NullMetricImpl : public MetricImpl { diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index ef6b42ae2ea2..d89e99887449 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -336,5 +336,56 @@ uint8_t* StatNameJoiner::alloc(uint64_t num_bytes) { return saveLengthToBytesReturningNext(num_bytes, bytes_.get()); } +StatNameList::~StatNameList() { ASSERT(!populated()); } + +void StatNameList::populate(const std::vector& names, + SymbolTable& symbol_table) { + ASSERT(names.size() < 256); + + // First encode all the names. + size_t total_size_bytes = 1; /* one byte for holding the number of names */ + std::vector encodings; + encodings.resize(names.size()); + int index = 0; + for (auto& name : names) { + SymbolEncoding encoding = symbol_table.encode(name); + total_size_bytes += encoding.bytesRequired(); + encodings[index++].swap(encoding); + } + + // Now allocate the exact number of bytes required and move the encodings + // into storage. + storage_ = std::make_unique(total_size_bytes); + uint8_t* p = &storage_[0]; + *p++ = encodings.size(); + for (auto& encoding : encodings) { + p += encoding.moveToStorage(p); + } + ASSERT(p == &storage_[0] + total_size_bytes); +} + +void StatNameList::foreach (std::function f) const { + uint8_t* p = &storage_[0]; + uint32_t num_elements = *p++; + for (uint32_t i = 0; i < num_elements; ++i) { + StatName stat_name(p); + p += stat_name.size(); + if (!f(stat_name)) { + break; + } + } +} + +void StatNameList::clear(SymbolTable& symbol_table) { + uint8_t* p = &storage_[0]; + uint32_t num_elements = *p++; + for (uint32_t i = 0; i < num_elements; ++i) { + StatName stat_name(p); + p += stat_name.size(); + symbol_table.free(stat_name); + } + storage_.reset(); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 8d90080ff379..aff92a55b4ca 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -497,5 +497,17 @@ struct StatNameLessThan { const SymbolTable& symbol_table_; }; +class StatNameList { +public: + ~StatNameList(); + void populate(const std::vector& encodings, SymbolTable& symbol_table); + bool populated() const { return storage_ != nullptr; } + void foreach (std::function) const; + void clear(SymbolTable& symbol_table); + +private: + std::unique_ptr storage_; +}; + } // namespace Stats } // namespace Envoy From 0b39814efe08d0b258c0da3868014deddfb519c1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Jan 2019 12:10:01 -0500 Subject: [PATCH 040/106] Got compile to work. Signed-off-by: Joshua Marantz --- .../filters/http/ext_authz/ext_authz_test.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index 1efcaf9ca7d2..e7543950ef86 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -47,6 +47,8 @@ namespace ExtAuthz { template class HttpFilterTestBase : public T { public: + HttpFilterTestBase() : http_context_(stats_store_.symbolTable()) {} + void initialize(std::string&& yaml) { envoy::config::filter::http::ext_authz::v2::ExtAuthz proto_config{}; if (!yaml.empty()) { @@ -82,7 +84,10 @@ template class HttpFilterTestBase : public T { } }; -class HttpFilterTest : public HttpFilterTestBase {}; +class HttpFilterTest : public HttpFilterTestBase { + public: + HttpFilterTest() = default; +}; typedef envoy::config::filter::http::ext_authz::v2::ExtAuthz CreateFilterConfigFunc(); @@ -157,8 +162,8 @@ class HttpExtAuthzFilterTestBase { public: HttpExtAuthzFilterTestBase() : http_context_(stats_store_.symbolTable()) {} - void initConfig(envoy::config::filter::http::ext_authz::v2alpha::ExtAuthz& proto_config) { - config_ = std::make_unique(proto_config, local_info_, stats_store_, runtime_, cm_, + void initConfig(envoy::config::filter::http::ext_authz::v2::ExtAuthz& proto_config) { + config_ = std::make_unique(proto_config, local_info_, stats_store_, runtime_, http_context_); } @@ -177,18 +182,20 @@ class HttpExtAuthzFilterTestBase { NiceMock connection_; Http::ContextImpl http_context_; + /* initialize(R"EOF( grpc_service: envoy_grpc: cluster_name: "ext_authz_server" failure_mode_allow: false )EOF"); + */ }; class HttpExtAuthzFilterTest : public testing::Test, public HttpExtAuthzFilterTestBase { public: void initialize(const std::string yaml) { - envoy::config::filter::http::ext_authz::v2alpha::ExtAuthz proto_config{}; + envoy::config::filter::http::ext_authz::v2::ExtAuthz proto_config{}; MessageUtil::loadFromYaml(yaml, proto_config); initConfig(proto_config); } From 665897f25c7cffda999e24335978c603ef5603b7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 27 Jan 2019 22:03:14 -0500 Subject: [PATCH 041/106] Get tests to work. Signed-off-by: Joshua Marantz --- source/common/stats/metric_impl.cc | 2 +- .../filters/http/ext_authz/ext_authz_test.cc | 42 ++++++------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 7177b331ca62..437f7b6cf322 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -46,7 +46,7 @@ StatName MetricImpl::tagExtractedStatName() const { std::vector MetricImpl::tags() const { std::vector tags; - enum { TagExtractedName, Name, Value } state; + enum { TagExtractedName, Name, Value } state = TagExtractedName; Tag tag; const SymbolTable& symbol_table = symbolTable(); stat_names_.foreach ([&tags, &state, &tag, &symbol_table](StatName stat_name) -> bool { diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index e7543950ef86..731d55d87ccb 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -69,7 +69,7 @@ template class HttpFilterTestBase : public T { Filters::Common::ExtAuthz::RequestCallbacks* request_callbacks_{}; Http::TestHeaderMapImpl request_headers_; Buffer::OwnedImpl data_; - Stats::IsolatedStoreImpl stats_store_; + NiceMock stats_store_; NiceMock runtime_; NiceMock cm_; NiceMock local_info_; @@ -85,7 +85,7 @@ template class HttpFilterTestBase : public T { }; class HttpFilterTest : public HttpFilterTestBase { - public: +public: HttpFilterTest() = default; }; @@ -158,41 +158,15 @@ TEST_F(HttpFilterTest, MergeConfig) { EXPECT_EQ("value", merged_extensions.at("key")); } -class HttpExtAuthzFilterTestBase { +class HttpExtAuthzFilterTestBase : public HttpFilterTest { public: - HttpExtAuthzFilterTestBase() : http_context_(stats_store_.symbolTable()) {} - void initConfig(envoy::config::filter::http::ext_authz::v2::ExtAuthz& proto_config) { config_ = std::make_unique(proto_config, local_info_, stats_store_, runtime_, http_context_); } - - FilterConfigSharedPtr config_; - Filters::Common::ExtAuthz::MockClient* client_; - std::unique_ptr filter_; - NiceMock filter_callbacks_; - Filters::Common::ExtAuthz::RequestCallbacks* request_callbacks_{}; - Http::TestHeaderMapImpl request_headers_; - Buffer::OwnedImpl data_; - NiceMock stats_store_; - NiceMock runtime_; - NiceMock cm_; - NiceMock local_info_; - Network::Address::InstanceConstSharedPtr addr_; - NiceMock connection_; - Http::ContextImpl http_context_; - - /* - initialize(R"EOF( - grpc_service: - envoy_grpc: - cluster_name: "ext_authz_server" - failure_mode_allow: false - )EOF"); - */ }; -class HttpExtAuthzFilterTest : public testing::Test, public HttpExtAuthzFilterTestBase { +class HttpExtAuthzFilterTest : public HttpExtAuthzFilterTestBase { public: void initialize(const std::string yaml) { envoy::config::filter::http::ext_authz::v2::ExtAuthz proto_config{}; @@ -205,6 +179,14 @@ class HttpExtAuthzFilterTest : public testing::Test, public HttpExtAuthzFilterTe // that the request is not allowed to continue. TEST_F(HttpFilterTest, ErrorFailClose) { InSequence s; + + initialize(R"EOF( + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + failure_mode_allow: false + )EOF"); + ON_CALL(filter_callbacks_, connection()).WillByDefault(Return(&connection_)); EXPECT_CALL(connection_, remoteAddress()).WillOnce(ReturnRef(addr_)); EXPECT_CALL(connection_, localAddress()).WillOnce(ReturnRef(addr_)); From 1028dbeeded101ffb7009a74fac488c6d05c75ee Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 3 Feb 2019 15:48:34 -0500 Subject: [PATCH 042/106] format Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 28 ++++++++----------- source/common/http/codes.h | 4 +-- source/common/stats/scope_prefixer.cc | 12 ++++---- .../upstream/cluster_manager_impl_test.cc | 4 +-- test/mocks/stats/mocks.h | 3 +- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 23cbadd3d963..7f82fc588856 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -51,14 +51,14 @@ Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { return storage_.back().statName(); } -void CodeStatsImpl::incCounter( - Stats::Scope& scope, const std::vector& names) const { +void CodeStatsImpl::incCounter(Stats::Scope& scope, + const std::vector& names) const { Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); scope.counterx(Stats::StatName(stat_name_storage.get())).inc(); } -void CodeStatsImpl::recordHistogram( - Stats::Scope& scope, const std::vector& names, uint64_t count) const { +void CodeStatsImpl::recordHistogram(Stats::Scope& scope, const std::vector& names, + uint64_t count) const { Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); scope.histogramx(Stats::StatName(stat_name_storage.get())).recordValue(count); } @@ -85,9 +85,9 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) const { Stats::StatName rq_code = upstream_rq_.statName(code); auto write_category = [this, prefix, rq_group, rq_code, &info](Stats::StatName category) { - incCounter(info.cluster_scope_, {prefix, category, upstream_rq_completed_}); - incCounter(info.cluster_scope_, {prefix, category, rq_group}); - incCounter(info.cluster_scope_, {prefix, category, rq_code}); + incCounter(info.cluster_scope_, {prefix, category, upstream_rq_completed_}); + incCounter(info.cluster_scope_, {prefix, category, rq_group}); + incCounter(info.cluster_scope_, {prefix, category, rq_code}); }; // If the response is from a canary, also create canary stats. @@ -110,11 +110,9 @@ void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) const { Stats::StatName vcluster_name = vcluster_storage.statName(); incCounter(info.global_scope_, - {vhost_, vhost_name, vcluster_, vcluster_name, upstream_rq_completed_}); - incCounter(info.global_scope_, - {vhost_, vhost_name, vcluster_, vcluster_name, rq_group}); - incCounter(info.global_scope_, - {vhost_, vhost_name, vcluster_, vcluster_name, rq_code}); + {vhost_, vhost_name, vcluster_, vcluster_name, upstream_rq_completed_}); + incCounter(info.global_scope_, {vhost_, vhost_name, vcluster_, vcluster_name, rq_group}); + incCounter(info.global_scope_, {vhost_, vhost_name, vcluster_, vcluster_name, rq_code}); } // Handle per zone stats. @@ -151,10 +149,8 @@ void CodeStatsImpl::chargeResponseTiming(const ResponseTimingInfo& info) const { Stats::StatName vhost_name = vhost_storage.statName(); Stats::StatNameTempStorage vcluster_storage(info.request_vcluster_name_, symbol_table_); Stats::StatName vcluster_name = vcluster_storage.statName(); - recordHistogram( - info.global_scope_, - {vhost_, vhost_name, vcluster_, vcluster_name, upstream_rq_time_}, - count); + recordHistogram(info.global_scope_, + {vhost_, vhost_name, vcluster_, vcluster_name, upstream_rq_time_}, count); } // Handle per zone stats. diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 84e4ad4c9e53..2523cf200a5b 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -28,8 +28,8 @@ class CodeStatsImpl : public CodeStats { friend class CodeStatsTest; void incCounter(Stats::Scope& scope, const std::vector& names) const; - void recordHistogram( - Stats::Scope& scope, const std::vector& names, uint64_t count) const; + void recordHistogram(Stats::Scope& scope, const std::vector& names, + uint64_t count) const; class RequestCodeGroup { public: diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 587d65875182..ddb82340cee9 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -35,20 +35,20 @@ ScopePtr ScopePrefixer::createScope(const std::string& name) { } Counter& ScopePrefixer::counterx(StatName name) { - Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join( - {prefix_.statName(), name}); + Stats::SymbolTable::StoragePtr stat_name_storage = + scope_->symbolTable().join({prefix_.statName(), name}); return scope_->counterx(StatName(stat_name_storage.get())); } Gauge& ScopePrefixer::gaugex(StatName name) { - Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join( - {prefix_.statName(), name}); + Stats::SymbolTable::StoragePtr stat_name_storage = + scope_->symbolTable().join({prefix_.statName(), name}); return scope_->gaugex(StatName(stat_name_storage.get())); } Histogram& ScopePrefixer::histogramx(StatName name) { - Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join( - {prefix_.statName(), name}); + Stats::SymbolTable::StoragePtr stat_name_storage = + scope_->symbolTable().join({prefix_.statName(), name}); return scope_->histogramx(StatName(stat_name_storage.get())); } diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 233a55bc6d39..70338d91a6b6 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -165,8 +165,8 @@ envoy::config::bootstrap::v2::Bootstrap parseBootstrapFromV2Yaml(const std::stri class ClusterManagerImplTest : public testing::Test { public: - ClusterManagerImplTest() : api_(Api::createApiForTest(stats_store_)), - http_context_(factory_.stats_.symbolTable()) {} + ClusterManagerImplTest() + : api_(Api::createApiForTest(stats_store_)), http_context_(factory_.stats_.symbolTable()) {} void create(const envoy::config::bootstrap::v2::Bootstrap& bootstrap) { cluster_manager_ = std::make_unique( diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 24d5fb49472e..f34e7d43ced8 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -198,7 +198,8 @@ class MockStore : public Store { * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : private Test::Global, public IsolatedStoreImpl { +class MockIsolatedStatsStore : private Test::Global, + public IsolatedStoreImpl { public: MockIsolatedStatsStore(); ~MockIsolatedStatsStore(); From 45ef91769f961c7b17cb5497dfc1814db5626ad9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 3 Mar 2019 16:19:06 -0500 Subject: [PATCH 043/106] all tests passing. Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 62 +++++++++++++++++-------------------- source/common/http/codes.h | 24 +++++++------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 7f82fc588856..ba6ca5732d61 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -188,49 +188,45 @@ Stats::StatName CodeStatsImpl::upstreamRqGroup(Code response_code) const { return upstream_rq_unknown_; } -CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { - for (auto& p : rc_stat_name_map_) { - std::unique_ptr& storage = p.second; - storage->free(code_stats_.symbol_table_); +CodeStatsImpl::RequestCodeGroup::RequestCodeGroup(absl::string_view prefix, + CodeStatsImpl& code_stats) + : code_stats_(code_stats), prefix_(std::string(prefix)) { + for (uint32_t i = 0; i < NumHttpCodes; ++i) { + rc_stat_names_[i] = nullptr; } -} -Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { - switch (response_code) { - case Code::OK: - return upstream_rq_200_; - case Code::NotFound: - return upstream_rq_404_; - case Code::ServiceUnavailable: - return upstream_rq_503_; - default: - break; - } - // RELEASE_ASSERT(false, absl::StrCat("need to optimize code ", response_code)); - return makeStatName(response_code); + // Pre-allocate response codes 200, 404, and 503, as those seem quite likely. + statName(Code::OK); + statName(Code::NotFound); + statName(Code::ServiceUnavailable); } -Stats::StatName CodeStatsImpl::RequestCodeGroup::makeStatName(Code response_code) { - { - absl::ReaderMutexLock lock(&mutex_); - auto p = rc_stat_name_map_.find(response_code); - if (p != rc_stat_name_map_.end()) { - return p->second->statName(); +CodeStatsImpl::RequestCodeGroup::~RequestCodeGroup() { + for (uint32_t i = 0; i < NumHttpCodes; ++i) { + Stats::StatNameStorage* storage = rc_stat_names_[i]; + if (storage != nullptr) { + storage->free(code_stats_.symbol_table_); + delete storage; } } +} - // Note -- another thread may swap in here and allocate the lock so we - // have to re-check after acquiring the write-lock. - - { +Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { + // Take a lock only if we've never seen this response-code before. + uint32_t rc_int = static_cast(response_code); + RELEASE_ASSERT(rc_int < NumHttpCodes, absl::StrCat("Unexpected http code: ", rc_int)); + std::atomic& atomic_ref = rc_stat_names_[rc_int]; + if (atomic_ref.load() == nullptr) { absl::MutexLock lock(&mutex_); - std::unique_ptr& stat_name_storage = rc_stat_name_map_[response_code]; - if (stat_name_storage == nullptr) { - stat_name_storage = std::make_unique( - absl::StrCat(prefix_, enumToInt(response_code)), code_stats_.symbol_table_); + + // Check again under lock as two threads might have raced to add a StatName + // for the same code. + if (atomic_ref.load() == nullptr) { + atomic_ref = new Stats::StatNameStorage(absl::StrCat(prefix_, enumToInt(response_code)), + code_stats_.symbol_table_); } - return stat_name_storage->statName(); } + return atomic_ref.load()->statName(); } std::string CodeUtility::groupStringForResponseCode(Code response_code) { diff --git a/source/common/http/codes.h b/source/common/http/codes.h index 2523cf200a5b..e939892e1b5c 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -33,10 +33,7 @@ class CodeStatsImpl : public CodeStats { class RequestCodeGroup { public: - RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats) - : code_stats_(code_stats), prefix_(std::string(prefix)), - upstream_rq_200_(makeStatName(Code::OK)), upstream_rq_404_(makeStatName(Code::NotFound)), - upstream_rq_503_(makeStatName(Code::ServiceUnavailable)) {} + RequestCodeGroup(absl::string_view prefix, CodeStatsImpl& code_stats); ~RequestCodeGroup(); Stats::StatName statName(Code response_code); @@ -54,17 +51,22 @@ class CodeStatsImpl : public CodeStats { */ private: - using RCStatNameMap = absl::flat_hash_map>; - - Stats::StatName makeStatName(Code response_code); + // Use an array of atomic pointers to hold StatNameStorage objects for + // every conceivable HTTP response code. In the hot-path we'll reference + // these with a null-check, and if we need to allocate a symbol for a + // new code, we'll take a mutex to avoid duplicate allocations and + // subsequent leaks. This is similar in principle to a ReaderMutexLock, + // but should be faster, as ReaderMutexLocks appear to be too expensive for + // fine-grained controls. Another option would be to use a lock per + // stat-name, which might have similar performance to atomics with default + // barrier policy. + + static constexpr uint32_t NumHttpCodes = 1000; + std::atomic rc_stat_names_[NumHttpCodes]; CodeStatsImpl& code_stats_; std::string prefix_; absl::Mutex mutex_; - RCStatNameMap rc_stat_name_map_ GUARDED_BY(mutex_); - Stats::StatName upstream_rq_200_; - Stats::StatName upstream_rq_404_; - Stats::StatName upstream_rq_503_; }; static absl::string_view stripTrailingDot(absl::string_view prefix); From fd8db0ead8ece58beed45063561b9bd5671ce568 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 3 Mar 2019 16:53:17 -0500 Subject: [PATCH 044/106] Use FakeSymbolTable in prod instances. Signed-off-by: Joshua Marantz --- source/common/stats/BUILD | 1 + source/common/stats/isolated_store_impl.cc | 4 ++-- source/exe/BUILD | 1 + source/exe/main_common.h | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index e29fdd7ec8ae..b01c94da80e3 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -43,6 +43,7 @@ envoy_cc_library( srcs = ["isolated_store_impl.cc"], hdrs = ["isolated_store_impl.h"], deps = [ + ":fake_symbol_table_lib", ":histogram_lib", ":scope_prefixer_lib", ":stats_lib", diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index f5d5c84975c3..b78d3e0ed782 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -6,15 +6,15 @@ #include #include "common/common/utility.h" +#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/histogram_impl.h" #include "common/stats/scope_prefixer.h" -#include "common/stats/symbol_table_impl.h" #include "common/stats/utility.h" namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/source/exe/BUILD b/source/exe/BUILD index 652073d61b9b..67071345e0a9 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -69,6 +69,7 @@ envoy_cc_library( "//source/common/api:os_sys_calls_lib", "//source/common/common:compiler_requirements_lib", "//source/common/common:perf_annotation_lib", + "//source/common/stats:fake_symbol_table_lib", "//source/common/thread:thread_factory_singleton_lib", "//source/server:hot_restart_lib", "//source/server:hot_restart_nop_lib", diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 7c6adfa498f0..c651602ba1b3 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -5,6 +5,7 @@ #include "common/common/thread.h" #include "common/event/real_time_system.h" +#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/thread_local_store.h" #include "common/thread_local/thread_local_impl.h" @@ -64,7 +65,7 @@ class MainCommonBase { protected: Envoy::OptionsImpl& options_; - Stats::SymbolTableImpl symbol_table_; + Stats::FakeSymbolTableImpl symbol_table_; Server::ComponentFactory& component_factory_; Thread::ThreadFactory& thread_factory_; From c6ec598ad9a7d39ced39e4c7d79eeff0f62ab70e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 3 Mar 2019 16:55:04 -0500 Subject: [PATCH 045/106] format Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index b78d3e0ed782..bbee9bc1f87e 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -14,7 +14,8 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} +IsolatedStoreImpl::IsolatedStoreImpl() + : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) : IsolatedStoreImpl(*symbol_table) { From 88f1f4d57b6fe8bfe065bd215d41fdf310f84127 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 3 Mar 2019 21:12:19 -0500 Subject: [PATCH 046/106] Use FakeSymbolTableImpl in most tests too (except the SymbolTable unit tests). Signed-off-by: Joshua Marantz --- test/common/grpc/grpc_client_integration_test_harness.h | 3 ++- test/mocks/server/mocks.h | 3 ++- test/mocks/stats/BUILD | 1 + test/mocks/stats/mocks.cc | 4 ++-- test/mocks/stats/mocks.h | 5 +++-- test/test_common/BUILD | 1 + test/test_common/utility.h | 3 ++- 7 files changed, 13 insertions(+), 7 deletions(-) diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 20049c474c4a..d2f9a1f25f89 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -13,6 +13,7 @@ #include "common/http/async_client_impl.h" #include "common/http/codes.h" #include "common/http/http2/conn_pool.h" +#include "common/stats/fake_symbol_table_impl.h" #include "common/network/connection_impl.h" #include "common/network/raw_buffer_socket.h" @@ -412,7 +413,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { FakeHttpConnectionPtr fake_connection_; std::vector fake_streams_; const Protobuf::MethodDescriptor* method_descriptor_; - Envoy::Test::Global symbol_table_; + Envoy::Test::Global symbol_table_; Stats::IsolatedStoreImpl* stats_store_ = new Stats::IsolatedStoreImpl(*symbol_table_); Api::ApiPtr api_; Event::DispatcherImpl dispatcher_; diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index a92bfe44d090..a64e8988a925 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -23,6 +23,7 @@ #include "common/http/context_impl.h" #include "common/secret/secret_manager_impl.h" +#include "common/stats/fake_symbol_table_impl.h" #include "extensions/transport_sockets/tls/context_manager_impl.h" @@ -206,7 +207,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::StatDataAllocator&()); private: - Test::Global symbol_table_; + Test::Global symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::HeapStatDataAllocator stats_allocator_; diff --git a/test/mocks/stats/BUILD b/test/mocks/stats/BUILD index ddb104e96124..711c90c01fc2 100644 --- a/test/mocks/stats/BUILD +++ b/test/mocks/stats/BUILD @@ -17,6 +17,7 @@ envoy_cc_mock( "//include/envoy/stats:timespan", "//include/envoy/thread_local:thread_local_interface", "//include/envoy/upstream:cluster_manager_interface", + "//source/common/stats:fake_symbol_table_lib", "//source/common/stats:histogram_lib", "//source/common/stats:isolated_store_lib", "//source/common/stats:stats_lib", diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 29b8726bc390..f5a38fef51e4 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -2,7 +2,7 @@ #include -#include "common/stats/symbol_table_impl.h" +#include "common/stats/fake_symbol_table_impl.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -128,7 +128,7 @@ MockStore::MockStore(SymbolTable& symbol_table) MockStore::~MockStore() {} MockIsolatedStatsStore::MockIsolatedStatsStore() - : IsolatedStoreImpl(Test::Global::get()) {} + : IsolatedStoreImpl(Test::Global::get()) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } } // namespace Stats diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index f34e7d43ced8..85a1d398dbdf 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -14,6 +14,7 @@ #include "envoy/thread_local/thread_local.h" #include "envoy/upstream/cluster_manager.h" +#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/histogram_impl.h" #include "common/stats/isolated_store_impl.h" @@ -187,7 +188,7 @@ class MockStore : public Store { SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& symbolTable() const override { return symbol_table_; } - SymbolTableImpl owned_symbol_table_; + FakeSymbolTableImpl owned_symbol_table_; SymbolTable& symbol_table_; testing::NiceMock counter_; std::vector> histograms_; @@ -198,7 +199,7 @@ class MockStore : public Store { * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : private Test::Global, +class MockIsolatedStatsStore : private Test::Global, public IsolatedStoreImpl { public: MockIsolatedStatsStore(); diff --git a/test/test_common/BUILD b/test/test_common/BUILD index 4b164f56c4b5..2e2029190c56 100644 --- a/test/test_common/BUILD +++ b/test/test_common/BUILD @@ -105,6 +105,7 @@ envoy_cc_test_library( "//source/common/network:address_lib", "//source/common/network:utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:fake_symbol_table_lib", "//source/common/stats:stats_lib", "//source/common/stats:stats_options_lib", "@envoy_api//envoy/config/bootstrap/v2:bootstrap_cc", diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 93d03d16cc94..5645a5eef2c9 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -21,6 +21,7 @@ #include "common/common/thread.h" #include "common/http/header_map_impl.h" #include "common/protobuf/utility.h" +#include "common/stats/fake_symbol_table_impl.h" #include "common/stats/raw_stat_data.h" #include "test/test_common/printers.h" @@ -504,7 +505,7 @@ class TestAllocator : public RawStatDataAllocator { ~TestAllocator() { EXPECT_EQ(0, hash_set_.size()); } private: - SymbolTableImpl symbol_table_; + FakeSymbolTableImpl symbol_table_; Thread::MutexBasicLockable mutex_; TestBlockMemoryHashSetOptions block_hash_options_; std::unique_ptr block_memory_; From 94c520523ef423025e860376e0fa4a37c5b87d47 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 4 Mar 2019 12:32:08 -0500 Subject: [PATCH 047/106] Don't pass SymbolTable in to stat Mocks but use the global; factor out the name lookup in the hystrix sink. Signed-off-by: Joshua Marantz --- include/envoy/stats/stats.h | 6 ++ source/common/stats/metric_impl.h | 3 +- source/extensions/stat_sinks/hystrix/BUILD | 1 + .../extensions/stat_sinks/hystrix/hystrix.cc | 7 ++- .../extensions/stat_sinks/hystrix/hystrix.h | 6 ++ test/common/http/codes_test.cc | 7 ++- test/common/http/user_agent_test.cc | 2 +- test/common/stats/source_impl_test.cc | 13 ++--- .../common/statsd/udp_statsd_test.cc | 6 +- .../stats_sinks/hystrix/hystrix_test.cc | 2 +- test/mocks/stats/mocks.cc | 56 +++++++++++++------ test/mocks/stats/mocks.h | 41 ++++++++------ test/test_common/global.h | 1 + test/test_common/utility.cc | 4 ++ test/test_common/utility.h | 8 +++ 15 files changed, 109 insertions(+), 54 deletions(-) diff --git a/include/envoy/stats/stats.h b/include/envoy/stats/stats.h index bb144fc47d62..d93fb76166d0 100644 --- a/include/envoy/stats/stats.h +++ b/include/envoy/stats/stats.h @@ -49,6 +49,12 @@ class Metric { */ virtual std::string tagExtractedName() const PURE; + /** + * Returns the name of the Metric with the portions designated as tags + * removed as a StatName + */ + virtual StatName tagExtractedStatName() const PURE; + /** * Indicates whether this metric has been updated since the server was started. */ diff --git a/source/common/stats/metric_impl.h b/source/common/stats/metric_impl.h index 314118f6f95e..57fb28b5a082 100644 --- a/source/common/stats/metric_impl.h +++ b/source/common/stats/metric_impl.h @@ -29,6 +29,7 @@ class MetricImpl : public virtual Metric { std::string name() const override { return symbolTable().toString(statName()); } std::string tagExtractedName() const override; std::vector tags() const override; + StatName tagExtractedStatName() const override; protected: /** @@ -41,8 +42,6 @@ class MetricImpl : public virtual Metric { void clear(); private: - StatName tagExtractedStatName() const; - StatNameList stat_names_; }; diff --git a/source/extensions/stat_sinks/hystrix/BUILD b/source/extensions/stat_sinks/hystrix/BUILD index 418c680513cc..388ad3dde8bc 100644 --- a/source/extensions/stat_sinks/hystrix/BUILD +++ b/source/extensions/stat_sinks/hystrix/BUILD @@ -37,5 +37,6 @@ envoy_cc_library( "//source/common/common:logger_lib", "//source/common/config:well_known_names", "//source/common/http:headers_lib", + "//source/common/stats:symbol_table_lib", ], ) diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 4191744624d1..5f523d70ef41 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -267,7 +267,8 @@ const std::string HystrixSink::printRollingWindows() { HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) : server_(server), current_index_(num_buckets > 0 ? num_buckets : DEFAULT_NUM_BUCKETS), - window_size_(current_index_ + 1) { + window_size_(current_index_ + 1), + cluster_upstream_rq_time_("cluster.upstream_rq_time", server.symbolTable()) { Server::Admin& admin = server_.admin(); ENVOY_LOG(debug, "adding hystrix_event_stream endpoint to enable connection to hystrix dashboard"); @@ -275,6 +276,8 @@ HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false); } +HystrixSink::~HystrixSink() { cluster_upstream_rq_time_.free(server_.symbolTable()); } + Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, @@ -327,7 +330,7 @@ void HystrixSink::flush(Stats::Source& source) { // Save a map of the relevant histograms per cluster in a convenient format. std::unordered_map time_histograms; for (const Stats::ParentHistogramSharedPtr& histogram : source.cachedHistograms()) { - if (histogram->tagExtractedName() == "cluster.upstream_rq_time") { + if (histogram->tagExtractedStatName() == cluster_upstream_rq_time_.statName()) { // TODO(mrice32): add an Envoy utility function to look up and return a tag for a metric. auto tags = histogram->tags(); auto it = std::find_if(tags.begin(), tags.end(), [](const Stats::Tag& tag) { diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index 54d26953abc8..4079a05fa144 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -10,6 +10,8 @@ #include "envoy/stats/sink.h" #include "envoy/stats/source.h" +#include "common/stats/symbol_table_impl.h" + namespace Envoy { namespace Extensions { namespace StatSinks { @@ -47,6 +49,7 @@ typedef std::unique_ptr ClusterStatsCachePtr; class HystrixSink : public Stats::Sink, public Logger::Loggable { public: HystrixSink(Server::Instance& server, uint64_t num_buckets); + ~HystrixSink() override; Http::Code handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, Server::AdminStream& admin_stream); void flush(Stats::Source& source) override; @@ -155,6 +158,9 @@ class HystrixSink : public Stats::Sink, public Logger::Loggable cluster_stats_cache_map_; + + // Saved StatName for "cluster.upstream_rq_time" for fast comparisons in loop. + Stats::StatNameStorage cluster_upstream_rq_time_; }; typedef std::unique_ptr HystrixSinkPtr; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index d1ca8e39c59a..fbbaa42d0c22 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -39,10 +39,11 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } - Stats::SymbolTableImpl symbol_table_; + Stats::FakeSymbolTableImpl symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; + std::vector stat_name_storage_; }; TEST_F(CodeUtilityTest, GroupStrings) { @@ -202,8 +203,8 @@ TEST_F(CodeUtilityTest, PerZoneStats) { } TEST_F(CodeUtilityTest, ResponseTimingTest) { - Stats::MockStore global_store(symbol_table_); - Stats::MockStore cluster_scope(symbol_table_); + Stats::MockStore global_store; // (symbol_table_); + Stats::MockStore cluster_scope; // (symbol_table_); Http::CodeStats::ResponseTimingInfo info{ global_store, cluster_scope, "prefix.", std::chrono::milliseconds(5), diff --git a/test/common/http/user_agent_test.cc b/test/common/http/user_agent_test.cc index a6869add3912..338afe303599 100644 --- a/test/common/http/user_agent_test.cc +++ b/test/common/http/user_agent_test.cc @@ -18,7 +18,7 @@ namespace Http { TEST(UserAgentTest, All) { Stats::MockStore stat_store; - NiceMock original_histogram(stat_store.symbolTable()); + NiceMock original_histogram; //(stat_store.symbolTable()); Event::SimulatedTimeSystem time_system; Stats::Timespan span(original_histogram, time_system); diff --git a/test/common/stats/source_impl_test.cc b/test/common/stats/source_impl_test.cc index d756b1e2ae25..8668626534c7 100644 --- a/test/common/stats/source_impl_test.cc +++ b/test/common/stats/source_impl_test.cc @@ -23,22 +23,21 @@ TEST(SourceImplTest, Caching) { ON_CALL(store, histograms()).WillByDefault(ReturnPointee(&stored_histograms)); SourceImpl source(store); - SymbolTableImpl symbol_table; // Once cached, new values should not be reflected by the return value. - stored_counters.push_back(std::make_shared(symbol_table)); + stored_counters.push_back(std::make_shared()); EXPECT_EQ(source.cachedCounters(), stored_counters); - stored_counters.push_back(std::make_shared(symbol_table)); + stored_counters.push_back(std::make_shared()); EXPECT_NE(source.cachedCounters(), stored_counters); - stored_gauges.push_back(std::make_shared(symbol_table)); + stored_gauges.push_back(std::make_shared()); EXPECT_EQ(source.cachedGauges(), stored_gauges); - stored_gauges.push_back(std::make_shared(symbol_table)); + stored_gauges.push_back(std::make_shared()); EXPECT_NE(source.cachedGauges(), stored_gauges); - stored_histograms.push_back(std::make_shared(symbol_table)); + stored_histograms.push_back(std::make_shared()); EXPECT_EQ(source.cachedHistograms(), stored_histograms); - stored_histograms.push_back(std::make_shared(symbol_table)); + stored_histograms.push_back(std::make_shared()); EXPECT_NE(source.cachedHistograms(), stored_histograms); // After clearing, the new values should be reflected in the cache. diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index 30b31c5c06b6..c3248920346a 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -186,7 +186,7 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { std::vector tags = {Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}; auto counter = std::make_shared>(); - counter->symbol_table_ = &symbol_table; + // counter->symbol_table_ = &symbol_table; counter->name_ = "test_counter"; counter->used_ = true; counter->latch_ = 1; @@ -199,7 +199,7 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { counter->used_ = false; auto gauge = std::make_shared>(); - gauge->symbol_table_ = &symbol_table; + // gauge->symbol_table_ = &symbol_table; gauge->name_ = "test_gauge"; gauge->value_ = 1; gauge->used_ = true; @@ -211,7 +211,7 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { sink.flush(source); NiceMock timer; - timer.symbol_table_ = &symbol_table; + // timer.symbol_table_ = &symbol_table; timer.name_ = "test_timer"; timer.tags_ = tags; EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), diff --git a/test/extensions/stats_sinks/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/hystrix/hystrix_test.cc index 97b8197bbd03..a4ce5f43f190 100644 --- a/test/extensions/stats_sinks/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/hystrix/hystrix_test.cc @@ -460,7 +460,7 @@ TEST_F(HystrixSinkTest, HistogramTest) { // Create histogram for the Hystrix sink to read. auto histogram = std::make_shared>(); histogram->name_ = "cluster." + cluster1_name_ + ".upstream_rq_time"; - histogram->tag_extracted_name_ = "cluster.upstream_rq_time"; + histogram->setTagExtractedName("cluster.upstream_rq_time"); histogram->tags_.emplace_back(Stats::Tag{Config::TagNames::get().CLUSTER_NAME, cluster1_name_}); histogram->used_ = true; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index f5a38fef51e4..6fe84b720103 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -17,14 +17,14 @@ using testing::ReturnRef; namespace Envoy { namespace Stats { -MockMetric::MockMetric() : symbol_table_(nullptr), name_(*this) { +MockMetric::MockMetric() : /*symbol_table_(nullptr), */ name_(*this) { // ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); // ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); } -MockMetric::MockMetric(SymbolTable& symbol_table) : symbol_table_(&symbol_table), name_(*this) { +/*MockMetric::MockMetric(SymbolTable& symbol_table) : symbol_table_(&symbol_table), name_(*this) { // ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); // ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); -} + }*/ MockMetric::~MockMetric() {} MockMetric::MetricName::~MetricName() { @@ -33,11 +33,22 @@ MockMetric::MetricName::~MetricName() { } } +void MockMetric::setTagExtractedName(absl::string_view name) { + tag_extracted_name_ = std::string(name); + tag_extracted_stat_name_ = + std::make_unique(tagExtractedName(), *symbol_table_); +} + void MockMetric::MetricName::MetricName::operator=(absl::string_view name) { name_ = std::string(name); - if (mock_metric_.symbol_table_ != nullptr) { - stat_name_storage_ = std::make_unique(name, *mock_metric_.symbol_table_); - } + // if (mock_metric_.symbol_table_ != nullptr) { + stat_name_storage_ = std::make_unique(name, mock_metric_.symbolTable()); + //} +} + +void MockMetric::MetricName::MetricName::operator=(StatName name) { + name_ = mock_metric_.symbolTable().toString(name); + stat_name_storage_ = std::make_unique(name_, mock_metric_.symbolTable()); } MockCounter::MockCounter() { @@ -45,21 +56,21 @@ MockCounter::MockCounter() { ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); } -MockCounter::MockCounter(SymbolTable& symbol_table) : MockMetric(symbol_table) { +/*MockCounter::MockCounter(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); -} + }*/ MockCounter::~MockCounter() {} MockGauge::MockGauge() { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); } -MockGauge::MockGauge(SymbolTable& symbol_table) : MockMetric(symbol_table) { +/*MockGauge::MockGauge(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); -} + }*/ MockGauge::~MockGauge() {} MockHistogram::MockHistogram() { @@ -69,13 +80,13 @@ MockHistogram::MockHistogram() { } })); } -MockHistogram::MockHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { +/*MockHistogram::MockHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { if (store_ != nullptr) { store_->deliverHistogramToSinks(*this, value); } })); -} + }*/ MockHistogram::~MockHistogram() {} MockParentHistogram::MockParentHistogram() { @@ -88,7 +99,7 @@ MockParentHistogram::MockParentHistogram() { ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); } -MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { +/*MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { if (store_ != nullptr) { store_->deliverHistogramToSinks(*this, value); @@ -97,7 +108,7 @@ MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) : MockMetric ON_CALL(*this, intervalStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); -} + }*/ MockParentHistogram::~MockParentHistogram() {} MockSource::MockSource() { @@ -111,18 +122,27 @@ MockSource::~MockSource() {} MockSink::MockSink() {} MockSink::~MockSink() {} -MockStore::MockStore() : MockStore(owned_symbol_table_) {} +// MockStore::MockStore() : MockStore(owned_symbol_table_) {} -MockStore::MockStore(SymbolTable& symbol_table) - : symbol_table_(symbol_table), counter_(symbol_table_) { +MockStore::MockStore() { // SymbolTable& symbol_table) + //: symbol_table_(symbol_table) { //, counter_(symbol_table_) { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); ON_CALL(*this, histogram(_)).WillByDefault(Invoke([this](const std::string& name) -> Histogram& { - auto* histogram = new NiceMock(symbol_table_); + auto* histogram = new NiceMock(); // symbol_table_); + histogram->name_ = name; + histogram->store_ = this; + histograms_.emplace_back(histogram); + return *histogram; + })); + /* + ON_CALL(*this, histogramx(_)).WillByDefault(Invoke([this](StatName name) -> Histogram& { + auto* histogram = new NiceMock(); //symbol_table_); histogram->name_ = name; histogram->store_ = this; histograms_.emplace_back(histogram); return *histogram; })); + */ ON_CALL(*this, statsOptions()).WillByDefault(ReturnRef(stats_options_)); } MockStore::~MockStore() {} diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 85a1d398dbdf..83b742cd78ed 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -27,7 +27,7 @@ namespace Stats { class MockMetric : public virtual Metric { public: - explicit MockMetric(SymbolTable& symbol_table); + // explicit MockMetric(SymbolTable& symbol_table); MockMetric(); ~MockMetric(); @@ -40,6 +40,7 @@ class MockMetric : public virtual Metric { ~MetricName(); void operator=(absl::string_view str); + void operator=(StatName stat_name); std::string name() const { return name_; } StatName statName() const { return stat_name_storage_->statName(); } @@ -50,8 +51,8 @@ class MockMetric : public virtual Metric { std::unique_ptr stat_name_storage_; }; - SymbolTable& symbolTable() override { return *symbol_table_; } - const SymbolTable& symbolTable() const override { return *symbol_table_; } + SymbolTable& symbolTable() override { return symbol_table_.get(); } + const SymbolTable& symbolTable() const override { return symbol_table_.get(); } // Note: cannot be mocked because it is accessed as a Property in a gmock EXPECT_CALL. This // creates a deadlock in gmock and is an unintended use of mock functions. @@ -59,21 +60,27 @@ class MockMetric : public virtual Metric { StatName statName() const override { return name_.statName(); } // MOCK_CONST_METHOD0(tags, std::vector()); std::vector tags() const override { return tags_; } + void setTagExtractedName(absl::string_view name); std::string tagExtractedName() const override { return tag_extracted_name_.empty() ? name() : tag_extracted_name_; } + StatName tagExtractedStatName() const override { return tag_extracted_stat_name_->statName(); } + // MOCK_CONST_METHOD0(tagExtractedName, std::string()); - SymbolTable* symbol_table_; + Test::Global symbol_table_; // Must outlive name_. MetricName name_; - std::string tag_extracted_name_; std::vector tags_; + +private: + std::string tag_extracted_name_; + std::unique_ptr tag_extracted_stat_name_; }; class MockCounter : public Counter, public MockMetric { public: MockCounter(); - explicit MockCounter(SymbolTable& symbol_table); + // explicit MockCounter(SymbolTable& symbol_table); ~MockCounter(); MOCK_METHOD1(add, void(uint64_t amount)); @@ -91,7 +98,7 @@ class MockCounter : public Counter, public MockMetric { class MockGauge : public Gauge, public MockMetric { public: MockGauge(); - explicit MockGauge(SymbolTable& symbol_table); + // explicit MockGauge(SymbolTable& symbol_table); ~MockGauge(); MOCK_METHOD1(add, void(uint64_t amount)); @@ -109,7 +116,7 @@ class MockGauge : public Gauge, public MockMetric { class MockHistogram : public Histogram, public MockMetric { public: MockHistogram(); - explicit MockHistogram(SymbolTable& symbol_table); + // explicit MockHistogram(SymbolTable& symbol_table); ~MockHistogram(); MOCK_METHOD1(recordValue, void(uint64_t value)); @@ -121,7 +128,7 @@ class MockHistogram : public Histogram, public MockMetric { class MockParentHistogram : public ParentHistogram, public MockMetric { public: MockParentHistogram(); - explicit MockParentHistogram(SymbolTable& symbol_table); + // explicit MockParentHistogram(SymbolTable& symbol_table); ~MockParentHistogram(); void merge() override {} @@ -165,7 +172,7 @@ class MockSink : public Sink { class MockStore : public Store { public: - explicit MockStore(SymbolTable& symbol_table); + // explicit MockStore(SymbolTable& symbol_table); MockStore(); ~MockStore(); @@ -181,15 +188,15 @@ class MockStore : public Store { MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); - Counter& counterx(StatName name) override { return counter(symbol_table_.toString(name)); } - Gauge& gaugex(StatName name) override { return gauge(symbol_table_.toString(name)); } - Histogram& histogramx(StatName name) override { return histogram(symbol_table_.toString(name)); } + Counter& counterx(StatName name) override { return counter(symbol_table_->toString(name)); } + Gauge& gaugex(StatName name) override { return gauge(symbol_table_->toString(name)); } + Histogram& histogramx(StatName name) override { return histogram(symbol_table_->toString(name)); } - SymbolTable& symbolTable() override { return symbol_table_; } - const SymbolTable& symbolTable() const override { return symbol_table_; } + SymbolTable& symbolTable() override { return symbol_table_.get(); } + const SymbolTable& symbolTable() const override { return symbol_table_.get(); } - FakeSymbolTableImpl owned_symbol_table_; - SymbolTable& symbol_table_; + Test::Global symbol_table_; + // SymbolTable& symbol_table_; testing::NiceMock counter_; std::vector> histograms_; StatsOptionsImpl stats_options_; diff --git a/test/test_common/global.h b/test/test_common/global.h index 4ed08cea8eda..b37a65b34422 100644 --- a/test/test_common/global.h +++ b/test/test_common/global.h @@ -117,6 +117,7 @@ template class Global { public: Global() : singleton_(Globals::get()) {} Type& get() { return singleton_->ref(); } + const Type& get() const { return singleton_->ref(); } Type* operator->() { return singleton_->ptr(); } Type& operator*() { return singleton_->ref(); } diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index 739760259763..39f278497a2f 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -139,6 +139,10 @@ Stats::GaugeSharedPtr TestUtility::findGauge(Stats::Store& store, const std::str return findByName(store.gauges(), name); } +Stats::HistogramSharedPtr TestUtility::findHistogram(Stats::Store& store, const std::string& name) { + return findByName(store.histograms(), name); +} + std::list TestUtility::makeDnsResponse(const std::list& addresses) { std::list ret; diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 5645a5eef2c9..e8a6ee0ad655 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -165,6 +165,14 @@ class TestUtility { */ static Stats::GaugeSharedPtr findGauge(Stats::Store& store, const std::string& name); + /** + * Find a histogram in a stats store. + * @param store supplies the stats store. + * @param name supplies the name to search for. + * @return Stats::HistogramSharedPtr the gauge or nullptr if there is none. + */ + static Stats::HistogramSharedPtr findHistogram(Stats::Store& store, const std::string& name); + /** * Convert a string list of IP addresses into a list of network addresses usable for DNS * response testing. From b8cbed346119002e0b6410ee923a1b84d7231fcd Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 4 Mar 2019 13:05:06 -0500 Subject: [PATCH 048/106] Remove comments and dead code. Signed-off-by: Joshua Marantz --- source/server/server.h | 1 - test/common/http/codes_test.cc | 23 +------- test/common/stats/thread_local_store_test.cc | 2 +- test/mocks/stats/mocks.cc | 56 +------------------- test/mocks/stats/mocks.h | 11 ---- 5 files changed, 5 insertions(+), 88 deletions(-) diff --git a/source/server/server.h b/source/server/server.h index 8ecd0e90b50c..7119f48b8999 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -185,7 +185,6 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } Event::TimeSystem& timeSystem() override { return time_system_; } Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } - // Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { return config_.statsFlushInterval(); diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index fbbaa42d0c22..c5c5a6e7902b 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -203,8 +203,8 @@ TEST_F(CodeUtilityTest, PerZoneStats) { } TEST_F(CodeUtilityTest, ResponseTimingTest) { - Stats::MockStore global_store; // (symbol_table_); - Stats::MockStore cluster_scope; // (symbol_table_); + Stats::MockStore global_store; + Stats::MockStore cluster_scope; Http::CodeStats::ResponseTimingInfo info{ global_store, cluster_scope, "prefix.", std::chrono::milliseconds(5), @@ -248,17 +248,6 @@ class CodeStatsTest : public testing::Test { return CodeStatsImpl::stripTrailingDot(prefix); } - std::string join(const std::vector& v) { - std::vector> storage_vec; - std::vector stat_name_vec; - for (auto str : v) { - storage_vec.emplace_back(std::make_unique(str, symbol_table_)); - stat_name_vec.push_back(storage_vec.back()->statName()); - } - Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(stat_name_vec); - return symbol_table_.toString(Stats::StatName(stat_name_storage.get())); - } - Stats::SymbolTableImpl symbol_table_; CodeStatsImpl code_stats_; }; @@ -270,13 +259,5 @@ TEST_F(CodeStatsTest, StripTrailingDot) { EXPECT_EQ("foo.", stripTrailingDot("foo..")); // only one dot gets stripped. } -TEST_F(CodeStatsTest, Join) { - EXPECT_EQ("hello.world", join({"hello", "world"})); - EXPECT_EQ("hello.world", join({"", "hello", "world"})); // leading empty token ignored. - // EXPECT_EQ("hello.", join({"hello", ""})); // trailing empty token not ignored. - EXPECT_EQ("hello", join({"hello"})); - EXPECT_EQ("", join({""})); -} - } // namespace Http } // namespace Envoy diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 3248fed70547..87baa6e8e656 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -451,7 +451,7 @@ TEST_F(StatsThreadLocalStoreTest, HotRestartTruncation) { // are all based on the untruncated name. EXPECT_NE(nullptr, TestUtility::findCounter(*store_, name_1).get()); - // Outside the stats system, no Enovy code can see the truncated view, so + // Outside the stats system, no Envoy code can see the truncated view, so // lookups for truncated names will fail. EXPECT_EQ(nullptr, TestUtility::findCounter(*store_, name_1.substr(0, max_name_length)).get()); diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 6fe84b720103..3c36096a6314 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -17,14 +17,7 @@ using testing::ReturnRef; namespace Envoy { namespace Stats { -MockMetric::MockMetric() : /*symbol_table_(nullptr), */ name_(*this) { - // ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); - // ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); -} -/*MockMetric::MockMetric(SymbolTable& symbol_table) : symbol_table_(&symbol_table), name_(*this) { - // ON_CALL(*this, tags()).WillByDefault(ReturnPointee(&tags_)); - // ON_CALL(*this, tagExtractedName()).WillByDefault(Return(tag_extracted_name_)); - }*/ +MockMetric::MockMetric() : name_(*this) {} MockMetric::~MockMetric() {} MockMetric::MetricName::~MetricName() { @@ -41,14 +34,7 @@ void MockMetric::setTagExtractedName(absl::string_view name) { void MockMetric::MetricName::MetricName::operator=(absl::string_view name) { name_ = std::string(name); - // if (mock_metric_.symbol_table_ != nullptr) { stat_name_storage_ = std::make_unique(name, mock_metric_.symbolTable()); - //} -} - -void MockMetric::MetricName::MetricName::operator=(StatName name) { - name_ = mock_metric_.symbolTable().toString(name); - stat_name_storage_ = std::make_unique(name_, mock_metric_.symbolTable()); } MockCounter::MockCounter() { @@ -56,21 +42,12 @@ MockCounter::MockCounter() { ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); } -/*MockCounter::MockCounter(SymbolTable& symbol_table) : MockMetric(symbol_table) { - ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); - ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); - ON_CALL(*this, latch()).WillByDefault(ReturnPointee(&latch_)); - }*/ MockCounter::~MockCounter() {} MockGauge::MockGauge() { ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); } -/*MockGauge::MockGauge(SymbolTable& symbol_table) : MockMetric(symbol_table) { - ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); - ON_CALL(*this, value()).WillByDefault(ReturnPointee(&value_)); - }*/ MockGauge::~MockGauge() {} MockHistogram::MockHistogram() { @@ -80,13 +57,6 @@ MockHistogram::MockHistogram() { } })); } -/*MockHistogram::MockHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { - ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { - if (store_ != nullptr) { - store_->deliverHistogramToSinks(*this, value); - } - })); - }*/ MockHistogram::~MockHistogram() {} MockParentHistogram::MockParentHistogram() { @@ -99,16 +69,6 @@ MockParentHistogram::MockParentHistogram() { ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); } -/*MockParentHistogram::MockParentHistogram(SymbolTable& symbol_table) : MockMetric(symbol_table) { - ON_CALL(*this, recordValue(_)).WillByDefault(Invoke([this](uint64_t value) { - if (store_ != nullptr) { - store_->deliverHistogramToSinks(*this, value); - } - })); - ON_CALL(*this, intervalStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); - ON_CALL(*this, cumulativeStatistics()).WillByDefault(ReturnRef(*histogram_stats_)); - ON_CALL(*this, used()).WillByDefault(ReturnPointee(&used_)); - }*/ MockParentHistogram::~MockParentHistogram() {} MockSource::MockSource() { @@ -122,10 +82,7 @@ MockSource::~MockSource() {} MockSink::MockSink() {} MockSink::~MockSink() {} -// MockStore::MockStore() : MockStore(owned_symbol_table_) {} - -MockStore::MockStore() { // SymbolTable& symbol_table) - //: symbol_table_(symbol_table) { //, counter_(symbol_table_) { +MockStore::MockStore() { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); ON_CALL(*this, histogram(_)).WillByDefault(Invoke([this](const std::string& name) -> Histogram& { auto* histogram = new NiceMock(); // symbol_table_); @@ -134,15 +91,6 @@ MockStore::MockStore() { // SymbolTable& symbol_table) histograms_.emplace_back(histogram); return *histogram; })); - /* - ON_CALL(*this, histogramx(_)).WillByDefault(Invoke([this](StatName name) -> Histogram& { - auto* histogram = new NiceMock(); //symbol_table_); - histogram->name_ = name; - histogram->store_ = this; - histograms_.emplace_back(histogram); - return *histogram; - })); - */ ON_CALL(*this, statsOptions()).WillByDefault(ReturnRef(stats_options_)); } MockStore::~MockStore() {} diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 83b742cd78ed..c62da6361f64 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -27,7 +27,6 @@ namespace Stats { class MockMetric : public virtual Metric { public: - // explicit MockMetric(SymbolTable& symbol_table); MockMetric(); ~MockMetric(); @@ -40,7 +39,6 @@ class MockMetric : public virtual Metric { ~MetricName(); void operator=(absl::string_view str); - void operator=(StatName stat_name); std::string name() const { return name_; } StatName statName() const { return stat_name_storage_->statName(); } @@ -58,7 +56,6 @@ class MockMetric : public virtual Metric { // creates a deadlock in gmock and is an unintended use of mock functions. std::string name() const override { return name_.name(); } StatName statName() const override { return name_.statName(); } - // MOCK_CONST_METHOD0(tags, std::vector()); std::vector tags() const override { return tags_; } void setTagExtractedName(absl::string_view name); std::string tagExtractedName() const override { @@ -66,8 +63,6 @@ class MockMetric : public virtual Metric { } StatName tagExtractedStatName() const override { return tag_extracted_stat_name_->statName(); } - // MOCK_CONST_METHOD0(tagExtractedName, std::string()); - Test::Global symbol_table_; // Must outlive name_. MetricName name_; std::vector tags_; @@ -80,7 +75,6 @@ class MockMetric : public virtual Metric { class MockCounter : public Counter, public MockMetric { public: MockCounter(); - // explicit MockCounter(SymbolTable& symbol_table); ~MockCounter(); MOCK_METHOD1(add, void(uint64_t amount)); @@ -98,7 +92,6 @@ class MockCounter : public Counter, public MockMetric { class MockGauge : public Gauge, public MockMetric { public: MockGauge(); - // explicit MockGauge(SymbolTable& symbol_table); ~MockGauge(); MOCK_METHOD1(add, void(uint64_t amount)); @@ -116,7 +109,6 @@ class MockGauge : public Gauge, public MockMetric { class MockHistogram : public Histogram, public MockMetric { public: MockHistogram(); - // explicit MockHistogram(SymbolTable& symbol_table); ~MockHistogram(); MOCK_METHOD1(recordValue, void(uint64_t value)); @@ -128,7 +120,6 @@ class MockHistogram : public Histogram, public MockMetric { class MockParentHistogram : public ParentHistogram, public MockMetric { public: MockParentHistogram(); - // explicit MockParentHistogram(SymbolTable& symbol_table); ~MockParentHistogram(); void merge() override {} @@ -172,7 +163,6 @@ class MockSink : public Sink { class MockStore : public Store { public: - // explicit MockStore(SymbolTable& symbol_table); MockStore(); ~MockStore(); @@ -196,7 +186,6 @@ class MockStore : public Store { const SymbolTable& symbolTable() const override { return symbol_table_.get(); } Test::Global symbol_table_; - // SymbolTable& symbol_table_; testing::NiceMock counter_; std::vector> histograms_; StatsOptionsImpl stats_options_; From 4a0d37d503e672a2ae6e2c677d045c514a8e9cf8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 5 Mar 2019 22:36:39 -0500 Subject: [PATCH 049/106] Go back to using real symbol tables in this client, and fix a contention issue in the router. Signed-off-by: Joshua Marantz --- include/envoy/stats/scope.h | 6 ++-- include/envoy/stats/symbol_table.h | 1 + source/common/http/codes.cc | 18 +++++++---- source/common/http/codes.h | 13 +------- source/common/router/router.cc | 9 +++--- source/common/router/router.h | 9 ++++-- source/common/stats/isolated_store_impl.cc | 2 +- source/common/stats/isolated_store_impl.h | 12 ++++---- source/common/stats/scope_prefixer.cc | 12 ++++---- source/common/stats/scope_prefixer.h | 12 ++++---- source/common/stats/symbol_table_impl.cc | 15 ++++++++++ source/common/stats/symbol_table_impl.h | 2 ++ source/common/stats/thread_local_store.cc | 14 ++++----- source/common/stats/thread_local_store.h | 18 +++++------ source/exe/main_common.h | 2 +- .../grpc_client_integration_test_harness.h | 2 +- test/common/http/codes_test.cc | 6 ++-- .../stats/thread_local_store_speed_test.cc | 2 +- test/integration/server.h | 30 +++++++++---------- test/mocks/server/mocks.h | 2 +- test/mocks/stats/mocks.cc | 2 +- test/mocks/stats/mocks.h | 12 ++++---- test/test_common/utility.h | 2 +- 23 files changed, 111 insertions(+), 92 deletions(-) diff --git a/include/envoy/stats/scope.h b/include/envoy/stats/scope.h index 70be7f9222a0..824f40e029fd 100644 --- a/include/envoy/stats/scope.h +++ b/include/envoy/stats/scope.h @@ -44,19 +44,19 @@ class Scope { /** * @return a counter within the scope's namespace. */ - virtual Counter& counterx(StatName name) PURE; + virtual Counter& counterFromStatName(StatName name) PURE; virtual Counter& counter(const std::string& name) PURE; /** * @return a gauge within the scope's namespace. */ - virtual Gauge& gaugex(StatName name) PURE; + virtual Gauge& gaugeFromStatName(StatName name) PURE; virtual Gauge& gauge(const std::string& name) PURE; /** * @return a histogram within the scope's namespace with a particular value type. */ - virtual Histogram& histogramx(StatName name) PURE; + virtual Histogram& histogramFromStatName(StatName name) PURE; virtual Histogram& histogram(const std::string& name) PURE; /** diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 7b88c6d4665a..8ada6d78cbcd 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -129,6 +129,7 @@ class SymbolTable { * @return Storage allocated for the joined name. */ virtual StoragePtr join(const std::vector& stat_names) const PURE; + virtual StoragePtr join(StatName a, StatName b) const PURE; #ifndef ENVOY_CONFIG_COVERAGE virtual void debugPrint() const PURE; diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index ba6ca5732d61..bef91f7153b1 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -54,13 +54,18 @@ Stats::StatName CodeStatsImpl::makeStatName(absl::string_view name) { void CodeStatsImpl::incCounter(Stats::Scope& scope, const std::vector& names) const { Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); - scope.counterx(Stats::StatName(stat_name_storage.get())).inc(); + scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); +} + +void CodeStatsImpl::incCounter(Stats::Scope& scope, Stats::StatName a, Stats::StatName b) const { + Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(a, b); + scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); } void CodeStatsImpl::recordHistogram(Stats::Scope& scope, const std::vector& names, uint64_t count) const { Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); - scope.histogramx(Stats::StatName(stat_name_storage.get())).recordValue(count); + scope.histogramFromStatName(Stats::StatName(stat_name_storage.get())).recordValue(count); } void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, @@ -68,9 +73,12 @@ void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName ASSERT(&symbol_table_ == &scope.symbolTable()); // Build a dynamic stat for the response code and increment it. - incCounter(scope, {prefix, upstream_rq_completed_}); - incCounter(scope, {prefix, upstreamRqGroup(response_code)}); - incCounter(scope, {prefix, upstream_rq_.statName(response_code)}); + //incCounter(scope, {prefix, upstream_rq_completed_}); + //incCounter(scope, {prefix, upstreamRqGroup(response_code)}); + //incCounter(scope, {prefix, upstream_rq_.statName(response_code)}); + incCounter(scope, prefix, upstream_rq_completed_); + incCounter(scope, prefix, upstreamRqGroup(response_code)); + incCounter(scope, prefix, upstream_rq_.statName(response_code)); } void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info) const { diff --git a/source/common/http/codes.h b/source/common/http/codes.h index e939892e1b5c..345e9436e508 100644 --- a/source/common/http/codes.h +++ b/source/common/http/codes.h @@ -28,6 +28,7 @@ class CodeStatsImpl : public CodeStats { friend class CodeStatsTest; void incCounter(Stats::Scope& scope, const std::vector& names) const; + void incCounter(Stats::Scope& scope, Stats::StatName a, Stats::StatName b) const; void recordHistogram(Stats::Scope& scope, const std::vector& names, uint64_t count) const; @@ -38,18 +39,6 @@ class CodeStatsImpl : public CodeStats { Stats::StatName statName(Code response_code); - /* - using LockedStatName = std::pair>; - - // We'll cover known HTTP status codes in a mapped array, which we'll - // discover by calling CodeUtility::toString(). Of course the response-code - // can be any 64-bit integer as far as we can tell from this class, so - // we'll have a fallback flast hash map for those. - - constexpr MaxResponseCode = 600; - LockStatName[MaxResponseCode] locked_stat_names_; - */ - private: // Use an array of atomic pointers to hold StatNameStorage objects for // every conceivable HTTP response code. In the hot-path we'll reference diff --git a/source/common/router/router.cc b/source/common/router/router.cc index cfb586978859..c1d631f38a4d 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -180,15 +180,14 @@ FilterUtility::finalTimeout(const RouteEntry& route, Http::HeaderMap& request_he } Filter::Filter(FilterConfig& config) - : config_(config), retry_("retry", config.scope_.symbolTable()), - downstream_response_started_(false), downstream_end_stream_(false), do_shadowing_(false), - is_retry_(false), attempting_internal_redirect_with_complete_stream_(false) {} + : config_(config), downstream_response_started_(false), downstream_end_stream_(false), + do_shadowing_(false), is_retry_(false), + attempting_internal_redirect_with_complete_stream_(false) {} Filter::~Filter() { // Upstream resources should already have been cleaned. ASSERT(!upstream_request_); ASSERT(!retry_state_); - retry_.free(config_.scope_.symbolTable()); } const std::string Filter::upstreamZone(Upstream::HostDescriptionConstSharedPtr upstream_host) { @@ -709,7 +708,7 @@ void Filter::onUpstreamHeaders(const uint64_t response_code, Http::HeaderMapPtr& const auto upstream_host = upstream_request_->upstream_host_; if (retry_status == RetryStatus::Yes && setupRetry(end_stream)) { Http::CodeStats& code_stats = httpContext().codeStats(); - code_stats.chargeBasicResponseStat(cluster_->statsScope(), retry_.statName(), + code_stats.chargeBasicResponseStat(cluster_->statsScope(), config_.retry_.statName(), static_cast(response_code)); upstream_host->stats().rq_error_.inc(); return; diff --git a/source/common/router/router.h b/source/common/router/router.h index a5d2ced2f6df..56b5b2af180d 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -105,7 +105,8 @@ class FilterConfig { random_(random), stats_{ALL_ROUTER_STATS(POOL_COUNTER_PREFIX(scope, stat_prefix))}, emit_dynamic_stats_(emit_dynamic_stats), start_child_span_(start_child_span), suppress_envoy_headers_(suppress_envoy_headers), http_context_(http_context), - shadow_writer_(std::move(shadow_writer)), time_source_(time_source) {} + retry_("retry", scope_.symbolTable()), shadow_writer_(std::move(shadow_writer)), + time_source_(time_source) {} FilterConfig(const std::string& stat_prefix, Server::Configuration::FactoryContext& context, ShadowWriterPtr&& shadow_writer, @@ -120,6 +121,10 @@ class FilterConfig { } } + ~FilterConfig() { + retry_.free(scope_.symbolTable()); + } + ShadowWriter& shadowWriter() { return *shadow_writer_; } TimeSource& timeSource() { return time_source_; } @@ -134,6 +139,7 @@ class FilterConfig { const bool suppress_envoy_headers_; std::list upstream_logs_; Http::Context& http_context_; + Stats::StatNameStorage retry_; private: ShadowWriterPtr shadow_writer_; @@ -405,7 +411,6 @@ class Filter : Logger::Loggable, MonotonicTime downstream_request_complete_time_; uint32_t buffer_limit_{0}; MetadataMatchCriteriaConstPtr metadata_match_; - Stats::StatNameStorage retry_; // list of cookies to add to upstream headers std::vector downstream_set_cookies_; diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index bbee9bc1f87e..a6c57b279a64 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -15,7 +15,7 @@ namespace Envoy { namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() - : IsolatedStoreImpl(std::make_unique()) {} + : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 459cd76dcdcf..41f87ca66c0d 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -64,11 +64,11 @@ class IsolatedStoreImpl : public Store { explicit IsolatedStoreImpl(SymbolTable& symbol_table); // Stats::Scope - Counter& counterx(StatName name) override { return counters_.get(name); } + Counter& counterFromStatName(StatName name) override { return counters_.get(name); } ScopePtr createScope(const std::string& name) override; void deliverHistogramToSinks(const Histogram&, uint64_t) override {} - Gauge& gaugex(StatName name) override { return gauges_.get(name); } - Histogram& histogramx(StatName name) override { + Gauge& gaugeFromStatName(StatName name) override { return gauges_.get(name); } + Histogram& histogramFromStatName(StatName name) override { Histogram& histogram = histograms_.get(name); return histogram; } @@ -85,15 +85,15 @@ class IsolatedStoreImpl : public Store { Counter& counter(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return counterx(storage.statName()); + return counterFromStatName(storage.statName()); } Gauge& gauge(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return gaugex(storage.statName()); + return gaugeFromStatName(storage.statName()); } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return histogramx(storage.statName()); + return histogramFromStatName(storage.statName()); } void clear(); diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index ddb82340cee9..aa00a3a67d0e 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -34,22 +34,22 @@ ScopePtr ScopePrefixer::createScope(const std::string& name) { *scope_); } -Counter& ScopePrefixer::counterx(StatName name) { +Counter& ScopePrefixer::counterFromStatName(StatName name) { Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join({prefix_.statName(), name}); - return scope_->counterx(StatName(stat_name_storage.get())); + return scope_->counterFromStatName(StatName(stat_name_storage.get())); } -Gauge& ScopePrefixer::gaugex(StatName name) { +Gauge& ScopePrefixer::gaugeFromStatName(StatName name) { Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join({prefix_.statName(), name}); - return scope_->gaugex(StatName(stat_name_storage.get())); + return scope_->gaugeFromStatName(StatName(stat_name_storage.get())); } -Histogram& ScopePrefixer::histogramx(StatName name) { +Histogram& ScopePrefixer::histogramFromStatName(StatName name) { Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join({prefix_.statName(), name}); - return scope_->histogramx(StatName(stat_name_storage.get())); + return scope_->histogramFromStatName(StatName(stat_name_storage.get())); } void ScopePrefixer::deliverHistogramToSinks(const Histogram& histograms, uint64_t val) { diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index c8abcc548176..1758a1f6e769 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -21,22 +21,22 @@ class ScopePrefixer : public Scope { // Scope ScopePtr createScope(const std::string& name) override; - Counter& counterx(StatName name) override; - Gauge& gaugex(StatName name) override; - Histogram& histogramx(StatName name) override; + Counter& counterFromStatName(StatName name) override; + Gauge& gaugeFromStatName(StatName name) override; + Histogram& histogramFromStatName(StatName name) override; void deliverHistogramToSinks(const Histogram& histograms, uint64_t val) override; Counter& counter(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return counterx(storage.statName()); + return counterFromStatName(storage.statName()); } Gauge& gauge(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return gaugex(storage.statName()); + return gaugeFromStatName(storage.statName()); } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return histogramx(storage.statName()); + return histogramFromStatName(storage.statName()); } const Stats::StatsOptions& statsOptions() const override { return scope_->statsOptions(); } diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 4608886511fb..e002c1a81371 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -283,6 +283,10 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { #ifndef ENVOY_CONFIG_COVERAGE void SymbolTableImpl::debugPrint() const { Thread::LockGuard lock(lock_); + debugPrintLockHeld(); +} + +void SymbolTableImpl::debugPrintLockHeld() const { std::vector symbols; for (const auto& p : decode_map_) { symbols.push_back(p.first); @@ -337,6 +341,17 @@ SymbolTable::StoragePtr SymbolTableImpl::join(const std::vector& stat_ return bytes; } +SymbolTable::StoragePtr SymbolTableImpl::join(StatName a, StatName b) const { + uint64_t a_size = a.dataSize(); + uint64_t b_size = b.dataSize(); + uint64_t num_bytes = a_size + b_size; + auto bytes = std::make_unique(num_bytes + StatNameSizeEncodingBytes); + uint8_t* p = saveLengthToBytesReturningNext(num_bytes, bytes.get()); + memcpy(p, a.data(), a_size); + memcpy(p + a_size, b.data(), b_size); + return bytes; +} + StatNameList::~StatNameList() { ASSERT(!populated()); } void StatNameList::populate(const std::vector& names, diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index c65c41c033df..0257c71934b1 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -141,9 +141,11 @@ class SymbolTableImpl : public SymbolTable { void free(const StatName& stat_name) override; void incRefCount(const StatName& stat_name) override; SymbolTable::StoragePtr join(const std::vector& stat_names) const override; + SymbolTable::StoragePtr join(StatName a, StatName b) const override; #ifndef ENVOY_CONFIG_COVERAGE void debugPrint() const override; + void debugPrintLockHeld() const EXCLUSIVE_LOCKS_REQUIRED(lock_); #endif private: diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 41fdf0d93fc1..fd47d3162072 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -28,7 +28,7 @@ ThreadLocalStoreImpl::ThreadLocalStoreImpl(const StatsOptions& stats_options, tag_producer_(std::make_unique()), stats_matcher_(std::make_unique()), stats_overflow_("stats.overflow", alloc.symbolTable()), - num_last_resort_stats_(default_scope_->counterx(stats_overflow_.statName())), + num_last_resort_stats_(default_scope_->counterFromStatName(stats_overflow_.statName())), heap_allocator_(alloc.symbolTable()), source_(*this), null_counter_(alloc.symbolTable()), null_gauge_(alloc.symbolTable()), null_histogram_(alloc.symbolTable()) {} @@ -330,7 +330,7 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( return **central_ref; } -Counter& ThreadLocalStoreImpl::ScopeImpl::counterx(StatName name) { +Counter& ThreadLocalStoreImpl::ScopeImpl::counterFromStatName(StatName name) { // Determine the final name based on the prefix and the passed name. // // Note that we can do map.find(final_name.c_str()), but we cannot do @@ -379,8 +379,8 @@ void ThreadLocalStoreImpl::ScopeImpl::deliverHistogramToSinks(const Histogram& h } } -Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugex(StatName name) { - // See comments in counterx(). There is no super clean way (via templates or otherwise) to +Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugeFromStatName(StatName name) { + // See comments in counterFromStatName(). There is no super clean way (via templates or otherwise) to // share this code so I'm leaving it largely duplicated for now. // // Note that we can do map.find(final_name.c_str()), but we cannot do @@ -409,8 +409,8 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugex(StatName name) { tls_cache); } -Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramx(StatName name) { - // See comments in counterx(). There is no super clean way (via templates or otherwise) to +Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramFromStatName(StatName name) { + // See comments in counterFromStatName(). There is no super clean way (via templates or otherwise) to // share this code so I'm leaving it largely duplicated for now. // // Note that we can do map.find(final_name.c_str()), but we cannot do @@ -460,7 +460,7 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(StatName name, return parent_.null_histogram_; } - // See comments in counterx() which explains the logic here. + // See comments in counterFromStatName() which explains the logic here. StatMap* tls_cache = nullptr; if (!parent_.shutting_down_ && parent_.tls_) { diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 01b727cc6d61..d91e31b7cadf 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -143,15 +143,15 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ~ThreadLocalStoreImpl() override; // Stats::Scope - Counter& counterx(StatName name) override { return default_scope_->counterx(name); } + Counter& counterFromStatName(StatName name) override { return default_scope_->counterFromStatName(name); } Counter& counter(const std::string& name) override { return default_scope_->counter(name); } ScopePtr createScope(const std::string& name) override; void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override { return default_scope_->deliverHistogramToSinks(histogram, value); } - Gauge& gaugex(StatName name) override { return default_scope_->gaugex(name); } + Gauge& gaugeFromStatName(StatName name) override { return default_scope_->gaugeFromStatName(name); } Gauge& gauge(const std::string& name) override { return default_scope_->gauge(name); } - Histogram& histogramx(StatName name) override { return default_scope_->histogramx(name); } + Histogram& histogramFromStatName(StatName name) override { return default_scope_->histogramFromStatName(name); } Histogram& histogram(const std::string& name) override { return default_scope_->histogram(name); } const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } SymbolTable& symbolTable() override { return alloc_.symbolTable(); } @@ -200,10 +200,10 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ~ScopeImpl() override; // Stats::Scope - Counter& counterx(StatName name) override; + Counter& counterFromStatName(StatName name) override; void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override; - Gauge& gaugex(StatName name) override; - Histogram& histogramx(StatName name) override; + Gauge& gaugeFromStatName(StatName name) override; + Histogram& histogramFromStatName(StatName name) override; Histogram& tlsHistogram(StatName name, ParentHistogramImpl& parent) override; const Stats::StatsOptions& statsOptions() const override { return parent_.statsOptions(); } ScopePtr createScope(const std::string& name) override { @@ -215,15 +215,15 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Counter& counter(const std::string& name) override { // std::cerr << "counter(" << name << ")" << std::endl; StatNameTempStorage storage(name, symbolTable()); - return counterx(storage.statName()); + return counterFromStatName(storage.statName()); } Gauge& gauge(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return gaugex(storage.statName()); + return gaugeFromStatName(storage.statName()); } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return histogramx(storage.statName()); + return histogramFromStatName(storage.statName()); } template diff --git a/source/exe/main_common.h b/source/exe/main_common.h index e56a6013bbd5..6afeed0bad33 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -65,7 +65,7 @@ class MainCommonBase { protected: const Envoy::OptionsImpl& options_; - Stats::FakeSymbolTableImpl symbol_table_; + Stats::SymbolTableImpl symbol_table_; Server::ComponentFactory& component_factory_; Thread::ThreadFactory& thread_factory_; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 831f357e509e..d2eb5a6ede93 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -415,7 +415,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { FakeHttpConnectionPtr fake_connection_; std::vector fake_streams_; const Protobuf::MethodDescriptor* method_descriptor_; - Envoy::Test::Global symbol_table_; + Envoy::Test::Global symbol_table_; Stats::IsolatedStoreImpl* stats_store_ = new Stats::IsolatedStoreImpl(*symbol_table_); Api::ApiPtr api_; Event::DispatcherPtr dispatcher_; diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index c5c5a6e7902b..662d363aa17d 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -25,7 +25,7 @@ namespace Http { class CodeUtilityTest : public testing::Test { public: CodeUtilityTest() - : global_store_(symbol_table_), cluster_scope_(symbol_table_), code_stats_(symbol_table_) {} + : global_store_(*symbol_table_), cluster_scope_(*symbol_table_), code_stats_(*symbol_table_) {} void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, @@ -39,7 +39,7 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } - Stats::FakeSymbolTableImpl symbol_table_; + Envoy::Test::Global symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; @@ -236,7 +236,7 @@ TEST_F(CodeUtilityTest, ResponseTimingTest) { EXPECT_CALL(cluster_scope, deliverHistogramToSinks( Property(&Stats::Metric::name, "prefix.zone.from_az.to_az.upstream_rq_time"), 5)); - Http::CodeStatsImpl code_stats(symbol_table_); + Http::CodeStatsImpl code_stats(*symbol_table_); code_stats.chargeResponseTiming(info); } diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 4bbbd6e654aa..5717026c6026 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -43,7 +43,7 @@ class ThreadLocalStorePerf { void accessCounters() { for (auto& stat_name_storage : stat_names_) { - store_.counterx(stat_name_storage->statName()); + store_.counterFromStatName(stat_name_storage->statName()); } } diff --git a/test/integration/server.h b/test/integration/server.h index d274beef5603..93cc37b636a0 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -75,32 +75,32 @@ class TestScopeWrapper : public Scope { wrapped_scope_->deliverHistogramToSinks(histogram, value); } - Counter& counterx(StatName name) override { + Counter& counterFromStatName(StatName name) override { Thread::LockGuard lock(lock_); - return wrapped_scope_->counterx(name); + return wrapped_scope_->counterFromStatName(name); } - Gauge& gaugex(StatName name) override { + Gauge& gaugeFromStatName(StatName name) override { Thread::LockGuard lock(lock_); - return wrapped_scope_->gaugex(name); + return wrapped_scope_->gaugeFromStatName(name); } - Histogram& histogramx(StatName name) override { + Histogram& histogramFromStatName(StatName name) override { Thread::LockGuard lock(lock_); - return wrapped_scope_->histogramx(name); + return wrapped_scope_->histogramFromStatName(name); } Counter& counter(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return counterx(storage.statName()); + return counterFromStatName(storage.statName()); } Gauge& gauge(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return gaugex(storage.statName()); + return gaugeFromStatName(storage.statName()); } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); - return histogramx(storage.statName()); + return histogramFromStatName(storage.statName()); } const SymbolTable& symbolTable() const override { return wrapped_scope_->symbolTable(); } @@ -121,9 +121,9 @@ class TestIsolatedStoreImpl : public StoreRoot { public: TestIsolatedStoreImpl() : source_(*this) {} // Stats::Scope - Counter& counterx(StatName name) override { + Counter& counterFromStatName(StatName name) override { Thread::LockGuard lock(lock_); - return store_.counterx(name); + return store_.counterFromStatName(name); } Counter& counter(const std::string& name) override { Thread::LockGuard lock(lock_); @@ -134,17 +134,17 @@ class TestIsolatedStoreImpl : public StoreRoot { return ScopePtr{new TestScopeWrapper(lock_, store_.createScope(name))}; } void deliverHistogramToSinks(const Histogram&, uint64_t) override {} - Gauge& gaugex(StatName name) override { + Gauge& gaugeFromStatName(StatName name) override { Thread::LockGuard lock(lock_); - return store_.gaugex(name); + return store_.gaugeFromStatName(name); } Gauge& gauge(const std::string& name) override { Thread::LockGuard lock(lock_); return store_.gauge(name); } - Histogram& histogramx(StatName name) override { + Histogram& histogramFromStatName(StatName name) override { Thread::LockGuard lock(lock_); - return store_.histogramx(name); + return store_.histogramFromStatName(name); } Histogram& histogram(const std::string& name) override { Thread::LockGuard lock(lock_); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index da484c4c2bac..c04fc99160f9 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -207,7 +207,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::StatDataAllocator&()); private: - Test::Global symbol_table_; + Test::Global symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::HeapStatDataAllocator stats_allocator_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 3c36096a6314..fb2f7ffb1c7c 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -96,7 +96,7 @@ MockStore::MockStore() { MockStore::~MockStore() {} MockIsolatedStatsStore::MockIsolatedStatsStore() - : IsolatedStoreImpl(Test::Global::get()) {} + : IsolatedStoreImpl(Test::Global::get()) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } } // namespace Stats diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index c62da6361f64..f521a823c1a3 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -63,7 +63,7 @@ class MockMetric : public virtual Metric { } StatName tagExtractedStatName() const override { return tag_extracted_stat_name_->statName(); } - Test::Global symbol_table_; // Must outlive name_. + Test::Global symbol_table_; // Must outlive name_. MetricName name_; std::vector tags_; @@ -178,14 +178,14 @@ class MockStore : public Store { MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); - Counter& counterx(StatName name) override { return counter(symbol_table_->toString(name)); } - Gauge& gaugex(StatName name) override { return gauge(symbol_table_->toString(name)); } - Histogram& histogramx(StatName name) override { return histogram(symbol_table_->toString(name)); } + Counter& counterFromStatName(StatName name) override { return counter(symbol_table_->toString(name)); } + Gauge& gaugeFromStatName(StatName name) override { return gauge(symbol_table_->toString(name)); } + Histogram& histogramFromStatName(StatName name) override { return histogram(symbol_table_->toString(name)); } SymbolTable& symbolTable() override { return symbol_table_.get(); } const SymbolTable& symbolTable() const override { return symbol_table_.get(); } - Test::Global symbol_table_; + Test::Global symbol_table_; testing::NiceMock counter_; std::vector> histograms_; StatsOptionsImpl stats_options_; @@ -195,7 +195,7 @@ class MockStore : public Store { * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : private Test::Global, +class MockIsolatedStatsStore : private Test::Global, public IsolatedStoreImpl { public: MockIsolatedStatsStore(); diff --git a/test/test_common/utility.h b/test/test_common/utility.h index dbd0055c7de1..33fd8b7b6f8e 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -491,7 +491,7 @@ class TestAllocator : public RawStatDataAllocator { ~TestAllocator() { EXPECT_EQ(0, hash_set_.size()); } private: - FakeSymbolTableImpl symbol_table_; + SymbolTableImpl symbol_table_; Thread::MutexBasicLockable mutex_; TestBlockMemoryHashSetOptions block_hash_options_; std::unique_ptr block_memory_; From 8bd401bbe6a6190f19fc0ba8081f1f7114941671 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 9 Mar 2019 10:07:07 -0500 Subject: [PATCH 050/106] format Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/http/codes.cc | 6 +++--- source/common/stats/isolated_store_impl.cc | 3 +-- source/common/stats/symbol_table_impl.h | 2 +- source/common/stats/thread_local_store.cc | 7 ++++--- source/common/stats/thread_local_store.h | 12 +++++++++--- test/common/http/codes_test.cc | 3 ++- test/common/stats/isolated_store_impl_test.cc | 7 +++---- test/mocks/stats/mocks.h | 4 +++- 9 files changed, 27 insertions(+), 19 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 5450e49939dc..9695a1571e6b 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -129,7 +129,7 @@ class SymbolTable { * @return Storage allocated for the joined name. */ virtual StoragePtr join(const std::vector& stat_names) const PURE; - //virtual StoragePtr join(StatName a, StatName b) const PURE; + // virtual StoragePtr join(StatName a, StatName b) const PURE; #ifndef ENVOY_CONFIG_COVERAGE virtual void debugPrint() const PURE; diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index ad0004b8a9e1..0e412461ee55 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -73,9 +73,9 @@ void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName ASSERT(&symbol_table_ == &scope.symbolTable()); // Build a dynamic stat for the response code and increment it. - //incCounter(scope, {prefix, upstream_rq_completed_}); - //incCounter(scope, {prefix, upstreamRqGroup(response_code)}); - //incCounter(scope, {prefix, upstream_rq_.statName(response_code)}); + // incCounter(scope, {prefix, upstream_rq_completed_}); + // incCounter(scope, {prefix, upstreamRqGroup(response_code)}); + // incCounter(scope, {prefix, upstream_rq_.statName(response_code)}); incCounter(scope, prefix, upstream_rq_completed_); incCounter(scope, prefix, upstreamRqGroup(response_code)); incCounter(scope, prefix, upstream_rq_.statName(response_code)); diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 14268bf2e460..fb9ac34c4402 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -14,8 +14,7 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() - : IsolatedStoreImpl(std::make_unique()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 409d1d434d87..46baba007183 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -141,7 +141,7 @@ class SymbolTableImpl : public SymbolTable { void free(const StatName& stat_name) override; void incRefCount(const StatName& stat_name) override; SymbolTable::StoragePtr join(const std::vector& stat_names) const override; - //SymbolTable::StoragePtr join(StatName a, StatName b) const override; + // SymbolTable::StoragePtr join(StatName a, StatName b) const override; #ifndef ENVOY_CONFIG_COVERAGE void debugPrint() const override; diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 9bdcd0ba5c6a..761a94a3c69c 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -398,8 +398,8 @@ void ThreadLocalStoreImpl::ScopeImpl::deliverHistogramToSinks(const Histogram& h } Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugeFromStatName(StatName name) { - // See comments in counterFromStatName(). There is no super clean way (via templates or otherwise) to - // share this code so I'm leaving it largely duplicated for now. + // See comments in counterFromStatName(). There is no super clean way (via templates or otherwise) + // to share this code so I'm leaving it largely duplicated for now. // // Note that we can do map.find(final_name.c_str()), but we cannot do // map[final_name.c_str()] as the char*-keyed maps would then save the pointer to @@ -427,7 +427,8 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugeFromStatName(StatName name) { tls_cache); } - // See comments in counterFromStatName(). There is no super clean way (via templates or otherwise) to +// See comments in counterFromStatName(). There is no super clean way (via templates or otherwise) +// to BoolIndicator& ThreadLocalStoreImpl::ScopeImpl::boolIndicatorFromStatName(StatName name) { // See comments in counter(). There is no super clean way (via templates or otherwise) to diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index b1d15d44e397..917d049b2391 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -143,13 +143,17 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo ~ThreadLocalStoreImpl() override; // Stats::Scope - Counter& counterFromStatName(StatName name) override { return default_scope_->counterFromStatName(name); } + Counter& counterFromStatName(StatName name) override { + return default_scope_->counterFromStatName(name); + } Counter& counter(const std::string& name) override { return default_scope_->counter(name); } ScopePtr createScope(const std::string& name) override; void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override { return default_scope_->deliverHistogramToSinks(histogram, value); } - Gauge& gaugeFromStatName(StatName name) override { return default_scope_->gaugeFromStatName(name); } + Gauge& gaugeFromStatName(StatName name) override { + return default_scope_->gaugeFromStatName(name); + } Gauge& gauge(const std::string& name) override { return default_scope_->gauge(name); } BoolIndicator& boolIndicatorFromStatName(StatName name) override { return default_scope_->boolIndicatorFromStatName(name); @@ -157,7 +161,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo BoolIndicator& boolIndicator(const std::string& name) override { return default_scope_->boolIndicator(name); } - Histogram& histogramFromStatName(StatName name) override { return default_scope_->histogramFromStatName(name); } + Histogram& histogramFromStatName(StatName name) override { + return default_scope_->histogramFromStatName(name); + } Histogram& histogram(const std::string& name) override { return default_scope_->histogram(name); } const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } SymbolTable& symbolTable() override { return alloc_.symbolTable(); } diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index 662d363aa17d..b2a5ab8e1d1e 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -25,7 +25,8 @@ namespace Http { class CodeUtilityTest : public testing::Test { public: CodeUtilityTest() - : global_store_(*symbol_table_), cluster_scope_(*symbol_table_), code_stats_(*symbol_table_) {} + : global_store_(*symbol_table_), cluster_scope_(*symbol_table_), code_stats_(*symbol_table_) { + } void addResponse(uint64_t code, bool canary, bool internal_request, const std::string& request_vhost_name = EMPTY_STRING, diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index ff3dc3ecf41b..68a05123032d 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -95,10 +95,9 @@ struct TestStats { }; TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { - TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(store_, "test."), - POOL_GAUGE_PREFIX(store_, "test."), - POOL_BOOL_INDICATOR_PREFIX(store_, "test."), - POOL_HISTOGRAM_PREFIX(store_, "test."))}; + TestStats test_stats{ALL_TEST_STATS( + POOL_COUNTER_PREFIX(store_, "test."), POOL_GAUGE_PREFIX(store_, "test."), + POOL_BOOL_INDICATOR_PREFIX(store_, "test."), POOL_HISTOGRAM_PREFIX(store_, "test."))}; Counter& counter = test_stats.test_counter_; EXPECT_EQ("test.test_counter", counter.name()); diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 5c29f5306064..64d13ea411a4 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -196,7 +196,9 @@ class MockStore : public Store { MOCK_CONST_METHOD0(histograms, std::vector()); MOCK_CONST_METHOD0(statsOptions, const StatsOptions&()); - Counter& counterFromStatName(StatName name) override { return counter(symbol_table_->toString(name)); } + Counter& counterFromStatName(StatName name) override { + return counter(symbol_table_->toString(name)); + } Gauge& gaugeFromStatName(StatName name) override { return gauge(symbol_table_->toString(name)); } BoolIndicator& boolIndicatorFromStatName(StatName name) override { return boolIndicator(symbol_table_->toString(name)); From 494f366bcb97b1d311820654fe285fae18299883 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 9 Mar 2019 10:38:02 -0500 Subject: [PATCH 051/106] All tests working. Signed-off-by: Joshua Marantz --- test/mocks/server/mocks.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 5813e5ee7adb..4354f5828034 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -37,6 +37,7 @@ #include "test/mocks/router/mocks.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/secret/mocks.h" +#include "test/mocks/stats/mocks.h" #include "test/mocks/thread_local/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/mocks.h" @@ -364,7 +365,7 @@ class MockInstance : public Instance { std::unique_ptr secret_manager_; testing::NiceMock thread_local_; - Stats::IsolatedStoreImpl stats_store_; + NiceMock stats_store_; std::shared_ptr> dns_resolver_{ new testing::NiceMock()}; testing::NiceMock api_; From aa6063804bae967c9a28b710f871c8dbfeab4227 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 9 Mar 2019 11:22:31 -0500 Subject: [PATCH 052/106] spelling Signed-off-by: Joshua Marantz --- source/common/http/codes.cc | 2 +- source/common/stats/thread_local_store.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/common/http/codes.cc b/source/common/http/codes.cc index 0e412461ee55..f97374dd4669 100644 --- a/source/common/http/codes.cc +++ b/source/common/http/codes.cc @@ -239,7 +239,7 @@ Stats::StatName CodeStatsImpl::RequestCodeGroup::statName(Code response_code) { std::string CodeUtility::groupStringForResponseCode(Code response_code) { // Note: this is only used in the unit test and in dynamo_filter.cc, which - // needs the same sort of symbloziation treatment we are doing here. + // needs the same sort of symbolization treatment we are doing here. if (CodeUtility::is1xx(enumToInt(response_code))) { return "1xx"; } else if (CodeUtility::is2xx(enumToInt(response_code))) { diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 917d049b2391..87ec9a7c0747 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -230,7 +230,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo SymbolTable& symbolTable() override { return parent_.symbolTable(); } Counter& counter(const std::string& name) override { - // std::cerr << "counter(" << name << ")" << std::endl; StatNameTempStorage storage(name, symbolTable()); return counterFromStatName(storage.statName()); } From b312ecd00c102349616c65c25bc7009ea2908c70 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 15 Mar 2019 14:53:28 -0400 Subject: [PATCH 053/106] Backed out a few changes that are not needed. Signed-off-by: Joshua Marantz --- source/common/http/user_agent.cc | 3 +-- source/common/upstream/cluster_manager_impl.h | 1 - test/common/http/user_agent_test.cc | 2 +- test/integration/integration.cc | 1 - test/integration/integration.h | 4 ---- test/integration/integration_admin_test.cc | 2 +- test/test_common/utility.cc | 4 ---- test/test_common/utility.h | 8 -------- 8 files changed, 3 insertions(+), 22 deletions(-) diff --git a/source/common/http/user_agent.cc b/source/common/http/user_agent.cc index 75c4befe294c..57cb82e57103 100644 --- a/source/common/http/user_agent.cc +++ b/source/common/http/user_agent.cc @@ -21,8 +21,7 @@ void UserAgent::completeConnectionLength(Stats::Timespan& span) { return; } - Stats::Histogram& histogram = scope_->histogram(prefix_ + "downstream_cx_length_ms"); - histogram.recordValue(span.getRawDuration().count()); + scope_->histogram(prefix_ + "downstream_cx_length_ms").recordValue(span.getRawDuration().count()); } void UserAgent::initializeFromHeaders(const HeaderMap& headers, const std::string& prefix, diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 23d23610121c..0e9e8eb52667 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -451,7 +451,6 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable original_histogram; //(stat_store.symbolTable()); + NiceMock original_histogram; Event::SimulatedTimeSystem time_system; Stats::Timespan span(original_histogram, time_system); diff --git a/test/integration/integration.cc b/test/integration/integration.cc index 25a0a5415c81..e38b97c209ee 100644 --- a/test/integration/integration.cc +++ b/test/integration/integration.cc @@ -389,7 +389,6 @@ void BaseIntegrationTest::createGeneratedApiTestServer(const std::string& bootst test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); registerTestServerPorts(port_names); } - // stats_ = std::make_unique(test_server_->stats().symbolTable()); } void BaseIntegrationTest::createApiTestServer(const ApiFilesystemConfig& api_filesystem_config, diff --git a/test/integration/integration.h b/test/integration/integration.h index 9e451fd7da16..cb7e8c937b7f 100644 --- a/test/integration/integration.h +++ b/test/integration/integration.h @@ -13,7 +13,6 @@ #include "test/integration/utility.h" #include "test/mocks/buffer/mocks.h" #include "test/mocks/server/mocks.h" -#include "test/mocks/stats/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" @@ -245,7 +244,6 @@ class BaseIntegrationTest : Logger::Loggable { public: Event::DispatcherPtr dispatcher_; - // Api::ApiPtr api_; /** * Open a connection to Envoy, send a series of bytes, and return the @@ -287,8 +285,6 @@ class BaseIntegrationTest : Logger::Loggable { uint32_t fake_upstreams_count_{1}; spdlog::level::level_enum default_log_level_; IntegrationTestServerPtr test_server_; - // std::unique_ptr stats_; - // A map of keys to port names. Generally the names are pulled from the v2 listener name // but if a listener is created via ADS, it will be from whatever key is used with registerPort. TestEnvironment::PortMap port_map_; diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 9f8cc10fae93..2ce9f3b73b34 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -465,7 +465,7 @@ class StatsMatcherIntegrationTest } void makeRequest() { response_ = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats", "", - downstreamProtocol(), version_, "host", ""); + downstreamProtocol(), version_); ASSERT_TRUE(response_->complete()); EXPECT_STREQ("200", response_->headers().Status()->value().c_str()); } diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index e5c5df635983..38c1ec6e7665 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -141,10 +141,6 @@ Stats::GaugeSharedPtr TestUtility::findGauge(Stats::Store& store, const std::str return findByName(store.gauges(), name); } -Stats::HistogramSharedPtr TestUtility::findHistogram(Stats::Store& store, const std::string& name) { - return findByName(store.histograms(), name); -} - Stats::BoolIndicatorSharedPtr TestUtility::findBoolIndicator(Stats::Store& store, const std::string& name) { return findByName(store.boolIndicators(), name); diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 47e862a1823c..2d1660f2f0fe 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -165,14 +165,6 @@ class TestUtility { */ static Stats::GaugeSharedPtr findGauge(Stats::Store& store, const std::string& name); - /** - * Find a histogram in a stats store. - * @param store supplies the stats store. - * @param name supplies the name to search for. - * @return Stats::HistogramSharedPtr the gauge or nullptr if there is none. - */ - static Stats::HistogramSharedPtr findHistogram(Stats::Store& store, const std::string& name); - /** * Find a bool in a stats store. * @param store supplies the stats store. From a0f0c9e79b2c0230bea97c299e50a452e94accee Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 15 Mar 2019 15:25:01 -0400 Subject: [PATCH 054/106] simplify the router changes. Signed-off-by: Joshua Marantz --- source/common/router/router.cc | 5 ----- source/common/router/router.h | 8 ++++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 3522e369dc2c..de527e072358 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -179,11 +179,6 @@ FilterUtility::finalTimeout(const RouteEntry& route, Http::HeaderMap& request_he return timeout; } -Filter::Filter(FilterConfig& config) - : config_(config), downstream_response_started_(false), downstream_end_stream_(false), - do_shadowing_(false), is_retry_(false), - attempting_internal_redirect_with_complete_stream_(false) {} - Filter::~Filter() { // Upstream resources should already have been cleaned. ASSERT(!upstream_request_); diff --git a/source/common/router/router.h b/source/common/router/router.h index 1dfba30cc786..1171c3ddf619 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -155,8 +155,12 @@ class Filter : Logger::Loggable, public Http::StreamDecoderFilter, public Upstream::LoadBalancerContextBase { public: - explicit Filter(FilterConfig& config); - ~Filter() override; + Filter(FilterConfig& config) + : config_(config), downstream_response_started_(false), downstream_end_stream_(false), + do_shadowing_(false), is_retry_(false), + attempting_internal_redirect_with_complete_stream_(false) {} + + ~Filter(); // Http::StreamFilterBase void onDestroy() override; From 156434e3771b52276a1c0749c4952fafea9ba256 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 15 Mar 2019 15:29:11 -0400 Subject: [PATCH 055/106] Clean up router changes slightly. Signed-off-by: Joshua Marantz --- source/common/router/router.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/common/router/router.h b/source/common/router/router.h index 1171c3ddf619..c2a1eede412f 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -121,9 +121,7 @@ class FilterConfig { } } - ~FilterConfig() { - retry_.free(scope_.symbolTable()); - } + ~FilterConfig() { retry_.free(scope_.symbolTable()); } ShadowWriter& shadowWriter() { return *shadow_writer_; } TimeSource& timeSource() { return time_source_; } From aa6ecffa257c02570bdbecd9347e776e3d2b63f0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 4 Apr 2019 11:03:04 -0400 Subject: [PATCH 056/106] checkpoint merge; does not compile. Signed-off-by: Joshua Marantz --- include/envoy/server/instance.h | 5 ----- source/common/stats/isolated_store_impl.cc | 4 +++- source/common/stats/isolated_store_impl.h | 7 ++++--- source/extensions/stat_sinks/hystrix/hystrix.cc | 4 ++-- source/server/config_validation/server.h | 1 - source/server/server.h | 1 - test/mocks/server/mocks.h | 1 - test/mocks/stats/mocks.cc | 2 +- 8 files changed, 10 insertions(+), 15 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 9a09d81b7d14..a3d548eb3c77 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -203,11 +203,6 @@ class Instance { */ virtual TimeSource& timeSource() PURE; - /** - * @return the statistics symbol table. - */ - virtual Stats::SymbolTable& symbolTable() PURE; - /** * @return the flush interval of stats sinks. */ diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index fb9ac34c4402..e9e0fca9ba8d 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -16,7 +16,7 @@ namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} -IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr symbol_table) +IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr&& symbol_table) : IsolatedStoreImpl(*symbol_table) { symbol_table_storage_ = std::move(symbol_table); } @@ -42,11 +42,13 @@ ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { return std::make_unique(name, *this); } +/* void IsolatedStoreImpl::clear() { counters_.clear(); gauges_.clear(); histograms_.clear(); } +*/ } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 6ef35136c2cf..f49246e357b6 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -50,7 +50,7 @@ template class IsolatedStatsCache { return vec; } - void clear() { stats_.clear(); } + // void clear() { stats_.clear(); } private: StatNameHashMap> stats_; @@ -60,7 +60,6 @@ template class IsolatedStatsCache { class IsolatedStoreImpl : public Store { public: IsolatedStoreImpl(); - explicit IsolatedStoreImpl(std::unique_ptr symbol_table); explicit IsolatedStoreImpl(SymbolTable& symbol_table); // Stats::Scope @@ -103,9 +102,11 @@ class IsolatedStoreImpl : public Store { return histogramFromStatName(storage.statName()); } - void clear(); + //void clear(); private: + IsolatedStoreImpl(std::unique_ptr&& symbol_table); + std::unique_ptr symbol_table_storage_; SymbolTable& symbol_table_; HeapStatDataAllocator alloc_; diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 2725e75f5687..9385b742d8a7 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -268,7 +268,7 @@ const std::string HystrixSink::printRollingWindows() { HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) : server_(server), current_index_(num_buckets > 0 ? num_buckets : DEFAULT_NUM_BUCKETS), window_size_(current_index_ + 1), - cluster_upstream_rq_time_("cluster.upstream_rq_time", server.symbolTable()) { + cluster_upstream_rq_time_("cluster.upstream_rq_time", server.api().symbolTable()) { Server::Admin& admin = server_.admin(); ENVOY_LOG(debug, "adding hystrix_event_stream endpoint to enable connection to hystrix dashboard"); @@ -276,7 +276,7 @@ HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false); } -HystrixSink::~HystrixSink() { cluster_upstream_rq_time_.free(server_.symbolTable()); } +HystrixSink::~HystrixSink() { cluster_upstream_rq_time_.free(server_.api().symbolTable()); } Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index c5a4790e2f8c..9f095ff7d36d 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -92,7 +92,6 @@ class ValidationInstance : Logger::Loggable, Http::Context& httpContext() override { return http_context_; } ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } - Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } TimeSource& timeSource() override { return api_->timeSource(); } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } // Http::CodeStats& codeStats() override { return code_stats_; } diff --git a/source/server/server.h b/source/server/server.h index 6340e30a406d..aa026fb65ce1 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -184,7 +184,6 @@ class InstanceImpl : Logger::Loggable, public Instance { Http::Context& httpContext() override { return http_context_; } ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } - Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } TimeSource& timeSource() override { return time_source_; } std::chrono::milliseconds statsFlushInterval() const override { diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 4354f5828034..1cc315d21332 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -360,7 +360,6 @@ class MockInstance : public Instance { MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_CONST_METHOD0(statsFlushInterval, std::chrono::milliseconds()); - Stats::SymbolTable& symbolTable() override { return stats_store_.symbolTable(); } TimeSource& timeSource() override { return time_system_; } std::unique_ptr secret_manager_; diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 698e7027ea82..4fbdc07c1f08 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -103,7 +103,7 @@ MockStore::~MockStore() {} MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(Test::Global::get()) {} -MockIsolatedStatsStore::~MockIsolatedStatsStore() { IsolatedStoreImpl::clear(); } +MockIsolatedStatsStore::~MockIsolatedStatsStore() { /*IsolatedStoreImpl::clear(); */} } // namespace Stats } // namespace Envoy From 9e133766d12be35859a2d0ae422858c634b533f2 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 6 Apr 2019 17:24:34 -0400 Subject: [PATCH 057/106] everything compiles, all tests pass except StatsThreadLocalStoreTest.ScopeDelete Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 1 - include/envoy/stats/tag_extractor.h | 2 +- include/envoy/stats/tag_producer.h | 5 +- source/common/stats/heap_stat_data.h | 6 -- source/common/stats/isolated_store_impl.cc | 3 +- source/common/stats/isolated_store_impl.h | 4 - source/common/stats/metric_impl.cc | 6 +- source/common/stats/scope_prefixer.cc | 6 -- source/common/stats/scope_prefixer.h | 7 +- source/common/stats/symbol_table_impl.cc | 36 ++++++++- source/common/stats/symbol_table_impl.h | 46 ++++++++++- source/common/stats/tag_extractor_impl.cc | 12 +-- source/common/stats/tag_extractor_impl.h | 4 +- source/common/stats/tag_producer_impl.cc | 6 +- source/common/stats/tag_producer_impl.h | 4 +- source/common/stats/thread_local_store.cc | 79 +++++++++---------- source/common/stats/thread_local_store.h | 39 ++++----- source/exe/main_common.h | 2 +- .../extensions/stat_sinks/hystrix/hystrix.cc | 4 +- test/common/stats/isolated_store_impl_test.cc | 16 +--- test/common/stats/symbol_table_impl_test.cc | 12 +++ test/integration/server.h | 18 +---- 22 files changed, 176 insertions(+), 142 deletions(-) diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index d6d7d0ad3343..4b4c8a4c4fe9 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -106,7 +106,6 @@ class SymbolTable { * @return Storage allocated for the joined name. */ virtual StoragePtr join(const std::vector& stat_names) const PURE; - // virtual StoragePtr join(StatName a, StatName b) const PURE; /** * Populates a StatNameList from a list of encodings. This is not done at diff --git a/include/envoy/stats/tag_extractor.h b/include/envoy/stats/tag_extractor.h index 270b78238633..361cda3e2266 100644 --- a/include/envoy/stats/tag_extractor.h +++ b/include/envoy/stats/tag_extractor.h @@ -40,7 +40,7 @@ class TagExtractor { * @param remove_characters set of intervals of character-indices to be removed from name. * @return bool indicates whether a tag was found in the name. */ - virtual bool extractTag(const std::string& stat_name, std::vector& tags, + virtual bool extractTag(absl::string_view stat_name, std::vector& tags, IntervalSet& remove_characters) const PURE; /** diff --git a/include/envoy/stats/tag_producer.h b/include/envoy/stats/tag_producer.h index 3e7986cd69d5..9da9ee106037 100644 --- a/include/envoy/stats/tag_producer.h +++ b/include/envoy/stats/tag_producer.h @@ -7,6 +7,8 @@ #include "envoy/common/pure.h" #include "envoy/stats/tag.h" +#include "absl/strings/string_view.h" + namespace Envoy { namespace Stats { @@ -20,8 +22,7 @@ class TagProducer { * @param metric_name std::string a name of Stats::Metric (Counter, Gauge, Histogram). * @param tags std::vector a set of Stats::Tag. */ - virtual std::string produceTags(const std::string& metric_name, - std::vector& tags) const PURE; + virtual std::string produceTags(absl::string_view metric_name, std::vector& tags) const PURE; }; typedef std::unique_ptr TagProducerPtr; diff --git a/source/common/stats/heap_stat_data.h b/source/common/stats/heap_stat_data.h index 6075fa139759..e0636b6db492 100644 --- a/source/common/stats/heap_stat_data.h +++ b/source/common/stats/heap_stat_data.h @@ -88,12 +88,6 @@ class HeapStatDataAllocator : public StatDataAllocatorImpl { tag_extracted_name, tags); } - BoolIndicatorSharedPtr makeBoolIndicator(StatName name, absl::string_view tag_extracted_name, - const std::vector& tags) override { - return std::make_shared>>(alloc(name), *this, - tag_extracted_name, tags); - } - #ifndef ENVOY_CONFIG_COVERAGE void debugPrint(); #endif diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index d7ae666b41de..7a082b4a333e 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -32,7 +32,8 @@ IsolatedStoreImpl::IsolatedStoreImpl(SymbolTable& symbol_table) histograms_([this](StatName name) -> HistogramSharedPtr { return std::make_shared(name, *this, alloc_.symbolTable().toString(name), std::vector()); - }) {} + }), + null_gauge_(alloc_.symbolTable()) {} ScopePtr IsolatedStoreImpl::createScope(const std::string& name) { return std::make_unique(name, *this); diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index b44cd6429cd9..f657438c2071 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -88,10 +88,6 @@ class IsolatedStoreImpl : public Store { StatNameTempStorage storage(name, symbolTable()); return gaugeFromStatName(storage.statName()); } - BoolIndicator& boolIndicator(const std::string& name) override { - StatNameTempStorage storage(name, symbolTable()); - return boolIndicatorFromStatName(storage.statName()); - } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); return histogramFromStatName(storage.statName()); diff --git a/source/common/stats/metric_impl.cc b/source/common/stats/metric_impl.cc index 3eed2af53931..6851d2abd3ee 100644 --- a/source/common/stats/metric_impl.cc +++ b/source/common/stats/metric_impl.cc @@ -18,15 +18,15 @@ MetricImpl::MetricImpl(absl::string_view tag_extracted_name, const std::vector names; - names.resize(1 /* tag_extracted_name */ + 2 * tags.size()); + uint32_t num_names = 1 /* tag_extracted_name */ + 2 * tags.size(); + STACK_ARRAY(names, absl::string_view, num_names); names[0] = tag_extracted_name; int index = 0; for (auto& tag : tags) { names[++index] = tag.name_; names[++index] = tag.value_; } - stat_names_.populate(names, symbol_table); + symbol_table.populateList(names.begin(), num_names, stat_names_); } void MetricImpl::clear() { stat_names_.clear(symbolTable()); } diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index ce8b3e6603e4..aa00a3a67d0e 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -46,12 +46,6 @@ Gauge& ScopePrefixer::gaugeFromStatName(StatName name) { return scope_->gaugeFromStatName(StatName(stat_name_storage.get())); } -BoolIndicator& ScopePrefixer::boolIndicatorFromStatName(StatName name) { - Stats::SymbolTable::StoragePtr stat_name_storage = - scope_->symbolTable().join({prefix_.statName(), name}); - return scope_->boolIndicatorFromStatName(StatName(stat_name_storage.get())); -} - Histogram& ScopePrefixer::histogramFromStatName(StatName name) { Stats::SymbolTable::StoragePtr stat_name_storage = scope_->symbolTable().join({prefix_.statName(), name}); diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index ff9df6fd29ec..9e8f587d218a 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -23,7 +23,6 @@ class ScopePrefixer : public Scope { ScopePtr createScope(const std::string& name) override; Counter& counterFromStatName(StatName name) override; Gauge& gaugeFromStatName(StatName name) override; - BoolIndicator& boolIndicatorFromStatName(StatName name) override; Histogram& histogramFromStatName(StatName name) override; void deliverHistogramToSinks(const Histogram& histograms, uint64_t val) override; @@ -39,15 +38,13 @@ class ScopePrefixer : public Scope { StatNameTempStorage storage(name, symbolTable()); return histogramFromStatName(storage.statName()); } - BoolIndicator& boolIndicator(const std::string& name) override { - StatNameTempStorage storage(name, symbolTable()); - return boolIndicatorFromStatName(storage.statName()); - } const Stats::StatsOptions& statsOptions() const override { return scope_->statsOptions(); } const SymbolTable& symbolTable() const override { return scope_->symbolTable(); } virtual SymbolTable& symbolTable() override { return scope_->symbolTable(); } + NullGaugeImpl& nullGauge(const std::string& str) override { return scope_->nullGauge(str); } + private: StatNameStorage prefix_; Scope* scope_; diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 26f1b962f9a4..172073a4ec1b 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -273,10 +273,6 @@ bool SymbolTableImpl::lessThan(const StatName& a, const StatName& b) const { #ifndef ENVOY_CONFIG_COVERAGE void SymbolTableImpl::debugPrint() const { Thread::LockGuard lock(lock_); - debugPrintLockHeld(); -} - -void SymbolTableImpl::debugPrintLockHeld() const { std::vector symbols; for (const auto& p : decode_map_) { symbols.push_back(p.first); @@ -321,6 +317,38 @@ void StatNameStorage::free(SymbolTable& table) { bytes_.reset(); } +SharedStatNameStorageSet::~SharedStatNameStorageSet() { + // free() must be called before destructing SharedStatNameStorageSet to + // decrement references to all symbols. + ASSERT(empty()); +} + +void SharedStatNameStorageSet::free(SymbolTable& symbol_table) { + // We must free() all symbols referenced in the set, otherwise the symbols + // will leak when the flat_hash_map superclass is destructed. They cannot + // self-destruct without an explicit free() as each individual StatNameStorage + // object does not have a reference to the symbol table, which would waste 8 + // bytes per stat-name. So we must iterate over the set and free it. But we + // don't want to mutate objects while they are in a set, so we just copy them, + // which is easy because they are shared_ptr. + + size_t sz = size(); + STACK_ARRAY(storage, SharedStatNameStorage, sz); + size_t i = 0; + for (const SharedStatNameStorage& name : *this) { + storage[i++] = name; + } + clear(); + + // Now that the associative container is clear, we can free all the referenced + // symbols. + for (i = 0; i < sz; ++i) { + SharedStatNameStorage& shared_stat_storage = storage[i]; + RELEASE_ASSERT(shared_stat_storage.use_count() == 1, "Freeing symbol that's in use"); + shared_stat_storage->free(symbol_table); + } +} + SymbolTable::StoragePtr SymbolTableImpl::join(const std::vector& stat_names) const { uint64_t num_bytes = 0; for (StatName stat_name : stat_names) { diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index 2a9280e8dad8..e1b1d5769123 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -139,9 +139,10 @@ class SymbolTableImpl : public SymbolTable { void callWithStringView(StatName stat_name, const std::function& fn) const override; + void encode(absl::string_view name, Encoding& encoding); + #ifndef ENVOY_CONFIG_COVERAGE void debugPrint() const override; - void debugPrintLockHeld() const EXCLUSIVE_LOCKS_REQUIRED(lock_); #endif /** @@ -477,5 +478,48 @@ struct StatNameLessThan { const SymbolTable& symbol_table_; }; +using SharedStatNameStorage = std::shared_ptr; + +struct HeterogeneousStatNameHash { + // Specifying is_transparent indicates to the library infrastructure that + // type-conversions should not be applied when calling find(), but instead + // pass the actual types of the contained and searched-for objects directly to + // these functors. See + // https://en.cppreference.com/w/cpp/utility/functional/less_void for an + // official reference, and https://abseil.io/tips/144 for a description of + // using it in the context of absl. + using is_transparent = void; + + size_t operator()(StatName a) const { return a.hash(); } + size_t operator()(const SharedStatNameStorage& a) const { return a->statName().hash(); } +}; + +struct HeterogeneousStatNameEqual { + // See description for HeterogeneousStatNameHash::is_transparent. + using is_transparent = void; + + size_t operator()(StatName a, StatName b) const { return a == b; } + size_t operator()(const SharedStatNameStorage& a, const SharedStatNameStorage& b) const { + return a->statName() == b->statName(); + } + size_t operator()(StatName a, const SharedStatNameStorage& b) const { return a == b->statName(); } + size_t operator()(const SharedStatNameStorage& a, StatName b) const { return a->statName() == b; } +}; + +// Encapsulates a set of shared_ptr. We use a subclass here +// rather than a 'using' alias because we need to ensure that when the set is +// destructed, StatNameStorage::free(symbol_table) is called on each entry. It +// is a little easier at the call-site in thread_local_store.cc to implement +// this an explicit free() method, analogous to StatNameStorage::free(), +// compared to storing a SymbolTable reference in the class and doing the free +// in the destructor, like StatNameTempStorage. +class SharedStatNameStorageSet + : public absl::flat_hash_set { +public: + ~SharedStatNameStorageSet(); + void free(SymbolTable& symbol_table); +}; + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/tag_extractor_impl.cc b/source/common/stats/tag_extractor_impl.cc index 092e66f0edd8..acf440fa695e 100644 --- a/source/common/stats/tag_extractor_impl.cc +++ b/source/common/stats/tag_extractor_impl.cc @@ -62,11 +62,11 @@ TagExtractorPtr TagExtractorImpl::createTagExtractor(const std::string& name, return TagExtractorPtr{new TagExtractorImpl(name, regex, substr)}; } -bool TagExtractorImpl::substrMismatch(const std::string& stat_name) const { - return !substr_.empty() && stat_name.find(substr_) == std::string::npos; +bool TagExtractorImpl::substrMismatch(absl::string_view stat_name) const { + return !substr_.empty() && stat_name.find(substr_) == absl::string_view::npos; } -bool TagExtractorImpl::extractTag(const std::string& stat_name, std::vector& tags, +bool TagExtractorImpl::extractTag(absl::string_view stat_name, std::vector& tags, IntervalSet& remove_characters) const { PERF_OPERATION(perf); @@ -75,9 +75,11 @@ bool TagExtractorImpl::extractTag(const std::string& stat_name, std::vector return false; } - std::smatch match; + std::match_results match; // The regex must match and contain one or more subexpressions (all after the first are ignored). - if (std::regex_search(stat_name, match, regex_) && match.size() > 1) { + if (std::regex_search(stat_name.begin(), stat_name.end(), match, + regex_) && + match.size() > 1) { // remove_subexpr is the first submatch. It represents the portion of the string to be removed. const auto& remove_subexpr = match[1]; diff --git a/source/common/stats/tag_extractor_impl.h b/source/common/stats/tag_extractor_impl.h index c72765a75234..138f25b6af7c 100644 --- a/source/common/stats/tag_extractor_impl.h +++ b/source/common/stats/tag_extractor_impl.h @@ -28,7 +28,7 @@ class TagExtractorImpl : public TagExtractor { TagExtractorImpl(const std::string& name, const std::string& regex, const std::string& substr = ""); std::string name() const override { return name_; } - bool extractTag(const std::string& tag_extracted_name, std::vector& tags, + bool extractTag(absl::string_view tag_extracted_name, std::vector& tags, IntervalSet& remove_characters) const override; absl::string_view prefixToken() const override { return prefix_; } @@ -37,7 +37,7 @@ class TagExtractorImpl : public TagExtractor { * @return bool indicates whether tag extraction should be skipped for this stat_name due * to a substring mismatch. */ - bool substrMismatch(const std::string& stat_name) const; + bool substrMismatch(absl::string_view stat_name) const; private: /** diff --git a/source/common/stats/tag_producer_impl.cc b/source/common/stats/tag_producer_impl.cc index d23d609c8a44..f03a4b6553e3 100644 --- a/source/common/stats/tag_producer_impl.cc +++ b/source/common/stats/tag_producer_impl.cc @@ -63,12 +63,12 @@ void TagProducerImpl::addExtractor(TagExtractorPtr extractor) { } void TagProducerImpl::forEachExtractorMatching( - const std::string& stat_name, std::function f) const { + absl::string_view stat_name, std::function f) const { IntervalSetImpl remove_characters; for (const TagExtractorPtr& tag_extractor : tag_extractors_without_prefix_) { f(tag_extractor); } - const std::string::size_type dot = stat_name.find('.'); + const absl::string_view::size_type dot = stat_name.find('.'); if (dot != std::string::npos) { const absl::string_view token = absl::string_view(stat_name.data(), dot); const auto iter = tag_extractor_prefix_map_.find(token); @@ -80,7 +80,7 @@ void TagProducerImpl::forEachExtractorMatching( } } -std::string TagProducerImpl::produceTags(const std::string& metric_name, +std::string TagProducerImpl::produceTags(absl::string_view metric_name, std::vector& tags) const { tags.insert(tags.end(), default_tags_.begin(), default_tags_.end()); IntervalSetImpl remove_characters; diff --git a/source/common/stats/tag_producer_impl.h b/source/common/stats/tag_producer_impl.h index 1614bcb28299..505cb71929aa 100644 --- a/source/common/stats/tag_producer_impl.h +++ b/source/common/stats/tag_producer_impl.h @@ -37,7 +37,7 @@ class TagProducerImpl : public TagProducer { * @param metric_name std::string a name of Stats::Metric (Counter, Gauge, Histogram). * @param tags std::vector a set of Stats::Tag. */ - std::string produceTags(const std::string& metric_name, std::vector& tags) const override; + std::string produceTags(absl::string_view metric_name, std::vector& tags) const override; private: friend class DefaultTagRegexTester; @@ -89,7 +89,7 @@ class TagProducerImpl : public TagProducer { * @param stat_name const std::string& the stat name. * @param f std::function function to call for each extractor. */ - void forEachExtractorMatching(const std::string& stat_name, + void forEachExtractorMatching(absl::string_view stat_name, std::function f) const; std::vector tag_extractors_without_prefix_; diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 18a13e23929a..861fd76fc466 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -30,8 +30,7 @@ ThreadLocalStoreImpl::ThreadLocalStoreImpl(const StatsOptions& stats_options, stats_overflow_("stats.overflow", alloc.symbolTable()), num_last_resort_stats_(default_scope_->counterFromStatName(stats_overflow_.statName())), heap_allocator_(alloc.symbolTable()), source_(*this), null_counter_(alloc.symbolTable()), - null_gauge_(alloc.symbolTable()), null_bool_(alloc.symbolTable()), - null_histogram_(alloc.symbolTable()) {} + null_gauge_(alloc.symbolTable()), null_histogram_(alloc.symbolTable()) {} ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(shutting_down_); @@ -67,10 +66,10 @@ void ThreadLocalStoreImpl::removeRejectedStats(StatMapClass& map, StatListClass& } } for (StatName stat_name : remove_list) { - auto p = map.find(stat_name); - ASSERT(p != map.end()); - list.push_back(p->second); // Save SharedPtr to the list to avoid invalidating refs to stat. - map.erase(p); + auto iter = map.find(stat_name); + ASSERT(iter != map.end()); + list.push_back(iter->second); // Save SharedPtr to the list to avoid invalidating refs to stat. + map.erase(iter); } } @@ -198,28 +197,40 @@ void ThreadLocalStoreImpl::releaseScopeCrossThread(ScopeImpl* scope) { ASSERT(scopes_.count(scope) == 1); scopes_.erase(scope); + // This is called directly from the ScopeImpl destructor, but we can't delay + // the destruction of scope->central_cache_.central_cache_.rejected_stats_ + // to wait for all the TLS rejected_stats_ caches are destructed, as those + // reference elements of SharedStatNameStorageSet. So simply swap out the set + // contents into a local that we can hold onto until the TLS cache is cleared + // of all references. + auto rejected_stats = new SharedStatNameStorageSet; + rejected_stats->swap(scope->central_cache_.rejected_stats_); + const uint64_t scope_id = scope->scope_id_; + auto clean_central_cache = [this, rejected_stats]() { + rejected_stats->free(symbolTable()); + delete rejected_stats; + }; + // This can happen from any thread. We post() back to the main thread which will initiate the // cache flush operation. if (!shutting_down_ && main_thread_dispatcher_) { - main_thread_dispatcher_->post( - [this, scope_id = scope->scope_id_]() -> void { clearScopeFromCaches(scope_id); }); + main_thread_dispatcher_->post([this, clean_central_cache, scope_id]() { + clearScopeFromCaches(scope_id, clean_central_cache); + }); + } else { + clean_central_cache(); } } -/* -std::string ThreadLocalStoreImpl::getTagsForName(const std::string& name, - std::vector& tags) const { - return tag_producer_->produceTags(name, tags); -} -*/ - -void ThreadLocalStoreImpl::clearScopeFromCaches(uint64_t scope_id) { +void ThreadLocalStoreImpl::clearScopeFromCaches(uint64_t scope_id, + const Event::PostCb& clean_central_cache) { // If we are shutting down we no longer perform cache flushes as workers may be shutting down // at the same time. if (!shutting_down_) { // Perform a cache flush on all threads. tls_->runOnAllThreads( - [this, scope_id]() -> void { tls_->getTyped().scope_cache_.erase(scope_id); }); + [this, scope_id]() -> void { tls_->getTyped().scope_cache_.erase(scope_id); }, + clean_central_cache); } } @@ -251,31 +262,15 @@ ThreadLocalStoreImpl::ScopeImpl::~ScopeImpl() { prefix_.free(symbolTable()); } -/* -void ThreadLocalStoreImpl::ScopeImpl::extractTagsAndTruncate( - StatName& name, std::unique_ptr& truncated_name_storage, - std::vector& tags, - std::string& tag_extracted_name) { - - // Tag extraction occurs on the original, untruncated name so the extraction - // can complete properly, even if the tag values are partially truncated. - std::string name_str = name.toString(parent_.symbolTable()); - tag_extracted_name = parent_.getTagsForName(name_str, tags); - absl::string_view truncated_name = parent_.truncateStatNameIfNeeded(name_str); - if (truncated_name.size() < name_str.size()) { - truncated_name_storage = std::make_unique(truncated_name, symbolTable()); - name = truncated_name_storage->statName(); - } - }*/ - // Manages the truncation and tag-extration of stat names. Tag extraction occurs // on the original, untruncated name so the extraction can complete properly, // even if the tag values are partially truncated. class TagExtraction { public: TagExtraction(ThreadLocalStoreImpl& tls, StatName name) { - std::string name_str = tls.symbolTable().toString(name); - tag_extracted_name_ = tls.tagProducer().produceTags(name_str, tags_); + tls.symbolTable().callWithStringView(name, [this, &tls](absl::string_view name_str) { + tag_extracted_name_ = tls.tagProducer().produceTags(name_str, tags_); + }); } const std::vector& tags() { return tags_; } @@ -286,26 +281,26 @@ class TagExtraction { std::string tag_extracted_name_; }; -bool ThreadLocalStoreImpl::checkAndRememberRejection(const std::string& name, - SharedStringSet& central_rejected_stats, - SharedStringSet* tls_rejected_stats) { +bool ThreadLocalStoreImpl::checkAndRememberRejection( + StatName name, SharedStatNameStorageSet& central_rejected_stats, + StatNameHashSet* tls_rejected_stats) { if (stats_matcher_->acceptsAll()) { return false; } auto iter = central_rejected_stats.find(name); - SharedString rejected_name; + SharedStatNameStorage rejected_name; if (iter != central_rejected_stats.end()) { rejected_name = *iter; } else { if (rejects(name)) { - rejected_name = std::make_shared(name); + rejected_name = std::make_shared(name, symbolTable()); central_rejected_stats.insert(rejected_name); } } if (rejected_name != nullptr) { if (tls_rejected_stats != nullptr) { - tls_rejected_stats->insert(rejected_name); + tls_rejected_stats->insert(rejected_name->statName()); } return true; } diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 030bd4b91e85..fee5a19bdaf2 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -130,7 +130,7 @@ class TlsScope : public Scope { * @return a ThreadLocalHistogram within the scope's namespace. * @param name name of the histogram with scope prefix attached. */ - virtual Histogram& tlsHistogramFromStatName(StatName name, ParentHistogramImpl& parent) PURE; + virtual Histogram& tlsHistogram(StatName name, ParentHistogramImpl& parent) PURE; }; /** @@ -159,10 +159,10 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo return default_scope_->histogramFromStatName(name); } Histogram& histogram(const std::string& name) override { return default_scope_->histogram(name); } + NullGaugeImpl& nullGauge(const std::string&) override { return null_gauge_; } const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } SymbolTable& symbolTable() override { return alloc_.symbolTable(); } const TagProducer& tagProducer() const { return *tag_producer_; } - NullGaugeImpl& nullGauge(const std::string&) override { return null_gauge_; } // Stats::Store std::vector counters() const override; @@ -198,17 +198,17 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo // We keep a TLS cache of rejected stat names. This costs memory, but // reduces runtime overhead running the matcher. Moreover, once symbol // tables are integrated, rejection will need the fully elaborated string, - // and it we need to take a global symbol-table lock to run. We keep - // this char* map here in the TLS cache to avoid taking a lock to compute + // and it we need to take a global symbol-table lock to run. We keep this + // StatName set here in the TLS cache to avoid taking a lock to compute // rejection. - SharedStringSet rejected_stats_; + StatNameHashSet rejected_stats_; }; struct CentralCacheEntry { StatMap counters_; StatMap gauges_; StatMap histograms_; - SharedStringSet rejected_stats_; + SharedStatNameStorageSet rejected_stats_; }; struct ScopeImpl : public TlsScope { @@ -219,12 +219,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Counter& counterFromStatName(StatName name) override; void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override; Gauge& gaugeFromStatName(StatName name) override; - BoolIndicator& boolIndicatorFromStatName(StatName name) override; Histogram& histogramFromStatName(StatName name) override; - Histogram& tlsHistogramFromStatName(StatName name, ParentHistogramImpl& parent) override; - - NullGaugeImpl& nullGauge(const std::string&) override { return null_gauge_; } - + Histogram& tlsHistogram(StatName name, ParentHistogramImpl& parent) override; const Stats::StatsOptions& statsOptions() const override { return parent_.statsOptions(); } ScopePtr createScope(const std::string& name) override { return parent_.createScope(symbolTable().toString(prefix_.statName()) + "." + name); @@ -240,15 +236,13 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatNameTempStorage storage(name, symbolTable()); return gaugeFromStatName(storage.statName()); } - BoolIndicator& boolIndicator(const std::string& name) override { - StatNameTempStorage storage(name, symbolTable()); - return boolIndicatorFromStatName(storage.statName()); - } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); return histogramFromStatName(storage.statName()); } + NullGaugeImpl& nullGauge(const std::string&) override { return parent_.null_gauge_; } + template using MakeStatFn = std::function(StatDataAllocator&, StatName name, absl::string_view tag_extracted_name, @@ -272,6 +266,10 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatMap>* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat); + void extractTagsAndTruncate(StatName& name, + std::unique_ptr& truncated_name_storage, + std::vector& tags, std::string& tag_extracted_name); + static std::atomic next_scope_id_; const uint64_t scope_id_; @@ -292,16 +290,15 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo }; std::string getTagsForName(const std::string& name, std::vector& tags) const; - void clearScopeFromCaches(uint64_t scope_id); + void clearScopeFromCaches(uint64_t scope_id, const Event::PostCb& clean_central_cache); void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb mergeCb); - absl::string_view truncateStatNameIfNeeded(absl::string_view name); - bool rejects(const std::string& name) const; + bool rejects(StatName name) const; bool rejectsAll() const { return stats_matcher_->rejectsAll(); } template void removeRejectedStats(StatMapClass& map, StatListClass& list); - bool checkAndRememberRejection(const std::string& name, SharedStringSet& central_rejected_stats, - SharedStringSet* tls_rejected_stats); + bool checkAndRememberRejection(StatName name, SharedStatNameStorageSet& central_rejected_stats, + StatNameHashSet* tls_rejected_stats); const Stats::StatsOptions& stats_options_; StatDataAllocator& alloc_; @@ -319,11 +316,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Counter& num_last_resort_stats_; HeapStatDataAllocator heap_allocator_; SourceImpl source_; - NullGaugeImpl null_gauge_; NullCounterImpl null_counter_; NullGaugeImpl null_gauge_; - NullBoolIndicatorImpl null_bool_; NullHistogramImpl null_histogram_; // Retain storage for deleted stats; these are no longer in maps because the diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 28e5708018a5..66780199d554 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -65,7 +65,7 @@ class MainCommonBase { protected: const Envoy::OptionsImpl& options_; - Stats::FakeSymbolTableImpl symbol_table_; + Stats::SymbolTableImpl symbol_table_; Server::ComponentFactory& component_factory_; Thread::ThreadFactory& thread_factory_; Filesystem::Instance& file_system_; diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 9385b742d8a7..2f4ad6155a06 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -268,7 +268,7 @@ const std::string HystrixSink::printRollingWindows() { HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) : server_(server), current_index_(num_buckets > 0 ? num_buckets : DEFAULT_NUM_BUCKETS), window_size_(current_index_ + 1), - cluster_upstream_rq_time_("cluster.upstream_rq_time", server.api().symbolTable()) { + cluster_upstream_rq_time_("cluster.upstream_rq_time", server.stats().symbolTable()) { Server::Admin& admin = server_.admin(); ENVOY_LOG(debug, "adding hystrix_event_stream endpoint to enable connection to hystrix dashboard"); @@ -276,7 +276,7 @@ HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false); } -HystrixSink::~HystrixSink() { cluster_upstream_rq_time_.free(server_.api().symbolTable()); } +HystrixSink::~HystrixSink() { cluster_upstream_rq_time_.free(server_.stats().symbolTable()); } Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index 261fb91e0a5d..5918661b6ac7 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -36,15 +36,6 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g2.tags().size()); - BoolIndicator& b1 = store_.boolIndicator("b1"); - BoolIndicator& b2 = scope1->boolIndicator("b2"); - EXPECT_EQ("b1", b1.name()); - EXPECT_EQ("scope1.b2", b2.name()); - EXPECT_EQ("b1", b1.tagExtractedName()); - EXPECT_EQ("scope1.b2", b2.tagExtractedName()); - EXPECT_EQ(0, b1.tags().size()); - EXPECT_EQ(0, b2.tags().size()); - Histogram& h1 = store_.histogram("h1"); Histogram& h2 = scope1->histogram("h2"); scope1->deliverHistogramToSinks(h2, 0); @@ -66,7 +57,6 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(4UL, store_.counters().size()); EXPECT_EQ(2UL, store_.gauges().size()); - EXPECT_EQ(2UL, store_.boolIndicators().size()); } TEST_F(StatsIsolatedStoreImplTest, LongStatName) { @@ -93,9 +83,9 @@ struct TestStats { }; TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { - TestStats test_stats{ALL_TEST_STATS( - POOL_COUNTER_PREFIX(store_, "test."), POOL_GAUGE_PREFIX(store_, "test."), - POOL_BOOL_INDICATOR_PREFIX(store_, "test."), POOL_HISTOGRAM_PREFIX(store_, "test."))}; + TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(store_, "test."), + POOL_GAUGE_PREFIX(store_, "test."), + POOL_HISTOGRAM_PREFIX(store_, "test."))}; Counter& counter = test_stats.test_counter_; EXPECT_EQ("test.test_counter", counter.name()); diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 36c86aaf4365..95291374056b 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -507,6 +507,18 @@ TEST_P(StatNameTest, MutexContentionOnExistingSymbols) { } } +TEST_P(StatNameTest, SharedStatNameStorageSet) { + SharedStatNameStorageSet set; + { + auto foo = std::make_shared("foo", *table_); + set.insert(foo); + StatNameTempStorage temp_foo("foo", *table_); + auto pos = set.find(temp_foo.statName()); + EXPECT_EQ(pos->get(), foo.get()); + } + set.free(*table_); +} + // Tests the memory savings realized from using symbol tables with 1k // clusters. This test shows the memory drops from almost 8M to less than // 2M. Note that only SymbolTableImpl is tested for memory consumption, diff --git a/test/integration/server.h b/test/integration/server.h index bb4276fd54d7..9a3a96406ef4 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -90,12 +90,7 @@ class TestScopeWrapper : public Scope { return wrapped_scope_->histogramFromStatName(name); } - NullGaugeImpl& nullGauge(const std::string&) override { return null_gauge_; } - - BoolIndicator& boolIndicatorFromStatName(StatName name) override { - Thread::LockGuard lock(lock_); - return wrapped_scope_->boolIndicatorFromStatName(name); - } + NullGaugeImpl& nullGauge(const std::string& s) override { return wrapped_scope_->nullGauge(s); } Counter& counter(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); @@ -105,10 +100,6 @@ class TestScopeWrapper : public Scope { StatNameTempStorage storage(name, symbolTable()); return gaugeFromStatName(storage.statName()); } - BoolIndicator& boolIndicator(const std::string& name) override { - StatNameTempStorage storage(name, symbolTable()); - return boolIndicatorFromStatName(storage.statName()); - } Histogram& histogram(const std::string& name) override { StatNameTempStorage storage(name, symbolTable()); return histogramFromStatName(storage.statName()); @@ -122,7 +113,6 @@ class TestScopeWrapper : public Scope { Thread::MutexBasicLockable& lock_; ScopePtr wrapped_scope_; StatsOptionsImpl stats_options_; - NullGaugeImpl null_gauge_; }; /** @@ -158,7 +148,7 @@ class TestIsolatedStoreImpl : public StoreRoot { Thread::LockGuard lock(lock_); return store_.histogramFromStatName(name); } - NullGaugeImpl& nullGauge(const std::string&) override { return null_gauge_; } + NullGaugeImpl& nullGauge(const std::string& s) override { return store_.nullGauge(s); } Histogram& histogram(const std::string& name) override { Thread::LockGuard lock(lock_); return store_.histogram(name); @@ -191,15 +181,11 @@ class TestIsolatedStoreImpl : public StoreRoot { void mergeHistograms(PostMergeCb) override {} Source& source() override { return source_; } - const SymbolTable& symbolTable() const override { return store_.symbolTable(); } - SymbolTable& symbolTable() override { return store_.symbolTable(); } - private: mutable Thread::MutexBasicLockable lock_; IsolatedStoreImpl store_; SourceImpl source_; StatsOptionsImpl stats_options_; - NullGaugeImpl null_gauge_; }; } // namespace Stats From 33f36f3bca82d05c740d2a1e90e17a3d9d7fd75e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 7 Apr 2019 12:08:32 -0400 Subject: [PATCH 058/106] format Signed-off-by: Joshua Marantz --- source/server/server.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/server/server.cc b/source/server/server.cc index c0d05caf3db7..788713bddce9 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -70,8 +70,7 @@ InstanceImpl::InstanceImpl(const Options& options, Event::TimeSystem& time_syste terminated_(false), mutex_tracer_(options.mutexTracingEnabled() ? &Envoy::MutexTracerImpl::getOrCreateTracer() : nullptr), - http_context_(store.symbolTable()), - main_thread_id_(std::this_thread::get_id()) { + http_context_(store.symbolTable()), main_thread_id_(std::this_thread::get_id()) { try { if (!options.logPath().empty()) { try { From 64d94fac08671696481a55388fe29ebf68608b96 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 7 Apr 2019 17:34:31 -0400 Subject: [PATCH 059/106] Fix tests and resync with #6161 and #6504 Signed-off-by: Joshua Marantz --- source/common/stats/scope_prefixer.cc | 27 ++++++++++--- source/common/stats/scope_prefixer.h | 26 +++++++++---- source/common/stats/symbol_table_impl.cc | 41 +++++++++----------- source/common/stats/symbol_table_impl.h | 33 +++++++--------- source/common/stats/thread_local_store.cc | 21 +++++----- source/common/stats/thread_local_store.h | 16 ++------ test/common/stats/symbol_table_impl_test.cc | 14 ++++--- test/common/stats/thread_local_store_test.cc | 2 +- test/integration/stats_integration_test.cc | 4 +- test/mocks/thread_local/mocks.cc | 4 +- test/mocks/thread_local/mocks.h | 5 ++- 11 files changed, 106 insertions(+), 87 deletions(-) diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 96e1e8be7b0a..8469f6b8c57b 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -9,22 +9,39 @@ namespace Envoy { namespace Stats { ScopePrefixer::ScopePrefixer(absl::string_view prefix, Scope& scope) - : prefix_(Utility::sanitizeStatsName(prefix)), scope_(scope) {} + : scope_(scope), prefix_(Utility::sanitizeStatsName(prefix), symbolTable()) {} + +ScopePrefixer::ScopePrefixer(StatName prefix, Scope& scope) + : scope_(scope), prefix_(prefix, symbolTable()) {} + +ScopePrefixer::~ScopePrefixer() { prefix_.free(symbolTable()); } + +ScopePtr ScopePrefixer::createScopeFromStatName(StatName name) { + SymbolTable::StoragePtr joined = symbolTable().join({prefix_.statName(), name}); + return std::make_unique(StatName(joined.get()), scope_); +} ScopePtr ScopePrefixer::createScope(const std::string& name) { - return std::make_unique(prefix_ + name, scope_); + StatNameTempStorage stat_name_storage(Utility::sanitizeStatsName(name), symbolTable()); + return createScopeFromStatName(stat_name_storage.statName()); } Counter& ScopePrefixer::counterFromStatName(StatName name) { - return counter(symbolTable().toString(name)); + Stats::SymbolTable::StoragePtr stat_name_storage = + scope_.symbolTable().join({prefix_.statName(), name}); + return scope_.counterFromStatName(StatName(stat_name_storage.get())); } Gauge& ScopePrefixer::gaugeFromStatName(StatName name) { - return gauge(symbolTable().toString(name)); + Stats::SymbolTable::StoragePtr stat_name_storage = + scope_.symbolTable().join({prefix_.statName(), name}); + return scope_.gaugeFromStatName(StatName(stat_name_storage.get())); } Histogram& ScopePrefixer::histogramFromStatName(StatName name) { - return histogram(symbolTable().toString(name)); + Stats::SymbolTable::StoragePtr stat_name_storage = + scope_.symbolTable().join({prefix_.statName(), name}); + return scope_.histogramFromStatName(StatName(stat_name_storage.get())); } void ScopePrefixer::deliverHistogramToSinks(const Histogram& histograms, uint64_t val) { diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index 4871840f549e..8b4db408397a 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -10,18 +10,30 @@ namespace Stats { class ScopePrefixer : public Scope { public: ScopePrefixer(absl::string_view prefix, Scope& scope); + ScopePrefixer(StatName prefix, Scope& scope); + ~ScopePrefixer() override; + + ScopePtr createScopeFromStatName(StatName name); // Scope ScopePtr createScope(const std::string& name) override; - Counter& counter(const std::string& name) override { return scope_.counter(prefix_ + name); } - Gauge& gauge(const std::string& name) override { return scope_.gauge(prefix_ + name); } - Histogram& histogram(const std::string& name) override { - return scope_.histogram(prefix_ + name); - } - void deliverHistogramToSinks(const Histogram& histograms, uint64_t val) override; Counter& counterFromStatName(StatName name) override; Gauge& gaugeFromStatName(StatName name) override; Histogram& histogramFromStatName(StatName name) override; + void deliverHistogramToSinks(const Histogram& histograms, uint64_t val) override; + + Counter& counter(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return counterFromStatName(storage.statName()); + } + Gauge& gauge(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return gaugeFromStatName(storage.statName()); + } + Histogram& histogram(const std::string& name) override { + StatNameTempStorage storage(name, symbolTable()); + return histogramFromStatName(storage.statName()); + } const Stats::StatsOptions& statsOptions() const override { return scope_.statsOptions(); } const SymbolTable& symbolTable() const override { return scope_.symbolTable(); } @@ -30,8 +42,8 @@ class ScopePrefixer : public Scope { NullGaugeImpl& nullGauge(const std::string& str) override { return scope_.nullGauge(str); } private: - std::string prefix_; Scope& scope_; + StatNameStorage prefix_; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 172073a4ec1b..40ebd666d4ec 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -317,35 +317,32 @@ void StatNameStorage::free(SymbolTable& table) { bytes_.reset(); } -SharedStatNameStorageSet::~SharedStatNameStorageSet() { - // free() must be called before destructing SharedStatNameStorageSet to - // decrement references to all symbols. +StatNameStorageSet::~StatNameStorageSet() { + // free() must be called before destructing StatNameStorageSet to decrement + // references to all symbols. ASSERT(empty()); } -void SharedStatNameStorageSet::free(SymbolTable& symbol_table) { +void StatNameStorageSet::free(SymbolTable& symbol_table) { // We must free() all symbols referenced in the set, otherwise the symbols // will leak when the flat_hash_map superclass is destructed. They cannot // self-destruct without an explicit free() as each individual StatNameStorage // object does not have a reference to the symbol table, which would waste 8 - // bytes per stat-name. So we must iterate over the set and free it. But we - // don't want to mutate objects while they are in a set, so we just copy them, - // which is easy because they are shared_ptr. - - size_t sz = size(); - STACK_ARRAY(storage, SharedStatNameStorage, sz); - size_t i = 0; - for (const SharedStatNameStorage& name : *this) { - storage[i++] = name; - } - clear(); - - // Now that the associative container is clear, we can free all the referenced - // symbols. - for (i = 0; i < sz; ++i) { - SharedStatNameStorage& shared_stat_storage = storage[i]; - RELEASE_ASSERT(shared_stat_storage.use_count() == 1, "Freeing symbol that's in use"); - shared_stat_storage->free(symbol_table); + // bytes per stat-name. The easiest way to safely free all the contents of the + // symbol table set is to use flat_hash_map::extract(), which removes and + // returns an element from the set without destructing the element + // immediately. This gives us a chance to call free() on each one before they + // are destroyed. + // + // One risk here is if removing elements via flat_hash_set::begin() is + // inefficient to use in a loop like this. One can imagine a hash-table + // implementation where the performance if this usage-model would be + // poor. However, tests with 100k elements appeared to run quickly when + // compiled for optimization, so at present this is not a performance issue. + + while (!empty()) { + auto storage = extract(begin()); + storage.value().free(symbol_table); } } diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index e1b1d5769123..3a951c5e94f7 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -478,8 +478,6 @@ struct StatNameLessThan { const SymbolTable& symbol_table_; }; -using SharedStatNameStorage = std::shared_ptr; - struct HeterogeneousStatNameHash { // Specifying is_transparent indicates to the library infrastructure that // type-conversions should not be applied when calling find(), but instead @@ -491,7 +489,7 @@ struct HeterogeneousStatNameHash { using is_transparent = void; size_t operator()(StatName a) const { return a.hash(); } - size_t operator()(const SharedStatNameStorage& a) const { return a->statName().hash(); } + size_t operator()(const StatNameStorage& a) const { return a.statName().hash(); } }; struct HeterogeneousStatNameEqual { @@ -499,25 +497,24 @@ struct HeterogeneousStatNameEqual { using is_transparent = void; size_t operator()(StatName a, StatName b) const { return a == b; } - size_t operator()(const SharedStatNameStorage& a, const SharedStatNameStorage& b) const { - return a->statName() == b->statName(); + size_t operator()(const StatNameStorage& a, const StatNameStorage& b) const { + return a.statName() == b.statName(); } - size_t operator()(StatName a, const SharedStatNameStorage& b) const { return a == b->statName(); } - size_t operator()(const SharedStatNameStorage& a, StatName b) const { return a->statName() == b; } + size_t operator()(StatName a, const StatNameStorage& b) const { return a == b.statName(); } + size_t operator()(const StatNameStorage& a, StatName b) const { return a.statName() == b; } }; -// Encapsulates a set of shared_ptr. We use a subclass here -// rather than a 'using' alias because we need to ensure that when the set is -// destructed, StatNameStorage::free(symbol_table) is called on each entry. It -// is a little easier at the call-site in thread_local_store.cc to implement -// this an explicit free() method, analogous to StatNameStorage::free(), -// compared to storing a SymbolTable reference in the class and doing the free -// in the destructor, like StatNameTempStorage. -class SharedStatNameStorageSet - : public absl::flat_hash_set { +// Encapsulates a set. We use a subclass here rather than a +// 'using' alias because we need to ensure that when the set is destructed, +// StatNameStorage::free(symbol_table) is called on each entry. It is a little +// easier at the call-sites in thread_local_store.cc to implement this an +// explicit free() method, analogous to StatNameStorage::free(), compared to +// storing a SymbolTable reference in the class and doing the free in the +// destructor, like StatNameTempStorage. +class StatNameStorageSet : public absl::flat_hash_set { public: - ~SharedStatNameStorageSet(); + ~StatNameStorageSet(); void free(SymbolTable& symbol_table); }; diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 861fd76fc466..fec140f760a6 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -199,11 +199,11 @@ void ThreadLocalStoreImpl::releaseScopeCrossThread(ScopeImpl* scope) { // This is called directly from the ScopeImpl destructor, but we can't delay // the destruction of scope->central_cache_.central_cache_.rejected_stats_ - // to wait for all the TLS rejected_stats_ caches are destructed, as those + // to wait for all the TLS rejected_stats_ caches to be destructed, as those // reference elements of SharedStatNameStorageSet. So simply swap out the set // contents into a local that we can hold onto until the TLS cache is cleared // of all references. - auto rejected_stats = new SharedStatNameStorageSet; + auto rejected_stats = new StatNameStorageSet; rejected_stats->swap(scope->central_cache_.rejected_stats_); const uint64_t scope_id = scope->scope_id_; auto clean_central_cache = [this, rejected_stats]() { @@ -281,21 +281,22 @@ class TagExtraction { std::string tag_extracted_name_; }; -bool ThreadLocalStoreImpl::checkAndRememberRejection( - StatName name, SharedStatNameStorageSet& central_rejected_stats, - StatNameHashSet* tls_rejected_stats) { +bool ThreadLocalStoreImpl::checkAndRememberRejection(StatName name, + StatNameStorageSet& central_rejected_stats, + StatNameHashSet* tls_rejected_stats) { if (stats_matcher_->acceptsAll()) { return false; } auto iter = central_rejected_stats.find(name); - SharedStatNameStorage rejected_name; + const StatNameStorage* rejected_name = nullptr; if (iter != central_rejected_stats.end()) { - rejected_name = *iter; + rejected_name = &(*iter); } else { if (rejects(name)) { - rejected_name = std::make_shared(name, symbolTable()); - central_rejected_stats.insert(rejected_name); + auto insertion = central_rejected_stats.insert(StatNameStorage(name, symbolTable())); + const StatNameStorage& rejected_name_ref = *(insertion.first); + rejected_name = &rejected_name_ref; } } if (rejected_name != nullptr) { @@ -310,7 +311,7 @@ bool ThreadLocalStoreImpl::checkAndRememberRejection( template StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( StatName name, StatMap>& central_cache_map, - SharedStatNameStorageSet& central_rejected_stats, MakeStatFn make_stat, + StatNameStorageSet& central_rejected_stats, MakeStatFn make_stat, StatMap>* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat) { diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 082bb706c720..4dab95a1f9e5 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -208,7 +208,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatMap counters_; StatMap gauges_; StatMap histograms_; - SharedStatNameStorageSet rejected_stats_; + StatNameStorageSet rejected_stats_; }; struct ScopeImpl : public TlsScope { @@ -261,7 +261,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo */ template StatType& safeMakeStat(StatName name, StatMap>& central_cache_map, - SharedStatNameStorageSet& central_rejected_stats, + StatNameStorageSet& central_rejected_stats, MakeStatFn make_stat, StatMap>* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat); @@ -270,16 +270,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo std::unique_ptr& truncated_name_storage, std::vector& tags, std::string& tag_extracted_name); - Counter& counterFromStatName(StatName name) override { - return counter(symbolTable().toString(name)); - } - - Gauge& gaugeFromStatName(StatName name) override { return gauge(symbolTable().toString(name)); } - - Histogram& histogramFromStatName(StatName name) override { - return histogram(symbolTable().toString(name)); - } - static std::atomic next_scope_id_; const uint64_t scope_id_; @@ -307,7 +297,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo bool rejectsAll() const { return stats_matcher_->rejectsAll(); } template void removeRejectedStats(StatMapClass& map, StatListClass& list); - bool checkAndRememberRejection(StatName name, SharedStatNameStorageSet& central_rejected_stats, + bool checkAndRememberRejection(StatName name, StatNameStorageSet& central_rejected_stats, StatNameHashSet* tls_rejected_stats); const Stats::StatsOptions& stats_options_; diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 95291374056b..f2bb83fdd6bd 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -508,13 +508,15 @@ TEST_P(StatNameTest, MutexContentionOnExistingSymbols) { } TEST_P(StatNameTest, SharedStatNameStorageSet) { - SharedStatNameStorageSet set; + StatNameStorageSet set; { - auto foo = std::make_shared("foo", *table_); - set.insert(foo); - StatNameTempStorage temp_foo("foo", *table_); - auto pos = set.find(temp_foo.statName()); - EXPECT_EQ(pos->get(), foo.get()); + for (int i = 0; i < 10; ++i) { + std::string foo = absl::StrCat("foo", i); + auto insertion = set.insert(StatNameStorage(foo, *table_)); + StatNameTempStorage temp_foo(foo, *table_); + auto found = set.find(temp_foo.statName()); + EXPECT_EQ(found->statName().data(), insertion.first->statName().data()); + } } set.free(*table_); } diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 3a4a9a15f3e1..1d14d8251174 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -312,7 +312,7 @@ TEST_F(StatsThreadLocalStoreTest, ScopeDelete) { EXPECT_EQ(TestUtility::findByName(store_->source().cachedCounters(), "scope1.c1"), c1); EXPECT_CALL(main_thread_dispatcher_, post(_)); - EXPECT_CALL(tls_, runOnAllThreads(_)); + EXPECT_CALL(tls_, runOnAllThreads(_, _)); scope1.reset(); EXPECT_EQ(1UL, store_->counters().size()); EXPECT_EQ(2UL, store_->source().cachedCounters().size()); diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index db0c1845b3af..8be72f574bcd 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -195,8 +195,8 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { EXPECT_LT(start_mem, m1); EXPECT_LT(start_mem, m1001); - // As of 2019/03/20, m_per_cluster = 59015 (libstdc++) - EXPECT_LT(m_per_cluster, 59100); + // As of 2019/03/20, m_per_cluster = 40017 (libstdc++) + EXPECT_LT(m_per_cluster, 40500); } } // namespace diff --git a/test/mocks/thread_local/mocks.cc b/test/mocks/thread_local/mocks.cc index cfabd7a7f52f..225ee9988b40 100644 --- a/test/mocks/thread_local/mocks.cc +++ b/test/mocks/thread_local/mocks.cc @@ -11,7 +11,9 @@ namespace ThreadLocal { MockInstance::MockInstance() { ON_CALL(*this, allocateSlot()).WillByDefault(Invoke(this, &MockInstance::allocateSlot_)); - ON_CALL(*this, runOnAllThreads(_)).WillByDefault(Invoke(this, &MockInstance::runOnAllThreads_)); + ON_CALL(*this, runOnAllThreads(_)).WillByDefault(Invoke(this, &MockInstance::runOnAllThreads1_)); + ON_CALL(*this, runOnAllThreads(_, _)) + .WillByDefault(Invoke(this, &MockInstance::runOnAllThreads2_)); ON_CALL(*this, shutdownThread()).WillByDefault(Invoke(this, &MockInstance::shutdownThread_)); } diff --git a/test/mocks/thread_local/mocks.h b/test/mocks/thread_local/mocks.h index af871695420d..24393722887a 100644 --- a/test/mocks/thread_local/mocks.h +++ b/test/mocks/thread_local/mocks.h @@ -18,6 +18,7 @@ class MockInstance : public Instance { ~MockInstance(); MOCK_METHOD1(runOnAllThreads, void(Event::PostCb cb)); + MOCK_METHOD2(runOnAllThreads, void(Event::PostCb cb, Event::PostCb main_callback)); // Server::ThreadLocal MOCK_METHOD0(allocateSlot, SlotPtr()); @@ -27,8 +28,8 @@ class MockInstance : public Instance { MOCK_METHOD0(dispatcher, Event::Dispatcher&()); SlotPtr allocateSlot_() { return SlotPtr{new SlotImpl(*this, current_slot_++)}; } - void runOnAllThreads_(Event::PostCb cb) { cb(); } - void runOnAllThreads(Event::PostCb cb, Event::PostCb main_callback) { + void runOnAllThreads1_(Event::PostCb cb) { cb(); } + void runOnAllThreads2_(Event::PostCb cb, Event::PostCb main_callback) { cb(); main_callback(); } From 4c89a6ffd88de17c5b96a3f9f25e06cec7214e1c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 26 Apr 2019 12:02:35 -0400 Subject: [PATCH 060/106] fix functional issues from merge. Signed-off-by: Joshua Marantz --- source/extensions/stat_sinks/hystrix/hystrix.cc | 2 -- source/extensions/stat_sinks/hystrix/hystrix.h | 1 - test/integration/stats_integration_test.cc | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 2f4ad6155a06..f8442b2c9d52 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -276,8 +276,6 @@ HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_buckets) MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false); } -HystrixSink::~HystrixSink() { cluster_upstream_rq_time_.free(server_.stats().symbolTable()); } - Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index c92ef8b845a1..c37a98db15c6 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -49,7 +49,6 @@ typedef std::unique_ptr ClusterStatsCachePtr; class HystrixSink : public Stats::Sink, public Logger::Loggable { public: HystrixSink(Server::Instance& server, uint64_t num_buckets); - ~HystrixSink() override; Http::Code handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, Server::AdminStream& admin_stream); void flush(Stats::Source& source) override; diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index f9ed55d35020..685cb089efc1 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -210,9 +210,9 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/04/12 6477 59576 Implementing Endpoint lease... // 2019/04/23 6659 59512 Reintroduce dispatcher stats... // 2019/04/24 6161 49415 Pack tags and tag extracted names - // 2019/04/26 4980 40017 Real symbol table implementation + // 2019/04/26 4980 40289 Real symbol table implementation - EXPECT_EQ(m_per_cluster, 40017); + EXPECT_EQ(m_per_cluster, 40289); } } // namespace From 27b9c065409e8d10a3bb464d6d7f10fe91a950b2 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 26 Apr 2019 13:52:26 -0400 Subject: [PATCH 061/106] remove some detritus Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.h | 2 -- source/server/config_validation/server.h | 1 - test/integration/server.cc | 16 ---------------- 3 files changed, 19 deletions(-) diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index aebcfd0926d5..173ad3e7a017 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -139,8 +139,6 @@ class SymbolTableImpl : public SymbolTable { void callWithStringView(StatName stat_name, const std::function& fn) const override; - void encode(absl::string_view name, Encoding& encoding); - #ifndef ENVOY_CONFIG_COVERAGE void debugPrint() const override; #endif diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index f806b0669adb..2d0761d36804 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -98,7 +98,6 @@ class ValidationInstance : Logger::Loggable, const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } TimeSource& timeSource() override { return api_->timeSource(); } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } - // Http::CodeStats& codeStats() override { return code_stats_; } std::chrono::milliseconds statsFlushInterval() const override { return config_.statsFlushInterval(); diff --git a/test/integration/server.cc b/test/integration/server.cc index 3aef8781167e..c358d9b86183 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -142,22 +142,6 @@ void IntegrationTestServer::serverReady() { void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion version, bool deterministic) { OptionsImpl options(Server::createTestOptionsImpl(config_path_, "", version)); - - /* - // TODO(jmarantz): Sadly, we use a mock symbol table here, as plumbing the - // real symbol table through the mocking hierarchy -- which generally - // constructs hierarchies of objects with no context, is too daunting. I think - // the right thing to do is to avoid mocks in integration tests. - Test::Global symbol_table; - Server::HotRestartNopImpl restarter(*symbol_table); - Thread::MutexBasicLockable lock; - - ThreadLocal::InstanceImpl tls; - Stats::HeapStatDataAllocator stats_allocator(*symbol_table); - Stats::StatsOptionsImpl stats_options; - Stats::ThreadLocalStoreImpl stats_store(stats_options, stats_allocator); - stat_store_ = &stats_store; - */ Thread::MutexBasicLockable lock; Runtime::RandomGeneratorPtr random_generator; From 60b82cfbe2a25bfaf7dbd2a0328a87238ba987b3 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 2 May 2019 22:20:43 -0400 Subject: [PATCH 062/106] get build to work again. Signed-off-by: Joshua Marantz --- test/common/stats/heap_stat_data_test.cc | 4 - test/common/stats/thread_local_store_test.cc | 116 ++++--------------- test/test_common/utility.h | 45 ------- 3 files changed, 21 insertions(+), 144 deletions(-) diff --git a/test/common/stats/heap_stat_data_test.cc b/test/common/stats/heap_stat_data_test.cc index 9f9d41ed2ce2..327215bb4b08 100644 --- a/test/common/stats/heap_stat_data_test.cc +++ b/test/common/stats/heap_stat_data_test.cc @@ -2,11 +2,7 @@ #include "common/stats/fake_symbol_table_impl.h" #include "common/stats/heap_stat_data.h" -<<<<<<< HEAD -#include "common/stats/stats_options_impl.h" #include "common/stats/symbol_table_impl.h" -======= ->>>>>>> master #include "test/test_common/logging.h" diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index e2d47988fdb4..527956e58e38 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -166,14 +166,7 @@ class HistogramTest : public testing::Test { } } -<<<<<<< HEAD - MOCK_METHOD1(alloc, RawStatData*(const std::string& name)); - MOCK_METHOD1(free, void(RawStatData& data)); - SymbolTableImpl symbol_table_; -======= - FakeSymbolTableImpl symbol_table_; ->>>>>>> master NiceMock main_thread_dispatcher_; NiceMock tls_; HeapStatDataAllocator alloc_; @@ -386,18 +379,16 @@ TEST_F(StatsThreadLocalStoreTest, OverlappingScopes) { class LookupWithStatNameTest : public testing::Test { public: - LookupWithStatNameTest() : alloc_(symbol_table_), store_(alloc_) {} + LookupWithStatNameTest() + : alloc_(symbol_table_), store_(std::make_unique(alloc_)) {} ~LookupWithStatNameTest() override { - store_.shutdownThreading(); - clearStorage(); - } - - void clearStorage() { + store_->shutdownThreading(); for (auto& stat_name_storage : stat_name_storage_) { - stat_name_storage.free(store_.symbolTable()); + stat_name_storage.free(store_->symbolTable()); } stat_name_storage_.clear(); - EXPECT_EQ(0, store_.symbolTable().numSymbols()); + store_.reset(); + EXPECT_EQ(0, symbol_table_.numSymbols()); } StatName makeStatName(absl::string_view name) { @@ -406,18 +397,18 @@ class LookupWithStatNameTest : public testing::Test { } StatNameStorage makeStatStorage(absl::string_view name) { - return StatNameStorage(name, store_.symbolTable()); + return StatNameStorage(name, symbol_table_); } - Stats::FakeSymbolTableImpl symbol_table_; + Stats::SymbolTableImpl symbol_table_; HeapStatDataAllocator alloc_; - ThreadLocalStoreImpl store_; + std::unique_ptr store_; std::vector stat_name_storage_; }; TEST_F(LookupWithStatNameTest, All) { - ScopePtr scope1 = store_.createScope("scope1."); - Counter& c1 = store_.counterFromStatName(makeStatName("c1")); + ScopePtr scope1 = store_->createScope("scope1."); + Counter& c1 = store_->counterFromStatName(makeStatName("c1")); Counter& c2 = scope1->counterFromStatName(makeStatName("c2")); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); @@ -426,7 +417,7 @@ TEST_F(LookupWithStatNameTest, All) { EXPECT_EQ(0, c1.tags().size()); EXPECT_EQ(0, c1.tags().size()); - Gauge& g1 = store_.gaugeFromStatName(makeStatName("g1")); + Gauge& g1 = store_->gaugeFromStatName(makeStatName("g1")); Gauge& g2 = scope1->gaugeFromStatName(makeStatName("g2")); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); @@ -435,7 +426,7 @@ TEST_F(LookupWithStatNameTest, All) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g1.tags().size()); - Histogram& h1 = store_.histogramFromStatName(makeStatName("h1")); + Histogram& h1 = store_->histogramFromStatName(makeStatName("h1")); Histogram& h2 = scope1->histogramFromStatName(makeStatName("h2")); scope1->deliverHistogramToSinks(h2, 0); EXPECT_EQ("h1", h1.name()); @@ -454,8 +445,8 @@ TEST_F(LookupWithStatNameTest, All) { ScopePtr scope3 = scope1->createScope(std::string("foo:\0:.", 7)); EXPECT_EQ("scope1.foo___.bar", scope3->counter("bar").name()); - EXPECT_EQ(4UL, store_.counters().size()); - EXPECT_EQ(2UL, store_.gauges().size()); + EXPECT_EQ(4UL, store_->counters().size()); + EXPECT_EQ(2UL, store_->gauges().size()); } class StatsMatcherTLSTest : public StatsThreadLocalStoreTest { @@ -696,7 +687,7 @@ class RememberStatsMatcherTest : public testing::TestWithParam { }; } - Stats::FakeSymbolTableImpl symbol_table_; + Stats::SymbolTableImpl symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; HeapStatDataAllocator heap_alloc_; @@ -793,7 +784,7 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTls) { } MockSink sink; - Stats::FakeSymbolTableImpl symbol_table; + Stats::SymbolTableImpl symbol_table; HeapStatDataAllocator alloc(symbol_table); auto store = std::make_unique(alloc); store->addSink(sink); @@ -811,11 +802,8 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTls) { 1000, [&store](absl::string_view name) { store->counter(std::string(name)); }); const size_t end_mem = Memory::Stats::totalCurrentlyAllocated(); EXPECT_LT(start_mem, end_mem); -<<<<<<< HEAD - EXPECT_LT(end_mem - start_mem, 13 * million); // actual value: 12443472 as of Nov 7, 2018 -======= const size_t million = 1000 * 1000; - EXPECT_LT(end_mem - start_mem, 20 * million); // actual value: 19601552 as of March 14, 2019 + EXPECT_LT(end_mem - start_mem, 13 * million); // actual value: 12443472 as of Nov 7, 2018 // HACK: doesn't like shutting down without threading having started. NiceMock main_thread_dispatcher; @@ -823,14 +811,13 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTls) { store->initializeThreading(main_thread_dispatcher, tls); store->shutdownThreading(); tls.shutdownThread(); ->>>>>>> master } TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithTls) { if (!TestUtil::hasDeterministicMallocStats()) { return; } - Stats::FakeSymbolTableImpl symbol_table; + Stats::SymbolTableImpl symbol_table; HeapStatDataAllocator alloc(symbol_table); auto store = std::make_unique(alloc); @@ -850,14 +837,10 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithTls) { 1000, [&store](absl::string_view name) { store->counter(std::string(name)); }); const size_t end_mem = Memory::Stats::totalCurrentlyAllocated(); EXPECT_LT(start_mem, end_mem); -<<<<<<< HEAD - EXPECT_LT(end_mem - start_mem, 16 * million); // actual value: 15722832 as of Nov 7, 2018 -======= const size_t million = 1000 * 1000; - EXPECT_LT(end_mem - start_mem, 23 * million); // actual value: 22880912 as of March 14, 2019 + EXPECT_LT(end_mem - start_mem, 16 * million); // actual value: 15722832 as of Nov 7, 2018 store->shutdownThreading(); tls.shutdownThread(); ->>>>>>> master } TEST_F(StatsThreadLocalStoreTest, ShuttingDown) { @@ -902,7 +885,7 @@ TEST_F(StatsThreadLocalStoreTest, MergeDuringShutDown) { } TEST(ThreadLocalStoreThreadTest, ConstructDestruct) { - Stats::FakeSymbolTableImpl symbol_table; + Stats::SymbolTableImpl symbol_table; Api::ApiPtr api = Api::createApiForTest(); Event::DispatcherPtr dispatcher = api->allocateDispatcher(); NiceMock tls; @@ -1083,62 +1066,5 @@ TEST_F(HistogramTest, BasicHistogramUsed) { } } -<<<<<<< HEAD -class TruncatingAllocTest : public HeapStatsThreadLocalStoreTest { -protected: - TruncatingAllocTest() - : test_alloc_(options_, symbol_table_), long_name_(options_.maxNameLength() + 1, 'A') {} - - void SetUp() override { - store_ = std::make_unique(options_, test_alloc_); - // Do not call superclass SetUp. - } - - SymbolTableImpl symbol_table_; - TestAllocator test_alloc_; - std::string long_name_; -}; - -TEST_F(TruncatingAllocTest, CounterNotTruncated) { - EXPECT_NO_LOGS({ - Counter& counter = store_->counter("simple"); - EXPECT_EQ(&counter, &store_->counter("simple")); - }); -} - -TEST_F(TruncatingAllocTest, GaugeNotTruncated) { - EXPECT_NO_LOGS({ - Gauge& gauge = store_->gauge("simple"); - EXPECT_EQ(&gauge, &store_->gauge("simple")); - }); -} - -TEST_F(TruncatingAllocTest, CounterTruncated) { - Counter* counter = nullptr; - EXPECT_LOG_CONTAINS("warning", "is too long with", { - Counter& c = store_->counter(long_name_); - counter = &c; - }); - EXPECT_NO_LOGS(EXPECT_EQ(counter, &store_->counter(long_name_))); -} - -TEST_F(TruncatingAllocTest, GaugeTruncated) { - Gauge* gauge = nullptr; - EXPECT_LOG_CONTAINS("warning", "is too long with", { - Gauge& g = store_->gauge(long_name_); - gauge = &g; - }); - EXPECT_NO_LOGS(EXPECT_EQ(gauge, &store_->gauge(long_name_))); -} - -TEST_F(TruncatingAllocTest, HistogramWithLongNameNotTruncated) { - EXPECT_NO_LOGS({ - Histogram& histogram = store_->histogram(long_name_); - EXPECT_EQ(&histogram, &store_->histogram(long_name_)); - }); -} - -======= ->>>>>>> master } // namespace Stats } // namespace Envoy diff --git a/test/test_common/utility.h b/test/test_common/utility.h index df9e8c5898d0..822c159a870d 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -493,51 +493,6 @@ makeHeaderMap(const std::initializer_list>& } // namespace Http -<<<<<<< HEAD -namespace Stats { - -/** - * Implements a RawStatDataAllocator using a contiguous block of heap-allocated - * memory, but is otherwise identical to the shared memory allocator in terms of - * reference counting, data structures, etc. - */ -class TestAllocator : public RawStatDataAllocator { -public: - struct TestBlockMemoryHashSetOptions : public BlockMemoryHashSetOptions { - TestBlockMemoryHashSetOptions() { - capacity = 200; - num_slots = 131; - } - }; - - TestAllocator(const StatsOptions& stats_options, SymbolTable& symbol_table) - : RawStatDataAllocator(mutex_, hash_set_, stats_options, symbol_table), - block_memory_(std::make_unique( - RawStatDataSet::numBytes(block_hash_options_, stats_options))), - hash_set_(block_hash_options_, true /* init */, block_memory_.get(), stats_options) {} - ~TestAllocator() { EXPECT_EQ(0, hash_set_.size()); } - -private: - SymbolTableImpl symbol_table_; - Thread::MutexBasicLockable mutex_; - TestBlockMemoryHashSetOptions block_hash_options_; - std::unique_ptr block_memory_; - RawStatDataSet hash_set_; -}; - -class MockedTestAllocator : public TestAllocator { -public: - MockedTestAllocator(const StatsOptions& stats_options, SymbolTable& symbol_table); - virtual ~MockedTestAllocator(); - - MOCK_METHOD1(alloc, RawStatData*(absl::string_view name)); - MOCK_METHOD1(free, void(RawStatData& data)); -}; - -} // namespace Stats - -======= ->>>>>>> master namespace Api { ApiPtr createApiForTest(); ApiPtr createApiForTest(Stats::Store& stat_store); From 9d2d673bd06f89226fabb60de5d597ae1c852fbd Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 3 May 2019 15:30:20 -0400 Subject: [PATCH 063/106] fix merge conflicts. Signed-off-by: Joshua Marantz --- test/integration/server.cc | 5 ----- test/server/hot_restart_impl_test.cc | 4 ---- test/server/http/admin_test.cc | 5 ----- 3 files changed, 14 deletions(-) diff --git a/test/integration/server.cc b/test/integration/server.cc index c1271bae8e07..86a2b793ea0e 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -168,13 +168,8 @@ void IntegrationTestServerImpl::createAndRunEnvoyServer( Network::Address::InstanceConstSharedPtr local_address, ListenerHooks& hooks, Thread::BasicLockable& access_log_lock, Server::ComponentFactory& component_factory, Runtime::RandomGeneratorPtr&& random_generator) { -<<<<<<< HEAD Stats::SymbolTableImpl symbol_table; - Server::HotRestartNopImpl restarter(symbol_table); -======= - Stats::FakeSymbolTableImpl symbol_table; Server::HotRestartNopImpl restarter; ->>>>>>> master ThreadLocal::InstanceImpl tls; Stats::HeapStatDataAllocator stats_allocator(symbol_table); Stats::ThreadLocalStoreImpl stat_store(stats_allocator); diff --git a/test/server/hot_restart_impl_test.cc b/test/server/hot_restart_impl_test.cc index 33acc5c7fd91..ce287407b5dc 100644 --- a/test/server/hot_restart_impl_test.cc +++ b/test/server/hot_restart_impl_test.cc @@ -46,10 +46,6 @@ class HotRestartImplTest : public testing::Test { hot_restart_->drainParentListeners(); } -<<<<<<< HEAD - Stats::SymbolTableImpl symbol_table_; -======= ->>>>>>> master Api::MockOsSysCalls os_sys_calls_; TestThreadsafeSingletonInjector os_calls{&os_sys_calls_}; NiceMock options_; diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 537a89f9fab5..52b181e1d26f 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -1272,12 +1272,7 @@ class PrometheusStatsFormatterTest : public testing::Test { histograms_.push_back(histogram); } -<<<<<<< HEAD Stats::SymbolTableImpl symbol_table_; - Stats::StatsOptionsImpl stats_options_; -======= - Stats::FakeSymbolTableImpl symbol_table_; ->>>>>>> master Stats::HeapStatDataAllocator alloc_; std::vector counters_; std::vector gauges_; From 49b80785fd7b50d0fb1a352b5687e253c43105c8 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 6 May 2019 08:20:01 -0400 Subject: [PATCH 064/106] Remove stale test file. Signed-off-by: Joshua Marantz --- test/common/stats/raw_stat_data_test.cc | 57 ------------------------- 1 file changed, 57 deletions(-) delete mode 100644 test/common/stats/raw_stat_data_test.cc diff --git a/test/common/stats/raw_stat_data_test.cc b/test/common/stats/raw_stat_data_test.cc deleted file mode 100644 index f06072922283..000000000000 --- a/test/common/stats/raw_stat_data_test.cc +++ /dev/null @@ -1,57 +0,0 @@ -#include - -#include "common/stats/raw_stat_data.h" -#include "common/stats/stats_options_impl.h" - -#include "test/test_common/logging.h" -#include "test/test_common/utility.h" - -#include "gtest/gtest.h" - -namespace Envoy { -namespace Stats { -namespace { - -class RawStatDataTest : public testing::Test { -public: - RawStatDataTest() : allocator_(stats_options_, symbol_table_) {} - - StatsOptionsImpl stats_options_; - SymbolTableImpl symbol_table_; - TestAllocator allocator_; // This is RawStatDataAllocator with some size settings. -}; - -// Note: a similar test using HeapStatData* is in heap_stat_data_test.cc. -TEST_F(RawStatDataTest, RawTruncate) { - const std::string long_string(stats_options_.maxNameLength() + 1, 'A'); - RawStatData* stat{}; - EXPECT_LOG_CONTAINS("warning", " is too long ", stat = allocator_.alloc(long_string)); - EXPECT_NE(stat->key(), long_string); - - // If I add more bytes to the key, I'll get the same allocation back - // due to the truncated map lookup. - EXPECT_EQ(stat, allocator_.alloc(long_string + " ignored")); - - allocator_.free(*stat); - allocator_.free(*stat); // Have to free it twice as second allocation bumped ref-count. -} - -// Note: a similar test using HeapStatData* is in heap_stat_data_test.cc. -TEST_F(RawStatDataTest, RawAlloc) { - Stats::RawStatData* stat_1 = allocator_.alloc("ref_name"); - ASSERT_NE(stat_1, nullptr); - Stats::RawStatData* stat_2 = allocator_.alloc("ref_name"); - ASSERT_NE(stat_2, nullptr); - Stats::RawStatData* stat_3 = allocator_.alloc("not_ref_name"); - ASSERT_NE(stat_3, nullptr); - EXPECT_EQ(stat_1, stat_2); - EXPECT_NE(stat_1, stat_3); - EXPECT_NE(stat_2, stat_3); - allocator_.free(*stat_1); - allocator_.free(*stat_2); - allocator_.free(*stat_3); -} - -} // namespace -} // namespace Stats -} // namespace Envoy From afbb94a1b4d9209685e6494903da279092a03712 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sun, 12 May 2019 22:02:14 -0400 Subject: [PATCH 065/106] clean up commented-out code. Signed-off-by: Joshua Marantz --- source/common/stats/heap_stat_data.cc | 2 -- test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc | 3 --- 2 files changed, 5 deletions(-) diff --git a/source/common/stats/heap_stat_data.cc b/source/common/stats/heap_stat_data.cc index fbca56d8d757..41494ed85641 100644 --- a/source/common/stats/heap_stat_data.cc +++ b/source/common/stats/heap_stat_data.cc @@ -1,7 +1,5 @@ #include "common/stats/heap_stat_data.h" -#include - #include "common/common/lock_guard.h" #include "common/common/logger.h" #include "common/common/thread.h" diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index 76ae86b7fdab..3ac2e60cc433 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -187,7 +187,6 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { std::vector tags = {Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}; auto counter = std::make_shared>(); - // counter->symbol_table_ = &symbol_table; counter->name_ = "test_counter"; counter->used_ = true; counter->latch_ = 1; @@ -200,7 +199,6 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { counter->used_ = false; auto gauge = std::make_shared>(); - // gauge->symbol_table_ = &symbol_table; gauge->name_ = "test_gauge"; gauge->value_ = 1; gauge->used_ = true; @@ -212,7 +210,6 @@ TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { sink.flush(source); NiceMock timer; - // timer.symbol_table_ = &symbol_table; timer.name_ = "test_timer"; timer.setTags(tags); EXPECT_CALL(*std::dynamic_pointer_cast>(writer_ptr), From 95c82b27f3c6aa47193ae2137058d08b9952542b Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 21 May 2019 19:52:48 -0400 Subject: [PATCH 066/106] Skip over empty StatNames. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index 9a330cabf08b..bb7394a0ce02 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -364,14 +364,18 @@ void StatNameStorageSet::free(SymbolTable& symbol_table) { SymbolTable::StoragePtr SymbolTableImpl::join(const std::vector& stat_names) const { uint64_t num_bytes = 0; for (StatName stat_name : stat_names) { - num_bytes += stat_name.dataSize(); + if (!stat_name.empty()) { + num_bytes += stat_name.dataSize(); + } } auto bytes = std::make_unique(num_bytes + StatNameSizeEncodingBytes); uint8_t* p = writeLengthReturningNext(num_bytes, bytes.get()); for (StatName stat_name : stat_names) { - const uint64_t stat_name_bytes = stat_name.dataSize(); - memcpy(p, stat_name.data(), stat_name_bytes); - p += stat_name_bytes; + if (!stat_name.empty()) { + const uint64_t stat_name_bytes = stat_name.dataSize(); + memcpy(p, stat_name.data(), stat_name_bytes); + p += stat_name_bytes; + } } return bytes; } From 64f8099e75099f562199f93d41771d30b3198405 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 21 May 2019 20:20:29 -0400 Subject: [PATCH 067/106] Remove superfluous changes. Signed-off-by: Joshua Marantz --- .../filters/http/ext_authz/ext_authz_test.cc | 17 ----------------- .../common/statsd/udp_statsd_test.cc | 1 - test/integration/stats_integration_test.cc | 3 +-- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index dc0f4d6361d3..16233d571eb1 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -159,23 +159,6 @@ TEST_F(HttpFilterTest, MergeConfig) { EXPECT_EQ("value", merged_extensions.at("key")); } -class HttpExtAuthzFilterTestBase : public HttpFilterTest { -public: - void initConfig(envoy::config::filter::http::ext_authz::v2::ExtAuthz& proto_config) { - config_ = std::make_unique(proto_config, local_info_, stats_store_, runtime_, - http_context_); - } -}; - -class HttpExtAuthzFilterTest : public HttpExtAuthzFilterTestBase { -public: - void initialize(const std::string yaml) { - envoy::config::filter::http::ext_authz::v2::ExtAuthz proto_config{}; - MessageUtil::loadFromYaml(yaml, proto_config); - initConfig(proto_config); - } -}; - // Test when failure_mode_allow is NOT set and the response from the authorization service is Error // that the request is not allowed to continue. TEST_F(HttpFilterTest, ErrorFailClose) { diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index 3ac2e60cc433..d48999fc258a 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -179,7 +179,6 @@ TEST(UdpStatsdSinkTest, CheckActualStatsWithCustomPrefix) { } TEST(UdpStatsdSinkWithTagsTest, CheckActualStats) { - Stats::SymbolTableImpl symbol_table; NiceMock source; auto writer_ptr = std::make_shared>(); NiceMock tls_; diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index c565a6952880..c8c29921be48 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -210,10 +210,9 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/04/12 6477 59576 Implementing Endpoint lease... // 2019/04/23 6659 59512 Reintroduce dispatcher stats... // 2019/04/24 6161 49415 Pack tags and tag extracted names - // 2019/04/26 4980 40289 Real symbol table implementation - // 2019/05/07 6794 49957 Stats for excluded hosts in cluster // 2019/04/27 6733 50213 Use SymbolTable API for HTTP codes + // 2019/05/21 4980 40961 Real symbol table implementation EXPECT_EQ(m_per_cluster, 40961); } From 801b1b6a5588e866e9ee25bd529c061208a007b0 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 21 May 2019 20:25:10 -0400 Subject: [PATCH 068/106] Remove superfluous comments. Signed-off-by: Joshua Marantz --- test/common/stats/thread_local_store_test.cc | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index fd81c106c3fc..0e5d1605ec47 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -858,19 +858,7 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTls) { const size_t end_mem = Memory::Stats::totalCurrentlyAllocated(); EXPECT_LT(start_mem, end_mem); const size_t million = 1000 * 1000; - /* - EXPECT_LT(end_mem - start_mem, 13 * million); // actual value: 12443472 as of Nov 7, 2018 - - // HACK: doesn't like shutting down without threading having started. - NiceMock main_thread_dispatcher; - NiceMock tls; - store->initializeThreading(main_thread_dispatcher, tls); - store->shutdownThreading(); - tls.shutdownThread(); - */ - - // EXPECT_LT(end_mem - start_mem, 20 * million); // actual value: 19601552 as of March 14, 2019 - EXPECT_LT(end_mem - start_mem, 13 * million); // actual value: 12443472 as of March 14, 2019 + EXPECT_LT(end_mem - start_mem, 13 * million); // actual value: 12443472 as of May 21, 2019 } TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithTls) { From ce7417d3012142a67857c96f2a8d7e828eed18b1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 18 Jun 2019 16:01:12 -0400 Subject: [PATCH 069/106] get CI mostly working; I think there's an unrelated problem in hotrestart_test.sh. Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 2 +- test/common/grpc/context_impl_test.cc | 4 +- test/common/stats/isolated_store_impl_test.cc | 68 ++++++++++--------- .../http1_bridge_filter_test.cc | 2 +- .../http/grpc_web/grpc_web_filter_test.cc | 2 +- .../lightstep/lightstep_tracer_impl_test.cc | 4 +- 6 files changed, 43 insertions(+), 39 deletions(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index dcbc0eb338de..9ee04a233454 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -15,7 +15,7 @@ namespace Envoy { namespace Stats { IsolatedStoreImpl::IsolatedStoreImpl() - : IsolatedStoreImpl(std::make_unique()) {} + : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr&& symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/test/common/grpc/context_impl_test.cc b/test/common/grpc/context_impl_test.cc index ace45801ca11..bff7390bcd27 100644 --- a/test/common/grpc/context_impl_test.cc +++ b/test/common/grpc/context_impl_test.cc @@ -18,7 +18,7 @@ namespace Grpc { TEST(GrpcContextTest, ChargeStats) { NiceMock cluster; - Envoy::Test::Global symbol_table_; + Envoy::Test::Global symbol_table_; Stats::StatNamePool pool(*symbol_table_); const Stats::StatName service = pool.add("service"); const Stats::StatName method = pool.add("method"); @@ -58,7 +58,7 @@ TEST(GrpcContextTest, ResolveServiceAndMethod) { Http::HeaderMapImpl headers; Http::HeaderEntry& path = headers.insertPath(); path.value(std::string("/service_name/method_name")); - Envoy::Test::Global symbol_table; + Envoy::Test::Global symbol_table; ContextImpl context(*symbol_table); absl::optional request_names = context.resolveServiceAndMethod(&path); EXPECT_TRUE(request_names); diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index 73e83407f6c0..08f5435f5b33 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -13,21 +13,25 @@ namespace Stats { class StatsIsolatedStoreImplTest : public testing::Test { protected: - StatsIsolatedStoreImplTest() : pool_(store_.symbolTable()) {} + StatsIsolatedStoreImplTest() + : pool_(symbol_table_), + store_(std::make_unique(symbol_table_)) {} ~StatsIsolatedStoreImplTest() override { pool_.clear(); - EXPECT_EQ(0, store_.symbolTable().numSymbols()); + store_.reset(); + EXPECT_EQ(0, symbol_table_.numSymbols()); } StatName makeStatName(absl::string_view name) { return pool_.add(name); } - IsolatedStoreImpl store_; + SymbolTableImpl symbol_table_; StatNamePool pool_; + std::unique_ptr store_; }; TEST_F(StatsIsolatedStoreImplTest, All) { - ScopePtr scope1 = store_.createScope("scope1."); - Counter& c1 = store_.counter("c1"); + ScopePtr scope1 = store_->createScope("scope1."); + Counter& c1 = store_->counter("c1"); Counter& c2 = scope1->counter("c2"); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); @@ -36,16 +40,16 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, c1.tags().size()); EXPECT_EQ(0, c1.tags().size()); - StatNameManagedStorage c1_name("c1", store_.symbolTable()); + StatNameManagedStorage c1_name("c1", store_->symbolTable()); c1.add(100); - auto found_counter = store_.findCounter(c1_name.statName()); + auto found_counter = store_->findCounter(c1_name.statName()); ASSERT_TRUE(found_counter.has_value()); EXPECT_EQ(&c1, &found_counter->get()); EXPECT_EQ(100, found_counter->get().value()); c1.add(100); EXPECT_EQ(200, found_counter->get().value()); - Gauge& g1 = store_.gauge("g1", Gauge::ImportMode::Accumulate); + Gauge& g1 = store_->gauge("g1", Gauge::ImportMode::Accumulate); Gauge& g2 = scope1->gauge("g2", Gauge::ImportMode::Accumulate); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); @@ -54,16 +58,16 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g2.tags().size()); - StatNameManagedStorage g1_name("g1", store_.symbolTable()); + StatNameManagedStorage g1_name("g1", store_->symbolTable()); g1.set(100); - auto found_gauge = store_.findGauge(g1_name.statName()); + auto found_gauge = store_->findGauge(g1_name.statName()); ASSERT_TRUE(found_gauge.has_value()); EXPECT_EQ(&g1, &found_gauge->get()); EXPECT_EQ(100, found_gauge->get().value()); g1.set(0); EXPECT_EQ(0, found_gauge->get().value()); - Histogram& h1 = store_.histogram("h1"); + Histogram& h1 = store_->histogram("h1"); EXPECT_TRUE(h1.used()); // hardcoded in impl to be true always. Histogram& h2 = scope1->histogram("h2"); scope1->deliverHistogramToSinks(h2, 0); @@ -76,8 +80,8 @@ TEST_F(StatsIsolatedStoreImplTest, All) { h1.recordValue(200); h2.recordValue(200); - StatNameManagedStorage h1_name("h1", store_.symbolTable()); - auto found_histogram = store_.findHistogram(h1_name.statName()); + StatNameManagedStorage h1_name("h1", store_->symbolTable()); + auto found_histogram = store_->findHistogram(h1_name.statName()); ASSERT_TRUE(found_histogram.has_value()); EXPECT_EQ(&h1, &found_histogram->get()); @@ -88,18 +92,18 @@ TEST_F(StatsIsolatedStoreImplTest, All) { ScopePtr scope3 = scope1->createScope(std::string("foo:\0:.", 7)); EXPECT_EQ("scope1.foo___.bar", scope3->counter("bar").name()); - EXPECT_EQ(4UL, store_.counters().size()); - EXPECT_EQ(2UL, store_.gauges().size()); + EXPECT_EQ(4UL, store_->counters().size()); + EXPECT_EQ(2UL, store_->gauges().size()); - StatNameManagedStorage nonexistent_name("nonexistent", store_.symbolTable()); - 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); + StatNameManagedStorage nonexistent_name("nonexistent", store_->symbolTable()); + 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); } TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { - ScopePtr scope1 = store_.createScope("scope1."); - Counter& c1 = store_.counterFromStatName(makeStatName("c1")); + ScopePtr scope1 = store_->createScope("scope1."); + Counter& c1 = store_->counterFromStatName(makeStatName("c1")); Counter& c2 = scope1->counterFromStatName(makeStatName("c2")); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); @@ -108,7 +112,7 @@ TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { EXPECT_EQ(0, c1.tags().size()); EXPECT_EQ(0, c1.tags().size()); - Gauge& g1 = store_.gaugeFromStatName(makeStatName("g1"), Gauge::ImportMode::Accumulate); + Gauge& g1 = store_->gaugeFromStatName(makeStatName("g1"), Gauge::ImportMode::Accumulate); Gauge& g2 = scope1->gaugeFromStatName(makeStatName("g2"), Gauge::ImportMode::Accumulate); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); @@ -117,7 +121,7 @@ TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g1.tags().size()); - Histogram& h1 = store_.histogramFromStatName(makeStatName("h1")); + Histogram& h1 = store_->histogramFromStatName(makeStatName("h1")); Histogram& h2 = scope1->histogramFromStatName(makeStatName("h2")); scope1->deliverHistogramToSinks(h2, 0); EXPECT_EQ("h1", h1.name()); @@ -136,12 +140,12 @@ TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { ScopePtr scope3 = scope1->createScope(std::string("foo:\0:.", 7)); EXPECT_EQ("scope1.foo___.bar", scope3->counter("bar").name()); - EXPECT_EQ(4UL, store_.counters().size()); - EXPECT_EQ(2UL, store_.gauges().size()); + EXPECT_EQ(4UL, store_->counters().size()); + EXPECT_EQ(2UL, store_->gauges().size()); } TEST_F(StatsIsolatedStoreImplTest, ConstSymtabAccessor) { - ScopePtr scope = store_.createScope("scope."); + ScopePtr scope = store_->createScope("scope."); const Scope& cscope = *scope; const SymbolTable& const_symbol_table = cscope.constSymbolTable(); SymbolTable& symbol_table = scope->symbolTable(); @@ -151,7 +155,7 @@ TEST_F(StatsIsolatedStoreImplTest, ConstSymtabAccessor) { TEST_F(StatsIsolatedStoreImplTest, LongStatName) { const std::string long_string(128, 'A'); - ScopePtr scope = store_.createScope("scope."); + ScopePtr scope = store_->createScope("scope."); Counter& counter = scope->counter(long_string); EXPECT_EQ(absl::StrCat("scope.", long_string), counter.name()); } @@ -169,9 +173,9 @@ struct TestStats { }; TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { - TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(store_, "test."), - POOL_GAUGE_PREFIX(store_, "test."), - POOL_HISTOGRAM_PREFIX(store_, "test."))}; + TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(*store_, "test."), + POOL_GAUGE_PREFIX(*store_, "test."), + POOL_HISTOGRAM_PREFIX(*store_, "test."))}; Counter& counter = test_stats.test_counter_; EXPECT_EQ("test.test_counter", counter.name()); @@ -184,9 +188,9 @@ TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { } TEST_F(StatsIsolatedStoreImplTest, NullImplCoverage) { - NullCounterImpl c(store_.symbolTable()); + NullCounterImpl c(store_->symbolTable()); EXPECT_EQ(0, c.latch()); - NullGaugeImpl g(store_.symbolTable()); + NullGaugeImpl g(store_->symbolTable()); EXPECT_EQ(0, g.value()); } 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 d7c40ab86991..26ef0a4f783d 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 @@ -36,7 +36,7 @@ class GrpcHttp1BridgeFilterTest : public testing::Test { ~GrpcHttp1BridgeFilterTest() override { filter_.onDestroy(); } - Envoy::Test::Global symbol_table_; + Envoy::Test::Global symbol_table_; Grpc::ContextImpl context_; Http1BridgeFilter filter_; NiceMock decoder_callbacks_; 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 1500708fe3c6..55e16e264ec2 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 @@ -106,7 +106,7 @@ class GrpcWebFilterTest : public testing::TestWithParamvalue().getStringView()); } - Envoy::Test::Global symbol_table_; + Envoy::Test::Global symbol_table_; Grpc::ContextImpl grpc_context_; GrpcWebFilter filter_; NiceMock decoder_callbacks_; diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index c6c2b72f753a..e0f6a0c63229 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -45,7 +45,7 @@ namespace { class LightStepDriverTest : public testing::Test { public: - LightStepDriverTest() : grpc_context_(*symbol_table_) {} + LightStepDriverTest() : grpc_context_(*symbol_table_), stats_(*symbol_table_) {} void setup(envoy::config::trace::v2::LightstepConfig& lightstep_config, bool init_timer, Common::Ot::OpenTracingDriver::PropagationMode propagation_mode = @@ -89,7 +89,7 @@ class LightStepDriverTest : public testing::Test { SystemTime start_time_; StreamInfo::MockStreamInfo stream_info_; - Envoy::Test::Global symbol_table_; + Envoy::Test::Global symbol_table_; Grpc::ContextImpl grpc_context_; NiceMock tls_; Stats::IsolatedStoreImpl stats_; From 713d97db7e8ca8b41433a33000c4b4f8b64ca426 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 18 Jun 2019 16:03:14 -0400 Subject: [PATCH 070/106] format Signed-off-by: Joshua Marantz --- source/common/stats/isolated_store_impl.cc | 3 +-- test/common/stats/isolated_store_impl_test.cc | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 9ee04a233454..d6d5f6213d34 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -14,8 +14,7 @@ namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() - : IsolatedStoreImpl(std::make_unique()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(std::make_unique()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr&& symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index 08f5435f5b33..d5097059d29f 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -14,8 +14,7 @@ namespace Stats { class StatsIsolatedStoreImplTest : public testing::Test { protected: StatsIsolatedStoreImplTest() - : pool_(symbol_table_), - store_(std::make_unique(symbol_table_)) {} + : pool_(symbol_table_), store_(std::make_unique(symbol_table_)) {} ~StatsIsolatedStoreImplTest() override { pool_.clear(); store_.reset(); From 5980a1017a75f7ba2afe1779b1e97c3fc9a65482 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 18 Jun 2019 17:00:01 -0400 Subject: [PATCH 071/106] updated memory usage Signed-off-by: Joshua Marantz --- test/common/stats/thread_local_store_test.cc | 4 ++-- test/integration/stats_integration_test.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 867c252cec07..4470df9e3fe0 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -861,7 +861,7 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTls) { TestUtil::forEachSampleStat( 1000, [&store](absl::string_view name) { store.counter(std::string(name)); }); const size_t million = 1000 * 1000; - EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 12443472); // June 18, 2019 + EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 12430144); // June 18, 2019 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 13 * million); } @@ -881,7 +881,7 @@ TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithTls) { TestUtil::forEachSampleStat( 1000, [&store](absl::string_view name) { store.counter(std::string(name)); }); const size_t million = 1000 * 1000; - EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 15722832); // June 18, 2019 + EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 15707232); // June 18, 2019 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 16 * million); store.shutdownThreading(); tls.shutdownThread(); diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 21c89a0141db..7b85ac5ae899 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -207,10 +207,10 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/03 7199 49393 absl update // 2019/06/06 7208 49650 make memory targets approximate // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks - // 2019/06/18 4980 40961 41200 macros for exact/upper-bound memory checks + // 2019/06/18 4980 41009 41500 macros for exact/upper-bound memory checks - EXPECT_MEMORY_EQ(m_per_cluster, 40961); - EXPECT_MEMORY_LE(m_per_cluster, 41200); + EXPECT_MEMORY_EQ(m_per_cluster, 41009); + EXPECT_MEMORY_LE(m_per_cluster, 41500); } } // namespace From 199ef473f10c6c9312db04eb7263e8b7846f46bb Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 18 Jun 2019 17:00:33 -0400 Subject: [PATCH 072/106] fix comment Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 7b85ac5ae899..3859f90ac51d 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -207,7 +207,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/03 7199 49393 absl update // 2019/06/06 7208 49650 make memory targets approximate // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks - // 2019/06/18 4980 41009 41500 macros for exact/upper-bound memory checks + // 2019/06/18 4980 41009 41500 use real symbol tables EXPECT_MEMORY_EQ(m_per_cluster, 41009); EXPECT_MEMORY_LE(m_per_cluster, 41500); From c831e3719fd06ad7468b5817804262b8707e0fdc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 5 Jul 2019 19:48:59 -0400 Subject: [PATCH 073/106] cleanup tests. Signed-off-by: Joshua Marantz --- test/common/stats/heap_stat_data_test.cc | 63 ---------------------- test/integration/stats_integration_test.cc | 2 +- 2 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 test/common/stats/heap_stat_data_test.cc diff --git a/test/common/stats/heap_stat_data_test.cc b/test/common/stats/heap_stat_data_test.cc deleted file mode 100644 index 889c3db2ec83..000000000000 --- a/test/common/stats/heap_stat_data_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#include "common/stats/fake_symbol_table_impl.h" -#include "common/stats/heap_stat_data.h" -#include "common/stats/symbol_table_impl.h" - -#include "test/test_common/logging.h" - -#include "gtest/gtest.h" - -namespace Envoy { -namespace Stats { -namespace { - -class HeapStatDataTest : public testing::Test { -protected: - HeapStatDataTest() : alloc_(symbol_table_), pool_(symbol_table_) {} - ~HeapStatDataTest() { clearStorage(); } - - StatNameStorage makeStatStorage(absl::string_view name) { - return StatNameStorage(name, symbol_table_); - } - - StatName makeStat(absl::string_view name) { return pool_.add(name); } - - void clearStorage() { - pool_.clear(); - EXPECT_EQ(0, symbol_table_.numSymbols()); - } - - SymbolTableImpl symbol_table_; - HeapStatDataAllocator alloc_; - StatNamePool pool_; -}; - -// No truncation occurs in the implementation of HeapStatData. -TEST_F(HeapStatDataTest, HeapNoTruncate) { - const std::string long_string(128, 'A'); - StatName stat_name = makeStat(long_string); - HeapStatData* stat{}; - EXPECT_NO_LOGS(stat = HeapStatData::alloc(stat_name, symbol_table_)); - EXPECT_EQ(stat->statName(), stat_name); - stat->free(symbol_table_); -}; - -TEST_F(HeapStatDataTest, HeapAlloc) { - HeapStatData* stat_1 = HeapStatData::alloc(makeStat("ref_name"), symbol_table_); - ASSERT_NE(stat_1, nullptr); - HeapStatData* stat_2 = HeapStatData::alloc(makeStat("ref_name"), symbol_table_); - ASSERT_NE(stat_2, nullptr); - HeapStatData* stat_3 = HeapStatData::alloc(makeStat("not_ref_name"), symbol_table_); - ASSERT_NE(stat_3, nullptr); - EXPECT_NE(stat_1, stat_2); - EXPECT_NE(stat_1, stat_3); - EXPECT_NE(stat_2, stat_3); - stat_1->free(symbol_table_); - stat_2->free(symbol_table_); - stat_3->free(symbol_table_); -} - -} // namespace -} // namespace Stats -} // namespace Envoy diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index caefb57b5166..0e242a7059f9 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -209,7 +209,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks // 2019/06/29 7364 45685 46000 combine 2 levels of stat ref-counting into 1 // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData - // 2019/07/03 4980 35224 35500 use real symbol tables + // 2019/07/03 4980 35393 35800 use real symbol tables // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you From 3cb2b91f7725f6f703abbb6a246e06367d130112 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 5 Jul 2019 23:26:05 -0400 Subject: [PATCH 074/106] fix expected value. Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 0e242a7059f9..bd41a706f567 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -209,7 +209,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks // 2019/06/29 7364 45685 46000 combine 2 levels of stat ref-counting into 1 // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData - // 2019/07/03 4980 35393 35800 use real symbol tables + // 2019/07/03 4980 35393 35500 use real symbol tables // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -219,7 +219,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 35224); + EXPECT_MEMORY_EQ(m_per_cluster, 35393); EXPECT_MEMORY_LE(m_per_cluster, 35500); } From 1016ae6b5da35eaf90cf87c9327b356c9601df78 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 24 Jul 2019 13:45:03 -0400 Subject: [PATCH 075/106] shared the main symbol-table with the isolated stats used for cluster info. Signed-off-by: Joshua Marantz --- source/common/upstream/upstream_impl.cc | 1 + source/common/upstream/upstream_impl.h | 1 + 2 files changed, 2 insertions(+) diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 69160fa95a99..aef1f903fbae 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -557,6 +557,7 @@ ClusterInfoImpl::ClusterInfoImpl(const envoy::api::v2::Cluster& config, PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, per_connection_buffer_limit_bytes, 1024 * 1024)), transport_socket_factory_(std::move(socket_factory)), stats_scope_(std::move(stats_scope)), stats_(generateStats(*stats_scope_)), + load_report_stats_store_(stats_scope_->symbolTable()), load_report_stats_(generateLoadReportStats(load_report_stats_store_)), features_(parseFeatures(config)), http2_settings_(Http::Utility::parseHttp2Settings(config.http2_protocol_options())), diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 70a7ab3c3617..d1af5fad1415 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -73,6 +73,7 @@ class HostDescriptionImpl : virtual public HostDescription { .bool_value()), metadata_(std::make_shared(metadata)), locality_(locality), locality_zone_stat_name_(locality.zone(), cluster->statsScope().symbolTable()), + stats_store_(cluster->statsScope().symbolTable()), stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}, priority_(priority) { if (health_check_config.port_value() != 0 && From e45e905e8512c265ef23a8840ff9ae79c1477eb7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 25 Jul 2019 06:19:35 -0400 Subject: [PATCH 076/106] Update memory usage to account for isolated stats sharing symbol tables. Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index ad2b87484f58..394d80f425eb 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -211,6 +211,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData // 2019/07/06 7477 42742 43000 fork gauge representation to drop pending_increment_ // 2019/07/15 7555 42806 43000 static link libstdc++ in tests + // 2019/07/24 xxxx 42798 43000 share main symtab for ClusterInfo's isolated stats. // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -220,7 +221,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 42806); // 104 bytes higher than a debug build. + EXPECT_MEMORY_EQ(m_per_cluster, 42798); // 104 bytes higher than a debug build. EXPECT_MEMORY_LE(m_per_cluster, 43000); } From e1787cb6eed17c57af1a0dc65516aa3d1d24c559 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 25 Jul 2019 06:20:59 -0400 Subject: [PATCH 077/106] pedantic spelling Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 394d80f425eb..23a6a7eb8c80 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -211,7 +211,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData // 2019/07/06 7477 42742 43000 fork gauge representation to drop pending_increment_ // 2019/07/15 7555 42806 43000 static link libstdc++ in tests - // 2019/07/24 xxxx 42798 43000 share main symtab for ClusterInfo's isolated stats. + // 2019/07/24 xxxx 42798 43000 Share SymbolTable for ClusterInfo's isolated stats // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you From 600977e4ba90c41cde7e8a8d49d3b4617013f9bf Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 25 Jul 2019 06:22:37 -0400 Subject: [PATCH 078/106] update PR # Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 23a6a7eb8c80..2c35ab6f4e9d 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -211,7 +211,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData // 2019/07/06 7477 42742 43000 fork gauge representation to drop pending_increment_ // 2019/07/15 7555 42806 43000 static link libstdc++ in tests - // 2019/07/24 xxxx 42798 43000 Share SymbolTable for ClusterInfo's isolated stats + // 2019/07/24 7717 42798 43000 Share SymbolTable for ClusterInfo's isolated stats // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you From 44bd5a50b528913b563b2339b1a8109818754d33 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 25 Jul 2019 06:47:43 -0400 Subject: [PATCH 079/106] format Signed-off-by: Joshua Marantz --- source/common/upstream/upstream_impl.cc | 3 +-- source/common/upstream/upstream_impl.h | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index aef1f903fbae..854976449522 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -556,8 +556,7 @@ ClusterInfoImpl::ClusterInfoImpl(const envoy::api::v2::Cluster& config, per_connection_buffer_limit_bytes_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, per_connection_buffer_limit_bytes, 1024 * 1024)), transport_socket_factory_(std::move(socket_factory)), stats_scope_(std::move(stats_scope)), - stats_(generateStats(*stats_scope_)), - load_report_stats_store_(stats_scope_->symbolTable()), + stats_(generateStats(*stats_scope_)), load_report_stats_store_(stats_scope_->symbolTable()), load_report_stats_(generateLoadReportStats(load_report_stats_store_)), features_(parseFeatures(config)), http2_settings_(Http::Utility::parseHttp2Settings(config.http2_protocol_options())), diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index d1af5fad1415..0b121ffd2bab 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -73,8 +73,9 @@ class HostDescriptionImpl : virtual public HostDescription { .bool_value()), metadata_(std::make_shared(metadata)), locality_(locality), locality_zone_stat_name_(locality.zone(), cluster->statsScope().symbolTable()), - stats_store_(cluster->statsScope().symbolTable()), - stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}, + stats_store_(cluster->statsScope().symbolTable()), stats_{ALL_HOST_STATS( + POOL_COUNTER(stats_store_), + POOL_GAUGE(stats_store_))}, priority_(priority) { if (health_check_config.port_value() != 0 && dest_address->type() != Network::Address::Type::Ip) { From d4744bdef6bdbee4be9a66347be9dc332a5200c1 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 8 Aug 2019 09:14:36 -0400 Subject: [PATCH 080/106] Add option to switch between fake and real symbol-tables on the command-line. Signed-off-by: Joshua Marantz --- include/envoy/server/options.h | 5 +++++ source/server/options_impl.cc | 7 ++++++- source/server/options_impl.h | 2 ++ test/mocks/server/mocks.h | 1 + test/server/options_impl_test.cc | 6 ++++-- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/include/envoy/server/options.h b/include/envoy/server/options.h index 8904925f28e9..ded38e55ee7b 100644 --- a/include/envoy/server/options.h +++ b/include/envoy/server/options.h @@ -179,6 +179,11 @@ class Options { */ virtual bool libeventBufferEnabled() const PURE; + /** + * @return whether to use the fake symbol table implementation. + */ + virtual bool fakeSymbolTableEnabled() const PURE; + /** * @return bool indicating whether cpuset size should determine the number of worker threads. */ diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index 9db443e9848f..9f7048756bf7 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -105,6 +105,9 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv, TCLAP::ValueArg use_libevent_buffer("", "use-libevent-buffers", "Use the original libevent buffer implementation", false, true, "bool", cmd); + TCLAP::ValueArg use_fake_symbol_table("", "use-fake-symbol-table", + "Use fake symbol table implementation", + false, true, "bool", cmd); cmd.setExceptionHandling(false); try { @@ -129,6 +132,7 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv, mutex_tracing_enabled_ = enable_mutex_tracing.getValue(); libevent_buffer_enabled_ = use_libevent_buffer.getValue(); + fake_symbol_table_enabled_ = use_fake_symbol_table.getValue(); cpuset_threads_ = cpuset_threads.getValue(); log_level_ = default_log_level; @@ -286,6 +290,7 @@ OptionsImpl::OptionsImpl(const std::string& service_cluster, const std::string& service_cluster_(service_cluster), service_node_(service_node), service_zone_(service_zone), file_flush_interval_msec_(10000), drain_time_(600), parent_shutdown_time_(900), mode_(Server::Mode::Serve), hot_restart_disabled_(false), signal_handling_enabled_(true), - mutex_tracing_enabled_(false), cpuset_threads_(false), libevent_buffer_enabled_(false) {} + mutex_tracing_enabled_(false), cpuset_threads_(false), libevent_buffer_enabled_(false), + fake_symbol_table_enabled_(false) {} } // namespace Envoy diff --git a/source/server/options_impl.h b/source/server/options_impl.h index e9663953aa99..be0bd0f02b9f 100644 --- a/source/server/options_impl.h +++ b/source/server/options_impl.h @@ -110,6 +110,7 @@ class OptionsImpl : public Server::Options, protected Logger::LoggableparentShutdownTime()); EXPECT_EQ(true, options->hotRestartDisabled()); EXPECT_EQ(true, options->libeventBufferEnabled()); + EXPECT_EQ(true, options->fakeSymbolTableEnabled()); EXPECT_EQ(true, options->cpusetThreadsEnabled()); options = createOptionsImpl("envoy --mode init_only"); @@ -212,12 +213,13 @@ TEST_F(OptionsImplTest, OptionsAreInSyncWithProto) { Server::CommandLineOptionsPtr command_line_options = options->toCommandLineOptions(); // Failure of this condition indicates that the server_info proto is not in sync with the options. // If an option is added/removed, please update server_info proto as well to keep it in sync. - // Currently the following 4 options are not defined in proto, hence the count differs by 5. + // Currently the following 5 options are not defined in proto, hence the count differs by 5. // 1. version - default TCLAP argument. // 2. help - default TCLAP argument. // 3. ignore_rest - default TCLAP argument. // 4. use-libevent-buffers - short-term override for rollout of new buffer implementation. - EXPECT_EQ(options->count() - 4, command_line_options->GetDescriptor()->field_count()); + // 5. use-fake-symbol-table - short-term override for rollout of real symbol-table implementation. + EXPECT_EQ(options->count() - 5, command_line_options->GetDescriptor()->field_count()); } TEST_F(OptionsImplTest, BadCliOption) { From 313d5915786bded99ec8e325a532d0768cb31e59 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 9 Aug 2019 08:15:35 -0400 Subject: [PATCH 081/106] Add command-line option to switch between real/fake symbol tables, and helper class to create based on that option. Signed-off-by: Joshua Marantz --- include/envoy/stats/symbol_table.h | 2 +- source/common/stats/BUILD | 12 ++++ source/common/stats/isolated_store_impl.cc | 4 +- source/common/stats/isolated_store_impl.h | 2 +- source/common/stats/symbol_table_creator.cc | 9 +++ source/common/stats/symbol_table_creator.h | 36 +++++++++++ source/exe/BUILD | 2 +- source/exe/main_common.cc | 5 +- source/exe/main_common.h | 2 +- source/server/BUILD | 1 + source/server/options_impl.cc | 4 +- test/common/grpc/context_impl_test.cc | 4 +- .../grpc_client_integration_test_harness.h | 2 +- test/integration/BUILD | 1 + test/integration/stats_integration_test.cc | 63 ++++++++++++++++++- test/mocks/router/BUILD | 1 + test/mocks/router/mocks.h | 5 +- test/mocks/server/mocks.cc | 2 +- test/mocks/server/mocks.h | 2 +- test/mocks/stats/BUILD | 1 + test/mocks/stats/mocks.cc | 5 +- test/mocks/stats/mocks.h | 35 ++++++++--- test/mocks/upstream/host.h | 4 +- test/tools/router_check/router.h | 2 +- 24 files changed, 174 insertions(+), 32 deletions(-) create mode 100644 source/common/stats/symbol_table_creator.cc create mode 100644 source/common/stats/symbol_table_creator.h diff --git a/include/envoy/stats/symbol_table.h b/include/envoy/stats/symbol_table.h index 4b4c8a4c4fe9..602865bd9ca2 100644 --- a/include/envoy/stats/symbol_table.h +++ b/include/envoy/stats/symbol_table.h @@ -184,7 +184,7 @@ class SymbolTable { virtual StoragePtr encode(absl::string_view name) PURE; }; -using SharedSymbolTable = std::shared_ptr; +using SymbolTablePtr = std::unique_ptr; } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index cb88ed2dc976..7bc419766cdc 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -50,6 +50,7 @@ envoy_cc_library( ":scope_prefixer_lib", ":stats_lib", ":store_impl_lib", + ":symbol_table_creator_lib", "//include/envoy/stats:stats_macros", "//source/common/stats:allocator_lib", ], @@ -155,6 +156,17 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "symbol_table_creator_lib", + srcs = ["symbol_table_creator.cc"], + hdrs = ["symbol_table_creator.h"], + external_deps = ["abseil_base"], + deps = [ + ":fake_symbol_table_lib", + ":symbol_table_lib", + ], +) + envoy_cc_library( name = "fake_symbol_table_lib", hdrs = ["fake_symbol_table_impl.h"], diff --git a/source/common/stats/isolated_store_impl.cc b/source/common/stats/isolated_store_impl.cc index 6f2da0f899af..aa58676ae59a 100644 --- a/source/common/stats/isolated_store_impl.cc +++ b/source/common/stats/isolated_store_impl.cc @@ -8,13 +8,13 @@ #include "common/stats/fake_symbol_table_impl.h" #include "common/stats/histogram_impl.h" #include "common/stats/scope_prefixer.h" +#include "common/stats/symbol_table_creator.h" #include "common/stats/utility.h" namespace Envoy { namespace Stats { -IsolatedStoreImpl::IsolatedStoreImpl() - : IsolatedStoreImpl(std::make_unique()) {} +IsolatedStoreImpl::IsolatedStoreImpl() : IsolatedStoreImpl(SymbolTableCreator::makeSymbolTable()) {} IsolatedStoreImpl::IsolatedStoreImpl(std::unique_ptr&& symbol_table) : IsolatedStoreImpl(*symbol_table) { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 6a11ceec2a7c..cce741211df1 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -133,7 +133,7 @@ class IsolatedStoreImpl : public StoreImpl { private: IsolatedStoreImpl(std::unique_ptr&& symbol_table); - std::unique_ptr symbol_table_storage_; + SymbolTablePtr symbol_table_storage_; AllocatorImpl alloc_; IsolatedStatsCache counters_; IsolatedStatsCache gauges_; diff --git a/source/common/stats/symbol_table_creator.cc b/source/common/stats/symbol_table_creator.cc new file mode 100644 index 000000000000..7ae2e363d22b --- /dev/null +++ b/source/common/stats/symbol_table_creator.cc @@ -0,0 +1,9 @@ +#include "common/stats/symbol_table_creator.h" + +namespace Envoy { +namespace Stats { + +bool SymbolTableCreator::use_fake_symbol_tables_ = true; + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/symbol_table_creator.h b/source/common/stats/symbol_table_creator.h new file mode 100644 index 000000000000..53020d683bd8 --- /dev/null +++ b/source/common/stats/symbol_table_creator.h @@ -0,0 +1,36 @@ +#pragma once + +#include "common/stats/fake_symbol_table_impl.h" +#include "common/stats/symbol_table_impl.h" + +namespace Envoy { +namespace Stats { + +class SymbolTableCreator { +public: + // Factory method to create SymbolTables. This is needed to help make it + // possible to flag-flip use of real symbol tables, and but ultimately + // should be removed. + static SymbolTablePtr makeSymbolTable(bool use_fake) { + use_fake_symbol_tables_ = use_fake; + return makeSymbolTable(); + } + + static SymbolTablePtr makeSymbolTable() { + if (use_fake_symbol_tables_) { + return std::make_unique(); + } + return std::make_unique(); + } + + // Sets whether fake or real symbol tables should be used. Tests that alter + // this should restore previous value at the end of the test. + static void setUseFakeSymbolTables(bool use_fakes) { use_fake_symbol_tables_ = use_fakes; } + static bool useFakeSymbolTables() { return use_fake_symbol_tables_; } + +private: + static bool use_fake_symbol_tables_; +}; + +} // namespace Stats +} // namespace Envoy diff --git a/source/exe/BUILD b/source/exe/BUILD index 4117a4bb83ca..d48f99ce33b8 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -70,7 +70,7 @@ envoy_cc_library( "//source/common/api:os_sys_calls_lib", "//source/common/common:compiler_requirements_lib", "//source/common/common:perf_annotation_lib", - "//source/common/stats:fake_symbol_table_lib", + "//source/common/stats:symbol_table_creator_lib", "//source/server:hot_restart_lib", "//source/server:hot_restart_nop_lib", "//source/server/config_validation:server_lib", diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index 84d57abfc5ac..5d2a53ea386d 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -7,6 +7,7 @@ #include "common/common/compiler_requirements.h" #include "common/common/perf_annotation.h" #include "common/network/utility.h" +#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "server/config_validation/server.h" @@ -45,7 +46,9 @@ MainCommonBase::MainCommonBase(const OptionsImpl& options, Event::TimeSystem& ti Filesystem::Instance& file_system, std::unique_ptr process_context) : options_(options), component_factory_(component_factory), thread_factory_(thread_factory), - file_system_(file_system), stats_allocator_(symbol_table_) { + file_system_(file_system), + symbol_table_(Stats::SymbolTableCreator::makeSymbolTable(options_.fakeSymbolTableEnabled())), + stats_allocator_(*symbol_table_) { switch (options_.mode()) { case Server::Mode::InitOnly: case Server::Mode::Serve: { diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 5faf31b547cc..a0a4796de18e 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -67,10 +67,10 @@ class MainCommonBase { protected: ProcessWide process_wide_; // Process-wide state setup/teardown. const Envoy::OptionsImpl& options_; - Stats::FakeSymbolTableImpl symbol_table_; Server::ComponentFactory& component_factory_; Thread::ThreadFactory& thread_factory_; Filesystem::Instance& file_system_; + Stats::SymbolTablePtr symbol_table_; Stats::AllocatorImpl stats_allocator_; std::unique_ptr tls_; diff --git a/source/server/BUILD b/source/server/BUILD index 2e83ab4671c6..bd9c72b56e63 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -378,6 +378,7 @@ envoy_cc_library( "//source/common/runtime:runtime_lib", "//source/common/secret:secret_manager_impl_lib", "//source/common/singleton:manager_impl_lib", + "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//source/common/upstream:cluster_manager_lib", "//source/common/upstream:health_discovery_service_lib", diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index 9f7048756bf7..d830c80b7312 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -106,8 +106,8 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv, "Use the original libevent buffer implementation", false, true, "bool", cmd); TCLAP::ValueArg use_fake_symbol_table("", "use-fake-symbol-table", - "Use fake symbol table implementation", - false, true, "bool", cmd); + "Use fake symbol table implementation", false, true, + "bool", cmd); cmd.setExceptionHandling(false); try { diff --git a/test/common/grpc/context_impl_test.cc b/test/common/grpc/context_impl_test.cc index ace45801ca11..d1d8079653ac 100644 --- a/test/common/grpc/context_impl_test.cc +++ b/test/common/grpc/context_impl_test.cc @@ -18,7 +18,7 @@ namespace Grpc { TEST(GrpcContextTest, ChargeStats) { NiceMock cluster; - Envoy::Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Stats::StatNamePool pool(*symbol_table_); const Stats::StatName service = pool.add("service"); const Stats::StatName method = pool.add("method"); @@ -58,7 +58,7 @@ TEST(GrpcContextTest, ResolveServiceAndMethod) { Http::HeaderMapImpl headers; Http::HeaderEntry& path = headers.insertPath(); path.value(std::string("/service_name/method_name")); - Envoy::Test::Global symbol_table; + Stats::TestSymbolTable symbol_table; ContextImpl context(*symbol_table); absl::optional request_names = context.resolveServiceAndMethod(&path); EXPECT_TRUE(request_names); diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 997bae8b6bb8..1d02a8e81de6 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -416,7 +416,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { FakeHttpConnectionPtr fake_connection_; std::vector fake_streams_; const Protobuf::MethodDescriptor* method_descriptor_; - Envoy::Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Stats::IsolatedStoreImpl* stats_store_ = new Stats::IsolatedStoreImpl(*symbol_table_); Api::ApiPtr api_; Event::DispatcherPtr dispatcher_; diff --git a/test/integration/BUILD b/test/integration/BUILD index 8df797ff0fc8..4415d53a73cf 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -547,6 +547,7 @@ envoy_cc_test( deps = [ ":integration_lib", "//source/common/memory:stats_lib", + "//source/common/stats:symbol_table_creator_lib", "//source/extensions/filters/http/router:config", "//source/extensions/filters/network/http_connection_manager:config", "//test/common/stats:stat_test_utility_lib", diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index cc016ffbea1a..788d34db337f 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -7,6 +7,7 @@ #include "common/config/well_known_names.h" #include "common/memory/stats.h" +#include "common/stats/symbol_table_creator.h" #include "test/common/stats/stat_test_utility.h" #include "test/config/utility.h" @@ -172,13 +173,71 @@ class ClusterMemoryTestHelper : public BaseIntegrationTest { return memory_test.consumedBytes(); } }; -class ClusterMemoryTestRunner : public testing::TestWithParam {}; +class ClusterMemoryTestRunner : public testing::TestWithParam { +protected: + ClusterMemoryTestRunner() : save_use_fakes_(Stats::SymbolTableCreator::useFakeSymbolTables()) {} + ~ClusterMemoryTestRunner() { Stats::SymbolTableCreator::setUseFakeSymbolTables(save_use_fakes_); } + +private: + const bool save_use_fakes_; +}; INSTANTIATE_TEST_SUITE_P(IpVersions, ClusterMemoryTestRunner, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); -TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithStats) { +TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(true); + + // A unique instance of ClusterMemoryTest allows for multiple runs of Envoy with + // differing configuration. This is necessary for measuring the memory consumption + // between the different instances within the same test. + const size_t m1 = ClusterMemoryTestHelper::computeMemory(1); + const size_t m1001 = ClusterMemoryTestHelper::computeMemory(1001); + const size_t m_per_cluster = (m1001 - m1) / 1000; + + // Note: if you are increasing this golden value because you are adding a + // stat, please confirm that this will be generally useful to most Envoy + // users. Otherwise you are adding to the per-cluster memory overhead, which + // will be significant for Envoy installations that are massively + // multi-tenant. + // + // History of golden values: + // + // Date PR Bytes Per Cluster Notes + // exact upper-bound + // ---------- ----- ----------------- ----- + // 2019/03/20 6329 59015 Initial version + // 2019/04/12 6477 59576 Implementing Endpoint lease... + // 2019/04/23 6659 59512 Reintroduce dispatcher stats... + // 2019/04/24 6161 49415 Pack tags and tag extracted names + // 2019/05/07 6794 49957 Stats for excluded hosts in cluster + // 2019/04/27 6733 50213 Use SymbolTable API for HTTP codes + // 2019/05/31 6866 50157 libstdc++ upgrade in CI + // 2019/06/03 7199 49393 absl update + // 2019/06/06 7208 49650 make memory targets approximate + // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks + // 2019/06/29 7364 45685 46000 combine 2 levels of stat ref-counting into 1 + // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData + // 2019/07/06 7477 42742 43000 fork gauge representation to drop pending_increment_ + // 2019/07/15 7555 42806 43000 static link libstdc++ in tests + // 2019/07/24 7503 43030 44000 add upstream filters to clusters + + // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI + // 'release' builds, where we control the platform and tool-chain. So you + // will need to find the correct value only after failing CI and looking + // at the logs. + // + // On a local clang8/libstdc++/linux flow, the memory usage was observed in + // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may + // vary. + EXPECT_MEMORY_EQ(m_per_cluster, 43030); // 104 bytes higher than a debug build. + EXPECT_MEMORY_LE(m_per_cluster, 44000); +} + +TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(false); + // A unique instance of ClusterMemoryTest allows for multiple runs of Envoy with // differing configuration. This is necessary for measuring the memory consumption // between the different instances within the same test. diff --git a/test/mocks/router/BUILD b/test/mocks/router/BUILD index eead78a0d5fc..d5d8729cccad 100644 --- a/test/mocks/router/BUILD +++ b/test/mocks/router/BUILD @@ -27,5 +27,6 @@ envoy_cc_mock( "//include/envoy/upstream:cluster_manager_interface", "//source/common/stats:fake_symbol_table_lib", "//test/mocks:common_lib", + "//test/mocks/stats:stats_mocks", ], ) diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index aeda21400f98..7e6ce48606ea 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -24,6 +24,7 @@ #include "common/stats/fake_symbol_table_impl.h" +#include "test/mocks/stats/mocks.h" #include "test/test_common/global.h" #include "gmock/gmock.h" @@ -199,7 +200,7 @@ class TestVirtualCluster : public VirtualCluster { // Router::VirtualCluster Stats::StatName statName() const override { return stat_name_.statName(); } - Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Stats::StatNameManagedStorage stat_name_{"fake_virtual_cluster", *symbol_table_}; }; @@ -223,7 +224,7 @@ class MockVirtualHost : public VirtualHost { return stat_name_->statName(); } - mutable Test::Global symbol_table_; + mutable Stats::TestSymbolTable symbol_table_; std::string name_{"fake_vhost"}; mutable std::unique_ptr stat_name_; testing::NiceMock rate_limit_policy_; diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index e1006497c8ba..bf2c80eeb3c9 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -71,7 +71,7 @@ MockGuardDog::MockGuardDog() : watch_dog_(new NiceMock()) { } MockGuardDog::~MockGuardDog() = default; -MockHotRestart::MockHotRestart() : stats_allocator_(symbol_table_.get()) { +MockHotRestart::MockHotRestart() : stats_allocator_(*symbol_table_) { ON_CALL(*this, logLock()).WillByDefault(ReturnRef(log_lock_)); ON_CALL(*this, accessLogLock()).WillByDefault(ReturnRef(access_log_lock_)); ON_CALL(*this, statsAllocator()).WillByDefault(ReturnRef(stats_allocator_)); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 68a62e81b609..a7f6e7b68984 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -214,7 +214,7 @@ class MockHotRestart : public HotRestart { MOCK_METHOD0(statsAllocator, Stats::Allocator&()); private: - Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Thread::MutexBasicLockable log_lock_; Thread::MutexBasicLockable access_log_lock_; Stats::AllocatorImpl stats_allocator_; diff --git a/test/mocks/stats/BUILD b/test/mocks/stats/BUILD index 6539e818467b..bf9048b25366 100644 --- a/test/mocks/stats/BUILD +++ b/test/mocks/stats/BUILD @@ -22,6 +22,7 @@ envoy_cc_mock( "//source/common/stats:isolated_store_lib", "//source/common/stats:stats_lib", "//source/common/stats:store_impl_lib", + "//source/common/stats:symbol_table_creator_lib", "//test/mocks:common_lib", "//test/test_common:global_lib", ], diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 56dfbaf84d40..ef5eeebeb488 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -63,7 +63,7 @@ MockMetricSnapshot::~MockMetricSnapshot() = default; MockSink::MockSink() = default; MockSink::~MockSink() = default; -MockStore::MockStore() : StoreImpl(*fake_symbol_table_) { +MockStore::MockStore() : StoreImpl(*global_symbol_table_) { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); ON_CALL(*this, histogram(_)).WillByDefault(Invoke([this](const std::string& name) -> Histogram& { auto* histogram = new NiceMock(); // symbol_table_); @@ -75,8 +75,7 @@ MockStore::MockStore() : StoreImpl(*fake_symbol_table_) { } MockStore::~MockStore() = default; -MockIsolatedStatsStore::MockIsolatedStatsStore() - : IsolatedStoreImpl(Test::Global::get()) {} +MockIsolatedStatsStore::MockIsolatedStatsStore() : IsolatedStoreImpl(*global_symbol_table_) {} MockIsolatedStatsStore::~MockIsolatedStatsStore() = default; MockStatsMatcher::MockStatsMatcher() = default; diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index d418ad430f2b..d5ad120ffcae 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -18,6 +18,7 @@ #include "common/stats/histogram_impl.h" #include "common/stats/isolated_store_impl.h" #include "common/stats/store_impl.h" +#include "common/stats/symbol_table_creator.h" #include "test/test_common/global.h" @@ -26,6 +27,25 @@ namespace Envoy { namespace Stats { +class TestSymbolTableHelper { +public: + TestSymbolTableHelper() : symbol_table_(SymbolTableCreator::makeSymbolTable()) {} + SymbolTable& symbolTable() { return *symbol_table_; } + const SymbolTable& constSymbolTable() const { return *symbol_table_; } + +private: + SymbolTablePtr symbol_table_; +}; + +class TestSymbolTable { +public: + SymbolTable& operator*() { return global_.get().symbolTable(); } + const SymbolTable& operator*() const { return global_.get().constSymbolTable(); } + SymbolTable* operator->() { return &global_.get().symbolTable(); } + const SymbolTable* operator->() const { return &global_.get().constSymbolTable(); } + Envoy::Test::Global global_; +}; + template class MockMetric : public BaseClass { public: MockMetric() : name_(*this), tag_pool_(*symbol_table_) {} @@ -39,7 +59,7 @@ template class MockMetric : public BaseClass { explicit MetricName(MockMetric& mock_metric) : mock_metric_(mock_metric) {} ~MetricName() { if (stat_name_storage_ != nullptr) { - stat_name_storage_->free(*mock_metric_.symbol_table_); + stat_name_storage_->free(mock_metric_.symbolTable()); } } @@ -57,8 +77,8 @@ template class MockMetric : public BaseClass { std::unique_ptr stat_name_storage_; }; - SymbolTable& symbolTable() override { return symbol_table_.get(); } - const SymbolTable& constSymbolTable() const override { return symbol_table_.get(); } + SymbolTable& symbolTable() override { return *symbol_table_; } + const SymbolTable& constSymbolTable() const override { return *symbol_table_; } // Note: cannot be mocked because it is accessed as a Property in a gmock EXPECT_CALL. This // creates a deadlock in gmock and is an unintended use of mock functions. @@ -90,7 +110,7 @@ template class MockMetric : public BaseClass { } } - Test::Global symbol_table_; // Must outlive name_. + TestSymbolTable symbol_table_; // Must outlive name_. MetricName name_; void setTags(const std::vector& tags) { @@ -251,7 +271,7 @@ class MockSink : public Sink { class SymbolTableProvider { public: - Test::Global fake_symbol_table_; + TestSymbolTable global_symbol_table_; }; class MockStore : public SymbolTableProvider, public StoreImpl { @@ -285,7 +305,7 @@ class MockStore : public SymbolTableProvider, public StoreImpl { return histogram(symbol_table_->toString(name)); } - Test::Global symbol_table_; + TestSymbolTable symbol_table_; testing::NiceMock counter_; std::vector> histograms_; }; @@ -294,8 +314,7 @@ class MockStore : public SymbolTableProvider, public StoreImpl { * With IsolatedStoreImpl it's hard to test timing stats. * MockIsolatedStatsStore mocks only deliverHistogramToSinks for better testing. */ -class MockIsolatedStatsStore : private Test::Global, - public IsolatedStoreImpl { +class MockIsolatedStatsStore : public SymbolTableProvider, public IsolatedStoreImpl { public: MockIsolatedStatsStore(); ~MockIsolatedStatsStore() override; diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index 095c67c1ae88..6fea5dec1f2e 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -108,7 +108,7 @@ class MockHostDescription : public HostDescription { testing::NiceMock cluster_; testing::NiceMock stats_store_; HostStats stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}; - mutable Test::Global symbol_table_; + mutable Stats::TestSymbolTable symbol_table_; mutable std::unique_ptr locality_zone_stat_name_; }; @@ -186,7 +186,7 @@ class MockHost : public Host { testing::NiceMock outlier_detector_; NiceMock stats_store_; HostStats stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}; - mutable Test::Global symbol_table_; + mutable Stats::TestSymbolTable symbol_table_; mutable std::unique_ptr locality_zone_stat_name_; }; diff --git a/test/tools/router_check/router.h b/test/tools/router_check/router.h index f4af695dcf7a..8e36a992e0fa 100644 --- a/test/tools/router_check/router.h +++ b/test/tools/router_check/router.h @@ -54,7 +54,7 @@ struct ToolConfig { private: ToolConfig(std::unique_ptr headers, int random_value); - Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; }; /** From 56ab60205a581bf8e072a1f8d4d2a79b8334dedd Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 9 Aug 2019 15:40:38 -0400 Subject: [PATCH 082/106] use configured symbol-table impl in integration tests. Signed-off-by: Joshua Marantz --- test/integration/BUILD | 1 + test/integration/server.cc | 5 +++-- test/integration/stats_integration_test.cc | 20 +++----------------- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/test/integration/BUILD b/test/integration/BUILD index 4415d53a73cf..093a1d82d0e6 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -433,6 +433,7 @@ envoy_cc_test_library( "//source/common/network:utility_lib", "//source/common/runtime:runtime_lib", "//source/common/stats:isolated_store_lib", + "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//source/common/thread_local:thread_local_lib", "//source/common/upstream:upstream_includes", diff --git a/test/integration/server.cc b/test/integration/server.cc index 382332aa80c3..e4542efd94bb 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -8,6 +8,7 @@ #include "common/common/thread.h" #include "common/local_info/local_info_impl.h" #include "common/network/utility.h" +#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "common/thread_local/thread_local_impl.h" @@ -177,10 +178,10 @@ void IntegrationTestServerImpl::createAndRunEnvoyServer( Runtime::RandomGeneratorPtr&& random_generator, absl::optional> process_object) { { - Stats::FakeSymbolTableImpl symbol_table; + Stats::SymbolTablePtr symbol_table = Stats::SymbolTableCreator::makeSymbolTable(); Server::HotRestartNopImpl restarter; ThreadLocal::InstanceImpl tls; - Stats::AllocatorImpl stats_allocator(symbol_table); + Stats::AllocatorImpl stats_allocator(*symbol_table); Stats::ThreadLocalStoreImpl stat_store(stats_allocator); std::unique_ptr process_context; if (process_object.has_value()) { diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 788d34db337f..2eed0842ceae 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -256,21 +256,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // Date PR Bytes Per Cluster Notes // exact upper-bound // ---------- ----- ----------------- ----- - // 2019/03/20 6329 59015 Initial version - // 2019/04/12 6477 59576 Implementing Endpoint lease... - // 2019/04/23 6659 59512 Reintroduce dispatcher stats... - // 2019/04/24 6161 49415 Pack tags and tag extracted names - // 2019/05/07 6794 49957 Stats for excluded hosts in cluster - // 2019/04/27 6733 50213 Use SymbolTable API for HTTP codes - // 2019/05/31 6866 50157 libstdc++ upgrade in CI - // 2019/06/03 7199 49393 absl update - // 2019/06/06 7208 49650 make memory targets approximate - // 2019/06/17 7243 49412 49700 macros for exact/upper-bound memory checks - // 2019/06/29 7364 45685 46000 combine 2 levels of stat ref-counting into 1 - // 2019/06/30 7428 42742 43000 remove stats multiple inheritance, inline HeapStatData - // 2019/07/06 7477 42742 43000 fork gauge representation to drop pending_increment_ - // 2019/07/15 7555 42806 43000 static link libstdc++ in tests - // 2019/07/24 7503 43030 44000 add upstream filters to clusters + // 2019/08/09 7882 35681 36000 Initial version // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -280,8 +266,8 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 43030); // 104 bytes higher than a debug build. - EXPECT_MEMORY_LE(m_per_cluster, 44000); + EXPECT_MEMORY_EQ(m_per_cluster, 35681); // 104 bytes higher than a debug build. + EXPECT_MEMORY_LE(m_per_cluster, 36000); } } // namespace From a0e1d7cc74233ec2e6b31faef0c4268dfea885fa Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Fri, 9 Aug 2019 17:40:16 -0400 Subject: [PATCH 083/106] clang-tidy error Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 2eed0842ceae..6003b8a4a9b8 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -176,7 +176,9 @@ class ClusterMemoryTestHelper : public BaseIntegrationTest { class ClusterMemoryTestRunner : public testing::TestWithParam { protected: ClusterMemoryTestRunner() : save_use_fakes_(Stats::SymbolTableCreator::useFakeSymbolTables()) {} - ~ClusterMemoryTestRunner() { Stats::SymbolTableCreator::setUseFakeSymbolTables(save_use_fakes_); } + ~ClusterMemoryTestRunner() override { + Stats::SymbolTableCreator::setUseFakeSymbolTables(save_use_fakes_); + } private: const bool save_use_fakes_; From 92442ef74a9cbc81ab05ec5161713c0eece6d466 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 15:12:15 -0400 Subject: [PATCH 084/106] Cover both real & fake symbol tables in symbol-table memory test. Signed-off-by: Joshua Marantz --- docs/root/intro/version_history.rst | 1 + test/common/stats/thread_local_store_test.cc | 96 ++++++++++++++------ 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index 4d5123dd04bb..3f2a88f1d831 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -18,6 +18,7 @@ Version history * http: added the ability to reject HTTP/1.1 requests with invalid HTTP header values, using the runtime feature `envoy.reloadable_features.strict_header_validation`. * http: added the ability to :ref:`merge adjacent slashes` in the path. * listeners: added :ref:`HTTP inspector listener filter `. +* performance: stats symbol table implementation (disabled by default; to test it, add "--use-fake-symbol-table 0" to the command-line arguments when starting Envoy). * router: added :ref:`rq_retry_skipped_request_not_complete ` counter stat to router stats. * router check tool: add coverage reporting & enforcement. * tls: added verification of IP address SAN fields in certificates against configured SANs in the diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 711d1c459e46..83b4eaeaa9e8 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -845,46 +845,90 @@ TEST_F(StatsThreadLocalStoreTest, NonHotRestartNoTruncation) { tls_.shutdownThread(); } -// Tests how much memory is consumed allocating 100k stats. -TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTls) { - MockSink sink; - Stats::FakeSymbolTableImpl symbol_table; - AllocatorImpl alloc(symbol_table); - ThreadLocalStoreImpl store(alloc); - store.addSink(sink); +class StatsThreadLocalStoreTestNoFixture : public testing::Test { +protected: + StatsThreadLocalStoreTestNoFixture() + : save_use_fakes_(Stats::SymbolTableCreator::useFakeSymbolTables()) {} + ~StatsThreadLocalStoreTestNoFixture() override { + Stats::SymbolTableCreator::setUseFakeSymbolTables(save_use_fakes_); + if (threading_enabled_) { + store_->shutdownThreading(); + tls_.shutdownThread(); + } + } - // Use a tag producer that will produce tags. - envoy::config::metrics::v2::StatsConfig stats_config; - store.setTagProducer(std::make_unique(stats_config)); + void init() { + symbol_table_ = SymbolTableCreator::makeSymbolTable(); + alloc_ = std::make_unique(*symbol_table_); + store_ = std::make_unique(*alloc_); + store_->addSink(sink_); + + // Use a tag producer that will produce tags. + envoy::config::metrics::v2::StatsConfig stats_config; + store_->setTagProducer(std::make_unique(stats_config)); + } + void initThreading() { + threading_enabled_ = true; + store_->initializeThreading(main_thread_dispatcher_, tls_); + } + + MockSink sink_; + SymbolTablePtr symbol_table_; + std::unique_ptr alloc_; + std::unique_ptr store_; + NiceMock main_thread_dispatcher_; + NiceMock tls_; + const bool save_use_fakes_; + bool threading_enabled_{false}; +}; + +// Tests how much memory is consumed allocating 100k stats. +TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsFakeSymbolTable) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(true); + init(); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( - 1000, [&store](absl::string_view name) { store.counter(std::string(name)); }); + 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); const size_t million = 1000 * 1000; EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 15268336); // June 30, 2019 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 16 * million); } -TEST(StatsThreadLocalStoreTestNoFixture, MemoryWithTls) { - Stats::FakeSymbolTableImpl symbol_table; - AllocatorImpl alloc(symbol_table); - NiceMock main_thread_dispatcher; - NiceMock tls; - ThreadLocalStoreImpl store(alloc); - - // Use a tag producer that will produce tags. - envoy::config::metrics::v2::StatsConfig stats_config; - store.setTagProducer(std::make_unique(stats_config)); - - store.initializeThreading(main_thread_dispatcher, tls); +TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsFakeSymbolTable) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(true); + init(); + initThreading(); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( - 1000, [&store](absl::string_view name) { store.counter(std::string(name)); }); + 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); const size_t million = 1000 * 1000; EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 17496848); // June 30, 2019 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 18 * million); - store.shutdownThreading(); - tls.shutdownThread(); +} + +// Tests how much memory is consumed allocating 100k stats. +TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsRealSymbolTable) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(false); + init(); + TestUtil::MemoryTest memory_test; + TestUtil::forEachSampleStat( + 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); + const size_t million = 1000 * 1000; + EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 9139120); // Aug 9, 2019 + EXPECT_MEMORY_LE(memory_test.consumedBytes(), 10 * million); +} + +TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsRealSymbolTable) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(false); + init(); + initThreading(); + TestUtil::MemoryTest memory_test; + TestUtil::forEachSampleStat( + 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); + const size_t million = 1000 * 1000; + EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 11367632); // Aug 9, 2019 + EXPECT_MEMORY_LE(memory_test.consumedBytes(), 12 * million); } TEST_F(StatsThreadLocalStoreTest, ShuttingDown) { From 96f4e858c74f48c135a4045d7103b0d6cd9bf2c9 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 15:15:38 -0400 Subject: [PATCH 085/106] cleanups Signed-off-by: Joshua Marantz --- test/common/stats/thread_local_store_test.cc | 29 ++++++++------------ 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 83b4eaeaa9e8..8a1d8bfc1818 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -857,7 +857,8 @@ class StatsThreadLocalStoreTestNoFixture : public testing::Test { } } - void init() { + void init(bool use_fakes) { + Stats::SymbolTableCreator::setUseFakeSymbolTables(use_fakes); symbol_table_ = SymbolTableCreator::makeSymbolTable(); alloc_ = std::make_unique(*symbol_table_); store_ = std::make_unique(*alloc_); @@ -873,6 +874,8 @@ class StatsThreadLocalStoreTestNoFixture : public testing::Test { store_->initializeThreading(main_thread_dispatcher_, tls_); } + static constexpr size_t million_ = 1000 * 1000; + MockSink sink_; SymbolTablePtr symbol_table_; std::unique_ptr alloc_; @@ -885,50 +888,42 @@ class StatsThreadLocalStoreTestNoFixture : public testing::Test { // Tests how much memory is consumed allocating 100k stats. TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsFakeSymbolTable) { - Stats::SymbolTableCreator::setUseFakeSymbolTables(true); - init(); + init(true); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); - const size_t million = 1000 * 1000; EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 15268336); // June 30, 2019 - EXPECT_MEMORY_LE(memory_test.consumedBytes(), 16 * million); + EXPECT_MEMORY_LE(memory_test.consumedBytes(), 16 * million_); } TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsFakeSymbolTable) { - Stats::SymbolTableCreator::setUseFakeSymbolTables(true); - init(); + init(true); initThreading(); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); - const size_t million = 1000 * 1000; EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 17496848); // June 30, 2019 - EXPECT_MEMORY_LE(memory_test.consumedBytes(), 18 * million); + EXPECT_MEMORY_LE(memory_test.consumedBytes(), 18 * million_); } // Tests how much memory is consumed allocating 100k stats. TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsRealSymbolTable) { - Stats::SymbolTableCreator::setUseFakeSymbolTables(false); - init(); + init(false); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); - const size_t million = 1000 * 1000; EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 9139120); // Aug 9, 2019 - EXPECT_MEMORY_LE(memory_test.consumedBytes(), 10 * million); + EXPECT_MEMORY_LE(memory_test.consumedBytes(), 10 * million_); } TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsRealSymbolTable) { - Stats::SymbolTableCreator::setUseFakeSymbolTables(false); - init(); + init(false); initThreading(); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( 1000, [this](absl::string_view name) { store_->counter(std::string(name)); }); - const size_t million = 1000 * 1000; EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 11367632); // Aug 9, 2019 - EXPECT_MEMORY_LE(memory_test.consumedBytes(), 12 * million); + EXPECT_MEMORY_LE(memory_test.consumedBytes(), 12 * million_); } TEST_F(StatsThreadLocalStoreTest, ShuttingDown) { From 08f444e653efc8ccb61b10a05989d33584d11343 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 16:06:04 -0400 Subject: [PATCH 086/106] convert more tests to use the symbol-table creator rather than hard-coding FakeSymbolTable. Signed-off-by: Joshua Marantz --- test/common/http/BUILD | 1 + test/common/http/codes_test.cc | 8 +- .../http/conn_manager_impl_fuzz_test.cc | 5 +- test/common/router/config_impl_test.cc | 2 +- .../http1_bridge_filter_test.cc | 2 +- .../http/grpc_web/grpc_web_filter_test.cc | 2 +- .../lightstep/lightstep_tracer_impl_test.cc | 2 +- .../config_validation/cluster_manager_test.cc | 1 - test/server/http/BUILD | 1 + test/server/http/admin_test.cc | 454 +++++++++--------- 10 files changed, 242 insertions(+), 236 deletions(-) diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 5cfeaff671fa..0552e325ba79 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -161,6 +161,7 @@ envoy_cc_fuzz_test( "//source/common/http:date_provider_lib", "//source/common/network:address_lib", "//source/common/network:utility_lib", + "//source/common/stats:symbol_table_creator_lib", "//test/fuzz:utility_lib", "//test/mocks/access_log:access_log_mocks", "//test/mocks/http:http_mocks", diff --git a/test/common/http/codes_test.cc b/test/common/http/codes_test.cc index 1bdd1f3202e4..0d005dbf6d2e 100644 --- a/test/common/http/codes_test.cc +++ b/test/common/http/codes_test.cc @@ -8,6 +8,7 @@ #include "common/common/empty_string.h" #include "common/http/codes.h" #include "common/http/header_map_impl.h" +#include "common/stats/symbol_table_creator.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/printers.h" @@ -45,7 +46,7 @@ class CodeUtilityTest : public testing::Test { code_stats_.chargeResponseStat(info); } - Envoy::Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Stats::IsolatedStoreImpl global_store_; Stats::IsolatedStoreImpl cluster_scope_; Http::CodeStatsImpl code_stats_; @@ -276,13 +277,14 @@ TEST_F(CodeUtilityTest, ResponseTimingTest) { class CodeStatsTest : public testing::Test { protected: - CodeStatsTest() : code_stats_(symbol_table_) {} + CodeStatsTest() + : symbol_table_(Stats::SymbolTableCreator::makeSybolTable()), code_stats_(symbol_table_) {} absl::string_view stripTrailingDot(absl::string_view prefix) { return CodeStatsImpl::stripTrailingDot(prefix); } - Stats::FakeSymbolTableImpl symbol_table_; + Stats::SymbolTablePtr symbol_table_; CodeStatsImpl code_stats_; }; diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 5c2bf01fe1ab..47f151b5b1db 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -19,6 +19,7 @@ #include "common/http/exception.h" #include "common/network/address_impl.h" #include "common/network/utility.h" +#include "common/stats/symbol_table_creator.h" #include "test/common/http/conn_manager_impl_common.h" #include "test/common/http/conn_manager_impl_fuzz.pb.h" @@ -382,8 +383,8 @@ DEFINE_PROTO_FUZZER(const test::common::http::ConnManagerImplTestCase& input) { FuzzConfig config; NiceMock drain_close; NiceMock random; - Stats::FakeSymbolTableImpl symbol_table; - Http::ContextImpl http_context(symbol_table); + Stats::SymbolTablePtr symbol_table(Stats::SymbolTableCreator::makeSymbolTable()); + Http::ContextImpl http_context(*symbol_table); NiceMock runtime; NiceMock local_info; NiceMock cluster_manager; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 5af81cbc1ff9..6b7c5743dc90 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -109,7 +109,7 @@ class ConfigImplTestBase { return factory_context_.scope().symbolTable().toString(name); } - Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Api::ApiPtr api_; NiceMock factory_context_; }; 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 aedebdfa6f0b..aafdb5bdeaa2 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 @@ -36,7 +36,7 @@ class GrpcHttp1BridgeFilterTest : public testing::Test { ~GrpcHttp1BridgeFilterTest() override { filter_.onDestroy(); } - Envoy::Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Grpc::ContextImpl context_; Http1BridgeFilter filter_; NiceMock decoder_callbacks_; 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 e0a501f48d69..b20f517ee5e9 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 @@ -106,7 +106,7 @@ class GrpcWebFilterTest : public testing::TestWithParamvalue().getStringView()); } - Envoy::Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Grpc::ContextImpl grpc_context_; GrpcWebFilter filter_; NiceMock decoder_callbacks_; diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index c6c2b72f753a..5521e48e8425 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -89,7 +89,7 @@ class LightStepDriverTest : public testing::Test { SystemTime start_time_; StreamInfo::MockStreamInfo stream_info_; - Envoy::Test::Global symbol_table_; + Stats::TestSymbolTable symbol_table_; Grpc::ContextImpl grpc_context_; NiceMock tls_; Stats::IsolatedStoreImpl stats_; diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index ce734f717301..98e9adee04f7 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -51,7 +51,6 @@ TEST(ValidationClusterManagerTest, MockedMethods) { log_manager, singleton_manager, time_system); const envoy::config::bootstrap::v2::Bootstrap bootstrap; - Stats::FakeSymbolTableImpl symbol_table; ClusterManagerPtr cluster_manager = factory.clusterManagerFromProto(bootstrap); EXPECT_EQ(nullptr, cluster_manager->httpConnPoolForCluster("cluster", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); diff --git a/test/server/http/BUILD b/test/server/http/BUILD index f513226fd1a5..20df4a917b1b 100644 --- a/test/server/http/BUILD +++ b/test/server/http/BUILD @@ -19,6 +19,7 @@ envoy_cc_test( "//source/common/profiler:profiler_lib", "//source/common/protobuf", "//source/common/protobuf:utility_lib", + "//source/common/stats:symbol_table_creator_lib", "//source/common/stats:thread_local_store_lib", "//source/extensions/transport_sockets/tls:context_config_lib", "//source/server/http:admin_lib", diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 481a1bbfd533..0b7e6d12e69e 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -14,6 +14,7 @@ #include "common/profiler/profiler.h" #include "common/protobuf/protobuf.h" #include "common/protobuf/utility.h" +#include "common/stats/symbol_table_creator.h" #include "common/stats/thread_local_store.h" #include "server/http/admin.h" @@ -50,7 +51,7 @@ namespace Server { class AdminStatsTest : public testing::TestWithParam { public: - AdminStatsTest() : alloc_(symbol_table_) { + AdminStatsTest() : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_) { store_ = std::make_unique(alloc_); store_->addSink(sink_); } @@ -63,7 +64,7 @@ class AdminStatsTest : public testing::TestWithParam main_thread_dispatcher_; NiceMock tls_; Stats::AllocatorImpl alloc_; @@ -1374,126 +1375,127 @@ class HistogramWrapper { class PrometheusStatsFormatterTest : public testing::Test { protected: - PrometheusStatsFormatterTest() : alloc_(symbol_table_) {} + PrometheusStatsFormatterTest() + : symbol_table_(SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_) { - void addCounter(const std::string& name, std::vector cluster_tags) { - Stats::StatNameManagedStorage storage(name, symbol_table_); - counters_.push_back(alloc_.makeCounter(storage.statName(), name, cluster_tags)); - } - - void addGauge(const std::string& name, std::vector cluster_tags) { - Stats::StatNameManagedStorage storage(name, symbol_table_); - gauges_.push_back(alloc_.makeGauge(storage.statName(), name, cluster_tags, - Stats::Gauge::ImportMode::Accumulate)); - } + void addCounter(const std::string& name, std::vector cluster_tags) { + Stats::StatNameManagedStorage storage(name, *symbol_table_); + counters_.push_back(alloc_.makeCounter(storage.statName(), name, cluster_tags)); + } - void addHistogram(const Stats::ParentHistogramSharedPtr histogram) { - histograms_.push_back(histogram); - } + void addGauge(const std::string& name, std::vector cluster_tags) { + Stats::StatNameManagedStorage storage(name, *symbol_table_); + gauges_.push_back(alloc_.makeGauge(storage.statName(), name, cluster_tags, + Stats::Gauge::ImportMode::Accumulate)); + } - using MockHistogramSharedPtr = Stats::RefcountPtr>; - MockHistogramSharedPtr makeHistogram() { - return MockHistogramSharedPtr(new NiceMock()); - } + void addHistogram(const Stats::ParentHistogramSharedPtr histogram) { + histograms_.push_back(histogram); + } - Stats::FakeSymbolTableImpl symbol_table_; - Stats::AllocatorImpl alloc_; - std::vector counters_; - std::vector gauges_; - std::vector histograms_; -}; + using MockHistogramSharedPtr = Stats::RefcountPtr>; + MockHistogramSharedPtr makeHistogram() { + return MockHistogramSharedPtr(new NiceMock()); + } -TEST_F(PrometheusStatsFormatterTest, MetricName) { - std::string raw = "vulture.eats-liver"; - std::string expected = "envoy_vulture_eats_liver"; - auto actual = PrometheusStatsFormatter::metricName(raw); - EXPECT_EQ(expected, actual); -} + Stats::SymbolTablePtr symbol_table_; + Stats::AllocatorImpl alloc_; + std::vector counters_; + std::vector gauges_; + std::vector histograms_; + }; + + TEST_F(PrometheusStatsFormatterTest, MetricName) { + std::string raw = "vulture.eats-liver"; + std::string expected = "envoy_vulture_eats_liver"; + auto actual = PrometheusStatsFormatter::metricName(raw); + EXPECT_EQ(expected, actual); + } -TEST_F(PrometheusStatsFormatterTest, SanitizeMetricName) { - std::string raw = "An.artist.plays-violin@019street"; - std::string expected = "envoy_An_artist_plays_violin_019street"; - auto actual = PrometheusStatsFormatter::metricName(raw); - EXPECT_EQ(expected, actual); -} + TEST_F(PrometheusStatsFormatterTest, SanitizeMetricName) { + std::string raw = "An.artist.plays-violin@019street"; + std::string expected = "envoy_An_artist_plays_violin_019street"; + auto actual = PrometheusStatsFormatter::metricName(raw); + EXPECT_EQ(expected, actual); + } -TEST_F(PrometheusStatsFormatterTest, SanitizeMetricNameDigitFirst) { - std::string raw = "3.artists.play-violin@019street"; - std::string expected = "envoy_3_artists_play_violin_019street"; - auto actual = PrometheusStatsFormatter::metricName(raw); - EXPECT_EQ(expected, actual); -} + TEST_F(PrometheusStatsFormatterTest, SanitizeMetricNameDigitFirst) { + std::string raw = "3.artists.play-violin@019street"; + std::string expected = "envoy_3_artists_play_violin_019street"; + auto actual = PrometheusStatsFormatter::metricName(raw); + EXPECT_EQ(expected, actual); + } -TEST_F(PrometheusStatsFormatterTest, FormattedTags) { - std::vector tags; - Stats::Tag tag1 = {"a.tag-name", "a.tag-value"}; - Stats::Tag tag2 = {"another_tag_name", "another_tag-value"}; - tags.push_back(tag1); - tags.push_back(tag2); - std::string expected = "a_tag_name=\"a.tag-value\",another_tag_name=\"another_tag-value\""; - auto actual = PrometheusStatsFormatter::formattedTags(tags); - EXPECT_EQ(expected, actual); -} + TEST_F(PrometheusStatsFormatterTest, FormattedTags) { + std::vector tags; + Stats::Tag tag1 = {"a.tag-name", "a.tag-value"}; + Stats::Tag tag2 = {"another_tag_name", "another_tag-value"}; + tags.push_back(tag1); + tags.push_back(tag2); + std::string expected = "a_tag_name=\"a.tag-value\",another_tag_name=\"another_tag-value\""; + auto actual = PrometheusStatsFormatter::formattedTags(tags); + EXPECT_EQ(expected, actual); + } -TEST_F(PrometheusStatsFormatterTest, MetricNameCollison) { + TEST_F(PrometheusStatsFormatterTest, MetricNameCollison) { - // Create two counters and two gauges with each pair having the same name, - // but having different tag names and values. - //`statsAsPrometheus()` should return two implying it found two unique stat names + // Create two counters and two gauges with each pair having the same name, + // but having different tag names and values. + //`statsAsPrometheus()` should return two implying it found two unique stat names - addCounter("cluster.test_cluster_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); - addCounter("cluster.test_cluster_1.upstream_cx_total", - {{"another_tag_name", "another_tag-value"}}); - addGauge("cluster.test_cluster_2.upstream_cx_total", - {{"another_tag_name_3", "another_tag_3-value"}}); - addGauge("cluster.test_cluster_2.upstream_cx_total", - {{"another_tag_name_4", "another_tag_4-value"}}); + addCounter("cluster.test_cluster_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); + addCounter("cluster.test_cluster_1.upstream_cx_total", + {{"another_tag_name", "another_tag-value"}}); + addGauge("cluster.test_cluster_2.upstream_cx_total", + {{"another_tag_name_3", "another_tag_3-value"}}); + addGauge("cluster.test_cluster_2.upstream_cx_total", + {{"another_tag_name_4", "another_tag_4-value"}}); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, - false, absl::nullopt); - EXPECT_EQ(2UL, size); -} + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, false, absl::nullopt); + EXPECT_EQ(2UL, size); + } -TEST_F(PrometheusStatsFormatterTest, UniqueMetricName) { + TEST_F(PrometheusStatsFormatterTest, UniqueMetricName) { - // Create two counters and two gauges, all with unique names. - // statsAsPrometheus() should return four implying it found - // four unique stat names. + // Create two counters and two gauges, all with unique names. + // statsAsPrometheus() should return four implying it found + // four unique stat names. - addCounter("cluster.test_cluster_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); - addCounter("cluster.test_cluster_2.upstream_cx_total", - {{"another_tag_name", "another_tag-value"}}); - addGauge("cluster.test_cluster_3.upstream_cx_total", - {{"another_tag_name_3", "another_tag_3-value"}}); - addGauge("cluster.test_cluster_4.upstream_cx_total", - {{"another_tag_name_4", "another_tag_4-value"}}); + addCounter("cluster.test_cluster_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); + addCounter("cluster.test_cluster_2.upstream_cx_total", + {{"another_tag_name", "another_tag-value"}}); + addGauge("cluster.test_cluster_3.upstream_cx_total", + {{"another_tag_name_3", "another_tag_3-value"}}); + addGauge("cluster.test_cluster_4.upstream_cx_total", + {{"another_tag_name_4", "another_tag_4-value"}}); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, - false, absl::nullopt); - EXPECT_EQ(4UL, size); -} + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, false, absl::nullopt); + EXPECT_EQ(4UL, size); + } -TEST_F(PrometheusStatsFormatterTest, HistogramWithNoValuesAndNoTags) { - HistogramWrapper h1_cumulative; - h1_cumulative.setHistogramValues(std::vector(0)); - Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); + TEST_F(PrometheusStatsFormatterTest, HistogramWithNoValuesAndNoTags) { + HistogramWrapper h1_cumulative; + h1_cumulative.setHistogramValues(std::vector(0)); + Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); - auto histogram = makeHistogram(); - histogram->name_ = "histogram1"; - histogram->used_ = true; - ON_CALL(*histogram, cumulativeStatistics()) - .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); + auto histogram = makeHistogram(); + histogram->name_ = "histogram1"; + histogram->used_ = true; + ON_CALL(*histogram, cumulativeStatistics()) + .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); - addHistogram(histogram); + addHistogram(histogram); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, - false, absl::nullopt); - EXPECT_EQ(1UL, size); + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, false, absl::nullopt); + EXPECT_EQ(1UL, size); - const std::string expected_output = R"EOF(# TYPE envoy_histogram1 histogram + const std::string expected_output = R"EOF(# TYPE envoy_histogram1 histogram envoy_histogram1_bucket{le="0.5"} 0 envoy_histogram1_bucket{le="1"} 0 envoy_histogram1_bucket{le="5"} 0 @@ -1518,35 +1520,35 @@ envoy_histogram1_sum{} 0 envoy_histogram1_count{} 0 )EOF"; - EXPECT_EQ(expected_output, response.toString()); -} + EXPECT_EQ(expected_output, response.toString()); + } -TEST_F(PrometheusStatsFormatterTest, HistogramWithHighCounts) { - HistogramWrapper h1_cumulative; + TEST_F(PrometheusStatsFormatterTest, HistogramWithHighCounts) { + HistogramWrapper h1_cumulative; - // Force large counts to prove that the +Inf bucket doesn't overflow to scientific notation. - h1_cumulative.setHistogramValuesWithCounts(std::vector>({ - {1, 100000}, - {100, 1000000}, - {1000, 100000000}, - })); + // Force large counts to prove that the +Inf bucket doesn't overflow to scientific notation. + h1_cumulative.setHistogramValuesWithCounts(std::vector>({ + {1, 100000}, + {100, 1000000}, + {1000, 100000000}, + })); - Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); + Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); - auto histogram = makeHistogram(); - histogram->name_ = "histogram1"; - histogram->used_ = true; - ON_CALL(*histogram, cumulativeStatistics()) - .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); + auto histogram = makeHistogram(); + histogram->name_ = "histogram1"; + histogram->used_ = true; + ON_CALL(*histogram, cumulativeStatistics()) + .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); - addHistogram(histogram); + addHistogram(histogram); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, - false, absl::nullopt); - EXPECT_EQ(1UL, size); + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, false, absl::nullopt); + EXPECT_EQ(1UL, size); - const std::string expected_output = R"EOF(# TYPE envoy_histogram1 histogram + const std::string expected_output = R"EOF(# TYPE envoy_histogram1 histogram envoy_histogram1_bucket{le="0.5"} 0 envoy_histogram1_bucket{le="1"} 0 envoy_histogram1_bucket{le="5"} 100000 @@ -1571,34 +1573,34 @@ envoy_histogram1_sum{} 105105105000 envoy_histogram1_count{} 101100000 )EOF"; - EXPECT_EQ(expected_output, response.toString()); -} + EXPECT_EQ(expected_output, response.toString()); + } -TEST_F(PrometheusStatsFormatterTest, OutputWithAllMetricTypes) { - addCounter("cluster.test_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); - addCounter("cluster.test_2.upstream_cx_total", {{"another_tag_name", "another_tag-value"}}); - addGauge("cluster.test_3.upstream_cx_total", {{"another_tag_name_3", "another_tag_3-value"}}); - addGauge("cluster.test_4.upstream_cx_total", {{"another_tag_name_4", "another_tag_4-value"}}); - - const std::vector h1_values = {50, 20, 30, 70, 100, 5000, 200}; - HistogramWrapper h1_cumulative; - h1_cumulative.setHistogramValues(h1_values); - Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); - - auto histogram1 = makeHistogram(); - histogram1->name_ = "cluster.test_1.upstream_rq_time"; - histogram1->used_ = true; - histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); - addHistogram(histogram1); - EXPECT_CALL(*histogram1, cumulativeStatistics()) - .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + TEST_F(PrometheusStatsFormatterTest, OutputWithAllMetricTypes) { + addCounter("cluster.test_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); + addCounter("cluster.test_2.upstream_cx_total", {{"another_tag_name", "another_tag-value"}}); + addGauge("cluster.test_3.upstream_cx_total", {{"another_tag_name_3", "another_tag_3-value"}}); + addGauge("cluster.test_4.upstream_cx_total", {{"another_tag_name_4", "another_tag_4-value"}}); + + const std::vector h1_values = {50, 20, 30, 70, 100, 5000, 200}; + HistogramWrapper h1_cumulative; + h1_cumulative.setHistogramValues(h1_values); + Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); + + auto histogram1 = makeHistogram(); + histogram1->name_ = "cluster.test_1.upstream_rq_time"; + histogram1->used_ = true; + histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); + addHistogram(histogram1); + EXPECT_CALL(*histogram1, cumulativeStatistics()) + .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, - false, absl::nullopt); - EXPECT_EQ(5UL, size); + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, false, absl::nullopt); + EXPECT_EQ(5UL, size); - const std::string expected_output = R"EOF(# TYPE envoy_cluster_test_1_upstream_cx_total counter + const std::string expected_output = R"EOF(# TYPE envoy_cluster_test_1_upstream_cx_total counter envoy_cluster_test_1_upstream_cx_total{a_tag_name="a.tag-value"} 0 # TYPE envoy_cluster_test_2_upstream_cx_total counter envoy_cluster_test_2_upstream_cx_total{another_tag_name="another_tag-value"} 0 @@ -1631,34 +1633,34 @@ envoy_cluster_test_1_upstream_rq_time_sum{key1="value1",key2="value2"} 5532 envoy_cluster_test_1_upstream_rq_time_count{key1="value1",key2="value2"} 7 )EOF"; - EXPECT_EQ(expected_output, response.toString()); -} + EXPECT_EQ(expected_output, response.toString()); + } -TEST_F(PrometheusStatsFormatterTest, OutputWithUsedOnly) { - addCounter("cluster.test_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); - addCounter("cluster.test_2.upstream_cx_total", {{"another_tag_name", "another_tag-value"}}); - addGauge("cluster.test_3.upstream_cx_total", {{"another_tag_name_3", "another_tag_3-value"}}); - addGauge("cluster.test_4.upstream_cx_total", {{"another_tag_name_4", "another_tag_4-value"}}); - - const std::vector h1_values = {50, 20, 30, 70, 100, 5000, 200}; - HistogramWrapper h1_cumulative; - h1_cumulative.setHistogramValues(h1_values); - Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); - - auto histogram1 = makeHistogram(); - histogram1->name_ = "cluster.test_1.upstream_rq_time"; - histogram1->used_ = true; - histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); - addHistogram(histogram1); - EXPECT_CALL(*histogram1, cumulativeStatistics()) - .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + TEST_F(PrometheusStatsFormatterTest, OutputWithUsedOnly) { + addCounter("cluster.test_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); + addCounter("cluster.test_2.upstream_cx_total", {{"another_tag_name", "another_tag-value"}}); + addGauge("cluster.test_3.upstream_cx_total", {{"another_tag_name_3", "another_tag_3-value"}}); + addGauge("cluster.test_4.upstream_cx_total", {{"another_tag_name_4", "another_tag_4-value"}}); + + const std::vector h1_values = {50, 20, 30, 70, 100, 5000, 200}; + HistogramWrapper h1_cumulative; + h1_cumulative.setHistogramValues(h1_values); + Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); + + auto histogram1 = makeHistogram(); + histogram1->name_ = "cluster.test_1.upstream_rq_time"; + histogram1->used_ = true; + histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); + addHistogram(histogram1); + EXPECT_CALL(*histogram1, cumulativeStatistics()) + .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, - true, absl::nullopt); - EXPECT_EQ(1UL, size); + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, true, absl::nullopt); + EXPECT_EQ(1UL, size); - const std::string expected_output = R"EOF(# TYPE envoy_cluster_test_1_upstream_rq_time histogram + const std::string expected_output = R"EOF(# TYPE envoy_cluster_test_1_upstream_rq_time histogram envoy_cluster_test_1_upstream_rq_time_bucket{key1="value1",key2="value2",le="0.5"} 0 envoy_cluster_test_1_upstream_rq_time_bucket{key1="value1",key2="value2",le="1"} 0 envoy_cluster_test_1_upstream_rq_time_bucket{key1="value1",key2="value2",le="5"} 0 @@ -1683,72 +1685,72 @@ envoy_cluster_test_1_upstream_rq_time_sum{key1="value1",key2="value2"} 5532 envoy_cluster_test_1_upstream_rq_time_count{key1="value1",key2="value2"} 7 )EOF"; - EXPECT_EQ(expected_output, response.toString()); -} + EXPECT_EQ(expected_output, response.toString()); + } -TEST_F(PrometheusStatsFormatterTest, OutputWithUsedOnlyHistogram) { - const std::vector h1_values = {}; - HistogramWrapper h1_cumulative; - h1_cumulative.setHistogramValues(h1_values); - Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); + TEST_F(PrometheusStatsFormatterTest, OutputWithUsedOnlyHistogram) { + const std::vector h1_values = {}; + HistogramWrapper h1_cumulative; + h1_cumulative.setHistogramValues(h1_values); + Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); - auto histogram1 = makeHistogram(); - histogram1->name_ = "cluster.test_1.upstream_rq_time"; - histogram1->used_ = false; - histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); - addHistogram(histogram1); + auto histogram1 = makeHistogram(); + histogram1->name_ = "cluster.test_1.upstream_rq_time"; + histogram1->used_ = false; + histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); + addHistogram(histogram1); - { - const bool used_only = true; - EXPECT_CALL(*histogram1, cumulativeStatistics()).Times(0); - - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, used_only, absl::nullopt); - EXPECT_EQ(0UL, size); - } + { + const bool used_only = true; + EXPECT_CALL(*histogram1, cumulativeStatistics()).Times(0); - { - const bool used_only = false; - EXPECT_CALL(*histogram1, cumulativeStatistics()) - .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, used_only, absl::nullopt); + EXPECT_EQ(0UL, size); + } - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, used_only, absl::nullopt); - EXPECT_EQ(1UL, size); + { + const bool used_only = false; + EXPECT_CALL(*histogram1, cumulativeStatistics()) + .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, + response, used_only, absl::nullopt); + EXPECT_EQ(1UL, size); + } } -} -TEST_F(PrometheusStatsFormatterTest, OutputWithRegexp) { - addCounter("cluster.test_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); - addCounter("cluster.test_2.upstream_cx_total", {{"another_tag_name", "another_tag-value"}}); - addGauge("cluster.test_3.upstream_cx_total", {{"another_tag_name_3", "another_tag_3-value"}}); - addGauge("cluster.test_4.upstream_cx_total", {{"another_tag_name_4", "another_tag_4-value"}}); + TEST_F(PrometheusStatsFormatterTest, OutputWithRegexp) { + addCounter("cluster.test_1.upstream_cx_total", {{"a.tag-name", "a.tag-value"}}); + addCounter("cluster.test_2.upstream_cx_total", {{"another_tag_name", "another_tag-value"}}); + addGauge("cluster.test_3.upstream_cx_total", {{"another_tag_name_3", "another_tag_3-value"}}); + addGauge("cluster.test_4.upstream_cx_total", {{"another_tag_name_4", "another_tag_4-value"}}); - const std::vector h1_values = {50, 20, 30, 70, 100, 5000, 200}; - HistogramWrapper h1_cumulative; - h1_cumulative.setHistogramValues(h1_values); - Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); + const std::vector h1_values = {50, 20, 30, 70, 100, 5000, 200}; + HistogramWrapper h1_cumulative; + h1_cumulative.setHistogramValues(h1_values); + Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); - auto histogram1 = makeHistogram(); - histogram1->name_ = "cluster.test_1.upstream_rq_time"; - histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); - addHistogram(histogram1); + auto histogram1 = makeHistogram(); + histogram1->name_ = "cluster.test_1.upstream_rq_time"; + histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); + addHistogram(histogram1); - Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus( - counters_, gauges_, histograms_, response, false, - absl::optional{std::regex("cluster.test_1.upstream_cx_total")}); - EXPECT_EQ(1UL, size); + Buffer::OwnedImpl response; + auto size = PrometheusStatsFormatter::statsAsPrometheus( + counters_, gauges_, histograms_, response, false, + absl::optional{std::regex("cluster.test_1.upstream_cx_total")}); + EXPECT_EQ(1UL, size); - const std::string expected_output = - R"EOF(# TYPE envoy_cluster_test_1_upstream_cx_total counter + const std::string expected_output = + R"EOF(# TYPE envoy_cluster_test_1_upstream_cx_total counter envoy_cluster_test_1_upstream_cx_total{a_tag_name="a.tag-value"} 0 )EOF"; - EXPECT_EQ(expected_output, response.toString()); -} + EXPECT_EQ(expected_output, response.toString()); + } } // namespace Server -} // namespace Envoy +} // namespace Server From 1c08f0a588c77501b7f0233900dc68fcc2f252db Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 16:41:16 -0400 Subject: [PATCH 087/106] format Signed-off-by: Joshua Marantz --- test/server/http/admin_test.cc | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 86fd246a9005..2c9e933ed881 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -51,8 +51,8 @@ namespace Server { class AdminStatsTest : public testing::TestWithParam { public: - AdminStatsTest() : symbol_table_(Stats::SymbolTableCreator::makeSymbolTable()), - alloc_(*symbol_table_) { + AdminStatsTest() + : symbol_table_(Stats::SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_) { store_ = std::make_unique(alloc_); store_->addSink(sink_); } @@ -1453,8 +1453,8 @@ TEST_F(PrometheusStatsFormatterTest, MetricNameCollison) { {{"another_tag_name_4", "another_tag_4-value"}}); Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, false, absl::nullopt); + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, + false, absl::nullopt); EXPECT_EQ(2UL, size); } @@ -1473,8 +1473,8 @@ TEST_F(PrometheusStatsFormatterTest, UniqueMetricName) { {{"another_tag_name_4", "another_tag_4-value"}}); Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, false, absl::nullopt); + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, + false, absl::nullopt); EXPECT_EQ(4UL, size); } @@ -1487,13 +1487,13 @@ TEST_F(PrometheusStatsFormatterTest, HistogramWithNoValuesAndNoTags) { histogram->name_ = "histogram1"; histogram->used_ = true; ON_CALL(*histogram, cumulativeStatistics()) - .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); + .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); addHistogram(histogram); Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, false, absl::nullopt); + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, + false, absl::nullopt); EXPECT_EQ(1UL, size); const std::string expected_output = R"EOF(# TYPE envoy_histogram1 histogram @@ -1529,10 +1529,10 @@ TEST_F(PrometheusStatsFormatterTest, HistogramWithHighCounts) { // Force large counts to prove that the +Inf bucket doesn't overflow to scientific notation. h1_cumulative.setHistogramValuesWithCounts(std::vector>({ - {1, 100000}, - {100, 1000000}, - {1000, 100000000}, - })); + {1, 100000}, + {100, 1000000}, + {1000, 100000000}, + })); Stats::HistogramStatisticsImpl h1_cumulative_statistics(h1_cumulative.getHistogram()); @@ -1540,13 +1540,13 @@ TEST_F(PrometheusStatsFormatterTest, HistogramWithHighCounts) { histogram->name_ = "histogram1"; histogram->used_ = true; ON_CALL(*histogram, cumulativeStatistics()) - .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); + .WillByDefault(testing::ReturnRef(h1_cumulative_statistics)); addHistogram(histogram); Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, false, absl::nullopt); + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, + false, absl::nullopt); EXPECT_EQ(1UL, size); const std::string expected_output = R"EOF(# TYPE envoy_histogram1 histogram @@ -1594,11 +1594,11 @@ TEST_F(PrometheusStatsFormatterTest, OutputWithAllMetricTypes) { histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); addHistogram(histogram1); EXPECT_CALL(*histogram1, cumulativeStatistics()) - .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, false, absl::nullopt); + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, + false, absl::nullopt); EXPECT_EQ(5UL, size); const std::string expected_output = R"EOF(# TYPE envoy_cluster_test_1_upstream_cx_total counter @@ -1654,11 +1654,11 @@ TEST_F(PrometheusStatsFormatterTest, OutputWithUsedOnly) { histogram1->setTags({Stats::Tag{"key1", "value1"}, Stats::Tag{"key2", "value2"}}); addHistogram(histogram1); EXPECT_CALL(*histogram1, cumulativeStatistics()) - .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); Buffer::OwnedImpl response; - auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, - response, true, absl::nullopt); + auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, response, + true, absl::nullopt); EXPECT_EQ(1UL, size); const std::string expected_output = R"EOF(# TYPE envoy_cluster_test_1_upstream_rq_time histogram @@ -1714,7 +1714,7 @@ TEST_F(PrometheusStatsFormatterTest, OutputWithUsedOnlyHistogram) { { const bool used_only = false; EXPECT_CALL(*histogram1, cumulativeStatistics()) - .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); + .WillOnce(testing::ReturnRef(h1_cumulative_statistics)); Buffer::OwnedImpl response; auto size = PrometheusStatsFormatter::statsAsPrometheus(counters_, gauges_, histograms_, @@ -1754,4 +1754,4 @@ envoy_cluster_test_1_upstream_cx_total{a_tag_name="a.tag-value"} 0 } } // namespace Server -} // namespace Server +} // namespace Envoy From 25367f65bdb6e9a3e6159238f8803176d51c0b80 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 16:43:05 -0400 Subject: [PATCH 088/106] use real symbol tables by default. Signed-off-by: Joshua Marantz --- docs/root/intro/version_history.rst | 2 +- source/server/options_impl.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index f7efefa5acbb..267cdc777405 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -19,7 +19,7 @@ Version history * http: added the ability to :ref:`merge adjacent slashes` in the path. * listeners: added :ref:`continue_on_listener_filters_timeout ` to configure whether a listener will still create a connection when listener filters time out. * listeners: added :ref:`HTTP inspector listener filter `. -* performance: stats symbol table implementation (disabled by default; to test it, add "--use-fake-symbol-table 0" to the command-line arguments when starting Envoy). +* performance: stats symbol table implementation (enabled by default; to test it, add "--use-fake-symbol-table 1" to the command-line arguments when starting Envoy). * router: added :ref:`rq_retry_skipped_request_not_complete ` counter stat to router stats. * router check tool: add coverage reporting & enforcement. * tls: added verification of IP address SAN fields in certificates against configured SANs in the diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index d830c80b7312..96d86f29ac78 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -106,7 +106,7 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv, "Use the original libevent buffer implementation", false, true, "bool", cmd); TCLAP::ValueArg use_fake_symbol_table("", "use-fake-symbol-table", - "Use fake symbol table implementation", false, true, + "Use fake symbol table implementation", true, true, "bool", cmd); cmd.setExceptionHandling(false); From 6551dee9118da1c344ceb9af7b091b78335daa50 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 16:45:14 -0400 Subject: [PATCH 089/106] format Signed-off-by: Joshua Marantz --- source/server/options_impl.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index 96d86f29ac78..b720dd6c7aba 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -105,9 +105,8 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv, TCLAP::ValueArg use_libevent_buffer("", "use-libevent-buffers", "Use the original libevent buffer implementation", false, true, "bool", cmd); - TCLAP::ValueArg use_fake_symbol_table("", "use-fake-symbol-table", - "Use fake symbol table implementation", true, true, - "bool", cmd); + TCLAP::ValueArg use_fake_symbol_table( + "", "use-fake-symbol-table", "Use fake symbol table implementation", true, true, "bool", cmd); cmd.setExceptionHandling(false); try { From 8a6521cdf92c399bcc5b1d11f4d5a6dc3abc6722 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 10 Aug 2019 17:13:27 -0400 Subject: [PATCH 090/106] swap default Signed-off-by: Joshua Marantz --- source/server/options_impl.cc | 5 +++-- test/server/options_impl_test.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index b720dd6c7aba..84124d541057 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -105,8 +105,9 @@ OptionsImpl::OptionsImpl(int argc, const char* const* argv, TCLAP::ValueArg use_libevent_buffer("", "use-libevent-buffers", "Use the original libevent buffer implementation", false, true, "bool", cmd); - TCLAP::ValueArg use_fake_symbol_table( - "", "use-fake-symbol-table", "Use fake symbol table implementation", true, true, "bool", cmd); + TCLAP::ValueArg use_fake_symbol_table("", "use-fake-symbol-table", + "Use fake symbol table implementation", false, false, + "bool", cmd); cmd.setExceptionHandling(false); try { diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index 18922084d6d1..1c22e6143b1e 100644 --- a/test/server/options_impl_test.cc +++ b/test/server/options_impl_test.cc @@ -95,7 +95,7 @@ TEST_F(OptionsImplTest, All) { EXPECT_EQ(std::chrono::seconds(90), options->parentShutdownTime()); EXPECT_EQ(true, options->hotRestartDisabled()); EXPECT_EQ(true, options->libeventBufferEnabled()); - EXPECT_EQ(true, options->fakeSymbolTableEnabled()); + EXPECT_EQ(false, options->fakeSymbolTableEnabled()); EXPECT_EQ(true, options->cpusetThreadsEnabled()); options = createOptionsImpl("envoy --mode init_only"); From a17e17c43f532625031032e715900a7c61bb3d1c Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 21 Aug 2019 19:42:24 -0400 Subject: [PATCH 091/106] fix test Signed-off-by: Joshua Marantz --- test/server/options_impl_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index bc4cc16f7fa7..3788d323f6dc 100644 --- a/test/server/options_impl_test.cc +++ b/test/server/options_impl_test.cc @@ -99,7 +99,7 @@ TEST_F(OptionsImplTest, All) { EXPECT_TRUE(options->cpusetThreadsEnabled()); EXPECT_TRUE(options->allowUnknownStaticFields()); EXPECT_TRUE(options->rejectUnknownDynamicFields()); - EXPECT_TRUE(options->fakeSymbolTableEnabled()); + EXPECT_FALSE(options->fakeSymbolTableEnabled()); options = createOptionsImpl("envoy --mode init_only"); EXPECT_EQ(Server::Mode::InitOnly, options->mode()); From bf6fcd19596b1e242584aa5ef4f7ec47029c117a Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 21 Aug 2019 21:19:31 -0400 Subject: [PATCH 092/106] back out some earlier attempts at patching in the options PR. Signed-off-by: Joshua Marantz --- test/common/stats/isolated_store_impl_test.cc | 67 +++++++++---------- .../redis_proxy/conn_pool_impl_test.cc | 1 - .../lightstep/lightstep_tracer_impl_test.cc | 2 +- test/mocks/stats/mocks.cc | 2 +- test/mocks/stats/mocks.h | 6 +- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/test/common/stats/isolated_store_impl_test.cc b/test/common/stats/isolated_store_impl_test.cc index 7e832f2e64cb..b74b9fb1a840 100644 --- a/test/common/stats/isolated_store_impl_test.cc +++ b/test/common/stats/isolated_store_impl_test.cc @@ -15,24 +15,21 @@ namespace Stats { class StatsIsolatedStoreImplTest : public testing::Test { protected: - StatsIsolatedStoreImplTest() - : pool_(symbol_table_), store_(std::make_unique(symbol_table_)) {} + StatsIsolatedStoreImplTest() : pool_(store_.symbolTable()) {} ~StatsIsolatedStoreImplTest() override { pool_.clear(); - store_.reset(); - EXPECT_EQ(0, symbol_table_.numSymbols()); + EXPECT_EQ(0, store_.symbolTable().numSymbols()); } StatName makeStatName(absl::string_view name) { return pool_.add(name); } - SymbolTableImpl symbol_table_; + IsolatedStoreImpl store_; StatNamePool pool_; - std::unique_ptr store_; }; TEST_F(StatsIsolatedStoreImplTest, All) { - ScopePtr scope1 = store_->createScope("scope1."); - Counter& c1 = store_->counter("c1"); + ScopePtr scope1 = store_.createScope("scope1."); + Counter& c1 = store_.counter("c1"); Counter& c2 = scope1->counter("c2"); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); @@ -41,16 +38,16 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, c1.tags().size()); EXPECT_EQ(0, c1.tags().size()); - StatNameManagedStorage c1_name("c1", store_->symbolTable()); + StatNameManagedStorage c1_name("c1", store_.symbolTable()); c1.add(100); - auto found_counter = store_->findCounter(c1_name.statName()); + auto found_counter = store_.findCounter(c1_name.statName()); ASSERT_TRUE(found_counter.has_value()); EXPECT_EQ(&c1, &found_counter->get()); EXPECT_EQ(100, found_counter->get().value()); c1.add(100); EXPECT_EQ(200, found_counter->get().value()); - Gauge& g1 = store_->gauge("g1", Gauge::ImportMode::Accumulate); + Gauge& g1 = store_.gauge("g1", Gauge::ImportMode::Accumulate); Gauge& g2 = scope1->gauge("g2", Gauge::ImportMode::Accumulate); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); @@ -59,16 +56,16 @@ TEST_F(StatsIsolatedStoreImplTest, All) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g2.tags().size()); - StatNameManagedStorage g1_name("g1", store_->symbolTable()); + StatNameManagedStorage g1_name("g1", store_.symbolTable()); g1.set(100); - auto found_gauge = store_->findGauge(g1_name.statName()); + auto found_gauge = store_.findGauge(g1_name.statName()); ASSERT_TRUE(found_gauge.has_value()); EXPECT_EQ(&g1, &found_gauge->get()); EXPECT_EQ(100, found_gauge->get().value()); g1.set(0); EXPECT_EQ(0, found_gauge->get().value()); - Histogram& h1 = store_->histogram("h1"); + Histogram& h1 = store_.histogram("h1"); EXPECT_TRUE(h1.used()); // hardcoded in impl to be true always. Histogram& h2 = scope1->histogram("h2"); scope1->deliverHistogramToSinks(h2, 0); @@ -81,8 +78,8 @@ TEST_F(StatsIsolatedStoreImplTest, All) { h1.recordValue(200); h2.recordValue(200); - StatNameManagedStorage h1_name("h1", store_->symbolTable()); - auto found_histogram = store_->findHistogram(h1_name.statName()); + StatNameManagedStorage h1_name("h1", store_.symbolTable()); + auto found_histogram = store_.findHistogram(h1_name.statName()); ASSERT_TRUE(found_histogram.has_value()); EXPECT_EQ(&h1, &found_histogram->get()); @@ -93,18 +90,18 @@ TEST_F(StatsIsolatedStoreImplTest, All) { ScopePtr scope3 = scope1->createScope(std::string("foo:\0:.", 7)); EXPECT_EQ("scope1.foo___.bar", scope3->counter("bar").name()); - EXPECT_EQ(4UL, store_->counters().size()); - EXPECT_EQ(2UL, store_->gauges().size()); + EXPECT_EQ(4UL, store_.counters().size()); + EXPECT_EQ(2UL, store_.gauges().size()); - StatNameManagedStorage nonexistent_name("nonexistent", store_->symbolTable()); - 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); + StatNameManagedStorage nonexistent_name("nonexistent", store_.symbolTable()); + 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); } TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { - ScopePtr scope1 = store_->createScope("scope1."); - Counter& c1 = store_->counterFromStatName(makeStatName("c1")); + ScopePtr scope1 = store_.createScope("scope1."); + Counter& c1 = store_.counterFromStatName(makeStatName("c1")); Counter& c2 = scope1->counterFromStatName(makeStatName("c2")); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); @@ -113,7 +110,7 @@ TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { EXPECT_EQ(0, c1.tags().size()); EXPECT_EQ(0, c1.tags().size()); - Gauge& g1 = store_->gaugeFromStatName(makeStatName("g1"), Gauge::ImportMode::Accumulate); + Gauge& g1 = store_.gaugeFromStatName(makeStatName("g1"), Gauge::ImportMode::Accumulate); Gauge& g2 = scope1->gaugeFromStatName(makeStatName("g2"), Gauge::ImportMode::Accumulate); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); @@ -122,7 +119,7 @@ TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { EXPECT_EQ(0, g1.tags().size()); EXPECT_EQ(0, g1.tags().size()); - Histogram& h1 = store_->histogramFromStatName(makeStatName("h1")); + Histogram& h1 = store_.histogramFromStatName(makeStatName("h1")); Histogram& h2 = scope1->histogramFromStatName(makeStatName("h2")); scope1->deliverHistogramToSinks(h2, 0); EXPECT_EQ("h1", h1.name()); @@ -141,12 +138,12 @@ TEST_F(StatsIsolatedStoreImplTest, AllWithSymbolTable) { ScopePtr scope3 = scope1->createScope(std::string("foo:\0:.", 7)); EXPECT_EQ("scope1.foo___.bar", scope3->counter("bar").name()); - EXPECT_EQ(4UL, store_->counters().size()); - EXPECT_EQ(2UL, store_->gauges().size()); + EXPECT_EQ(4UL, store_.counters().size()); + EXPECT_EQ(2UL, store_.gauges().size()); } TEST_F(StatsIsolatedStoreImplTest, ConstSymtabAccessor) { - ScopePtr scope = store_->createScope("scope."); + ScopePtr scope = store_.createScope("scope."); const Scope& cscope = *scope; const SymbolTable& const_symbol_table = cscope.constSymbolTable(); SymbolTable& symbol_table = scope->symbolTable(); @@ -156,7 +153,7 @@ TEST_F(StatsIsolatedStoreImplTest, ConstSymtabAccessor) { TEST_F(StatsIsolatedStoreImplTest, LongStatName) { const std::string long_string(128, 'A'); - ScopePtr scope = store_->createScope("scope."); + ScopePtr scope = store_.createScope("scope."); Counter& counter = scope->counter(long_string); EXPECT_EQ(absl::StrCat("scope.", long_string), counter.name()); } @@ -174,9 +171,9 @@ struct TestStats { }; TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { - TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(*store_, "test."), - POOL_GAUGE_PREFIX(*store_, "test."), - POOL_HISTOGRAM_PREFIX(*store_, "test."))}; + TestStats test_stats{ALL_TEST_STATS(POOL_COUNTER_PREFIX(store_, "test."), + POOL_GAUGE_PREFIX(store_, "test."), + POOL_HISTOGRAM_PREFIX(store_, "test."))}; Counter& counter = test_stats.test_counter_; EXPECT_EQ("test.test_counter", counter.name()); @@ -189,10 +186,10 @@ TEST_F(StatsIsolatedStoreImplTest, StatsMacros) { } TEST_F(StatsIsolatedStoreImplTest, NullImplCoverage) { - NullCounterImpl& c = store_->nullCounter(); + NullCounterImpl& c = store_.nullCounter(); c.inc(); EXPECT_EQ(0, c.value()); - NullGaugeImpl& g = store_->nullGauge(""); + NullGaugeImpl& g = store_.nullGauge(""); g.inc(); EXPECT_EQ(0, g.value()); } diff --git a/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc b/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc index 2c5f9446b398..22161d0e00fb 100644 --- a/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc +++ b/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc @@ -199,7 +199,6 @@ class RedisConnPoolImplTest : public testing::Test, public Common::Redis::Client MOCK_METHOD1(create_, Common::Redis::Client::Client*(Upstream::HostConstSharedPtr host)); const std::string cluster_name_{"fake_cluster"}; - Envoy::Test::Global symbol_table_; NiceMock cm_; NiceMock tls_; InstanceSharedPtr conn_pool_; diff --git a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc index 286ef130023b..cf9e44e1b9f3 100644 --- a/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc +++ b/test/extensions/tracers/lightstep/lightstep_tracer_impl_test.cc @@ -45,7 +45,7 @@ namespace { class LightStepDriverTest : public testing::Test { public: - LightStepDriverTest() : grpc_context_(*symbol_table_), stats_(*symbol_table_) {} + LightStepDriverTest() : grpc_context_(*symbol_table_) {} void setup(envoy::config::trace::v2::LightstepConfig& lightstep_config, bool init_timer, Common::Ot::OpenTracingDriver::PropagationMode propagation_mode = diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index a4c876fbae94..ef5eeebeb488 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -66,7 +66,7 @@ MockSink::~MockSink() = default; MockStore::MockStore() : StoreImpl(*global_symbol_table_) { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); ON_CALL(*this, histogram(_)).WillByDefault(Invoke([this](const std::string& name) -> Histogram& { - auto* histogram = new NiceMock(); + auto* histogram = new NiceMock(); // symbol_table_); histogram->name_ = name; histogram->store_ = this; histograms_.emplace_back(histogram); diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index 7d2f0c21a81e..d5ad120ffcae 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -296,13 +296,13 @@ class MockStore : public SymbolTableProvider, public StoreImpl { MOCK_CONST_METHOD1(findHistogram, OptionalHistogram(StatName)); Counter& counterFromStatName(StatName name) override { - return counter(symbolTable().toString(name)); + return counter(symbol_table_->toString(name)); } Gauge& gaugeFromStatName(StatName name, Gauge::ImportMode import_mode) override { - return gauge(symbolTable().toString(name), import_mode); + return gauge(symbol_table_->toString(name), import_mode); } Histogram& histogramFromStatName(StatName name) override { - return histogram(symbolTable().toString(name)); + return histogram(symbol_table_->toString(name)); } TestSymbolTable symbol_table_; From dc690b2e5736b2dccc7244f2e0a60729bb1521b5 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 22 Aug 2019 00:15:04 -0400 Subject: [PATCH 093/106] update byte-size golds. Signed-off-by: Joshua Marantz --- test/integration/stats_integration_test.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 746baf523926..7579e13e77bf 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -234,7 +234,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 42838); // 104 bytes higher than a debug build. + EXPECT_MEMORY_EQ(m_per_cluster, 42830); // 104 bytes higher than a debug build. EXPECT_MEMORY_LE(m_per_cluster, 44000); } @@ -259,7 +259,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // Date PR Bytes Per Cluster Notes // exact upper-bound // ---------- ----- ----------------- ----- - // 2019/08/09 7882 35489 36000 Initial version + // 2019/08/09 7882 34585 36000 Initial version // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -269,7 +269,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 35489); // 104 bytes higher than a debug build. + EXPECT_MEMORY_EQ(m_per_cluster, 34585); // 104 bytes higher than a debug build. EXPECT_MEMORY_LE(m_per_cluster, 36000); } From 71e0fcd137eccbef4fb1c8efaa6d3aeb9ad53ef5 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 22 Aug 2019 08:26:11 -0400 Subject: [PATCH 094/106] back out the sharing of symbol tables for upstream cluster isolated stats. Signed-off-by: Joshua Marantz --- source/common/upstream/upstream_impl.cc | 2 +- source/common/upstream/upstream_impl.h | 4 +--- test/integration/stats_integration_test.cc | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index b33f857fbdc8..f44e01f5a5de 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -598,7 +598,7 @@ ClusterInfoImpl::ClusterInfoImpl( per_connection_buffer_limit_bytes_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, per_connection_buffer_limit_bytes, 1024 * 1024)), transport_socket_factory_(std::move(socket_factory)), stats_scope_(std::move(stats_scope)), - stats_(generateStats(*stats_scope_)), load_report_stats_store_(stats_scope_->symbolTable()), + stats_(generateStats(*stats_scope_)), load_report_stats_(generateLoadReportStats(load_report_stats_store_)), features_(parseFeatures(config)), http2_settings_(Http::Utility::parseHttp2Settings(config.http2_protocol_options())), diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 3f0f34d7545b..07469e36d0f3 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -75,9 +75,7 @@ class HostDescriptionImpl : virtual public HostDescription { .bool_value()), metadata_(std::make_shared(metadata)), locality_(locality), locality_zone_stat_name_(locality.zone(), cluster->statsScope().symbolTable()), - stats_store_(cluster->statsScope().symbolTable()), stats_{ALL_HOST_STATS( - POOL_COUNTER(stats_store_), - POOL_GAUGE(stats_store_))}, + stats_{ALL_HOST_STATS(POOL_COUNTER(stats_store_), POOL_GAUGE(stats_store_))}, priority_(priority) { if (health_check_config.port_value() != 0 && dest_address->type() != Network::Address::Type::Ip) { diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 7579e13e77bf..746baf523926 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -234,7 +234,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 42830); // 104 bytes higher than a debug build. + EXPECT_MEMORY_EQ(m_per_cluster, 42838); // 104 bytes higher than a debug build. EXPECT_MEMORY_LE(m_per_cluster, 44000); } @@ -259,7 +259,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // Date PR Bytes Per Cluster Notes // exact upper-bound // ---------- ----- ----------------- ----- - // 2019/08/09 7882 34585 36000 Initial version + // 2019/08/09 7882 35489 36000 Initial version // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -269,7 +269,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // On a local clang8/libstdc++/linux flow, the memory usage was observed in // June 2019 to be 64 bytes higher than it is in CI/release. Your mileage may // vary. - EXPECT_MEMORY_EQ(m_per_cluster, 34585); // 104 bytes higher than a debug build. + EXPECT_MEMORY_EQ(m_per_cluster, 35489); // 104 bytes higher than a debug build. EXPECT_MEMORY_LE(m_per_cluster, 36000); } From 5ff494a03ad4616631903ef647b90f0f133f4474 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 21 Jan 2020 10:52:04 -0500 Subject: [PATCH 095/106] remove superfluous check. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_impl.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index a817908ecfd4..164ff9ec2999 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -513,9 +513,7 @@ void StatNameStorageSet::free(SymbolTable& symbol_table) { SymbolTable::StoragePtr SymbolTableImpl::join(const StatNameVec& stat_names) const { uint64_t num_bytes = 0; for (StatName stat_name : stat_names) { - if (!stat_name.empty()) { - num_bytes += stat_name.dataSize(); - } + num_bytes += stat_name.dataSize(); } MemBlockBuilder mem_block(Encoding::totalSizeBytes(num_bytes)); Encoding::appendEncoding(num_bytes, mem_block); From b8dfdc0331edbfed96404b9de4a72c81353c1834 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 19 Mar 2020 14:40:40 -0400 Subject: [PATCH 096/106] switch another default to use real symbol tables. Signed-off-by: Joshua Marantz --- source/common/stats/symbol_table_creator.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/stats/symbol_table_creator.cc b/source/common/stats/symbol_table_creator.cc index 8b29313130b5..755c8fcce2e4 100644 --- a/source/common/stats/symbol_table_creator.cc +++ b/source/common/stats/symbol_table_creator.cc @@ -4,7 +4,7 @@ namespace Envoy { namespace Stats { bool SymbolTableCreator::initialized_ = false; -bool SymbolTableCreator::use_fake_symbol_tables_ = true; +bool SymbolTableCreator::use_fake_symbol_tables_ = false; SymbolTablePtr SymbolTableCreator::initAndMakeSymbolTable(bool use_fake) { ASSERT(!initialized_ || (use_fake_symbol_tables_ == use_fake)); From f834e4e674c2530c5c3e48273d08280f7e02903f Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Mon, 20 Apr 2020 15:13:10 -0400 Subject: [PATCH 097/106] move version history where it belongs. Signed-off-by: Joshua Marantz --- docs/root/intro/version_history.rst | 1187 ------------------------- docs/root/version_history/current.rst | 2 + 2 files changed, 2 insertions(+), 1187 deletions(-) delete mode 100644 docs/root/intro/version_history.rst diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst deleted file mode 100644 index d31802406b1e..000000000000 --- a/docs/root/intro/version_history.rst +++ /dev/null @@ -1,1187 +0,0 @@ -Version history ---------------- - -1.14.0 (Pending) -================ -* access loggers: access logger extensions use the "envoy.access_loggers" name space. A mapping - of extension names is available in the :ref:`deprecated ` documentation. -* access log: fix %DOWSTREAM_DIRECT_REMOTE_ADDRESS% when used with PROXY protocol listener filter -* adaptive concurrency: fixed bug that allowed concurrency limits to drop below the configured - minimum. -* aws_request_signing: a few fixes so that it works with S3. -* admin: added support for displaying ip address subject alternate names in :ref:`certs` end point. -* admin: added :http:post:`/reopen_logs` endpoint to control log rotation. -* buffer: force copy when appending small slices to OwnedImpl buffer to avoid fragmentation. -* config: use type URL to select an extension whenever the config type URL (or its previous versions) uniquely identify a typed extension, see :ref:`extension configuration `. -* config: added stat :ref:`update_time `. -* datasource: added retry policy for remote async data source. -* dns: the STRICT_DNS cluster now only resolves to 0 hosts if DNS resolution successfully returns 0 hosts. -* dns: added support for :ref:`dns_failure_refresh_rate ` for the :ref:`dns cache ` to set the DNS refresh rate during failures. -* http filters: http filter extensions use the "envoy.filters.http" name space. A mapping - of extension names is available in the :ref:`deprecated ` documentation. -* ext_authz: disabled the use of lowercase string matcher for headers matching in HTTP-based `ext_authz`. - Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.ext_authz_http_service_enable_case_sensitive_string_matcher` to false. -* fault: added support for controlling abort faults with :ref:`HTTP header fault configuration ` to the HTTP fault filter. -* http: added HTTP/1.1 flood protection. Can be temporarily disabled using the runtime feature `envoy.reloadable_features.http1_flood_protection` -* http: fixing a bug in HTTP/1.0 responses where Connection: keep-alive was not appended for connections which were kept alive. -* http: fixed a bug that could send extra METADATA frames and underflow memory when encoding METADATA frames on a connection that was dispatching data. -* http: connection header sanitizing has been modified to always sanitize if there is no upgrade, including when an h2c upgrade attempt has been removed. -* listener filters: listener filter extensions use the "envoy.filters.listener" name space. A - mapping of extension names is available in the :ref:`deprecated ` documentation. -* listeners: fixed issue where :ref:`TLS inspector listener filter ` could have been bypassed by a client using only TLS 1.3. -* lua: added a parameter to `httpCall` that makes it possible to have the call be asynchronous. -* lua: added moonjit support. -* mongo: the stat emitted for queries without a max time set in the :ref:`MongoDB filter` was modified to emit correctly for Mongo v3.2+. -* network filters: network filter extensions use the "envoy.filters.network" name space. A mapping - of extension names is available in the :ref:`deprecated ` documentation. -* network filters: added a :ref:`direct response filter `. -* performance: stats symbol table implementation (enabled by default; to disable it, add - `--use-fake-symbol-table 1` to the command-line arguments when starting Envoy). -* rbac: added :ref:`url_path ` for matching URL path without the query and fragment string. -* retry: added a retry predicate that :ref:`rejects hosts based on metadata. ` -* router: added :ref:`auto_san_validation ` to support overrriding SAN validation to transport socket for new upstream connections based on the downstream HTTP host/authority header. -* router: added the ability to match a route based on whether a downstream TLS connection certificate has been - :ref:`validated `. -* router: added support for :ref:`regex_rewrite - ` for path rewriting using regular expressions and capture groups. -* router: don't ignore :ref:`per_try_timeout ` when the :ref:`global route timeout ` is disabled. -* router: added ability to set attempt count in downstream response, see :ref:`virtual host's include response - attempt count config `. -* router: strip whitespace for :ref:`retry_on `, :ref:`grpc-retry-on header ` and :ref:`retry-on header `. -* runtime: enabling the runtime feature "envoy.deprecated_features.allow_deprecated_extension_names" - disables the use of deprecated extension names. -* runtime: integer values may now be parsed as booleans. -* sds: added :ref:`GenericSecret ` to support secret of generic type. -* sds: fix the SDS vulnerability that TLS validation context (e.g., subject alt name or hash) cannot be effectively validated in some cases. -* stat sinks: stat sink extensions use the "envoy.stat_sinks" name space. A mapping of extension - names is available in the :ref:`deprecated ` documentation. -* thrift_proxy: add router filter stats to docs. -* tracers: tracer extensions use the "envoy.tracers" name space. A mapping of extension names is - available in the :ref:`deprecated ` documentation. -* tracing: added gRPC service configuration to the OpenCensus Stackdriver and OpenCensus Agent tracers. -* upstream: combined HTTP/1 and HTTP/2 connection pool code. This means that circuit breaker - limits for both requests and connections apply to both pool types. Also, HTTP/2 now has - the option to limit concurrent requests on a connection, and allow multiple draining - connections. The old behavior is deprecated, but can be used during the deprecation - period by disabling runtime feature "envoy.reloadable_features.new_http1_connection_pool_behavior" or - "envoy.reloadable_features.new_http2_connection_pool_behavior" and then re-configure your clusters or - restart Envoy. The behavior will not switch until the connection pools are recreated. The new - circuit breaker behavior is described :ref:`here `. -* upstream: changed load distribution algorithm when all priorities enter :ref:`panic mode`. - -1.13.0 (January 20, 2020) -========================= -* access log: added FILTER_STATE :ref:`access log formatters ` and gRPC access logger. -* admin: added the ability to filter :ref:`/config_dump `. -* access log: added a :ref:`typed JSON logging mode ` to output access logs in JSON format with non-string values -* access log: fixed UPSTREAM_LOCAL_ADDRESS :ref:`access log formatters ` to work for http requests -* access log: added HOSTNAME. -* api: remove all support for v1 -* api: added ability to specify `mode` for :ref:`Pipe `. -* api: support for the v3 xDS API added. See :ref:`api_supported_versions`. -* aws_request_signing: added new alpha :ref:`HTTP AWS request signing filter `. -* buffer: remove old implementation -* build: official released binary is now built against libc++. -* cluster: added :ref:`aggregate cluster ` that allows load balancing between clusters. -* config: all category names of internal envoy extensions are prefixed with the 'envoy.' prefix to follow the reverse DNS naming notation. -* decompressor: remove decompressor hard assert failure and replace with an error flag. -* ext_authz: added :ref:`configurable ability` to send the :ref:`certificate` to the `ext_authz` service. -* fault: fixed an issue where the http fault filter would repeatedly check the percentage of abort/delay when the `x-envoy-downstream-service-cluster` header was included in the request to ensure that the actual percentage of abort/delay matches the configuration of the filter. -* health check: gRPC health checker sets the gRPC deadline to the configured timeout duration. -* health check: added :ref:`TlsOptions ` to allow TLS configuration overrides. -* health check: added :ref:`service_name_matcher ` to better compare the service name patterns for health check identity. -* http: added strict validation that CONNECT is refused as it is not yet implemented. This can be reversed temporarily by setting the runtime feature `envoy.reloadable_features.strict_method_validation` to false. -* http: added support for http1 trailers. To enable use :ref:`enable_trailers `. -* http: added the ability to sanitize headers nominated by the Connection header. This new behavior is guarded by envoy.reloadable_features.connection_header_sanitization which defaults to true. -* http: blocks unsupported transfer-encodings. Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.reject_unsupported_transfer_encodings` to false. -* http: support :ref:`auto_host_rewrite_header` in the dynamic forward proxy. -* jwt_authn: added :ref:`allow_missing` option that accepts request without token but rejects bad request with bad tokens. -* jwt_authn: added :ref:`bypass_cors_preflight` to allow bypassing the CORS preflight request. -* lb_subset_config: new fallback policy for selectors: :ref:`KEYS_SUBSET` -* listeners: added :ref:`reuse_port` option. -* logger: added :ref:`--log-format-escaped ` command line option to escape newline characters in application logs. -* ratelimit: added :ref:`local rate limit ` network filter. -* rbac: added support for matching all subject alt names instead of first in :ref:`principal_name `. -* redis: performance improvement for larger split commands by avoiding string copies. -* redis: correctly follow MOVE/ASK redirection for mirrored clusters. -* redis: add :ref:`host_degraded_refresh_threshold ` and :ref:`failure_refresh_threshold ` to refresh topology when nodes are degraded or when requests fails. -* router: added histograms to show timeout budget usage to the :ref:`cluster stats `. -* router check tool: added support for testing and marking coverage for routes of runtime fraction 0. -* router: added :ref:`request_mirror_policies` to support sending multiple mirrored requests in one route. -* router: added support for REQ(header-name) :ref:`header formatter `. -* router: added support for percentage-based :ref:`retry budgets ` -* router: allow using a :ref:`query parameter ` for HTTP consistent hashing. -* router: exposed DOWNSTREAM_REMOTE_ADDRESS as custom HTTP request/response headers. -* router: added support for :ref:`max_internal_redirects ` for configurable maximum internal redirect hops. -* router: skip the Location header when the response code is not a 201 or a 3xx. -* router: added :ref:`auto_sni ` to support setting SNI to transport socket for new upstream connections based on the downstream HTTP host/authority header. -* router: added support for HOSTNAME :ref:`header formatter - `. -* server: added the :option:`--disable-extensions` CLI option, to disable extensions at startup. -* server: fixed a bug in config validation for configs with runtime layers. -* server: added :ref:`workers_started ` that indicates whether listeners have been fully initialized on workers. -* tcp_proxy: added :ref:`ClusterWeight.metadata_match`. -* tcp_proxy: added :ref:`hash_policy`. -* thrift_proxy: added support for cluster header based routing. -* thrift_proxy: added stats to the router filter. -* tls: remove TLS 1.0 and 1.1 from client defaults -* tls: added support for :ref:`generic string matcher ` for subject alternative names. -* tracing: added the ability to set custom tags on both the :ref:`HTTP connection manager` and the :ref:`HTTP route `. -* tracing: added upstream_address tag. -* tracing: added initial support for AWS X-Ray (local sampling rules only) :ref:`X-Ray Tracing `. -* tracing: added tags for gRPC request path, authority, content-type and timeout. -* udp: added initial support for :ref:`UDP proxy ` - -1.12.2 (December 10, 2019) -========================== -* http: fixed CVE-2019-18801 by allocating sufficient memory for request headers. -* http: fixed CVE-2019-18802 by implementing stricter validation of HTTP/1 headers. -* http: trim LWS at the end of header keys, for correct HTTP/1.1 header parsing. -* http: added strict authority checking. This can be reversed temporarily by setting the runtime feature `envoy.reloadable_features.strict_authority_validation` to false. -* route config: fixed CVE-2019-18838 by checking for presence of host/path headers. - -1.12.1 (November 8, 2019) -========================= -* listener: fixed CVE-2019-18836 by clearing accept filters before connection creation. - -1.12.0 (October 31, 2019) -========================= -* access log: added a new flag for :ref:`downstream protocol error `. -* access log: added :ref:`buffering ` and :ref:`periodical flushing ` support to gRPC access logger. Defaults to 16KB buffer and flushing every 1 second. -* access log: added DOWNSTREAM_DIRECT_REMOTE_ADDRESS and DOWNSTREAM_DIRECT_REMOTE_ADDRESS_WITHOUT_PORT :ref:`access log formatters ` and gRPC access logger. -* access log: gRPC Access Log Service (ALS) support added for :ref:`TCP access logs `. -* access log: reintroduced :ref:`filesystem ` stats and added the `write_failed` counter to track failed log writes. -* admin: added ability to configure listener :ref:`socket options `. -* admin: added config dump support for Secret Discovery Service :ref:`SecretConfigDump `. -* admin: added support for :ref:`draining ` listeners via admin interface. -* admin: added :http:get:`/stats/recentlookups`, :http:post:`/stats/recentlookups/clear`, :http:post:`/stats/recentlookups/disable`, and :http:post:`/stats/recentlookups/enable` endpoints. -* api: added :ref:`set_node_on_first_message_only ` option to omit the node identifier from the subsequent discovery requests on the same stream. -* buffer filter: now populates content-length header if not present. This behavior can be temporarily disabled using the runtime feature `envoy.reloadable_features.buffer_filter_populate_content_length`. -* build: official released binary is now PIE so it can be run with ASLR. -* config: added support for :ref:`delta xDS ` (including ADS) delivery. -* config: enforcing that terminal filters (e.g. HttpConnectionManager for L4, router for L7) be the last in their respective filter chains. -* config: added access log :ref:`extension filter`. -* config: added support for :option:`--reject-unknown-dynamic-fields`, providing independent control - over whether unknown fields are rejected in static and dynamic configuration. By default, unknown - fields in static configuration are rejected and are allowed in dynamic configuration. Warnings - are logged for the first use of any unknown field and these occurrences are counted in the - :ref:`server.static_unknown_fields ` and :ref:`server.dynamic_unknown_fields - ` statistics. -* config: added async data access for local and remote data sources. -* config: changed the default value of :ref:`initial_fetch_timeout ` from 0s to 15s. This is a change in behaviour in the sense that Envoy will move to the next initialization phase, even if the first config is not delivered in 15s. Refer to :ref:`initialization process ` for more details. -* config: added stat :ref:`init_fetch_timeout `. -* config: tls_context in Cluster and FilterChain are deprecated in favor of transport socket. See :ref:`deprecated documentation` for more information. -* csrf: added PATCH to supported methods. -* dns: added support for configuring :ref:`dns_failure_refresh_rate ` to set the DNS refresh rate during failures. -* ext_authz: added :ref:`configurable ability ` to send dynamic metadata to the `ext_authz` service. -* ext_authz: added :ref:`filter_enabled RuntimeFractionalPercent flag ` to filter. -* ext_authz: added tracing to the HTTP client. -* ext_authz: deprecated :ref:`cluster scope stats ` in favour of filter scope stats. -* fault: added overrides for default runtime keys in :ref:`HTTPFault ` filter. -* grpc: added :ref:`AWS IAM grpc credentials extension ` for AWS-managed xDS. -* grpc: added :ref:`gRPC stats filter ` for collecting stats about gRPC calls and streaming message counts. -* grpc-json: added support for :ref:`ignoring unknown query parameters`. -* grpc-json: added support for :ref:`the grpc-status-details-bin header`. -* header to metadata: added :ref:`PROTOBUF_VALUE ` and :ref:`ValueEncode ` to support protobuf Value and Base64 encoding. -* http: added a default one hour idle timeout to upstream and downstream connections. HTTP connections with no streams and no activity will be closed after one hour unless the default idle_timeout is overridden. To disable upstream idle timeouts, set the :ref:`idle_timeout ` to zero in Cluster :ref:`http_protocol_options`. To disable downstream idle timeouts, either set :ref:`idle_timeout ` to zero in the HttpConnectionManager :ref:`common_http_protocol_options ` or set the deprecated :ref:`connection manager ` field to zero. -* http: added the ability to format HTTP/1.1 header keys using :ref:`header_key_format `. -* http: added the ability to reject HTTP/1.1 requests with invalid HTTP header values, using the runtime feature `envoy.reloadable_features.strict_header_validation`. -* http: changed Envoy to forward existing x-forwarded-proto from upstream trusted proxies. Guarded by `envoy.reloadable_features.trusted_forwarded_proto` which defaults true. -* http: added the ability to configure the behavior of the server response header, via the :ref:`server_header_transformation` field. -* http: added the ability to :ref:`merge adjacent slashes` in the path. -* http: :ref:`AUTO ` codec protocol inference now requires the H2 magic bytes to be the first bytes transmitted by a downstream client. -* http: remove h2c upgrade headers for HTTP/1 as h2c upgrades are currently not supported. -* http: absolute URL support is now on by default. The prior behavior can be reinstated by setting :ref:`allow_absolute_url ` to false. -* http: support :ref:`host rewrite ` in the dynamic forward proxy. -* http: support :ref:`disabling the filter per route ` in the grpc http1 reverse bridge filter. -* http: added the ability to :ref:`configure max connection duration ` for downstream connections. -* listeners: added :ref:`continue_on_listener_filters_timeout ` to configure whether a listener will still create a connection when listener filters time out. -* listeners: added :ref:`HTTP inspector listener filter `. -* performance: stats symbol table implementation (enabled by default; to test it, add "--use-fake-symbol-table 1" to the command-line arguments when starting Envoy). -* performance: stats symbol table implementation (disabled by default; to test it, add "--use-fake-symbol-table 0" to the command-line arguments when starting Envoy). -* redis: added :ref:`read_policy ` to allow reading from redis replicas for Redis Cluster deployments. -* rbac: added support for DNS SAN as :ref:`principal_name `. -* listeners: added :ref:`connection balancer ` - configuration for TCP listeners. -* listeners: listeners now close the listening socket as part of the draining stage as soon as workers stop accepting their connections. -* lua: extended `httpCall()` and `respond()` APIs to accept headers with entry values that can be a string or table of strings. -* lua: extended `dynamicMetadata:set()` to allow setting complex values. -* metrics_service: added support for flushing histogram buckets. -* outlier_detector: added :ref:`support for the grpc-status response header ` by mapping it to HTTP status. Guarded by envoy.reloadable_features.outlier_detection_support_for_grpc_status which defaults to true. -* performance: new buffer implementation enabled by default (to disable add "--use-libevent-buffers 1" to the command-line arguments when starting Envoy). -* performance: stats symbol table implementation (disabled by default; to test it, add "--use-fake-symbol-table 0" to the command-line arguments when starting Envoy). -* rbac: added support for DNS SAN as :ref:`principal_name `. -* redis: added :ref:`enable_command_stats ` to enable :ref:`per command statistics ` for upstream clusters. -* redis: added :ref:`read_policy ` to allow reading from redis replicas for Redis Cluster deployments. -* redis: fixed a bug where the redis health checker ignored the upstream auth password. -* redis: enable_hashtaging is always enabled when the upstream uses open source Redis cluster protocol. -* regex: introduced new :ref:`RegexMatcher ` type that - provides a safe regex implementation for untrusted user input. This type is now used in all - configuration that processes user provided input. See :ref:`deprecated configuration details - ` for more information. -* rbac: added conditions to the policy, see :ref:`condition `. -* router: added :ref:`rq_retry_skipped_request_not_complete ` counter stat to router stats. -* router: :ref:`scoped routing ` is supported. -* router: added new :ref:`retriable-headers ` retry policy. Retries can now be configured to trigger by arbitrary response header matching. -* router: added ability for most specific header mutations to take precedence, see :ref:`route configuration's most specific - header mutations wins flag `. -* router: added :ref:`respect_expected_rq_timeout ` that instructs ingress Envoy to respect :ref:`config_http_filters_router_x-envoy-expected-rq-timeout-ms` header, populated by egress Envoy, when deriving timeout for upstream cluster. -* router: added new :ref:`retriable request headers ` to route configuration, to allow limiting buffering for retries and shadowing. -* router: added new :ref:`retriable request headers ` to retry policies. Retries can now be configured to only trigger on request header match. -* router: added the ability to match a route based on whether a TLS certificate has been - :ref:`presented ` by the - downstream connection. -* router check tool: added coverage reporting & enforcement. -* router check tool: added comprehensive coverage reporting. -* router check tool: added deprecated field check. -* router check tool: added flag for only printing results of failed tests. -* router check tool: added support for outputting missing tests in the detailed coverage report. -* router check tool: added coverage reporting for direct response routes. -* runtime: allows for the ability to parse boolean values. -* runtime: allows for the ability to parse integers as double values and vice-versa. -* sds: added :ref:`session_ticket_keys_sds_secret_config ` for loading TLS Session Ticket Encryption Keys using SDS API. -* server: added a post initialization lifecycle event, in addition to the existing startup and shutdown events. -* server: added :ref:`per-handler listener stats ` and - :ref:`per-worker watchdog stats ` to help diagnosing event - loop imbalance and general performance issues. -* stats: added unit support to histogram. -* tcp_proxy: the default :ref:`idle_timeout - ` is now 1 hour. -* thrift_proxy: fixed crashing bug on invalid transport/protocol framing. -* thrift_proxy: added support for stripping service name from method when using the multiplexed protocol. -* tls: added verification of IP address SAN fields in certificates against configured SANs in the certificate validation context. -* tracing: added support to the Zipkin reporter for sending list of spans as Zipkin JSON v2 and protobuf message over HTTP. - certificate validation context. -* tracing: added tags for gRPC response status and message. -* tracing: added :ref:`max_path_tag_length ` to support customizing the length of the request path included in the extracted `http.url `_ tag. -* upstream: added :ref:`an option ` that allows draining HTTP, TCP connection pools on cluster membership change. -* upstream: added :ref:`transport_socket_matches `, support using different transport socket config when connecting to different upstream endpoints within a cluster. -* upstream: added network filter chains to upstream connections, see :ref:`filters`. -* upstream: added new :ref:`failure-percentage based outlier detection` mode. -* upstream: uses p2c to select hosts for least-requests load balancers if all host weights are the same, even in cases where weights are not equal to 1. -* upstream: added :ref:`fail_traffic_on_panic ` to allow failing all requests to a cluster during panic state. -* zookeeper: parses responses and emits latency stats. - -1.11.2 (October 8, 2019) -======================== -* http: fixed CVE-2019-15226 by adding a cached byte size in HeaderMap. -* http: added :ref:`max headers count ` for http connections. The default limit is 100. -* upstream: runtime feature `envoy.reloadable_features.max_response_headers_count` overrides the default limit for upstream :ref:`max headers count ` -* http: added :ref:`common_http_protocol_options ` - Runtime feature `envoy.reloadable_features.max_request_headers_count` overrides the default limit for downstream :ref:`max headers count ` -* regex: backported safe regex matcher fix for CVE-2019-15225. - -1.11.1 (August 13, 2019) -======================== -* http: added mitigation of client initiated attacks that result in flooding of the downstream HTTP/2 connections. Those attacks can be logged at the "warning" level when the runtime feature `http.connection_manager.log_flood_exception` is enabled. The runtime setting defaults to disabled to avoid log spam when under attack. -* http: added :ref:`inbound_empty_frames_flood ` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the limit on consecutive inbound frames with an empty payload and no end stream flag. The limit is configured by setting the :ref:`max_consecutive_inbound_frames_with_empty_payload config setting `. - Runtime feature `envoy.reloadable_features.http2_protocol_options.max_consecutive_inbound_frames_with_empty_payload` overrides :ref:`max_consecutive_inbound_frames_with_empty_payload setting `. Large override value (i.e. 2147483647) effectively disables mitigation of inbound frames with empty payload. -* http: added :ref:`inbound_priority_frames_flood ` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the limit on inbound PRIORITY frames. The limit is configured by setting the :ref:`max_inbound_priority_frames_per_stream config setting `. - Runtime feature `envoy.reloadable_features.http2_protocol_options.max_inbound_priority_frames_per_stream` overrides :ref:`max_inbound_priority_frames_per_stream setting `. Large override value effectively disables flood mitigation of inbound PRIORITY frames. -* http: added :ref:`inbound_window_update_frames_flood ` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the limit on inbound WINDOW_UPDATE frames. The limit is configured by setting the :ref:`max_inbound_window_update_frames_per_data_frame_sent config setting `. - Runtime feature `envoy.reloadable_features.http2_protocol_options.max_inbound_window_update_frames_per_data_frame_sent` overrides :ref:`max_inbound_window_update_frames_per_data_frame_sent setting `. Large override value effectively disables flood mitigation of inbound WINDOW_UPDATE frames. -* http: added :ref:`outbound_flood ` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the outbound queue limit. The limit is configured by setting the :ref:`max_outbound_frames config setting ` - Runtime feature `envoy.reloadable_features.http2_protocol_options.max_outbound_frames` overrides :ref:`max_outbound_frames config setting `. Large override value effectively disables flood mitigation of outbound frames of all types. -* http: added :ref:`outbound_control_flood ` counter stat to the HTTP/2 codec stats, for tracking number of connections terminated for exceeding the outbound queue limit for PING, SETTINGS and RST_STREAM frames. The limit is configured by setting the :ref:`max_outbound_control_frames config setting `. - Runtime feature `envoy.reloadable_features.http2_protocol_options.max_outbound_control_frames` overrides :ref:`max_outbound_control_frames config setting `. Large override value effectively disables flood mitigation of outbound frames of types PING, SETTINGS and RST_STREAM. -* http: enabled strict validation of HTTP/2 messaging. Previous behavior can be restored using :ref:`stream_error_on_invalid_http_messaging config setting `. - Runtime feature `envoy.reloadable_features.http2_protocol_options.stream_error_on_invalid_http_messaging` overrides :ref:`stream_error_on_invalid_http_messaging config setting `. - -1.11.0 (July 11, 2019) -====================== -* access log: added a new field for downstream TLS session ID to file and gRPC access logger. -* access log: added a new field for route name to file and gRPC access logger. -* access log: added a new field for response code details in :ref:`file access logger` and :ref:`gRPC access logger`. -* access log: added several new variables for exposing information about the downstream TLS connection to :ref:`file access logger` and :ref:`gRPC access logger`. -* access log: added a new flag for request rejected due to failed strict header check. -* admin: the administration interface now includes a :ref:`/ready endpoint ` for easier readiness checks. -* admin: extend :ref:`/runtime_modify endpoint ` to support parameters within the request body. -* admin: the :ref:`/listener endpoint ` now returns :ref:`listeners.proto` which includes listener names and ports. -* admin: added host priority to :http:get:`/clusters` and :http:get:`/clusters?format=json` endpoint response -* admin: the :ref:`/clusters endpoint ` now shows hostname - for each host, useful for DNS based clusters. -* api: track and report requests issued since last load report. -* build: releases are built with Clang and linked with LLD. -* config: added :ref:stats_server_version_override` ` in bootstrap, that can be used to override :ref:`server.version statistic `. -* control-plane: management servers can respond with HTTP 304 to indicate that config is up to date for Envoy proxies polling a :ref:`REST API Config Type ` -* csrf: added support for whitelisting additional source origins. -* dns: added support for getting DNS record TTL which is used by STRICT_DNS/LOGICAL_DNS cluster as DNS refresh rate. -* dubbo_proxy: support the :ref:`dubbo proxy filter `. -* dynamo_request_parser: adding support for transactions. Adds check for new types of dynamodb operations (TransactWriteItems, TransactGetItems) and awareness for new types of dynamodb errors (IdempotentParameterMismatchException, TransactionCanceledException, TransactionInProgressException). -* eds: added support to specify max time for which endpoints can be used :ref:`gRPC filter `. -* eds: removed max limit for `load_balancing_weight`. -* event: added :ref:`loop duration and poll delay statistics `. -* ext_authz: added a `x-envoy-auth-partial-body` metadata header set to `false|true` indicating if there is a partial body sent in the authorization request message. -* ext_authz: added configurable status code that allows customizing HTTP responses on filter check status errors. -* ext_authz: added option to `ext_authz` that allows the filter clearing route cache. -* grpc-json: added support for :ref:`auto mapping - `. -* health check: added :ref:`initial jitter ` to add jitter to the first health check in order to prevent thundering herd on Envoy startup. -* hot restart: stats are no longer shared between hot restart parent/child via shared memory, but rather by RPC. Hot restart version incremented to 11. -* http: added the ability to pass a URL encoded PEM encoded peer certificate chain in the - :ref:`config_http_conn_man_headers_x-forwarded-client-cert` header. -* http: fixed a bug where large unbufferable responses were not tracked in stats and logs correctly. -* http: fixed a crashing bug where gRPC local replies would cause segfaults when upstream access logging was on. -* http: mitigated a race condition with the :ref:`delayed_close_timeout` where it could trigger while actively flushing a pending write buffer for a downstream connection. -* http: added support for :ref:`preserve_external_request_id` that represents whether the x-request-id should not be reset on edge entry inside mesh -* http: changed `sendLocalReply` to send percent-encoded `GrpcMessage`. -* http: added a :ref:header_prefix` ` configuration option to allow Envoy to send and process x-custom- prefixed headers rather than x-envoy. -* http: added :ref:`dynamic forward proxy ` support. -* http: tracking the active stream and dumping state in Envoy crash handlers. This can be disabled by building with `--define disable_object_dump_on_signal_trace=disabled` -* jwt_authn: make filter's parsing of JWT more flexible, allowing syntax like ``jwt=eyJhbGciOiJS...ZFnFIw,extra=7,realm=123`` -* listener: added :ref:`source IP ` - and :ref:`source port ` filter - chain matching. -* lua: exposed functions to Lua to verify digital signature. -* original_src filter: added the :ref:`filter`. -* outlier_detector: added configuration :ref:`outlier_detection.split_external_local_origin_errors` to distinguish locally and externally generated errors. See :ref:`arch_overview_outlier_detection` for full details. -* rbac: migrated from v2alpha to v2. -* redis: add support for Redis cluster custom cluster type. -* redis: automatically route commands using cluster slots for Redis cluster. -* redis: added :ref:`prefix routing ` to enable routing commands based on their key's prefix to different upstream. -* redis: added :ref:`request mirror policy ` to enable shadow traffic and/or dual writes. -* redis: add support for zpopmax and zpopmin commands. -* redis: added - :ref:`max_buffer_size_before_flush ` to batch commands together until the encoder buffer hits a certain size, and - :ref:`buffer_flush_timeout ` to control how quickly the buffer is flushed if it is not full. -* redis: added auth support :ref:`downstream_auth_password ` for downstream client authentication, and :ref:`auth_password ` to configure authentication passwords for upstream server clusters. -* retry: added a retry predicate that :ref:`rejects canary hosts. ` -* router: add support for configuring a :ref:`gRPC timeout offset ` on incoming requests. -* router: added ability to control retry back-off intervals via :ref:`retry policy `. -* router: added ability to issue a hedged retry in response to a per try timeout via a :ref:`hedge policy `. -* router: added a route name field to each http route in route.Route list -* router: added several new variables for exposing information about the downstream TLS connection via :ref:`header - formatters `. -* router: per try timeouts will no longer start before the downstream request has been received in full by the router.This ensures that the per try timeout does not account for slow downstreams and that will not start before the global timeout. -* router: added :ref:`RouteAction's auto_host_rewrite_header ` to allow upstream host header substitution with some other header's value -* router: added support for UPSTREAM_REMOTE_ADDRESS :ref:`header formatter - `. -* router: add ability to reject a request that includes invalid values for - headers configured in :ref:`strict_check_headers ` -* runtime: added support for :ref:`flexible layering configuration - `. -* runtime: added support for statically :ref:`specifying the runtime in the bootstrap configuration - `. -* runtime: :ref:`Runtime Discovery Service (RTDS) ` support added to layered runtime configuration. -* sandbox: added :ref:`CSRF sandbox `. -* server: ``--define manual_stamp=manual_stamp`` was added to allow server stamping outside of binary rules. - more info in the `bazel docs `_. -* server: added :ref:`server state ` statistic. -* server: added :ref:`initialization_time_ms` statistic. -* subset: added :ref:`list_as_any` option to - the subset lb which allows matching metadata against any of the values in a list value - on the endpoints. -* tools: added :repo:`proto ` support for :ref:`router check tool ` tests. -* tracing: add trace sampling configuration to the route, to override the route level. -* upstream: added :ref:`upstream_cx_pool_overflow ` for the connection pool circuit breaker. -* upstream: an EDS management server can now force removal of a host that is still passing active - health checking by first marking the host as failed via EDS health check and subsequently removing - it in a future update. This is a mechanism to work around a race condition in which an EDS - implementation may remove a host before it has stopped passing active HC, thus causing the host - to become stranded until a future update. -* upstream: added :ref:`an option ` - that allows ignoring new hosts for the purpose of load balancing calculations until they have - been health checked for the first time. -* upstream: added runtime error checking to prevent setting dns type to STRICT_DNS or LOGICAL_DNS when custom resolver name is specified. -* upstream: added possibility to override fallback_policy per specific selector in :ref:`subset load balancer `. -* upstream: the :ref:`logical DNS cluster ` now - displays the current resolved IP address in admin output instead of 0.0.0.0. - -1.10.0 (Apr 5, 2019) -==================== -* access log: added a new flag for upstream retry count exceeded. -* access log: added a :ref:`gRPC filter ` to allow filtering on gRPC status. -* access log: added a new flag for stream idle timeout. -* access log: added a new field for upstream transport failure reason in :ref:`file access logger` and - :ref:`gRPC access logger` for HTTP access logs. -* access log: added new fields for downstream x509 information (URI sans and subject) to file and gRPC access logger. -* admin: the admin server can now be accessed via HTTP/2 (prior knowledge). -* admin: changed HTTP response status code from 400 to 405 when attempting to GET a POST-only route (such as /quitquitquit). -* buffer: fix vulnerabilities when allocation fails. -* build: releases are built with GCC-7 and linked with LLD. -* build: dev docker images :ref:`have been split ` from tagged images for easier - discoverability in Docker Hub. Additionally, we now build images for point releases. -* config: added support of using google.protobuf.Any in opaque configs for extensions. -* config: logging warnings when deprecated fields are in use. -* config: removed deprecated --v2-config-only from command line config. -* config: removed deprecated_v1 sds_config from :ref:`Bootstrap config `. -* config: removed the deprecated_v1 config option from :ref:`ring hash `. -* config: removed REST_LEGACY as a valid :ref:`ApiType `. -* config: finish cluster warming only when a named response i.e. ClusterLoadAssignment associated to the cluster being warmed comes in the EDS response. This is a behavioural change from the current implementation where warming of cluster completes on missing load assignments also. -* config: use Envoy cpuset size to set the default number or worker threads if :option:`--cpuset-threads` is enabled. -* config: added support for :ref:`initial_fetch_timeout `. The timeout is disabled by default. -* cors: added :ref:`filter_enabled & shadow_enabled RuntimeFractionalPercent flags ` to filter. -* csrf: added :ref:`CSRF filter `. -* ext_authz: added support for buffering request body. -* ext_authz: migrated from v2alpha to v2 and improved docs. -* ext_authz: added a configurable option to make the gRPC service cross-compatible with V2Alpha. Note that this feature is already deprecated. It should be used for a short time, and only when transitioning from alpha to V2 release version. -* ext_authz: migrated from v2alpha to v2 and improved the documentation. -* ext_authz: authorization request and response configuration has been separated into two distinct objects: :ref:`authorization request - ` and :ref:`authorization response - `. In addition, :ref:`client headers - ` and :ref:`upstream headers - ` replaces the previous *allowed_authorization_headers* object. - All the control header lists now support :ref:`string matcher ` instead of standard string. -* fault: added the :ref:`max_active_faults - ` setting, as well as - :ref:`statistics ` for the number of active faults - and the number of faults the overflowed. -* fault: added :ref:`response rate limit - ` fault injection. -* fault: added :ref:`HTTP header fault configuration - ` to the HTTP fault filter. -* governance: extending Envoy deprecation policy from 1 release (0-3 months) to 2 releases (3-6 months). -* health check: expected response codes in http health checks are now :ref:`configurable `. -* http: added new grpc_http1_reverse_bridge filter for converting gRPC requests into HTTP/1.1 requests. -* http: fixed a bug where Content-Length:0 was added to HTTP/1 204 responses. -* http: added :ref:`max request headers size `. The default behaviour is unchanged. -* http: added modifyDecodingBuffer/modifyEncodingBuffer to allow modifying the buffered request/response data. -* http: added encodeComplete/decodeComplete. These are invoked at the end of the stream, after all data has been encoded/decoded respectively. Default implementation is a no-op. -* outlier_detection: added support for :ref:`outlier detection event protobuf-based logging `. -* mysql: added a MySQL proxy filter that is capable of parsing SQL queries over MySQL wire protocol. Refer to :ref:`MySQL proxy` for more details. -* performance: new buffer implementation (disabled by default; to test it, add "--use-libevent-buffers 0" to the command-line arguments when starting Envoy). -* jwt_authn: added :ref:`filter_state_rules ` to allow specifying requirements from filterState by other filters. -* ratelimit: removed deprecated rate limit configuration from bootstrap. -* redis: added :ref:`hashtagging ` to guarantee a given key's upstream. -* redis: added :ref:`latency stats ` for commands. -* redis: added :ref:`success and error stats ` for commands. -* redis: migrate hash function for host selection to `MurmurHash2 `_ from std::hash. MurmurHash2 is compatible with std::hash in GNU libstdc++ 3.4.20 or above. This is typically the case when compiled on Linux and not macOS. -* redis: added :ref:`latency_in_micros ` to specify the redis commands stats time unit in microseconds. -* router: added ability to configure a :ref:`retry policy ` at the - virtual host level. -* router: added reset reason to response body when upstream reset happens. After this change, the response body will be of the form `upstream connect error or disconnect/reset before headers. reset reason:` -* router: added :ref:`rq_reset_after_downstream_response_started ` counter stat to router stats. -* router: added per-route configuration of :ref:`internal redirects `. -* router: removed deprecated route-action level headers_to_add/remove. -* router: made :ref:`max retries header ` take precedence over the number of retries in route and virtual host retry policies. -* router: added support for prefix wildcards in :ref:`virtual host domains` -* stats: added support for histograms in prometheus -* stats: added usedonly flag to prometheus stats to only output metrics which have been - updated at least once. -* stats: added gauges tracking remaining resources before circuit breakers open. -* tap: added new alpha :ref:`HTTP tap filter `. -* tls: enabled TLS 1.3 on the server-side (non-FIPS builds). -* upstream: add hash_function to specify the hash function for :ref:`ring hash` as either xxHash or `murmurHash2 `_. MurmurHash2 is compatible with std::hash in GNU libstdc++ 3.4.20 or above. This is typically the case when compiled on Linux and not macOS. -* upstream: added :ref:`degraded health value` which allows - routing to certain hosts only when there are insufficient healthy hosts available. -* upstream: add cluster factory to allow creating and registering :ref:`custom cluster type`. -* upstream: added a :ref:`circuit breaker ` to limit the number of concurrent connection pools in use. -* tracing: added :ref:`verbose ` to support logging annotations on spans. -* upstream: added support for host weighting and :ref:`locality weighting ` in the :ref:`ring hash load balancer `, and added a :ref:`maximum_ring_size` config parameter to strictly bound the ring size. -* zookeeper: added a ZooKeeper proxy filter that parses ZooKeeper messages (requests/responses/events). - Refer to :ref:`ZooKeeper proxy` for more details. -* upstream: added configuration option to select any host when the fallback policy fails. -* upstream: stopped incrementing upstream_rq_total for HTTP/1 conn pool when request is circuit broken. - -1.9.1 (Apr 2, 2019) -=================== -* http: fixed CVE-2019-9900 by rejecting HTTP/1.x headers with embedded NUL characters. -* http: fixed CVE-2019-9901 by normalizing HTTP paths prior to routing or L7 data plane processing. - This defaults off and is configurable via either HTTP connection manager :ref:`normalize_path - ` - or the :ref:`runtime `. - -1.9.0 (Dec 20, 2018) -==================== -* access log: added a :ref:`JSON logging mode ` to output access logs in JSON format. -* access log: added dynamic metadata to access log messages streamed over gRPC. -* access log: added DOWNSTREAM_CONNECTION_TERMINATION. -* admin: :http:post:`/logging` now responds with 200 while there are no params. -* admin: added support for displaying subject alternate names in :ref:`certs` end point. -* admin: added host weight to the :http:get:`/clusters?format=json` end point response. -* admin: :http:get:`/server_info` now responds with a JSON object instead of a single string. -* admin: :http:get:`/server_info` now exposes what stage of initialization the server is currently in. -* admin: added support for displaying command line options in :http:get:`/server_info` end point. -* circuit-breaker: added cx_open, rq_pending_open, rq_open and rq_retry_open gauges to expose live - state via :ref:`circuit breakers statistics `. -* cluster: set a default of 1s for :ref:`option `. -* config: removed support for the v1 API. -* config: added support for :ref:`rate limiting` discovery request calls. -* cors: added :ref:`invalid/valid stats ` to filter. -* ext-authz: added support for providing per route config - optionally disable the filter and provide context extensions. -* fault: removed integer percentage support. -* grpc-json: added support for :ref:`ignoring query parameters - `. -* health check: added :ref:`logging health check failure events `. -* health check: added ability to set :ref:`authority header value - ` for gRPC health check. -* http: added HTTP/2 WebSocket proxying via :ref:`extended CONNECT `. -* http: added limits to the number and length of header modifications in all fields request_headers_to_add and response_headers_to_add. These limits are very high and should only be used as a last-resort safeguard. -* http: added support for a :ref:`request timeout `. The timeout is disabled by default. -* http: no longer adding whitespace when appending X-Forwarded-For headers. **Warning**: this is not - compatible with 1.7.0 builds prior to `9d3a4eb4ac44be9f0651fcc7f87ad98c538b01ee `_. - See `#3611 `_ for details. -* http: augmented the `sendLocalReply` filter API to accept an optional `GrpcStatus` - value to override the default HTTP to gRPC status mapping. -* http: no longer close the TCP connection when a HTTP/1 request is retried due - to a response with empty body. -* http: added support for more gRPC content-type headers in :ref:`gRPC bridge filter `, like application/grpc+proto. -* listeners: all listener filters are now governed by the :ref:`listener_filters_timeout - ` setting. The hard coded 15s timeout in - the :ref:`TLS inspector listener filter ` is superseded by - this setting. -* listeners: added the ability to match :ref:`FilterChain ` using :ref:`source_type `. -* load balancer: added a `configuration ` option to specify the number of choices made in P2C. -* logging: added missing [ in log prefix. -* mongo_proxy: added :ref:`dynamic metadata `. -* network: removed the reference to `FilterState` in `Connection` in favor of `StreamInfo`. -* rate-limit: added :ref:`configuration ` - to specify whether the `GrpcStatus` status returned should be `RESOURCE_EXHAUSTED` or - `UNAVAILABLE` when a gRPC call is rate limited. -* rate-limit: removed support for the legacy ratelimit service and made the data-plane-api - :ref:`rls.proto ` based implementation default. -* rate-limit: removed the deprecated cluster_name attribute in :ref:`rate limit service configuration `. -* rate-limit: added :ref:`rate_limit_service ` configuration to filters. -* rbac: added dynamic metadata to the network level filter. -* rbac: added support for permission matching by :ref:`requested server name `. -* redis: static cluster configuration is no longer required. Redis proxy will work with clusters - delivered via CDS. -* router: added ability to configure arbitrary :ref:`retriable status codes. ` -* router: added ability to set attempt count in upstream requests, see :ref:`virtual host's include request - attempt count flag `. -* router: added internal :ref:`grpc-retry-on ` policy. -* router: added :ref:`scheme_redirect ` and - :ref:`port_redirect ` to define the respective - scheme and port rewriting RedirectAction. -* router: when :ref:`max_grpc_timeout ` - is set, Envoy will now add or update the grpc-timeout header to reflect Envoy's expected timeout. -* router: per try timeouts now starts when an upstream stream is ready instead of when the request has - been fully decoded by Envoy. -* router: added support for not retrying :ref:`rate limited requests`. Rate limit filter now sets the :ref:`x-envoy-ratelimited` - header so the rate limited requests that may have been retried earlier will not be retried with this change. -* router: added support for enabling upgrades on a :ref:`per-route ` basis. -* router: support configuring a default fraction of mirror traffic via - :ref:`runtime_fraction `. -* sandbox: added :ref:`cors sandbox `. -* server: added `SIGINT` (Ctrl-C) handler to gracefully shutdown Envoy like `SIGTERM`. -* stats: added :ref:`stats_matcher ` to the bootstrap config for granular control of stat instantiation. -* stream: renamed the `RequestInfo` namespace to `StreamInfo` to better match - its behaviour within TCP and HTTP implementations. -* stream: renamed `perRequestState` to `filterState` in `StreamInfo`. -* stream: added `downstreamDirectRemoteAddress` to `StreamInfo`. -* thrift_proxy: introduced thrift rate limiter filter. -* tls: added ssl.curves., ssl.sigalgs. and ssl.versions. to - :ref:`listener metrics ` to track TLS algorithms and versions in use. -* tls: added support for :ref:`client-side session resumption `. -* tls: added support for CRLs in :ref:`trusted_ca `. -* tls: added support for :ref:`multiple server TLS certificates `. -* tls: added support for :ref:`password encrypted private keys `. -* tls: added the ability to build :ref:`BoringSSL FIPS ` using ``--define boringssl=fips`` Bazel option. -* tls: removed support for ECDSA certificates with curves other than P-256. -* tls: removed support for RSA certificates with keys smaller than 2048-bits. -* tracing: added support to the Zipkin tracer for the :ref:`b3 ` single header format. -* tracing: added support for :ref:`Datadog ` tracer. -* upstream: added :ref:`scale_locality_weight` to enable - scaling locality weights by number of hosts removed by subset lb predicates. -* upstream: changed how load calculation for :ref:`priority levels` and :ref:`panic thresholds` interact. As long as normalized total health is 100% panic thresholds are disregarded. -* upstream: changed the default hash for :ref:`ring hash ` from std::hash to `xxHash `_. -* upstream: when using active health checking and STRICT_DNS with several addresses that resolve - to the same hosts, Envoy will now health check each host independently. - -1.8.0 (Oct 4, 2018) -=================== -* access log: added :ref:`response flag filter ` - to filter based on the presence of Envoy response flags. -* access log: added RESPONSE_DURATION and RESPONSE_TX_DURATION. -* access log: added REQUESTED_SERVER_NAME for SNI to tcp_proxy and http -* admin: added :http:get:`/hystrix_event_stream` as an endpoint for monitoring envoy's statistics - through `Hystrix dashboard `_. -* cli: added support for :ref:`component log level ` command line option for configuring log levels of individual components. -* cluster: added :ref:`option ` to merge - health check/weight/metadata updates within the given duration. -* config: regex validation added to limit to a maximum of 1024 characters. -* config: v1 disabled by default. v1 support remains available until October via flipping --v2-config-only=false. -* config: v1 disabled by default. v1 support remains available until October via deprecated flag --allow-deprecated-v1-api. -* config: fixed stat inconsistency between xDS and ADS implementation. :ref:`update_failure ` - stat is incremented in case of network failure and :ref:`update_rejected ` stat is incremented - in case of schema/validation error. -* config: added a stat :ref:`connected_state ` that indicates current connected state of Envoy with - management server. -* ext_authz: added support for configuring additional :ref:`authorization headers ` - to be sent from Envoy to the authorization service. -* fault: added support for fractional percentages in :ref:`FaultDelay ` - and in :ref:`FaultAbort `. -* grpc-json: added support for building HTTP response from - `google.api.HttpBody `_. -* health check: added support for :ref:`custom health check `. -* health check: added support for :ref:`specifying jitter as a percentage `. -* health_check: added support for :ref:`health check event logging `. -* health_check: added :ref:`timestamp ` - to the :ref:`health check event ` definition. -* health_check: added support for specifying :ref:`custom request headers ` - to HTTP health checker requests. -* http: added support for a :ref:`per-stream idle timeout - `. This applies at both :ref:`connection manager - ` - and :ref:`per-route granularity `. The timeout - defaults to 5 minutes; if you have other timeouts (e.g. connection idle timeout, upstream - response per-retry) that are longer than this in duration, you may want to consider setting a - non-default per-stream idle timeout. -* http: added upstream_rq_completed counter for :ref:`total requests completed ` to dynamic HTTP counters. -* http: added downstream_rq_completed counter for :ref:`total requests completed `, including on a :ref:`per-listener basis `. -* http: added generic :ref:`Upgrade support - `. -* http: better handling of HEAD requests. Now sending transfer-encoding: chunked rather than content-length: 0. -* http: fixed missing support for appending to predefined inline headers, e.g. - *authorization*, in features that interact with request and response headers, - e.g. :ref:`request_headers_to_add - `. For example, a - request header *authorization: token1* will appear as *authorization: - token1,token2*, after having :ref:`request_headers_to_add - ` with *authorization: - token2* applied. -* http: response filters not applied to early error paths such as http_parser generated 400s. -* http: restrictions added to reject *:*-prefixed pseudo-headers in :ref:`custom - request headers `. -* http: :ref:`hpack_table_size ` now controls - dynamic table size of both: encoder and decoder. -* http: added support for removing request headers using :ref:`request_headers_to_remove - `. -* http: added support for a :ref:`delayed close timeout` to mitigate race conditions when closing connections to downstream HTTP clients. The timeout defaults to 1 second. -* jwt-authn filter: add support for per route JWT requirements. -* listeners: added the ability to match :ref:`FilterChain ` using - :ref:`destination_port ` and - :ref:`prefix_ranges `. -* lua: added :ref:`connection() ` wrapper and *ssl()* API. -* lua: added :ref:`streamInfo() ` wrapper and *protocol()* API. -* lua: added :ref:`streamInfo():dynamicMetadata() ` API. -* network: introduced :ref:`sni_cluster ` network filter that forwards connections to the - upstream cluster specified by the SNI value presented by the client during a TLS handshake. -* proxy_protocol: added support for HAProxy Proxy Protocol v2 (AF_INET/AF_INET6 only). -* ratelimit: added support for :repo:`api/envoy/service/ratelimit/v2/rls.proto`. - Lyft's reference implementation of the `ratelimit `_ service also supports the data-plane-api proto as of v1.1.0. - Envoy can use either proto to send client requests to a ratelimit server with the use of the - `use_data_plane_proto` boolean flag in the ratelimit configuration. - Support for the legacy proto `source/common/ratelimit/ratelimit.proto` is deprecated and will be removed at the start of the 1.9.0 release cycle. -* ratelimit: added :ref:`failure_mode_deny ` option to control traffic flow in - case of rate limit service error. -* rbac config: added a :ref:`principal_name ` field and - removed the old `name` field to give more flexibility for matching certificate identity. -* rbac network filter: a :ref:`role-based access control network filter ` has been added. -* rest-api: added ability to set the :ref:`request timeout ` for REST API requests. -* route checker: added v2 config support and removed support for v1 configs. -* router: added ability to set request/response headers at the :ref:`envoy_api_msg_route.Route` level. -* stats: added :ref:`option to configure the DogStatsD metric name prefix` to DogStatsdSink. -* tcp_proxy: added support for :ref:`weighted clusters `. -* thrift_proxy: introduced thrift routing, moved configuration to correct location -* thrift_proxy: introduced thrift configurable decoder filters -* tls: implemented :ref:`Secret Discovery Service `. -* tracing: added support for configuration of :ref:`tracing sampling - `. -* upstream: added configuration option to the subset load balancer to take locality weights into account when - selecting a host from a subset. -* upstream: require opt-in to use the :ref:`x-envoy-original-dst-host ` header - for overriding destination address when using the :ref:`Original Destination ` - load balancing policy. - -1.7.0 (Jun 21, 2018) -==================== -* access log: added ability to log response trailers. -* access log: added ability to format START_TIME. -* access log: added DYNAMIC_METADATA :ref:`access log formatter `. -* access log: added :ref:`HeaderFilter ` - to filter logs based on request headers. -* access log: added `%([1-9])?f` as one of START_TIME specifiers to render subseconds. -* access log: gRPC Access Log Service (ALS) support added for :ref:`HTTP access logs - `. -* access log: improved WebSocket logging. -* admin: added :http:get:`/config_dump` for dumping the current configuration and associated xDS - version information (if applicable). -* admin: added :http:get:`/clusters?format=json` for outputing a JSON-serialized proto detailing - the current status of all clusters. -* admin: added :http:get:`/stats/prometheus` as an alternative endpoint for getting stats in prometheus format. -* admin: added :ref:`/runtime_modify endpoint ` to add or change runtime values. -* admin: mutations must be sent as POSTs, rather than GETs. Mutations include: - :http:post:`/cpuprofiler`, :http:post:`/healthcheck/fail`, :http:post:`/healthcheck/ok`, - :http:post:`/logging`, :http:post:`/quitquitquit`, :http:post:`/reset_counters`, - :http:post:`/runtime_modify?key1=value1&key2=value2&keyN=valueN`. -* admin: removed `/routes` endpoint; route configs can now be found at the :ref:`/config_dump endpoint `. -* buffer filter: the buffer filter can be optionally - :ref:`disabled ` or - :ref:`overridden ` with - route-local configuration. -* cli: added --config-yaml flag to the Envoy binary. When set its value is interpreted as a yaml - representation of the bootstrap config and overrides --config-path. -* cluster: added :ref:`option ` - to close tcp_proxy upstream connections when health checks fail. -* cluster: added :ref:`option ` to drain - connections from hosts after they are removed from service discovery, regardless of health status. -* cluster: fixed bug preventing the deletion of all endpoints in a priority -* debug: added symbolized stack traces (where supported) -* ext-authz filter: added support to raw HTTP authorization. -* ext-authz filter: added support to gRPC responses to carry HTTP attributes. -* grpc: support added for the full set of :ref:`Google gRPC call credentials - `. -* gzip filter: added :ref:`stats ` to the filter. -* gzip filter: sending *accept-encoding* header as *identity* no longer compresses the payload. -* health check: added ability to set :ref:`additional HTTP headers - ` for HTTP health check. -* health check: added support for EDS delivered :ref:`endpoint health status - `. -* health check: added interval overrides for health state transitions from :ref:`healthy to unhealthy - `, :ref:`unhealthy to healthy - ` and for subsequent checks on - :ref:`unhealthy hosts `. -* health check: added support for :ref:`custom health check `. -* health check: health check connections can now be configured to use http/2. -* health check http filter: added - :ref:`generic header matching ` - to trigger health check response. Deprecated the endpoint option. -* http: filters can now optionally support - :ref:`virtual host `, - :ref:`route `, and - :ref:`weighted cluster ` - local configuration. -* http: added the ability to pass DNS type Subject Alternative Names of the client certificate in the - :ref:`config_http_conn_man_headers_x-forwarded-client-cert` header. -* http: local responses to gRPC requests are now sent as trailers-only gRPC responses instead of plain HTTP responses. - Notably the HTTP response code is always "200" in this case, and the gRPC error code is carried in "grpc-status" - header, optionally accompanied with a text message in "grpc-message" header. -* http: added support for :ref:`via header - ` - append. -* http: added a :ref:`configuration option - ` - to elide *x-forwarded-for* header modifications. -* http: fixed a bug in inline headers where addCopy and addViaMove didn't add header values when - encountering inline headers with multiple instances. -* listeners: added :ref:`tcp_fast_open_queue_length ` option. -* listeners: added the ability to match :ref:`FilterChain ` using - :ref:`application_protocols ` - (e.g. ALPN for TLS protocol). -* listeners: `sni_domains` has been deprecated/renamed to :ref:`server_names `. -* listeners: removed restriction on all filter chains having identical filters. -* load balancer: added :ref:`weighted round robin - ` support. The round robin - scheduler now respects endpoint weights and also has improved fidelity across - picks. -* load balancer: :ref:`locality weighted load balancing - ` is now supported. -* load balancer: ability to configure zone aware load balancer settings :ref:`through the API - `. -* load balancer: the :ref:`weighted least request - ` load balancing algorithm has been improved - to have better balance when operating in weighted mode. -* logger: added the ability to optionally set the log format via the :option:`--log-format` option. -* logger: all :ref:`logging levels ` can be configured - at run-time: trace debug info warning error critical. -* rbac http filter: a :ref:`role-based access control http filter ` has been added. -* router: the behavior of per-try timeouts have changed in the case where a portion of the response has - already been proxied downstream when the timeout occurs. Previously, the response would be reset - leading to either an HTTP/2 reset or an HTTP/1 closed connection and a partial response. Now, the - timeout will be ignored and the response will continue to proxy up to the global request timeout. -* router: changed the behavior of :ref:`source IP routing ` - to ignore the source port. -* router: added an :ref:`prefix_match ` match type - to explicitly match based on the prefix of a header value. -* router: added an :ref:`suffix_match ` match type - to explicitly match based on the suffix of a header value. -* router: added an :ref:`present_match ` match type - to explicitly match based on a header's presence. -* router: added an :ref:`invert_match ` config option - which supports inverting all other match types to match based on headers which are not a desired value. -* router: allow :ref:`cookie routing ` to - generate session cookies. -* router: added START_TIME as one of supported variables in :ref:`header - formatters `. -* router: added a :ref:`max_grpc_timeout ` - config option to specify the maximum allowable value for timeouts decoded from gRPC header field - `grpc-timeout`. -* router: added a :ref:`configuration option - ` to disable *x-envoy-* - header generation. -* router: added 'unavailable' to the retriable gRPC status codes that can be specified - through :ref:`x-envoy-retry-grpc-on `. -* sockets: added :ref:`tap transport socket extension ` to support - recording plain text traffic and PCAP generation. -* sockets: added `IP_FREEBIND` socket option support for :ref:`listeners - ` and upstream connections via - :ref:`cluster manager wide - ` and - :ref:`cluster specific ` options. -* sockets: added `IP_TRANSPARENT` socket option support for :ref:`listeners - `. -* sockets: added `SO_KEEPALIVE` socket option for upstream connections - :ref:`per cluster `. -* stats: added support for histograms. -* stats: added :ref:`option to configure the statsd prefix`. -* stats: updated stats sink interface to flush through a single call. -* tls: added support for - :ref:`verify_certificate_spki `. -* tls: added support for multiple - :ref:`verify_certificate_hash ` - values. -* tls: added support for using - :ref:`verify_certificate_spki ` - and :ref:`verify_certificate_hash ` - without :ref:`trusted_ca `. -* tls: added support for allowing expired certificates with - :ref:`allow_expired_certificate `. -* tls: added support for :ref:`renegotiation ` - when acting as a client. -* tls: removed support for legacy SHA-2 CBC cipher suites. -* tracing: the sampling decision is now delegated to the tracers, allowing the tracer to decide when and if - to use it. For example, if the :ref:`x-b3-sampled ` header - is supplied with the client request, its value will override any sampling decision made by the Envoy proxy. -* websocket: support configuring idle_timeout and max_connect_attempts. -* upstream: added support for host override for a request in :ref:`Original destination host request header `. -* header to metadata: added :ref:`HTTP Header to Metadata filter`. - -1.6.0 (March 20, 2018) -====================== - -* access log: added DOWNSTREAM_REMOTE_ADDRESS, DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT, and - DOWNSTREAM_LOCAL_ADDRESS :ref:`access log formatters `. - DOWNSTREAM_ADDRESS access log formatter has been deprecated. -* access log: added less than or equal (LE) :ref:`comparison filter - `. -* access log: added configuration to :ref:`runtime filter - ` to set default sampling rate, divisor, - and whether to use independent randomness or not. -* admin: added :ref:`/runtime ` admin endpoint to read the - current runtime values. -* build: added support for :repo:`building Envoy with exported symbols - `. This change allows scripts loaded with the Lua filter to - load shared object libraries such as those installed via `LuaRocks `_. -* config: added support for sending error details as - `grpc.rpc.Status `_ - in :ref:`DiscoveryRequest `. -* config: added support for :ref:`inline delivery ` of TLS - certificates and private keys. -* config: added restrictions for the backing :ref:`config sources ` - of xDS resources. For filesystem based xDS the file must exist at configuration time. For cluster - based xDS the backing cluster must be statically defined and be of non-EDS type. -* grpc: the Google gRPC C++ library client is now supported as specified in the :ref:`gRPC services - overview ` and :ref:`GrpcService `. -* grpc-json: added support for :ref:`inline descriptors - `. -* health check: added :ref:`gRPC health check ` - based on `grpc.health.v1.Health `_ - service. -* health check: added ability to set :ref:`host header value - ` for http health check. -* health check: extended the health check filter to support computation of the health check response - based on the :ref:`percentage of healthy servers in upstream clusters - `. -* health check: added setting for :ref:`no-traffic - interval`. -* http: added idle timeout for :ref:`upstream http connections - `. -* http: added support for :ref:`proxying 100-Continue responses - `. -* http: added the ability to pass a URL encoded PEM encoded peer certificate in the - :ref:`config_http_conn_man_headers_x-forwarded-client-cert` header. -* http: added support for trusting additional hops in the - :ref:`config_http_conn_man_headers_x-forwarded-for` request header. -* http: added support for :ref:`incoming HTTP/1.0 - `. -* hot restart: added SIGTERM propagation to children to :ref:`hot-restarter.py - `, which enables using it as a parent of containers. -* ip tagging: added :ref:`HTTP IP Tagging filter`. -* listeners: added support for :ref:`listening for both IPv4 and IPv6 - ` when binding to ::. -* listeners: added support for listening on :ref:`UNIX domain sockets - `. -* listeners: added support for :ref:`abstract unix domain sockets ` on - Linux. The abstract namespace can be used by prepending '@' to a socket path. -* load balancer: added cluster configuration for :ref:`healthy panic threshold - ` percentage. -* load balancer: added :ref:`Maglev ` consistent hash - load balancer. -* load balancer: added support for - :ref:`LocalityLbEndpoints` priorities. -* lua: added headers :ref:`replace() ` API. -* lua: extended to support :ref:`metadata object ` API. -* redis: added local `PING` support to the :ref:`Redis filter `. -* redis: added `GEORADIUS_RO` and `GEORADIUSBYMEMBER_RO` to the :ref:`Redis command splitter - ` whitelist. -* router: added DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT, DOWNSTREAM_LOCAL_ADDRESS, - DOWNSTREAM_LOCAL_ADDRESS_WITHOUT_PORT, PROTOCOL, and UPSTREAM_METADATA :ref:`header - formatters `. The CLIENT_IP header formatter - has been deprecated. -* router: added gateway-error :ref:`retry-on ` policy. -* router: added support for route matching based on :ref:`URL query string parameters - `. -* router: added support for more granular weighted cluster routing by allowing the :ref:`total_weight - ` to be specified in configuration. -* router: added support for :ref:`custom request/response headers - ` with mixed static and dynamic values. -* router: added support for :ref:`direct responses `. - I.e., sending a preconfigured HTTP response without proxying anywhere. -* router: added support for :ref:`HTTPS redirects - ` on specific routes. -* router: added support for :ref:`prefix_rewrite - ` for redirects. -* router: added support for :ref:`stripping the query string - ` for redirects. -* router: added support for downstream request/upstream response - :ref:`header manipulation ` in :ref:`weighted - cluster `. -* router: added support for :ref:`range based header matching - ` for request routing. -* squash: added support for the :ref:`Squash microservices debugger `. - Allows debugging an incoming request to a microservice in the mesh. -* stats: added metrics service API implementation. -* stats: added native :ref:`DogStatsd ` support. -* stats: added support for :ref:`fixed stats tag values - ` which will be added to all metrics. -* tcp proxy: added support for specifying a :ref:`metadata matcher - ` for upstream - clusters in the tcp filter. -* tcp proxy: improved TCP proxy to correctly proxy TCP half-close. -* tcp proxy: added :ref:`idle timeout - `. -* tcp proxy: access logs now bring an IP address without a port when using DOWNSTREAM_ADDRESS. - Use :ref:`DOWNSTREAM_REMOTE_ADDRESS ` instead. -* tracing: added support for dynamically loading an :ref:`OpenTracing tracer - `. -* tracing: when using the Zipkin tracer, it is now possible for clients to specify the sampling - decision (using the :ref:`x-b3-sampled ` header) and - have the decision propagated through to subsequently invoked services. -* tracing: when using the Zipkin tracer, it is no longer necessary to propagate the - :ref:`x-ot-span-context ` header. - See more on trace context propagation :ref:`here `. -* transport sockets: added transport socket interface to allow custom implementations of transport - sockets. A transport socket provides read and write logic with buffer encryption and decryption - (if applicable). The existing TLS implementation has been refactored with the interface. -* upstream: added support for specifying an :ref:`alternate stats name - ` while emitting stats for clusters. -* Many small bug fixes and performance improvements not listed. - -1.5.0 (December 4, 2017) -======================== - -* access log: added fields for :ref:`UPSTREAM_LOCAL_ADDRESS and DOWNSTREAM_ADDRESS - `. -* admin: added :ref:`JSON output ` for stats admin endpoint. -* admin: added basic :ref:`Prometheus output ` for stats admin - endpoint. Histograms are not currently output. -* admin: added ``version_info`` to the :ref:`/clusters admin endpoint`. -* config: the :ref:`v2 API ` is now considered production ready. -* config: added --v2-config-only CLI flag. -* cors: added :ref:`CORS filter `. -* health check: added :ref:`x-envoy-immediate-health-check-fail - ` header support. -* health check: added :ref:`reuse_connection ` option. -* http: added :ref:`per-listener stats `. -* http: end-to-end HTTP flow control is now complete across both connections, streams, and filters. -* load balancer: added :ref:`subset load balancer `. -* load balancer: added ring size and hash :ref:`configuration options - `. This used to be configurable via runtime. The runtime - configuration was deleted without deprecation as we are fairly certain no one is using it. -* log: added the ability to optionally log to a file instead of stderr via the - :option:`--log-path` option. -* listeners: added :ref:`drain_type ` option. -* lua: added experimental :ref:`Lua filter `. -* mongo filter: added :ref:`fault injection `. -* mongo filter: added :ref:`"drain close" ` support. -* outlier detection: added :ref:`HTTP gateway failure type `. - See :ref:`deprecated log ` - for outlier detection stats deprecations in this release. -* redis: the :ref:`redis proxy filter ` is now considered - production ready. -* redis: added :ref:`"drain close" ` functionality. -* router: added :ref:`x-envoy-overloaded ` support. -* router: added :ref:`regex ` route matching. -* router: added :ref:`custom request headers ` - for upstream requests. -* router: added :ref:`downstream IP hashing - ` for HTTP ketama routing. -* router: added :ref:`cookie hashing `. -* router: added :ref:`start_child_span ` option - to create child span for egress calls. -* router: added optional :ref:`upstream logs `. -* router: added complete :ref:`custom append/override/remove support - ` of request/response headers. -* router: added support to :ref:`specify response code during redirect - `. -* router: added :ref:`configuration ` - to return either a 404 or 503 if the upstream cluster does not exist. -* runtime: added :ref:`comment capability `. -* server: change default log level (:option:`-l`) to `info`. -* stats: maximum stat/name sizes and maximum number of stats are now variable via the - `--max-obj-name-len` and `--max-stats` options. -* tcp proxy: added :ref:`access logging `. -* tcp proxy: added :ref:`configurable connect retries - `. -* tcp proxy: enable use of :ref:`outlier detector `. -* tls: added :ref:`SNI support `. -* tls: added support for specifying :ref:`TLS session ticket keys - `. -* tls: allow configuration of the :ref:`min - ` and :ref:`max - ` TLS protocol versions. -* tracing: added :ref:`custom trace span decorators `. -* Many small bug fixes and performance improvements not listed. - -1.4.0 (August 24, 2017) -======================= - -* macOS is :repo:`now supported `. (A few features - are missing such as hot restart and original destination routing). -* YAML is now directly supported for config files. -* Added /routes admin endpoint. -* End-to-end flow control is now supported for TCP proxy, HTTP/1, and HTTP/2. HTTP flow control - that includes filter buffering is incomplete and will be implemented in 1.5.0. -* Log verbosity :repo:`compile time flag ` added. -* Hot restart :repo:`compile time flag ` added. -* Original destination :ref:`cluster ` - and :ref:`load balancer ` added. -* :ref:`WebSocket ` is now supported. -* Virtual cluster priorities have been hard removed without deprecation as we are reasonably sure - no one is using this feature. -* Route `validate_clusters` option added. -* :ref:`x-envoy-downstream-service-node ` - header added. -* :ref:`x-forwarded-client-cert ` header - added. -* Initial HTTP/1 forward proxy support for absolute URLs has been added. -* HTTP/2 codec settings are now configurable. -* gRPC/JSON transcoder :ref:`filter ` added. -* gRPC web :ref:`filter ` added. -* Configurable timeout for the rate limit service call in the :ref:`network - ` and :ref:`HTTP ` rate limit - filters. -* :ref:`x-envoy-retry-grpc-on ` header added. -* :ref:`LDS API ` added. -* TLS :`require_client_certificate` option added. -* :ref:`Configuration check tool ` added. -* :ref:`JSON schema check tool ` added. -* Config validation mode added via the :option:`--mode` option. -* :option:`--local-address-ip-version` option added. -* IPv6 support is now complete. -* UDP `statsd_ip_address` option added. -* Per-cluster DNS resolvers added. -* :ref:`Fault filter ` enhancements and fixes. -* Several features are :ref:`deprecated as of the 1.4.0 release `. They - will be removed at the beginning of the 1.5.0 release cycle. We explicitly call out that the - `HttpFilterConfigFactory` filter API has been deprecated in favor of - `NamedHttpFilterConfigFactory`. -* Many small bug fixes and performance improvements not listed. - -1.3.0 (May 17, 2017) -==================== - -* As of this release, we now have an official :repo:`breaking change policy - `. Note that there are numerous breaking configuration - changes in this release. They are not listed here. Future releases will adhere to the policy and - have clear documentation on deprecations and changes. -* Bazel is now the canonical build system (replacing CMake). There have been a huge number of - changes to the development/build/test flow. See :repo:`/bazel/README.md` and - :repo:`/ci/README.md` for more information. -* :ref:`Outlier detection ` has been expanded to include success - rate variance, and all parameters are now configurable in both runtime and in the JSON - configuration. -* TCP level listener and cluster connections now have configurable receive buffer - limits at which point connection level back pressure is applied. - Full end to end flow control will be available in a future release. -* :ref:`Redis health checking ` has been added as an active - health check type. Full Redis support will be documented/supported in 1.4.0. -* :ref:`TCP health checking ` now supports a - "connect only" mode that only checks if the remote server can be connected to without - writing/reading any data. -* `BoringSSL `_ is now the only supported TLS provider. - The default cipher suites and ECDH curves have been updated with more modern defaults for both - listener and cluster connections. -* The `header value match` rate limit action has been expanded to include an `expect - match` parameter. -* Route level HTTP rate limit configurations now do not inherit the virtual host level - configurations by default. Use `include_vh_rate_limits` to inherit the virtual host - level options if desired. -* HTTP routes can now add request headers on a per route and per virtual host basis via the - :ref:`request_headers_to_add ` option. -* The :ref:`example configurations ` have been refreshed to demonstrate the - latest features. -* `per_try_timeout_ms` can now be configured in - a route's retry policy in addition to via the :ref:`x-envoy-upstream-rq-per-try-timeout-ms - ` HTTP header. -* HTTP virtual host matching now includes support for prefix wildcard domains (e.g., `*.lyft.com`). -* The default for tracing random sampling has been changed to 100% and is still configurable in - :ref:`runtime `. -* HTTP tracing configuration has been extended to allow tags - to be populated from arbitrary HTTP headers. -* The :ref:`HTTP rate limit filter ` can now be applied to internal, - external, or all requests via the `request_type` option. -* :ref:`Listener binding ` now requires specifying an `address` field. This can be - used to bind a listener to both a specific address as well as a port. -* The :ref:`MongoDB filter ` now emits a stat for queries that - do not have `$maxTimeMS` set. -* The :ref:`MongoDB filter ` now emits logs that are fully valid - JSON. -* The CPU profiler output path is now configurable. -* A watchdog system has been added that can kill the server if a deadlock is detected. -* A :ref:`route table checking tool ` has been added that can - be used to test route tables before use. -* We have added an :ref:`example repo ` that shows how to compile/link a custom filter. -* Added additional cluster wide information related to outlier detection to the :ref:`/clusters - admin endpoint `. -* Multiple SANs can now be verified via the `verify_subject_alt_name` setting. - Additionally, URI type SANs can be verified. -* HTTP filters can now be passed opaque configuration specified on a per route basis. -* By default Envoy now has a built in crash handler that will print a back trace. This behavior can - be disabled if desired via the ``--define=signal_trace=disabled`` Bazel option. -* Zipkin has been added as a supported :ref:`tracing provider `. -* Numerous small changes and fixes not listed here. - -1.2.0 (March 7, 2017) -===================== - -* :ref:`Cluster discovery service (CDS) API `. -* :ref:`Outlier detection ` (passive health checking). -* Envoy configuration is now checked against a JSON schema. -* :ref:`Ring hash ` consistent load balancer, as well as HTTP - consistent hash routing based on a policy. -* Vastly :ref:`enhanced global rate limit configuration ` via the HTTP - rate limiting filter. -* HTTP routing to a cluster retrieved from a header. -* Weighted cluster HTTP routing. -* Auto host rewrite during HTTP routing. -* Regex header matching during HTTP routing. -* HTTP access log runtime filter. -* LightStep tracer :ref:`parent/child span association `. -* :ref:`Route discovery service (RDS) API `. -* HTTP router :ref:`x-envoy-upstream-rq-timeout-alt-response header - ` support. -* *use_original_dst* and *bind_to_port* :ref:`listener options ` (useful for - iptables based transparent proxy support). -* TCP proxy filter :ref:`route table support `. -* Configurable stats flush interval. -* Various :ref:`third party library upgrades `, including using BoringSSL as - the default SSL provider. -* No longer maintain closed HTTP/2 streams for priority calculations. Leads to substantial memory - savings for large meshes. -* Numerous small changes and fixes not listed here. - -1.1.0 (November 30, 2016) -========================= - -* Switch from Jannson to RapidJSON for our JSON library (allowing for a configuration schema in - 1.2.0). -* Upgrade :ref:`recommended version ` of various other libraries. -* Configurable DNS refresh rate for DNS service discovery types. -* Upstream circuit breaker configuration can be :ref:`overridden via runtime - `. -* :ref:`Zone aware routing support `. -* Generic header matching routing rule. -* HTTP/2 graceful connection draining (double GOAWAY). -* DynamoDB filter :ref:`per shard statistics ` (pre-release AWS - feature). -* Initial release of the :ref:`fault injection HTTP filter `. -* HTTP :ref:`rate limit filter ` enhancements (note that the - configuration for HTTP rate limiting is going to be overhauled in 1.2.0). -* Added :ref:`refused-stream retry policy `. -* Multiple :ref:`priority queues ` for upstream clusters - (configurable on a per route basis, with separate connection pools, circuit breakers, etc.). -* Added max connection circuit breaking to the :ref:`TCP proxy filter `. -* Added :ref:`CLI ` options for setting the logging file flush interval as well - as the drain/shutdown time during hot restart. -* A very large number of performance enhancements for core HTTP/TCP proxy flows as well as a - few new configuration flags to allow disabling expensive features if they are not needed - (specifically request ID generation and dynamic response code stats). -* Support Mongo 3.2 in the :ref:`Mongo sniffing filter `. -* Lots of other small fixes and enhancements not listed. - -1.0.0 (September 12, 2016) -========================== - -Initial open source release. diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index df79a2a34364..8d2c7d995f60 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -15,6 +15,8 @@ Changes `google.api.HttpBody `_. * http: fixed a bug where the upgrade header was not cleared on responses to non-upgrade requests. Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.fix_upgrade_response` to false. +* performance: stats symbol table implementation (enabled by default; to disable it, add + `--use-fake-symbol-table 1` to the command-line arguments when starting Envoy). * router: allow retries of streaming or incomplete requests. This removes stat `rq_retry_skipped_request_not_complete`. * tracing: tracing configuration has been made fully dynamic and every HTTP connection manager can now have a separate :ref:`tracing provider `. From febd650beabb6eee4a5f63278d71b571aa191cde Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Tue, 5 May 2020 13:17:05 -0400 Subject: [PATCH 098/106] format Signed-off-by: Joshua Marantz --- docs/root/version_history/current.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 66f26e86d2b1..d49549fa390d 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -22,8 +22,6 @@ Changes * http: fixed a bug where in some cases slash was moved from path to query string when :ref:`merging of adjacent slashes` is enabled. * http: fixed a bug where the upgrade header was not cleared on responses to non-upgrade requests. Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.fix_upgrade_response` to false. -* performance: stats symbol table implementation (enabled by default; to disable it, add - `--use-fake-symbol-table 1` to the command-line arguments when starting Envoy). * http: remove legacy connection pool code and their runtime features: `envoy.reloadable_features.new_http1_connection_pool_behavior` and `envoy.reloadable_features.new_http2_connection_pool_behavior`. * listener: added in place filter chain update flow for tcp listener update which doesn't close connections if the corresponding network filter chain is equivalent during the listener update. @@ -36,6 +34,8 @@ Changes in :ref:`client_features` field. * network filters: added a :ref:`postgres proxy filter `. * network filters: added a :ref:`rocketmq proxy filter `. +* performance: stats symbol table implementation (enabled by default; to disable it, add + `--use-fake-symbol-table 1` to the command-line arguments when starting Envoy). * prometheus stats: fix the sort order of output lines to comply with the standard. * request_id: added to :ref:`always_set_request_id_in_response setting ` to set :ref:`x-request-id ` header in response even if From 16077ab8a5d8899ba6aacfd1e98481d8356f7bc7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 24 Jun 2020 22:10:25 -0400 Subject: [PATCH 099/106] Instantiate a TestSymboLTable so that mocks reference the same one. Signed-off-by: Joshua Marantz --- source/common/common/thread.h | 6 +++--- source/extensions/common/tap/admin.cc | 2 +- .../filters/http/ext_authz/ext_authz.cc | 2 +- test/mocks/common.h | 6 +++--- test/server/admin/prometheus_stats_test.cc | 6 ++---- test/test_common/simulated_time_system.cc | 7 +++---- test/test_common/simulated_time_system.h | 6 +++--- test/test_common/test_time.h | 6 +++--- test/test_common/test_time_system.h | 19 +++++++++---------- 9 files changed, 28 insertions(+), 32 deletions(-) diff --git a/source/common/common/thread.h b/source/common/common/thread.h index 4808d391dfbd..bbad9fee6913 100644 --- a/source/common/common/thread.h +++ b/source/common/common/thread.h @@ -64,9 +64,9 @@ class CondVar { * @return WaitStatus whether the condition timed out or not. */ template - WaitStatus waitFor(MutexBasicLockable& mutex, - std::chrono::duration duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + WaitStatus waitFor( + MutexBasicLockable& mutex, + std::chrono::duration duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { return condvar_.WaitWithTimeout(&mutex.mutex_, absl::FromChrono(duration)) ? WaitStatus::Timeout : WaitStatus::NoTimeout; diff --git a/source/extensions/common/tap/admin.cc b/source/extensions/common/tap/admin.cc index 9dc6b9b08411..b6c4449db177 100644 --- a/source/extensions/common/tap/admin.cc +++ b/source/extensions/common/tap/admin.cc @@ -110,7 +110,7 @@ void AdminHandler::AdminPerTapSinkHandle::submitTrace( std::shared_ptr shared_trace{std::move(trace)}; // The handle can be destroyed before the cross thread post is complete. Thus, we capture a // reference to our parent. - parent_.main_thread_dispatcher_.post([&parent = parent_, trace = shared_trace, format]() { + parent_.main_thread_dispatcher_.post([& parent = parent_, trace = shared_trace, format]() { if (!parent.attached_request_.has_value()) { return; } diff --git a/source/extensions/filters/http/ext_authz/ext_authz.cc b/source/extensions/filters/http/ext_authz/ext_authz.cc index 8159b2b9e196..524dc78770af 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.cc +++ b/source/extensions/filters/http/ext_authz/ext_authz.cc @@ -216,7 +216,7 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { callbacks_->sendLocalReply( response->status_code, response->body, - [&headers = response->headers_to_set, + [& headers = response->headers_to_set, &callbacks = *callbacks_](Http::HeaderMap& response_headers) -> void { ENVOY_STREAM_LOG(trace, "ext_authz filter added header(s) to the local response:", callbacks); diff --git a/test/mocks/common.h b/test/mocks/common.h index 57bdc20623f6..1c5d899d975e 100644 --- a/test/mocks/common.h +++ b/test/mocks/common.h @@ -58,9 +58,9 @@ class MockTimeSystem : public Event::TestTimeSystem { void advanceTimeAsync(const Duration& duration) override { real_time_.advanceTimeAsync(duration); } - Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { + Thread::CondVar::WaitStatus + waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { return real_time_.waitFor(mutex, condvar, duration); // NO_CHECK_FORMAT(real_time) } MOCK_METHOD(SystemTime, systemTime, ()); diff --git a/test/server/admin/prometheus_stats_test.cc b/test/server/admin/prometheus_stats_test.cc index 7994da560249..35528290dace 100644 --- a/test/server/admin/prometheus_stats_test.cc +++ b/test/server/admin/prometheus_stats_test.cc @@ -36,9 +36,7 @@ class HistogramWrapper { class PrometheusStatsFormatterTest : public testing::Test { protected: - PrometheusStatsFormatterTest() - : symbol_table_(Stats::SymbolTableCreator::makeSymbolTable()), alloc_(*symbol_table_), - pool_(*symbol_table_) {} + PrometheusStatsFormatterTest() : alloc_(*symbol_table_), pool_(*symbol_table_) {} ~PrometheusStatsFormatterTest() override { clearStorage(); } @@ -92,7 +90,7 @@ class PrometheusStatsFormatterTest : public testing::Test { EXPECT_EQ(0, symbol_table_->numSymbols()); } - Stats::SymbolTablePtr symbol_table_; + Stats::TestSymbolTable symbol_table_; Stats::AllocatorImpl alloc_; Stats::StatNamePool pool_; std::vector counters_; diff --git a/test/test_common/simulated_time_system.cc b/test/test_common/simulated_time_system.cc index b840fd137a6f..61462225bb49 100644 --- a/test/test_common/simulated_time_system.cc +++ b/test/test_common/simulated_time_system.cc @@ -263,10 +263,9 @@ void SimulatedTimeSystemHelper::waitForNoPendingLockHeld() const &pending_alarms_)); } -Thread::CondVar::WaitStatus SimulatedTimeSystemHelper::waitFor(Thread::MutexBasicLockable& mutex, - Thread::CondVar& condvar, - const Duration& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { +Thread::CondVar::WaitStatus SimulatedTimeSystemHelper::waitFor( + Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { only_one_thread_.checkOneThread(); // TODO(#10568): This real-time polling delay should not be necessary. Without diff --git a/test/test_common/simulated_time_system.h b/test/test_common/simulated_time_system.h index 2b31a564c8f7..6cc9b461e8cc 100644 --- a/test/test_common/simulated_time_system.h +++ b/test/test_common/simulated_time_system.h @@ -28,9 +28,9 @@ class SimulatedTimeSystemHelper : public TestTimeSystem { // TestTimeSystem void advanceTimeWait(const Duration& duration) override; void advanceTimeAsync(const Duration& duration) override; - Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; + Thread::CondVar::WaitStatus + waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; // TimeSource SystemTime systemTime() override; diff --git a/test/test_common/test_time.h b/test/test_common/test_time.h index f5e24b8bd8a5..4b0beec54439 100644 --- a/test/test_common/test_time.h +++ b/test/test_common/test_time.h @@ -14,9 +14,9 @@ class TestRealTimeSystem : public TestTimeSystem { // TestTimeSystem void advanceTimeAsync(const Duration& duration) override; void advanceTimeWait(const Duration& duration) override; - Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; + Thread::CondVar::WaitStatus + waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; // Event::TimeSystem Event::SchedulerPtr createScheduler(Scheduler& base_scheduler) override { diff --git a/test/test_common/test_time_system.h b/test/test_common/test_time_system.h index cb9ac3480215..449ec8065391 100644 --- a/test/test_common/test_time_system.h +++ b/test/test_common/test_time_system.h @@ -56,15 +56,14 @@ class TestTimeSystem : public Event::TimeSystem { * @param duration The maximum amount of time to wait. * @return Thread::CondVar::WaitStatus whether the condition timed out or not. */ - virtual Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, - Thread::CondVar& condvar, - const Duration& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) PURE; + virtual Thread::CondVar::WaitStatus + waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) PURE; template - Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const D& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + Thread::CondVar::WaitStatus + waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const D& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { return waitFor(mutex, condvar, std::chrono::duration_cast(duration)); } }; @@ -109,9 +108,9 @@ template class DelegatingTestTimeSystemBase : public T timeSystem().advanceTimeWait(duration); } - Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { + Thread::CondVar::WaitStatus + waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { return timeSystem().waitFor(mutex, condvar, duration); } From dcabf96287de1adbe3bd7fedf6670964ca4aef07 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Wed, 24 Jun 2020 23:24:14 -0400 Subject: [PATCH 100/106] reformat with clang-10. Signed-off-by: Joshua Marantz --- source/common/common/thread.h | 6 +++--- source/extensions/common/tap/admin.cc | 2 +- .../filters/http/ext_authz/ext_authz.cc | 2 +- test/mocks/common.h | 6 +++--- test/test_common/simulated_time_system.cc | 7 ++++--- test/test_common/simulated_time_system.h | 6 +++--- test/test_common/test_time.h | 6 +++--- test/test_common/test_time_system.h | 19 ++++++++++--------- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/source/common/common/thread.h b/source/common/common/thread.h index bbad9fee6913..4808d391dfbd 100644 --- a/source/common/common/thread.h +++ b/source/common/common/thread.h @@ -64,9 +64,9 @@ class CondVar { * @return WaitStatus whether the condition timed out or not. */ template - WaitStatus waitFor( - MutexBasicLockable& mutex, - std::chrono::duration duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + WaitStatus waitFor(MutexBasicLockable& mutex, + std::chrono::duration duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { return condvar_.WaitWithTimeout(&mutex.mutex_, absl::FromChrono(duration)) ? WaitStatus::Timeout : WaitStatus::NoTimeout; diff --git a/source/extensions/common/tap/admin.cc b/source/extensions/common/tap/admin.cc index b6c4449db177..9dc6b9b08411 100644 --- a/source/extensions/common/tap/admin.cc +++ b/source/extensions/common/tap/admin.cc @@ -110,7 +110,7 @@ void AdminHandler::AdminPerTapSinkHandle::submitTrace( std::shared_ptr shared_trace{std::move(trace)}; // The handle can be destroyed before the cross thread post is complete. Thus, we capture a // reference to our parent. - parent_.main_thread_dispatcher_.post([& parent = parent_, trace = shared_trace, format]() { + parent_.main_thread_dispatcher_.post([&parent = parent_, trace = shared_trace, format]() { if (!parent.attached_request_.has_value()) { return; } diff --git a/source/extensions/filters/http/ext_authz/ext_authz.cc b/source/extensions/filters/http/ext_authz/ext_authz.cc index 524dc78770af..8159b2b9e196 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.cc +++ b/source/extensions/filters/http/ext_authz/ext_authz.cc @@ -216,7 +216,7 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { callbacks_->sendLocalReply( response->status_code, response->body, - [& headers = response->headers_to_set, + [&headers = response->headers_to_set, &callbacks = *callbacks_](Http::HeaderMap& response_headers) -> void { ENVOY_STREAM_LOG(trace, "ext_authz filter added header(s) to the local response:", callbacks); diff --git a/test/mocks/common.h b/test/mocks/common.h index 1c5d899d975e..57bdc20623f6 100644 --- a/test/mocks/common.h +++ b/test/mocks/common.h @@ -58,9 +58,9 @@ class MockTimeSystem : public Event::TestTimeSystem { void advanceTimeAsync(const Duration& duration) override { real_time_.advanceTimeAsync(duration); } - Thread::CondVar::WaitStatus - waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { + Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { return real_time_.waitFor(mutex, condvar, duration); // NO_CHECK_FORMAT(real_time) } MOCK_METHOD(SystemTime, systemTime, ()); diff --git a/test/test_common/simulated_time_system.cc b/test/test_common/simulated_time_system.cc index 61462225bb49..b840fd137a6f 100644 --- a/test/test_common/simulated_time_system.cc +++ b/test/test_common/simulated_time_system.cc @@ -263,9 +263,10 @@ void SimulatedTimeSystemHelper::waitForNoPendingLockHeld() const &pending_alarms_)); } -Thread::CondVar::WaitStatus SimulatedTimeSystemHelper::waitFor( - Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { +Thread::CondVar::WaitStatus SimulatedTimeSystemHelper::waitFor(Thread::MutexBasicLockable& mutex, + Thread::CondVar& condvar, + const Duration& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { only_one_thread_.checkOneThread(); // TODO(#10568): This real-time polling delay should not be necessary. Without diff --git a/test/test_common/simulated_time_system.h b/test/test_common/simulated_time_system.h index 6cc9b461e8cc..2b31a564c8f7 100644 --- a/test/test_common/simulated_time_system.h +++ b/test/test_common/simulated_time_system.h @@ -28,9 +28,9 @@ class SimulatedTimeSystemHelper : public TestTimeSystem { // TestTimeSystem void advanceTimeWait(const Duration& duration) override; void advanceTimeAsync(const Duration& duration) override; - Thread::CondVar::WaitStatus - waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; + Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; // TimeSource SystemTime systemTime() override; diff --git a/test/test_common/test_time.h b/test/test_common/test_time.h index 4b0beec54439..f5e24b8bd8a5 100644 --- a/test/test_common/test_time.h +++ b/test/test_common/test_time.h @@ -14,9 +14,9 @@ class TestRealTimeSystem : public TestTimeSystem { // TestTimeSystem void advanceTimeAsync(const Duration& duration) override; void advanceTimeWait(const Duration& duration) override; - Thread::CondVar::WaitStatus - waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; + Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override; // Event::TimeSystem Event::SchedulerPtr createScheduler(Scheduler& base_scheduler) override { diff --git a/test/test_common/test_time_system.h b/test/test_common/test_time_system.h index 449ec8065391..cb9ac3480215 100644 --- a/test/test_common/test_time_system.h +++ b/test/test_common/test_time_system.h @@ -56,14 +56,15 @@ class TestTimeSystem : public Event::TimeSystem { * @param duration The maximum amount of time to wait. * @return Thread::CondVar::WaitStatus whether the condition timed out or not. */ - virtual Thread::CondVar::WaitStatus - waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) PURE; + virtual Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, + Thread::CondVar& condvar, + const Duration& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) PURE; template - Thread::CondVar::WaitStatus - waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const D& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const D& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { return waitFor(mutex, condvar, std::chrono::duration_cast(duration)); } }; @@ -108,9 +109,9 @@ template class DelegatingTestTimeSystemBase : public T timeSystem().advanceTimeWait(duration); } - Thread::CondVar::WaitStatus - waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, - const Duration& duration) noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { + Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar, + const Duration& duration) noexcept + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) override { return timeSystem().waitFor(mutex, condvar, duration); } From c0e45c66c30f76b416af2cf6d101070d64f6aafc Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 25 Jun 2020 09:12:44 -0400 Subject: [PATCH 101/106] improve assert message for dynamic/symbolic mismatch, and don't check the value of an uninitialized counter in the grpc tests. Signed-off-by: Joshua Marantz --- test/common/stats/stat_test_utility.cc | 9 ++++++--- test/extensions/filters/http/grpc_stats/config_test.cc | 8 +++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/test/common/stats/stat_test_utility.cc b/test/common/stats/stat_test_utility.cc index a195614e5682..55c4acc73590 100644 --- a/test/common/stats/stat_test_utility.cc +++ b/test/common/stats/stat_test_utility.cc @@ -152,7 +152,8 @@ Counter& TestStore::counterFromStatNameWithTags(const StatName& stat_name, } else { // Ensures StatNames with the same string representation are specified // consistently using symbolic/dynamic components on every access. - ASSERT(counter_ref->statName() == stat_name); + ASSERT(counter_ref->statName() == stat_name, "Inconsistent dynamic vs symbolic " + "stat name specification"); } return *counter_ref; } @@ -173,7 +174,8 @@ Gauge& TestStore::gaugeFromStatNameWithTags(const StatName& stat_name, if (gauge_ref == nullptr) { gauge_ref = &IsolatedStoreImpl::gaugeFromStatNameWithTags(stat_name, tags, mode); } else { - ASSERT(gauge_ref->statName() == stat_name); + ASSERT(gauge_ref->statName() == stat_name, "Inconsistent dynamic vs symbolic " + "stat name specification"); } return *gauge_ref; } @@ -194,7 +196,8 @@ Histogram& TestStore::histogramFromStatNameWithTags(const StatName& stat_name, if (histogram_ref == nullptr) { histogram_ref = &IsolatedStoreImpl::histogramFromStatNameWithTags(stat_name, tags, unit); } else { - ASSERT(histogram_ref->statName() == stat_name); + ASSERT(histogram_ref->statName() == stat_name, "Inconsistent dynamic vs symbolic " + "stat name specification"); } return *histogram_ref; } diff --git a/test/extensions/filters/http/grpc_stats/config_test.cc b/test/extensions/filters/http/grpc_stats/config_test.cc index b75737b06cd2..aeaaec8ba7a6 100644 --- a/test/extensions/filters/http/grpc_stats/config_test.cc +++ b/test/extensions/filters/http/grpc_stats/config_test.cc @@ -358,11 +358,9 @@ TEST_F(GrpcStatsFilterConfigTest, MessageCounts) { .counterFromString( "grpc.lyft.users.BadCompanions.GetBadCompanions.request_message_count") .value()); - EXPECT_EQ(0U, decoder_callbacks_.clusterInfo() - ->statsScope() - .counterFromString( - "grpc.lyft.users.BadCompanions.GetBadCompanions.response_message_count") - .value()); + EXPECT_FALSE(stats_store_.findCounterByString( + "grpc.lyft.users.BadCompanions.GetBadCompanions.response_message_count")); + const auto& data = stream_info_.filterState()->getDataReadOnly( HttpFilterNames::get().GrpcStats); EXPECT_EQ(2U, data.request_message_count); From f954e3547c0b00abb8541e942895c0cbc29d08da Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Thu, 25 Jun 2020 09:28:27 -0400 Subject: [PATCH 102/106] clean up the config_test so it has minimal documnted changes from the previous state. Signed-off-by: Joshua Marantz --- test/common/stats/stat_test_utility.cc | 6 +++--- test/extensions/filters/http/grpc_stats/config_test.cc | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/common/stats/stat_test_utility.cc b/test/common/stats/stat_test_utility.cc index 55c4acc73590..cc0f0a8d47a7 100644 --- a/test/common/stats/stat_test_utility.cc +++ b/test/common/stats/stat_test_utility.cc @@ -153,7 +153,7 @@ Counter& TestStore::counterFromStatNameWithTags(const StatName& stat_name, // Ensures StatNames with the same string representation are specified // consistently using symbolic/dynamic components on every access. ASSERT(counter_ref->statName() == stat_name, "Inconsistent dynamic vs symbolic " - "stat name specification"); + "stat name specification"); } return *counter_ref; } @@ -175,7 +175,7 @@ Gauge& TestStore::gaugeFromStatNameWithTags(const StatName& stat_name, gauge_ref = &IsolatedStoreImpl::gaugeFromStatNameWithTags(stat_name, tags, mode); } else { ASSERT(gauge_ref->statName() == stat_name, "Inconsistent dynamic vs symbolic " - "stat name specification"); + "stat name specification"); } return *gauge_ref; } @@ -197,7 +197,7 @@ Histogram& TestStore::histogramFromStatNameWithTags(const StatName& stat_name, histogram_ref = &IsolatedStoreImpl::histogramFromStatNameWithTags(stat_name, tags, unit); } else { ASSERT(histogram_ref->statName() == stat_name, "Inconsistent dynamic vs symbolic " - "stat name specification"); + "stat name specification"); } return *histogram_ref; } diff --git a/test/extensions/filters/http/grpc_stats/config_test.cc b/test/extensions/filters/http/grpc_stats/config_test.cc index aeaaec8ba7a6..3b303fe0a8ea 100644 --- a/test/extensions/filters/http/grpc_stats/config_test.cc +++ b/test/extensions/filters/http/grpc_stats/config_test.cc @@ -358,8 +358,17 @@ TEST_F(GrpcStatsFilterConfigTest, MessageCounts) { .counterFromString( "grpc.lyft.users.BadCompanions.GetBadCompanions.request_message_count") .value()); + + // Check that there is response_message_count stat yet. We use + // stats_store_.findCounterByString rather than looking on + // clusterInfo()->statsScope() because findCounterByString is not an API on + // Stats::Store, and there is no prefix so the names will match. We verify + // that by double-checking we can find the request_message_count using the + // same API. EXPECT_FALSE(stats_store_.findCounterByString( "grpc.lyft.users.BadCompanions.GetBadCompanions.response_message_count")); + EXPECT_TRUE(stats_store_.findCounterByString( + "grpc.lyft.users.BadCompanions.GetBadCompanions.request_message_count")); const auto& data = stream_info_.filterState()->getDataReadOnly( HttpFilterNames::get().GrpcStats); From 5695a17b0adb4b81fa7ef5da7fab2055f0208278 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 27 Jun 2020 13:06:39 -0400 Subject: [PATCH 103/106] try to avoid race by waiting for a timeout stat rather than just strobing it. Signed-off-by: Joshua Marantz --- test/integration/http2_integration_test.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/http2_integration_test.cc b/test/integration/http2_integration_test.cc index 0f28f2b180c0..9de0d17da08b 100644 --- a/test/integration/http2_integration_test.cc +++ b/test/integration/http2_integration_test.cc @@ -1562,8 +1562,7 @@ void Http2FloodMitigationTest::floodServer(const Http2Frame& frame, const std::s EXPECT_LE(total_bytes_sent, TransmitThreshold) << "Flood mitigation is broken."; EXPECT_EQ(1, test_server_->counter(flood_stat)->value()); - EXPECT_EQ(1, - test_server_->counter("http.config_test.downstream_cx_delayed_close_timeout")->value()); + test_server_->waitForCounterGe("http.config_test.downstream_cx_delayed_close_timeout", 1); } // Verify that the server detects the flood using specified request parameters. From 9d33cce4b50f4dc4cb2598942e047d6f2cac1fde Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 27 Jun 2020 13:56:24 -0400 Subject: [PATCH 104/106] Declare ads_integregion_test as enormous to try to avoid tsan timeouts. Signed-off-by: Joshua Marantz --- test/integration/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/BUILD b/test/integration/BUILD index cd527321b6d4..233c5834ba6a 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -50,6 +50,7 @@ envoy_cc_test_library( envoy_cc_test( name = "ads_integration_test", + size = "enormous", srcs = ["ads_integration_test.cc"], tags = ["fails_on_windows"], deps = [ From 96e2c6ccfa95f9a72e4f78fdc5ee504bd87b805e Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 27 Jun 2020 23:03:17 -0400 Subject: [PATCH 105/106] remove superfluous line, that probably was from a merge. Signed-off-by: Joshua Marantz --- docs/root/version_history/current.rst | 1 - source/common/stats/thread_local_store.cc | 19 +++++++++++++++++-- source/common/stats/thread_local_store.h | 4 ++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 2c46f592854b..240b1b62ddf9 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -109,7 +109,6 @@ New Features * network filters: added a :ref:`rocketmq proxy filter `. * performance: stats symbol table implementation (enabled by default; to disable it, add `--use-fake-symbol-table 1` to the command-line arguments when starting Envoy). -* prometheus stats: fix the sort order of output lines to comply with the standard. * ratelimit: add support for use of dynamic metadata :ref:`dynamic_metadata ` as a ratelimit action. * ratelimit: added :ref:`API version ` to explicitly set the version of gRPC service endpoint and message to be used. * redis: added acl support :ref:`downstream_auth_username ` for downstream client ACL authentication, and :ref:`auth_username ` to configure authentication usernames for upstream Redis 6+ server clusters with ACL enabled. diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index e2eeae81f2f4..b8e279632537 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -28,7 +28,13 @@ ThreadLocalStoreImpl::ThreadLocalStoreImpl(Allocator& alloc) tag_producer_(std::make_unique()), stats_matcher_(std::make_unique()), heap_allocator_(alloc.symbolTable()), null_counter_(alloc.symbolTable()), null_gauge_(alloc.symbolTable()), - null_histogram_(alloc.symbolTable()), null_text_readout_(alloc.symbolTable()) {} + null_histogram_(alloc.symbolTable()), null_text_readout_(alloc.symbolTable()), + well_known_tags_(alloc.symbolTable().makeSet("well_known_tags")) { + for (const auto& desc : Config::TagNames::get().descriptorVec()) { + well_known_tags_->rememberBuiltin(desc.name_); + } + well_known_tags_->rememberBuiltin("admin"); +} ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(shutting_down_ || !threading_ever_initialized_); @@ -295,8 +301,17 @@ class StatNameTagHelper { tls.symbolTable().callWithStringView(name, [&tags, &tls, this](absl::string_view name_str) { tag_extracted_name_ = pool_.add(tls.tagProducer().produceTags(name_str, tags)); }); + StatName empty; for (const auto& tag : tags) { - stat_name_tags_.emplace_back(pool_.add(tag.name_), pool_.add(tag.value_)); + StatName tag_name = tls.wellKnownTags().getBuiltin(tag.name_, empty); + if (tag_name.empty()) { + tag_name = pool_.add(tag.name_); + } + StatName tag_value = tls.wellKnownTags().getBuiltin(tag.value_, empty); + if (tag_value.empty()) { + tag_value = pool_.add(tag.value_); + } + stat_name_tags_.emplace_back(tag_name, tag_value); } } else { tag_extracted_name_ = name; diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 83c7b51e2a70..8eee3d71826e 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -263,6 +263,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo */ Thread::ThreadSynchronizer& sync() { return sync_; } + StatNameSet& wellKnownTags() { return *well_known_tags_; } + private: template using StatRefMap = StatNameHashMap>; @@ -454,6 +456,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Thread::ThreadSynchronizer sync_; std::atomic next_scope_id_{}; + + StatNameSetPtr well_known_tags_; }; } // namespace Stats From 141c7167a60b4cf6d0c1f7a640bf174ef02069d7 Mon Sep 17 00:00:00 2001 From: Joshua Marantz Date: Sat, 27 Jun 2020 23:04:52 -0400 Subject: [PATCH 106/106] back out accidental commit Signed-off-by: Joshua Marantz --- source/common/stats/thread_local_store.cc | 19 ++----------------- source/common/stats/thread_local_store.h | 4 ---- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index b8e279632537..e2eeae81f2f4 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -28,13 +28,7 @@ ThreadLocalStoreImpl::ThreadLocalStoreImpl(Allocator& alloc) tag_producer_(std::make_unique()), stats_matcher_(std::make_unique()), heap_allocator_(alloc.symbolTable()), null_counter_(alloc.symbolTable()), null_gauge_(alloc.symbolTable()), - null_histogram_(alloc.symbolTable()), null_text_readout_(alloc.symbolTable()), - well_known_tags_(alloc.symbolTable().makeSet("well_known_tags")) { - for (const auto& desc : Config::TagNames::get().descriptorVec()) { - well_known_tags_->rememberBuiltin(desc.name_); - } - well_known_tags_->rememberBuiltin("admin"); -} + null_histogram_(alloc.symbolTable()), null_text_readout_(alloc.symbolTable()) {} ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(shutting_down_ || !threading_ever_initialized_); @@ -301,17 +295,8 @@ class StatNameTagHelper { tls.symbolTable().callWithStringView(name, [&tags, &tls, this](absl::string_view name_str) { tag_extracted_name_ = pool_.add(tls.tagProducer().produceTags(name_str, tags)); }); - StatName empty; for (const auto& tag : tags) { - StatName tag_name = tls.wellKnownTags().getBuiltin(tag.name_, empty); - if (tag_name.empty()) { - tag_name = pool_.add(tag.name_); - } - StatName tag_value = tls.wellKnownTags().getBuiltin(tag.value_, empty); - if (tag_value.empty()) { - tag_value = pool_.add(tag.value_); - } - stat_name_tags_.emplace_back(tag_name, tag_value); + stat_name_tags_.emplace_back(pool_.add(tag.name_), pool_.add(tag.value_)); } } else { tag_extracted_name_ = name; diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 8eee3d71826e..83c7b51e2a70 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -263,8 +263,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo */ Thread::ThreadSynchronizer& sync() { return sync_; } - StatNameSet& wellKnownTags() { return *well_known_tags_; } - private: template using StatRefMap = StatNameHashMap>; @@ -456,8 +454,6 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Thread::ThreadSynchronizer sync_; std::atomic next_scope_id_{}; - - StatNameSetPtr well_known_tags_; }; } // namespace Stats