Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Stats::RawStatData into a StatsOptions struct #3629

Merged
merged 24 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bb349a1
Refactor Stats::RawStatData into a StatsOptions struct.
ambuc Jun 14, 2018
7bfa762
Store StatsOptions values as size_t
ambuc Jun 14, 2018
27990bf
Add heapAllocator stat name truncation in ThreadLocalStore, plus nits
ambuc Jun 25, 2018
b92ab75
Remove unused BlockMemoryHashSet accessor function
ambuc Jun 25, 2018
3a7aff1
Rename safeInitialize() to truncateAndInit()
ambuc Jun 26, 2018
3f38407
Change interface of sizeGivenName()
ambuc Jun 26, 2018
19f9f3c
Use new translateBootstrap interface in v1_to_bootstrap
ambuc Jul 6, 2018
06bd4c2
Fix formatting
ambuc Jul 6, 2018
217f72f
Add documentation, re-trigger ci
ambuc Jul 6, 2018
1ae932c
Rework some interfaces to pass statsOptions rather than Scope at lowe…
ambuc Jul 10, 2018
b07d5c6
Prefer UNREFERENCED_PARAMETER to (void)
ambuc Jul 10, 2018
732af4f
Better inline docs for stat name / object / suffix terminology
ambuc Jul 10, 2018
7328dc0
Merge remote-tracking branch 'upstream/master' into refactor-StatsOpt…
ambuc Jul 10, 2018
71f6b24
Edit stats inline documentation
ambuc Jul 11, 2018
2aa9d8c
Provide safe copy assertion in RawStatData::initialize()
ambuc Jul 11, 2018
911fc5d
Abstract checkAndInit() and truncateAndInit() with shared initializat…
ambuc Jul 11, 2018
dbcdb72
Formatting fixes and nits
ambuc Jul 11, 2018
9a30244
Merge remote-tracking branch 'upstream/master' into refactor-StatsOpt…
ambuc Jul 12, 2018
aeb9251
Refactor sizeGivenName(), sizeGivenStatsOptions() into structSize(), …
ambuc Jul 12, 2018
cb0fd8a
Add todo addressing embedded assumption about allocator fallback
ambuc Jul 12, 2018
19e0311
Merge remote-tracking branch 'upstream/master' into refactor-StatsOpt…
ambuc Jul 12, 2018
4cad816
Throw std::bad_alloc() in HeapRawStatDataAllocator::alloc()
ambuc Jul 13, 2018
9c6b880
Merge remote-tracking branch 'upstream/master' into refactor-StatsOpt…
ambuc Jul 13, 2018
c1fa6fb
Merge remote-tracking branch 'upstream/master' into refactor-StatsOpt…
ambuc Jul 13, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ envoy_cc_library(
hdrs = ["options.h"],
deps = [
"//include/envoy/network:address_interface",
"//include/envoy/stats:stats_interface",
],
)

Expand Down
5 changes: 3 additions & 2 deletions include/envoy/server/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "envoy/common/pure.h"
#include "envoy/network/address.h"
#include "envoy/stats/stats.h"

#include "spdlog/spdlog.h"

Expand Down Expand Up @@ -149,10 +150,10 @@ class Options {
virtual uint64_t maxStats() const PURE;

/**
* @return uint64_t the maximum name length of the name field in
* @return StatsOptions& the max stat name / suffix lengths for stats.
* router/cluster/listener.
*/
virtual uint64_t maxObjNameLength() const PURE;
virtual const Stats::StatsOptions& statsOptions() const PURE;

/**
* @return bool indicating whether the hot restart functionality has been disabled via cli flags.
Expand Down
44 changes: 44 additions & 0 deletions include/envoy/stats/stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,44 @@ class Instance;

namespace Stats {

/**
* Struct stored under Server::Options to hold information about the maximum object name length and
* maximum stat suffix length of a stat. These have defaults in StatsOptionsImpl, and the maximum
* object name length can be overridden. The default initialization is used in IsolatedStatImpl, and
* the user-overridden struct is stored in Options.
*
* As noted in the comment above StatsOptionsImpl in source/common/stats/stats_impl.h, a stat name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: errant space, start of sentence

* often contains both a string whose length is user-defined (cluster_name in the below example),
* and a specific statistic name generated by Envoy, which are often as long as
* `upstream_cx_connect_attempts_exceeded`. To make room for growth on both fronts, we limit the max
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure the example here is really helpful, I would just delete it.

* allowed length of each separately.
*
* name / stat name
* |----------------------------------------------------------------|
* cluster.<cluster_name>.outlier_detection.ejections_consecutive_5xx
* |--------------------------------------| |-----------------------|
* object name suffix
*/
class StatsOptions {
public:
virtual ~StatsOptions() {}

/**
* The max allowed length of a complete stat name, including suffix.
*/
virtual size_t maxNameLength() const PURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document these methods.


/**
* The max allowed length of the object part of a stat name.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the object part and the suffix part of a stat? Can you potentially provide an example in the comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's more color in /source/common/stats/stats_impl.h, which I'm not sure how much of to duplicate in the include file. So the object part is something like cluster.<cluster_name>.outlier_detection, and the suffix part is one of the statistics listed in the docs here like ejections_enforced_consecutive_gateway_failure. We just want to limit the lengths of these halves separately. Do you think it's worth adding that to the include too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just comment about where the other comments are? In general I would expect the public includes to have the detail so I can understand what things mean, but fine to link to other places. Whatever works. It's mostly that this nomenclature caught me off guard so I think it might confuse others also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me. I did some work in 732af4f.

*/
virtual size_t maxObjNameLength() const PURE;

/**
* The max allowed length of a stat suffix.
*/
virtual size_t maxStatSuffixLength() const PURE;
};

/**
* General representation of a tag.
*/
Expand Down Expand Up @@ -329,6 +367,12 @@ class Scope {
* @return a histogram within the scope's namespace with a particular value type.
*/
virtual Histogram& histogram(const std::string& name) PURE;

/**
* @return a reference to the top-level StatsOptions struct, containing information about the
* maximum allowable object name length and stat suffix length.
*/
virtual const Stats::StatsOptions& statsOptions() const PURE;
};

/**
Expand Down
2 changes: 2 additions & 0 deletions source/common/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ envoy_cc_library(
deps = [
":assert_lib",
":logger_lib",
"//include/envoy/stats:stats_interface",
"//source/common/stats:stats_lib",
],
)

Expand Down
93 changes: 49 additions & 44 deletions source/common/common/block_memory_hash_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
#include <utility>

#include "envoy/common/exception.h"
#include "envoy/stats/stats.h"

#include "common/common/assert.h"
#include "common/common/fmt.h"
#include "common/common/logger.h"
#include "common/stats/stats_impl.h"

#include "absl/strings/string_view.h"

Expand Down Expand Up @@ -57,21 +59,24 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger

/**
* Constructs a map control structure given a set of options, which cannot be changed.
* @param options describes the parameters comtrolling set layout.
* @param hash_set_options describes the parameters comtrolling set layout.
* @param init true if the memory should be initialized on construction. If false,
* the data in the table will be sanity checked, and an exception thrown if
* it is incoherent or mismatches the passed-in options.
* @param memory the memory buffer for the set data.
* @param stats_options a reference to the top-level StatsOptions struct containing
* information about max allowable stat name lengths.
*
* Note that no locking of any kind is done by this class; this must be done at the
* call-site to support concurrent access.
*/
BlockMemoryHashSet(const BlockMemoryHashSetOptions& options, bool init, uint8_t* memory)
: cells_(nullptr), control_(nullptr), slots_(nullptr) {
mapMemorySegments(options, memory);
BlockMemoryHashSet(const BlockMemoryHashSetOptions& hash_set_options, bool init, uint8_t* memory,
const Stats::StatsOptions& stats_options)
: cells_(nullptr), control_(nullptr), slots_(nullptr), stats_options_(stats_options) {
mapMemorySegments(hash_set_options, memory);
if (init) {
initialize(options);
} else if (!attach(options)) {
initialize(hash_set_options);
} else if (!attach(hash_set_options)) {
throw EnvoyException("BlockMemoryHashSet: Incompatible memory block");
}
}
Expand All @@ -82,22 +87,20 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
* backing-store (eg) in memory, which we do after
* constructing the object with the desired sizing.
*/
static uint64_t numBytes(const BlockMemoryHashSetOptions& options) {
uint64_t size =
cellOffset(options.capacity) + sizeof(Control) + options.num_slots * sizeof(uint32_t);
static uint64_t numBytes(const BlockMemoryHashSetOptions& hash_set_options,
const Stats::StatsOptions& stats_options) {
uint64_t size = cellOffset(hash_set_options.capacity, stats_options) + sizeof(Control) +
hash_set_options.num_slots * sizeof(uint32_t);
return align(size);
}

uint64_t numBytes() const { return numBytes(control_->options); }

/**
* Returns the options structure that was used to construct the set.
*/
const BlockMemoryHashSetOptions& options() const { return control_->options; }
uint64_t numBytes(const Stats::StatsOptions& stats_options) const {
return numBytes(control_->hash_set_options, stats_options);
}

/** Examines the data structures to see if they are sane, assert-failing on any trouble. */
void sanityCheck() {
RELEASE_ASSERT(control_->size <= control_->options.capacity);
RELEASE_ASSERT(control_->size <= control_->hash_set_options.capacity);

// As a sanity check, make sure there are control_->size values
// reachable from the slots, each of which has a valid
Expand All @@ -107,11 +110,11 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
// slot. Note that the num_values message will be emitted outside
// the loop.
uint32_t num_values = 0;
for (uint32_t slot = 0; slot < control_->options.num_slots; ++slot) {
for (uint32_t slot = 0; slot < control_->hash_set_options.num_slots; ++slot) {
uint32_t next = 0; // initialized to silence compilers.
for (uint32_t cell_index = slots_[slot];
(cell_index != Sentinal) && (num_values <= control_->size); cell_index = next) {
RELEASE_ASSERT(cell_index < control_->options.capacity);
RELEASE_ASSERT(cell_index < control_->hash_set_options.capacity);
Cell& cell = getCell(cell_index);
absl::string_view key = cell.value.key();
RELEASE_ASSERT(computeSlot(key) == slot);
Expand All @@ -122,7 +125,7 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
RELEASE_ASSERT(num_values == control_->size);

uint32_t num_free_entries = 0;
uint32_t expected_free_entries = control_->options.capacity - control_->size;
uint32_t expected_free_entries = control_->hash_set_options.capacity - control_->size;

// Don't infinite-loop with a corruption; break when we see there's a problem.
for (uint32_t cell_index = control_->free_cell_index;
Expand Down Expand Up @@ -152,7 +155,7 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
if (value != nullptr) {
return ValueCreatedPair(value, false);
}
if (control_->size >= control_->options.capacity) {
if (control_->size >= control_->hash_set_options.capacity) {
return ValueCreatedPair(nullptr, false);
}
const uint32_t slot = computeSlot(key);
Expand All @@ -162,7 +165,7 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
cell.next_cell_index = slots_[slot];
slots_[slot] = cell_index;
value = &cell.value;
value->initialize(key);
value->truncateAndInit(key, stats_options_);
++control_->size;
return ValueCreatedPair(value, true);
}
Expand Down Expand Up @@ -215,9 +218,9 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
/**
* Computes a version signature based on the options and the hash function.
*/
std::string version() {
return fmt::format("options={} hash={} size={}", control_->options.toString(),
control_->hash_signature, numBytes());
std::string version(const Stats::StatsOptions& stats_options) {
return fmt::format("options={} hash={} size={}", control_->hash_set_options.toString(),
control_->hash_signature, numBytes(stats_options));
}

private:
Expand All @@ -228,20 +231,20 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
* coming in.
* @param memory
*/
void initialize(const BlockMemoryHashSetOptions& options) {
void initialize(const BlockMemoryHashSetOptions& hash_set_options) {
control_->hash_signature = Value::hash(signatureStringToHash());
control_->num_bytes = numBytes(options);
control_->options = options;
control_->num_bytes = numBytes(hash_set_options, stats_options_);
control_->hash_set_options = hash_set_options;
control_->size = 0;
control_->free_cell_index = 0;

// Initialize all the slots;
for (uint32_t slot = 0; slot < options.num_slots; ++slot) {
for (uint32_t slot = 0; slot < hash_set_options.num_slots; ++slot) {
slots_[slot] = Sentinal;
}

// Initialize the free-cell list.
const uint32_t last_cell = options.capacity - 1;
const uint32_t last_cell = hash_set_options.capacity - 1;
for (uint32_t cell_index = 0; cell_index < last_cell; ++cell_index) {
Cell& cell = getCell(cell_index);
cell.next_cell_index = cell_index + 1;
Expand All @@ -254,10 +257,10 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
* sanity check to make sure the options copied to the provided memory match, and also
* that the slot, cell, and key-string structures look sane.
*/
bool attach(const BlockMemoryHashSetOptions& options) {
if (numBytes(options) != control_->num_bytes) {
ENVOY_LOG(error, "BlockMemoryHashSet unexpected memory size {} != {}", numBytes(options),
control_->num_bytes);
bool attach(const BlockMemoryHashSetOptions& hash_set_options) {
if (numBytes(hash_set_options, stats_options_) != control_->num_bytes) {
ENVOY_LOG(error, "BlockMemoryHashSet unexpected memory size {} != {}",
numBytes(hash_set_options, stats_options_), control_->num_bytes);
return false;
}
if (Value::hash(signatureStringToHash()) != control_->hash_signature) {
Expand All @@ -269,7 +272,7 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
}

uint32_t computeSlot(absl::string_view key) {
return Value::hash(key) % control_->options.num_slots;
return Value::hash(key) % control_->hash_set_options.num_slots;
}

/**
Expand All @@ -290,11 +293,11 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
* Represents control-values for the hash-table.
*/
struct Control {
BlockMemoryHashSetOptions options; // Options established at map construction time.
uint64_t hash_signature; // Hash of a constant signature string.
uint64_t num_bytes; // Bytes allocated on behalf of the map.
uint32_t size; // Number of values currently stored.
uint32_t free_cell_index; // Offset of first free cell.
BlockMemoryHashSetOptions hash_set_options; // Options established at map construction time.
uint64_t hash_signature; // Hash of a constant signature string.
uint64_t num_bytes; // Bytes allocated on behalf of the map.
uint32_t size; // Number of values currently stored.
uint32_t free_cell_index; // Offset of first free cell.
};

/**
Expand Down Expand Up @@ -323,10 +326,11 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
* simply an array index because we don't know the size of a key at
* compile-time.
*/
static uint64_t cellOffset(uint32_t cell_index) {
static uint64_t cellOffset(uint32_t cell_index, const Stats::StatsOptions& stats_options) {
// sizeof(Cell) includes 'sizeof Value' which may not be accurate. So we need to
// subtract that off, and add the template method's view of the actual value-size.
uint64_t cell_size = align(sizeof(Cell) + Value::size() - sizeof(Value));
uint64_t cell_size =
align(sizeof(Cell) + Value::sizeGivenStatsOptions(stats_options) - sizeof(Value));
return cell_index * cell_size;
}

Expand All @@ -335,17 +339,17 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
*/
Cell& getCell(uint32_t cell_index) {
// Because the key-size is parameteriziable, an array-lookup on sizeof(Cell) does not work.
char* ptr = reinterpret_cast<char*>(cells_) + cellOffset(cell_index);
char* ptr = reinterpret_cast<char*>(cells_) + cellOffset(cell_index, stats_options_);
RELEASE_ASSERT((reinterpret_cast<uint64_t>(ptr) & (calculateAlignment() - 1)) == 0);
return *reinterpret_cast<Cell*>(ptr);
}

/** Maps out the segments of memory for us to work with. */
void mapMemorySegments(const BlockMemoryHashSetOptions& options, uint8_t* memory) {
void mapMemorySegments(const BlockMemoryHashSetOptions& hash_set_options, uint8_t* memory) {
// Note that we are not examining or mutating memory here, just looking at the pointer,
// so we don't need to hold any locks.
cells_ = reinterpret_cast<Cell*>(memory); // First because Value may need to be aligned.
memory += cellOffset(options.capacity);
memory += cellOffset(hash_set_options.capacity, stats_options_);
control_ = reinterpret_cast<Control*>(memory);
memory += sizeof(Control);
slots_ = reinterpret_cast<uint32_t*>(memory);
Expand All @@ -356,6 +360,7 @@ template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger
Cell* cells_;
Control* control_;
uint32_t* slots_;
const Stats::StatsOptions& stats_options_;
};

} // namespace Envoy
5 changes: 5 additions & 0 deletions source/common/config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ envoy_cc_library(
"//source/common/common:assert_lib",
"//source/common/json:config_schemas_lib",
"//source/common/protobuf:utility_lib",
"//source/common/stats:stats_lib",
"//source/extensions/stat_sinks:well_known_names",
"@envoy_api//envoy/config/bootstrap/v2:bootstrap_cc",
],
Expand Down Expand Up @@ -66,6 +67,7 @@ envoy_cc_library(
"//source/common/common:assert_lib",
"//source/common/json:config_schemas_lib",
"//source/common/network:utility_lib",
"//source/common/stats:stats_lib",
"@envoy_api//envoy/api/v2:cds_cc",
"@envoy_api//envoy/api/v2/cluster:circuit_breaker_cc",
],
Expand Down Expand Up @@ -106,6 +108,7 @@ envoy_cc_library(
":rds_json_lib",
":utility_lib",
"//include/envoy/json:json_object_interface",
"//include/envoy/stats:stats_interface",
"//source/common/common:assert_lib",
"//source/common/common:utility_lib",
"//source/common/json:config_schemas_lib",
Expand Down Expand Up @@ -218,6 +221,7 @@ envoy_cc_library(
"//source/common/common:assert_lib",
"//source/common/json:config_schemas_lib",
"//source/common/network:utility_lib",
"//source/common/stats:stats_lib",
"//source/extensions/filters/network:well_known_names",
"@envoy_api//envoy/api/v2:lds_cc",
],
Expand Down Expand Up @@ -271,6 +275,7 @@ envoy_cc_library(
"//source/common/common:assert_lib",
"//source/common/config:utility_lib",
"//source/common/json:config_schemas_lib",
"//source/common/stats:stats_lib",
"//source/extensions/filters/http:well_known_names",
"@envoy_api//envoy/api/v2:rds_cc",
],
Expand Down
Loading