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

merge from upstream #10

Merged
merged 6 commits into from
Jul 16, 2020
Merged
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Add new NH statistic class CircllhistStatistic and SinkableCircllhist… (
envoyproxy#391)

Add new NH statistic class CircllhistStatistic and SinkableCircllhistStatistic.

CircllhistStatistic uses Circllhist under the hood to compute statistics where Circllhist is used in the implementation of Envoy Histograms. Compared to HdrHistogram Circllhist trades precision for fast performance in merge and insertion according to envoyproxy#115.

SinkableCircllhistStatistic wraps the Envoy::Stats::Histogram interface and is used to flush histogram value to downstream Envoy stats Sinks.

Linked Issues: envoyproxy#344

Testing: unit tests
qqustc authored Jul 14, 2020

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
commit b4cebe483749004c1996ae76b521aa5f894b2776
2 changes: 1 addition & 1 deletion source/common/BUILD
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ envoy_cc_library(
"@envoy//source/common/http:utility_lib_with_external_headers",
"@envoy//source/common/network:utility_lib_with_external_headers",
"@envoy//source/common/protobuf:utility_lib_with_external_headers",
"@envoy//source/common/stats:stats_lib_with_external_headers",
"@envoy//source/common/stats:histogram_lib_with_external_headers",
"@envoy//source/exe:envoy_common_lib_with_external_headers",
"@envoy//source/server/config_validation:server_lib_with_external_headers",
],
100 changes: 99 additions & 1 deletion source/common/statistic_impl.cc
Original file line number Diff line number Diff line change
@@ -236,4 +236,102 @@ nighthawk::client::Statistic HdrStatistic::toProto(SerializationDomain domain) c
return proto;
}

} // namespace Nighthawk
CircllhistStatistic::CircllhistStatistic() {
histogram_ = hist_alloc();
ASSERT(histogram_ != nullptr);
}

CircllhistStatistic::~CircllhistStatistic() { hist_free(histogram_); }

void CircllhistStatistic::addValue(uint64_t value) {
hist_insert_intscale(histogram_, value, 0, 1);
StatisticImpl::addValue(value);
}
double CircllhistStatistic::mean() const { return hist_approx_mean(histogram_); }
double CircllhistStatistic::pvariance() const { return pstdev() * pstdev(); }
double CircllhistStatistic::pstdev() const {
return count() == 0 ? std::nan("") : hist_approx_stddev(histogram_);
}

StatisticPtr CircllhistStatistic::combine(const Statistic& statistic) const {
auto combined = std::make_unique<CircllhistStatistic>();
const auto& stat = dynamic_cast<const CircllhistStatistic&>(statistic);
hist_accumulate(combined->histogram_, &this->histogram_, /*cnt=*/1);
hist_accumulate(combined->histogram_, &stat.histogram_, /*cnt=*/1);

combined->min_ = std::min(this->min(), stat.min());
combined->max_ = std::max(this->max(), stat.max());
combined->count_ = this->count() + stat.count();
return combined;
}

StatisticPtr CircllhistStatistic::createNewInstanceOfSameType() const {
return std::make_unique<CircllhistStatistic>();
}

nighthawk::client::Statistic CircllhistStatistic::toProto(SerializationDomain domain) const {
nighthawk::client::Statistic proto = StatisticImpl::toProto(domain);
if (count() == 0) {
return proto;
}

// List of quantiles is based on hdr_proto_json.gold.
const std::vector<double> quantiles{0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.55, 0.6,
0.65, 0.7, 0.75, 0.775, 0.8, 0.825, 0.85, 0.875,
0.90, 0.925, 0.95, 0.975, 0.99, 0.995, 0.999, 1};
std::vector<double> computed_quantiles(quantiles.size(), 0.0);
hist_approx_quantile(histogram_, quantiles.data(), quantiles.size(), computed_quantiles.data());
for (size_t i = 0; i < quantiles.size(); i++) {
nighthawk::client::Percentile* percentile = proto.add_percentiles();
if (domain == Statistic::SerializationDomain::DURATION) {
setDurationFromNanos(*percentile->mutable_duration(),
static_cast<int64_t>(computed_quantiles[i]));
} else {
percentile->set_raw_value(computed_quantiles[i]);
}
percentile->set_percentile(quantiles[i]);
percentile->set_count(hist_approx_count_below(histogram_, computed_quantiles[i]));
}

return proto;
}

SinkableStatistic::SinkableStatistic(Envoy::Stats::Scope& scope, absl::optional<int> worker_id)
: Envoy::Stats::HistogramImplHelper(scope.symbolTable()), scope_(scope), worker_id_(worker_id) {
}

SinkableStatistic::~SinkableStatistic() {
// We must explicitly free the StatName here in order to supply the
// SymbolTable reference.
MetricImpl::clear(scope_.symbolTable());
}

Envoy::Stats::Histogram::Unit SinkableStatistic::unit() const {
return Envoy::Stats::Histogram::Unit::Unspecified;
}

Envoy::Stats::SymbolTable& SinkableStatistic::symbolTable() { return scope_.symbolTable(); }

SinkableHdrStatistic::SinkableHdrStatistic(Envoy::Stats::Scope& scope,
absl::optional<int> worker_id)
: SinkableStatistic(scope, worker_id) {}

void SinkableHdrStatistic::recordValue(uint64_t value) {
addValue(value);
// Currently in Envoy Scope implementation, deliverHistogramToSinks() will flush the histogram
// value directly to stats Sinks.
scope_.deliverHistogramToSinks(*this, value);
}

SinkableCircllhistStatistic::SinkableCircllhistStatistic(Envoy::Stats::Scope& scope,
absl::optional<int> worker_id)
: SinkableStatistic(scope, worker_id) {}

void SinkableCircllhistStatistic::recordValue(uint64_t value) {
addValue(value);
// Currently in Envoy Scope implementation, deliverHistogramToSinks() will flush the histogram
// value directly to stats Sinks.
scope_.deliverHistogramToSinks(*this, value);
}

} // namespace Nighthawk
93 changes: 92 additions & 1 deletion source/common/statistic_impl.h
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@

#include "external/dep_hdrhistogram_c/src/hdr_histogram.h"
#include "external/envoy/source/common/common/logger.h"
#include "external/envoy/source/common/stats/histogram_impl.h"

#include "common/frequency.h"

@@ -150,4 +151,94 @@ class HdrStatistic : public StatisticImpl {
struct hdr_histogram* histogram_;
};

} // namespace Nighthawk
/**
* CircllhistStatistic uses Circllhist under the hood to compute statistics.
* Circllhist is used in the implementation of Envoy Histograms, compared to HdrHistogram it trades
* precision for fast performance in merge and insertion. For more info, please see
* https://github.com/circonus-labs/libcircllhist
*/
class CircllhistStatistic : public StatisticImpl {
public:
CircllhistStatistic();
~CircllhistStatistic() override;

void addValue(uint64_t value) override;
double mean() const override;
double pvariance() const override;
double pstdev() const override;
StatisticPtr combine(const Statistic& statistic) const override;
// circllhist has low significant digit precision as a result of base 10
// algorithm.
uint64_t significantDigits() const override { return 1; }
StatisticPtr createNewInstanceOfSameType() const override;
nighthawk::client::Statistic toProto(SerializationDomain domain) const override;

private:
histogram_t* histogram_;
};

/**
* In order to be able to flush a histogram value to downstream Envoy stats Sinks, abstract class
* SinkableStatistic takes the Scope reference in the constructor and wraps the
* Envoy::Stats::HistogramHelper interface. Implementation of sinkable Nighthawk Statistic class
* will inherit from this class.
*/
class SinkableStatistic : public Envoy::Stats::HistogramImplHelper {
public:
// Calling HistogramImplHelper(SymbolTable& symbol_table) constructor to construct an empty
// MetricImpl. This is to bypass the complicated logic of setting up SymbolTable/StatName in
// Envoy.
SinkableStatistic(Envoy::Stats::Scope& scope, absl::optional<int> worker_id);
~SinkableStatistic() override;

// Currently Envoy Histogram Unit supports {Unspecified, Bytes, Microseconds, Milliseconds}. By
// default, Nighthawk::Statistic uses nanosecond as the unit of latency histograms, so Unspecified
// is returned here to isolate Nighthawk Statistic from Envoy Histogram Unit.
Envoy::Stats::Histogram::Unit unit() const override;
Envoy::Stats::SymbolTable& symbolTable() override;
// Return the id of the worker where this statistic is defined. Per worker
// statistic should always set worker_id. Return absl::nullopt when the
// statistic is not defined per worker.
const absl::optional<int> worker_id() { return worker_id_; }

protected:
// This is used in child class for delivering the histogram data to sinks.
Envoy::Stats::Scope& scope_;

private:
// worker_id can be used in downstream stats Sinks as the stats tag.
absl::optional<int> worker_id_;
};

// Implementation of sinkable Nighthawk Statistic with HdrHistogram.
class SinkableHdrStatistic : public SinkableStatistic, public HdrStatistic {
public:
// The constructor takes the Scope reference which is used to flush a histogram value to
// downstream stats Sinks through deliverHistogramToSinks().
SinkableHdrStatistic(Envoy::Stats::Scope& scope, absl::optional<int> worker_id = absl::nullopt);

// Envoy::Stats::Histogram
void recordValue(uint64_t value) override;
bool used() const override { return count() > 0; }
// Overriding name() to return Nighthawk::Statistic::id().
std::string name() const override { return id(); }
std::string tagExtractedName() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; }
};

// Implementation of sinkable Nighthawk Statistic with Circllhist Histogram.
class SinkableCircllhistStatistic : public SinkableStatistic, public CircllhistStatistic {
public:
// The constructor takes the Scope reference which is used to flush a histogram value to
// downstream stats Sinks through deliverHistogramToSinks().
SinkableCircllhistStatistic(Envoy::Stats::Scope& scope,
absl::optional<int> worker_id = absl::nullopt);

// Envoy::Stats::Histogram
void recordValue(uint64_t value) override;
bool used() const override { return count() > 0; }
// Overriding name() to return Nighthawk::Statistic::id().
std::string name() const override { return id(); }
std::string tagExtractedName() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; }
};

} // namespace Nighthawk
6 changes: 5 additions & 1 deletion test/BUILD
Original file line number Diff line number Diff line change
@@ -195,13 +195,17 @@ envoy_cc_test(
envoy_cc_test(
name = "statistic_test",
srcs = ["statistic_test.cc"],
data = ["test_data/hdr_proto_json.gold"],
data = [
"test_data/circllhist_proto_json.gold",
"test_data/hdr_proto_json.gold",
],
repository = "@envoy",
deps = [
"//source/common:nighthawk_common_lib",
"//test/test_common:environment_lib",
"@envoy//source/common/protobuf:utility_lib_with_external_headers",
"@envoy//source/common/stats:isolated_store_lib_with_external_headers",
"@envoy//test/mocks/stats:stats_mocks",
],
)

4 changes: 2 additions & 2 deletions test/run_nighthawk_bazel_coverage.sh
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ COVERAGE_VALUE=${COVERAGE_VALUE%?}

if [ "$VALIDATE_COVERAGE" == "true" ]
then
COVERAGE_THRESHOLD=98.6
COVERAGE_THRESHOLD=98.4
COVERAGE_FAILED=$(echo "${COVERAGE_VALUE}<${COVERAGE_THRESHOLD}" | bc)
if test ${COVERAGE_FAILED} -eq 1; then
echo Code coverage ${COVERAGE_VALUE} is lower than limit of ${COVERAGE_THRESHOLD}
@@ -52,4 +52,4 @@ then
echo Code coverage ${COVERAGE_VALUE} is good and higher than limit of ${COVERAGE_THRESHOLD}
fi
fi
echo "HTML coverage report is in ${COVERAGE_DIR}/index.html"
echo "HTML coverage report is in ${COVERAGE_DIR}/index.html"
Loading