Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions include/nighthawk/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ envoy_basic_cc_library(
"@envoy//include/envoy/upstream:cluster_manager_interface_with_external_headers",
"@envoy//source/common/common:minimal_logger_lib",
"@envoy//source/common/common:non_copyable_with_external_headers",
"@envoy//source/common/common:statusor_lib_with_external_headers",
"@envoy//source/common/event:dispatcher_lib_with_external_headers",
"@envoy//source/common/network:utility_lib_with_external_headers",
],
Expand Down
18 changes: 18 additions & 0 deletions include/nighthawk/common/statistic.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "api/client/output.pb.h"

#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"

namespace Nighthawk {
Expand Down Expand Up @@ -115,6 +116,23 @@ class Statistic : Envoy::NonCopyable {
* @param id The id that should be set for the Statistic instance.
*/
virtual void setId(absl::string_view id) PURE;

/**
* Build a string representation of this Statistic instance.
*
* @return absl::StatusOr<std::unique_ptr<std::istream>> Status or a stream that will yield
* a serialized representation of this Statistic instance.
*/
virtual absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const PURE;
Comment thread
mum4k marked this conversation as resolved.

/**
* Reconstruct this Statistic instance using the serialization delivered by the input stream.
*
* @param input_stream Stream that will deliver a serialized representation.
* @return absl::Status Status indicating success or failure. Upon success the statistic
* instance this was called for will now represent what the stream contained.
*/
virtual absl::Status deserializeNative(std::istream& input_stream) PURE;
};

} // namespace Nighthawk
35 changes: 35 additions & 0 deletions source/common/statistic_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

#include <cmath>
#include <cstdio>
#include <fstream>
#include <sstream>

#include "external/dep_hdrhistogram_c/src/hdr_histogram_log.h"
#include "external/envoy/source/common/common/assert.h"
#include "external/envoy/source/common/protobuf/utility.h"

Expand Down Expand Up @@ -68,6 +70,14 @@ uint64_t StatisticImpl::min() const { return min_; };

uint64_t StatisticImpl::max() const { return max_; };

absl::StatusOr<std::unique_ptr<std::istream>> StatisticImpl::serializeNative() const {
return absl::Status(absl::StatusCode::kUnimplemented, "serializeNative not implemented.");
}

absl::Status StatisticImpl::deserializeNative(std::istream&) {
return absl::Status(absl::StatusCode::kUnimplemented, "deserializeNative not implemented.");
}

void SimpleStatistic::addValue(uint64_t value) {
StatisticImpl::addValue(value);
sum_x_ += value;
Expand Down Expand Up @@ -236,6 +246,31 @@ nighthawk::client::Statistic HdrStatistic::toProto(SerializationDomain domain) c
return proto;
}

absl::StatusOr<std::unique_ptr<std::istream>> HdrStatistic::serializeNative() const {
char* data;
if (hdr_log_encode(histogram_, &data) == 0) {
absl::string_view s(data, strlen(data));
auto ss = std::make_unique<std::stringstream>();
Comment thread
mum4k marked this conversation as resolved.
Outdated
*ss << s;
free(data);
return ss;
}
ENVOY_LOG(error, "Failed to write HdrHistogram data.");
return absl::Status(absl::StatusCode::kInternal, "Failed to write HdrHistogram data");
}

absl::Status HdrStatistic::deserializeNative(std::istream& stream) {
std::string s(std::istreambuf_iterator<char>(stream), {});
struct hdr_histogram* new_histogram = nullptr;
Comment thread
mum4k marked this conversation as resolved.
if (hdr_log_decode(&new_histogram, const_cast<char*>(s.c_str()), s.length()) == 0) {
hdr_close(histogram_);
histogram_ = new_histogram;
return absl::OkStatus();
}
ENVOY_LOG(error, "Failed to read back HdrHistogram data.");
return absl::Status(absl::StatusCode::kInternal, "Failed to read back HdrHistogram data");
}

CircllhistStatistic::CircllhistStatistic() {
histogram_ = hist_alloc();
ASSERT(histogram_ != nullptr);
Expand Down
5 changes: 5 additions & 0 deletions source/common/statistic_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class StatisticImpl : public Statistic, public Envoy::Logger::Loggable<Envoy::Lo
uint64_t count() const override;
uint64_t max() const override;
uint64_t min() const override;
absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const override;
absl::Status deserializeNative(std::istream&) override;

protected:
std::string id_;
Expand Down Expand Up @@ -146,6 +148,9 @@ class HdrStatistic : public StatisticImpl {
return std::make_unique<HdrStatistic>();
};

absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const override;
absl::Status deserializeNative(std::istream&) override;

private:
static const int SignificantDigits;
struct hdr_histogram* histogram_;
Expand Down
36 changes: 36 additions & 0 deletions test/statistic_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,42 @@ TYPED_TEST(TypedStatisticTest, ProtoOutputEmptyStats) {
EXPECT_EQ(proto.pstdev().nanos(), 0);
}

TYPED_TEST(TypedStatisticTest, NativeRoundtrip) {
TypeParam a;

a.setId("bar");
a.addValue(6543456);
a.addValue(342335);
a.addValue(543);

const absl::StatusOr<std::unique_ptr<std::istream>> status_or_stream = a.serializeNative();
if (status_or_stream.ok()) {
// If the histogram states it implements native serialization/deserialization, put it through
// a round trip test.
TypeParam b;
absl::Status status = b.deserializeNative(*status_or_stream.value());
EXPECT_TRUE(status.ok());
EXPECT_EQ(3, b.count());
EXPECT_EQ(a.count(), b.count());
EXPECT_EQ(a.mean(), b.mean());
EXPECT_EQ(a.pstdev(), b.pstdev());
} else {
EXPECT_EQ(status_or_stream.status().code(), absl::StatusCode::kUnimplemented);
}
}

TYPED_TEST(TypedStatisticTest, AttemptsToDeserializeBogusBehaveWell) {
// Deserializing corrupted data should either result in the statistic reporting
// it didn't implement deserialization, or having it report an internal failure.
const std::vector<absl::StatusCode> expected_status_list{absl::StatusCode::kInternal,
absl::StatusCode::kUnimplemented};
TypeParam a;
std::istringstream bogus_input(std::string("BOGUS"));
const absl::Status status = a.deserializeNative(bogus_input);
EXPECT_FALSE(status.ok());
EXPECT_THAT(expected_status_list, Contains(status.code()));
}

TYPED_TEST(TypedStatisticTest, StringOutput) {
TypeParam a;

Expand Down