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

cleanup: replace ad-hoc [0, 1] value types with UnitFloat #14081

Merged
merged 7 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
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/envoy/event/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ envoy_cc_library(
hdrs = ["scaled_range_timer_manager.h"],
deps = [
":timer_interface",
"//source/common/common:interval_value",
],
)

Expand Down
10 changes: 6 additions & 4 deletions include/envoy/event/scaled_range_timer_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "envoy/common/pure.h"
#include "envoy/event/timer.h"

#include "common/common/interval_value.h"

#include "absl/types/variant.h"

namespace Envoy {
Expand All @@ -12,8 +14,8 @@ namespace Event {
* Describes a minimum timer value that is equal to a scale factor applied to the maximum.
*/
struct ScaledMinimum {
explicit ScaledMinimum(double scale_factor) : scale_factor_(scale_factor) {}
const double scale_factor_;
explicit ScaledMinimum(UnitFloat scale_factor) : scale_factor_(scale_factor) {}
const UnitFloat scale_factor_;
};

/**
Expand Down Expand Up @@ -42,8 +44,8 @@ class ScaledTimerMinimum : private absl::variant<ScaledMinimum, AbsoluteMinimum>
struct Visitor {
explicit Visitor(std::chrono::milliseconds value) : value_(value) {}
std::chrono::milliseconds operator()(ScaledMinimum scale_factor) {
return std::chrono::duration_cast<std::chrono::milliseconds>(scale_factor.scale_factor_ *
value_);
return std::chrono::duration_cast<std::chrono::milliseconds>(
scale_factor.scale_factor_.value() * value_);
}
std::chrono::milliseconds operator()(AbsoluteMinimum absolute_value) {
return absolute_value.value_;
Expand Down
1 change: 1 addition & 0 deletions include/envoy/server/overload/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ envoy_cc_library(
deps = [
"//include/envoy/event:timer_interface",
"//include/envoy/thread_local:thread_local_object",
"//source/common/common:interval_value",
],
)
15 changes: 8 additions & 7 deletions include/envoy/server/overload/thread_local_overload_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "envoy/event/timer.h"
#include "envoy/thread_local/thread_local_object.h"

#include "common/common/interval_value.h"

namespace Envoy {
namespace Server {

Expand All @@ -19,18 +21,17 @@ namespace Server {
*/
class OverloadActionState {
public:
static constexpr OverloadActionState inactive() { return OverloadActionState(0); }
static constexpr OverloadActionState inactive() { return OverloadActionState(UnitFloat::min()); }

static constexpr OverloadActionState saturated() { return OverloadActionState(1.0); }
static constexpr OverloadActionState saturated() { return OverloadActionState(UnitFloat::max()); }

explicit constexpr OverloadActionState(float value)
: action_value_(std::min(1.0f, std::max(0.0f, value))) {}
explicit constexpr OverloadActionState(UnitFloat value) : action_value_(value) {}

float value() const { return action_value_; }
bool isSaturated() const { return action_value_ == 1; }
float value() const { return action_value_.value(); }
bool isSaturated() const { return action_value_.value() == UnitFloat::max().value(); }

private:
float action_value_;
UnitFloat action_value_;
};

/**
Expand Down
5 changes: 5 additions & 0 deletions source/common/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ envoy_cc_library(
deps = [":utility_lib"],
)

envoy_cc_library(
name = "interval_value",
hdrs = ["interval_value.h"],
)

envoy_cc_library(
name = "linked_object",
hdrs = ["linked_object.h"],
Expand Down
37 changes: 37 additions & 0 deletions source/common/common/interval_value.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include <algorithm>

namespace Envoy {

// Template helper type that represents a closed interval with the given minimum and maximum values.
template <typename T, T MinValue, T MaxValue> struct Interval {
Copy link
Contributor

Choose a reason for hiding this comment

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

super nit: Could call this closedInterval since it represents a closed interval in particular

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the name is still "Interval"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I opted to make the change to the value type since it didn't seem helpful to give the interval type an opinion on whether it is open or closed - that is, the value type is templated over a thing with a min and a max, which sounds like it could apply to both an open or closed interval (though "bounds" might be better for an open interval).

static_assert(MinValue <= MaxValue, "min must be <= max");
static constexpr T min_value = MinValue;
static constexpr T max_value = MaxValue;
};

// Utility type that represents a value of type T in the given interval.
template <typename T, typename Interval> class ClosedIntervalValue {
antoniovicente marked this conversation as resolved.
Show resolved Hide resolved
public:
static ClosedIntervalValue min() { return ClosedIntervalValue(Interval::min_value); }
static ClosedIntervalValue max() { return ClosedIntervalValue(Interval::max_value); }

constexpr explicit ClosedIntervalValue(T value)
: value_(std::max<T>(Interval::min_value, std::min<T>(Interval::max_value, value))) {}

T value() const { return value_; }

private:
T value_;
};

// C++17 doesn't allow templating on floating point values, otherwise that's
// what we should do here instead of relying on a int => float implicit
// conversion. TODO(akonradi): when Envoy is using C++20, switch these template
// parameters to floats.

// Floating point value in the range [0, 1].
using UnitFloat = ClosedIntervalValue<float, Interval<int, 0, 1>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you avoid mixing float and int in this definition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No; float template parameters aren't allowed until C++20.

Copy link
Contributor

Choose a reason for hiding this comment

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

Darn. Ok.
Worth a comment/todo to clean this up once C++ 20 is here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


} // namespace Envoy
7 changes: 2 additions & 5 deletions source/common/event/scaled_range_timer_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ TimerPtr ScaledRangeTimerManagerImpl::createTimer(ScaledTimerMinimum minimum, Ti

void ScaledRangeTimerManagerImpl::setScaleFactor(double scale_factor) {
const MonotonicTime now = dispatcher_.approximateMonotonicTime();
scale_factor_ = DurationScaleFactor(scale_factor);
scale_factor_ = UnitFloat(scale_factor);
for (auto& queue : queues_) {
resetQueueTimer(*queue, now);
}
Expand All @@ -171,12 +171,9 @@ ScaledRangeTimerManagerImpl::ScalingTimerHandle::ScalingTimerHandle(Queue& queue
Queue::Iterator iterator)
: queue_(queue), iterator_(iterator) {}

ScaledRangeTimerManagerImpl::DurationScaleFactor::DurationScaleFactor(double value)
: value_(std::max(0.0, std::min(value, 1.0))) {}

MonotonicTime ScaledRangeTimerManagerImpl::computeTriggerTime(const Queue::Item& item,
std::chrono::milliseconds duration,
DurationScaleFactor scale_factor) {
UnitFloat scale_factor) {
return item.active_time_ +
std::chrono::duration_cast<MonotonicTime::duration>(duration * scale_factor.value());
}
Expand Down
14 changes: 2 additions & 12 deletions source/common/event/scaled_range_timer_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,6 @@ class ScaledRangeTimerManagerImpl : public ScaledRangeTimerManager {
Queue::Iterator iterator_;
};

// A simple wrapper around a float that ensures value() is sane (in the range [0, 1]).
class DurationScaleFactor {
public:
DurationScaleFactor(double value);
double value() const { return value_; }

private:
double value_;
};

struct Hash {
// Magic declaration to allow heterogeneous lookup.
using is_transparent = void; // NOLINT(readability-identifier-naming)
Expand Down Expand Up @@ -113,7 +103,7 @@ class ScaledRangeTimerManagerImpl : public ScaledRangeTimerManager {

static MonotonicTime computeTriggerTime(const Queue::Item& item,
std::chrono::milliseconds duration,
DurationScaleFactor scale_factor);
UnitFloat scale_factor);

ScalingTimerHandle activateTimer(std::chrono::milliseconds duration, RangeTimerImpl& timer);

Expand All @@ -124,7 +114,7 @@ class ScaledRangeTimerManagerImpl : public ScaledRangeTimerManager {
void onQueueTimerFired(Queue& queue);

Dispatcher& dispatcher_;
DurationScaleFactor scale_factor_;
UnitFloat scale_factor_;
absl::flat_hash_set<std::unique_ptr<Queue>, Hash, Eq> queues_;
};

Expand Down
15 changes: 8 additions & 7 deletions source/server/overload_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ThreadLocalOverloadStateImpl : public ThreadLocalOverloadState {
const NamedOverloadActionSymbolTable& action_symbol_table,
const absl::flat_hash_map<OverloadTimerType, Event::ScaledTimerMinimum>& timer_minimums)
: action_symbol_table_(action_symbol_table), timer_minimums_(timer_minimums),
actions_(action_symbol_table.size(), OverloadActionState(0)),
actions_(action_symbol_table.size(), OverloadActionState(UnitFloat::min())),
scaled_timer_action_(action_symbol_table.lookup(OverloadActionNames::get().ReduceTimeouts)),
scaled_timer_manager_(std::move(scaled_timer_manager)) {}

Expand All @@ -46,8 +46,9 @@ class ThreadLocalOverloadStateImpl : public ThreadLocalOverloadState {
Event::TimerCb callback) override {
auto minimum_it = timer_minimums_.find(timer_type);
const Event::ScaledTimerMinimum minimum =
minimum_it != timer_minimums_.end() ? minimum_it->second
: Event::ScaledTimerMinimum(Event::ScaledMinimum(1.0));
minimum_it != timer_minimums_.end()
? minimum_it->second
: Event::ScaledTimerMinimum(Event::ScaledMinimum(UnitFloat::max()));
return scaled_timer_manager_->createTimer(minimum, std::move(callback));
}

Expand All @@ -67,7 +68,7 @@ class ThreadLocalOverloadStateImpl : public ThreadLocalOverloadState {
const Event::ScaledRangeTimerManagerPtr scaled_timer_manager_;
};

const OverloadActionState ThreadLocalOverloadStateImpl::always_inactive_{0.0};
const OverloadActionState ThreadLocalOverloadStateImpl::always_inactive_{UnitFloat::min()};

namespace {

Expand Down Expand Up @@ -108,8 +109,8 @@ class ScaledTriggerImpl final : public OverloadAction::Trigger {
} else if (value >= saturated_threshold_) {
state_ = OverloadActionState::saturated();
} else {
state_ = OverloadActionState((value - scaling_threshold_) /
(saturated_threshold_ - scaling_threshold_));
state_ = OverloadActionState(
UnitFloat((value - scaling_threshold_) / (saturated_threshold_ - scaling_threshold_)));
}
return state_.value() != old_state.value();
}
Expand Down Expand Up @@ -164,7 +165,7 @@ parseTimerMinimums(const ProtobufWkt::Any& typed_config,
? Event::ScaledTimerMinimum(Event::AbsoluteMinimum(std::chrono::milliseconds(
DurationUtil::durationToMilliseconds(scale_timer.min_timeout()))))
: Event::ScaledTimerMinimum(
Event::ScaledMinimum(scale_timer.min_scale().value() / 100.0));
Event::ScaledMinimum(UnitFloat(scale_timer.min_scale().value() / 100.0)));

auto [_, inserted] = timer_map.insert(std::make_pair(timer_type, minimum));
if (!inserted) {
Expand Down
12 changes: 6 additions & 6 deletions test/common/event/scaled_range_timer_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ TEST_F(ScaledRangeTimerManagerTest, CreateAndDestroyTimer) {

{
MockFunction<TimerCb> callback;
auto timer = manager.createTimer(ScaledMinimum(1.0), callback.AsStdFunction());
auto timer = manager.createTimer(ScaledMinimum(UnitFloat(1.0)), callback.AsStdFunction());
}
}

TEST_F(ScaledRangeTimerManagerTest, CreateSingleScaledTimer) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

MockFunction<TimerCb> callback;
auto timer = manager.createTimer(ScaledMinimum(0.5), callback.AsStdFunction());
auto timer = manager.createTimer(ScaledMinimum(UnitFloat(0.5)), callback.AsStdFunction());

timer->enableTimer(std::chrono::seconds(10));
EXPECT_TRUE(timer->enabled());
Expand Down Expand Up @@ -110,7 +110,7 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWhileDisabled) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

MockFunction<TimerCb> callback;
auto timer = manager.createTimer(ScaledMinimum(1.0), callback.AsStdFunction());
auto timer = manager.createTimer(ScaledMinimum(UnitFloat(1.0)), callback.AsStdFunction());

EXPECT_FALSE(timer->enabled());
timer->disableTimer();
Expand Down Expand Up @@ -155,7 +155,7 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWithZeroMinTime) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

MockFunction<TimerCb> callback;
auto timer = manager.createTimer(ScaledMinimum(0.0), callback.AsStdFunction());
auto timer = manager.createTimer(ScaledMinimum(UnitFloat(0.0)), callback.AsStdFunction());

timer->enableTimer(std::chrono::seconds(100));

Expand Down Expand Up @@ -319,7 +319,7 @@ TEST_F(ScaledRangeTimerManagerTest, SingleTimerSameMinMax) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

MockFunction<TimerCb> callback;
auto timer = manager.createTimer(ScaledMinimum(1.0), callback.AsStdFunction());
auto timer = manager.createTimer(ScaledMinimum(UnitFloat(1.0)), callback.AsStdFunction());
EXPECT_CALL(callback, Call());

timer->enableTimer(std::chrono::seconds(1));
Expand All @@ -333,7 +333,7 @@ TEST_F(ScaledRangeTimerManagerTest, ScaledMinimumFactorGreaterThan1) {

// If the minimum scale factor is > 1, it should be treated as if it was 1.
MockFunction<TimerCb> callback;
auto timer = manager.createTimer(ScaledMinimum(2), callback.AsStdFunction());
auto timer = manager.createTimer(ScaledMinimum(UnitFloat(2)), callback.AsStdFunction());

timer->enableTimer(std::chrono::seconds(10));
EXPECT_CALL(callback, Call);
Expand Down
4 changes: 2 additions & 2 deletions test/common/http/conn_manager_impl_test_2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2070,7 +2070,7 @@ TEST(HttpConnectionManagerTracingStatsTest, verifyTracingStats) {
}

TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) {
Server::OverloadActionState stop_accepting_requests = Server::OverloadActionState(0.8);
Server::OverloadActionState stop_accepting_requests(UnitFloat(0.8));
ON_CALL(overload_manager_.overload_state_,
getState(Server::OverloadActionNames::get().StopAcceptingRequests))
.WillByDefault(ReturnRef(stop_accepting_requests));
Expand All @@ -2095,7 +2095,7 @@ TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) {
}

TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) {
Server::OverloadActionState disable_http_keep_alive = Server::OverloadActionState(0.8);
Server::OverloadActionState disable_http_keep_alive(UnitFloat(0.8));
ON_CALL(overload_manager_.overload_state_,
getState(Server::OverloadActionNames::get().DisableHttpKeepAlive))
.WillByDefault(ReturnRef(disable_http_keep_alive));
Expand Down