Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a76a706
Phases concept
oschaaf Oct 25, 2019
4a348eb
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Nov 27, 2019
7883d59
Update Envoy dep for access to enableHRTimer()
oschaaf Dec 3, 2019
e458ea5
Whoops, amend bad copying
oschaaf Dec 3, 2019
e7b65c1
Also, sync .bazelrc while at it
oschaaf Dec 3, 2019
db1c468
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 5, 2019
731736b
save state
oschaaf Dec 6, 2019
79bdf33
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 6, 2019
ebf82a3
back out dns for future dep update
oschaaf Dec 6, 2019
658bd48
Fix sequencer execution duration issue
oschaaf Dec 8, 2019
bff40ac
Fix format
oschaaf Dec 8, 2019
d12d0d5
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 12, 2019
b5b5fd2
Merge remote-tracking branch 'upstream/master' into update-envoy-dep-8
oschaaf Dec 12, 2019
fb85732
Update Envoy to the latest
oschaaf Dec 12, 2019
3ea8055
Add documentation to phase.h
oschaaf Dec 12, 2019
ca49ab5
Converge state to mostly a no-op compared to what's on master
oschaaf Dec 12, 2019
9528d92
Add comments
oschaaf Dec 12, 2019
281a4e3
Comment update
oschaaf Dec 12, 2019
8a268fe
Move to the sha that has our target
oschaaf Dec 12, 2019
34f649b
Unbreak it
oschaaf Dec 13, 2019
b31f03e
Merge remote-tracking branch 'origin/update-envoy-dep-8' into phases-pt1
oschaaf Dec 13, 2019
cdb10e5
Changes to avoid functional changes
oschaaf Dec 13, 2019
3298929
Fix comments
oschaaf Dec 13, 2019
62914e4
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 13, 2019
07fa643
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 16, 2019
c32ba0e
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 19, 2019
0fd8271
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Dec 23, 2019
4dcb33b
Review: fix a typo
oschaaf Dec 23, 2019
efd8a51
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 3, 2020
128b633
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 4, 2020
c927a6e
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 6, 2020
be3f183
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 10, 2020
0bd9cfd
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 10, 2020
b81f4c5
Review feedback
oschaaf Jan 10, 2020
0ab72fd
Review feedback
oschaaf Jan 10, 2020
49c9344
Review feedback
oschaaf Jan 10, 2020
fc998a2
Test TerminationPredicate::appendToChain
oschaaf Jan 11, 2020
29872a7
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 14, 2020
e8300c9
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 15, 2020
f3df58c
Drop <vector> includes we no longer need
oschaaf Jan 15, 2020
f3f3271
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 15, 2020
ba832c3
Merge remote-tracking branch 'upstream/master' into phases-pt1
oschaaf Jan 15, 2020
b75803e
Leverage ScheduledStartingRateLimiter
oschaaf Jan 15, 2020
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
7 changes: 4 additions & 3 deletions include/nighthawk/client/client_worker.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#pragma once

#include <memory>
#include <vector>

#include "envoy/common/pure.h"
#include "envoy/stats/store.h"

#include "nighthawk/client/benchmark_client.h"
#include "nighthawk/common/sequencer.h"
#include "nighthawk/common/phase.h"
#include "nighthawk/common/statistic.h"
#include "nighthawk/common/worker.h"

Expand All @@ -31,9 +32,9 @@ class ClientWorker : virtual public Worker {
virtual const std::map<std::string, uint64_t>& thread_local_counter_values() PURE;

/**
* @return const Sequencer&
* @return const std::vector<PhasePtr>& vector of phases associated to this worker.
*/
virtual const Sequencer& sequencer() const PURE;
virtual const std::vector<PhasePtr>& phases() const PURE;
};

using ClientWorkerPtr = std::unique_ptr<ClientWorker>;
Expand Down
10 changes: 5 additions & 5 deletions include/nighthawk/client/factories.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class SequencerFactory {
public:
virtual ~SequencerFactory() = default;
virtual SequencerPtr create(Envoy::TimeSource& time_source, Envoy::Event::Dispatcher& dispatcher,
Envoy::MonotonicTime start_time, BenchmarkClient& benchmark_client,
TerminationPredicate& termination_predicate,
Envoy::Stats::Scope& scope) const PURE;
BenchmarkClient& benchmark_client,
TerminationPredicatePtr&& termination_predicate,
Envoy::Stats::Scope& scope, const bool warmup) const PURE;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Similar to our discussion in one of the previous PRs, how about we use an enum instead of the boolean flag argument? Looks like this boolean is used in a couple of interfaces that plumb the phases through. We might be able to reuse the enum in multiple locations.

This however makes me wonder how the end result is going to look like. If we go down the path of dynamically configured phases, what are we going to replace the boolean here with? Do we need a class carrying information about phases?

With that said - is this something we should leave to the next PR? Or would it be more beneficial to come up with a good structure now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm happy to iterate here. My goal with this is to land a decent chunk of the changes required to implement phases as a no-op, as an intermediate step to make review more fun.
Later on we'll use them generically, we can also iterate on this in here in this PR here if you prefer. Or we can go for shipping this, and iterate on a second step in another one.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I eliminated the warmup bool arg, to minimize changes that won't have future value as per earlier discussion.

};

class StoreFactory {
Expand Down Expand Up @@ -72,8 +72,8 @@ class RequestSourceFactory {
class TerminationPredicateFactory {
public:
virtual ~TerminationPredicateFactory() = default;
virtual TerminationPredicatePtr create(Envoy::TimeSource& time_source, Envoy::Stats::Scope& scope,
Envoy::MonotonicTime time) const PURE;
virtual TerminationPredicatePtr create(Envoy::TimeSource& time_source,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

(optional) If you don't mind, can we document this interface since we are touching this? Optional, since this is unrelated to this PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe I should just go over all the interfaces, and document everything that isn't in one go in a separate PR? I'd be happy to.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Assuming you don't mind doing that, that would be well worth the time and would help us when reading the code base.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Filed #268

Envoy::Stats::Scope& scope) const PURE;
};

} // namespace Nighthawk
1 change: 1 addition & 0 deletions include/nighthawk/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ envoy_basic_cc_library(
hdrs = [
"exception.h",
"operation_callback.h",
"phase.h",
"platform_util.h",
"rate_limiter.h",
"request_source.h",
Expand Down
50 changes: 50 additions & 0 deletions include/nighthawk/common/phase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

#pragma once

#include <memory>

#include "envoy/common/pure.h"

#include "nighthawk/common/sequencer.h"

namespace Nighthawk {

/**
* Phase represents a distinct phase of a benchmmark execution, like warmup and cooldown.
* A phase is associated to a sequencer, which in turn can be associated to separate termination
* and failure predicates as well as its own rate limiter policy. The end of a phase also represents
* a natural boundary for reporting a snapshot of stats and latencies associated to the phase.
* High level, a worker statically configure a vector of phases, and will transfer the hot
* connection pool when transitioning between them. At this time, nothing is stopping us from
* dynamically injecting phases later, be it via grpc calls and/or live CLI input.
*/
class Phase {
public:
virtual ~Phase() = default;

/**
* @return absl::string_view Contains the id of the phase. Should be unique but that is not
* enforced at this time so take care.
*/
virtual absl::string_view id() const PURE;

/**
* @return Sequencer& Sequencer associated to this phase.
*/
virtual Sequencer& sequencer() const PURE;

/**
* @return bool Indicates if latencies should be tracked for this phase.
*/
virtual bool measureLatencies() const PURE;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

(nit) The current name sounds like a verb (a command) and could be misread as a request to start measuring latencies. It helps to include the word "should" in boolean functions that are technically asking a question. How about: shouldMeasureLatencies() or similar?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think the convention is that field_name translates into (set)fieldName() for the getter/setter. Possibly the naming is unfortunate here, for the field it is pretty clear, but for the associated getter/setter it turns out unfortunate. I'll amend.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Renamed to shouldMeasureLatencies


/**
* Runs the sequencer associated to this phase and blocks until completion, which means this phase
* has ended as well.
*/
virtual void run() const PURE;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Doesn't the phase need to communicate any result status / error code, etc?

If there already is a way for us to examine the result, can we document it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm mulling this over; we can query the stats and have this decoupled, but we could probably also return the last termination predicate result here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added a doc comment here

};

using PhasePtr = std::unique_ptr<Phase>;

} // namespace Nighthawk
8 changes: 8 additions & 0 deletions include/nighthawk/common/termination_predicate.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ class TerminationPredicate {
*/
virtual TerminationPredicate& link(TerminationPredicatePtr&& child) PURE;

/**
* Appends a predicate to the last element of the chain.
*
* @param child the child predicate to link. nullptr is not allowed.
* @return the dereferenced input child predicate. For convenience, so calls can be chained.
*/
virtual TerminationPredicate& appendToChain(TerminationPredicatePtr&& child) PURE;

/**
* Recursively evaluates chain of linked predicates, this instance last.
* If any linked element returns anything other then PROCEED that status will
Expand Down
65 changes: 46 additions & 19 deletions source/client/client_worker_impl.cc
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
#include "client/client_worker_impl.h"

#include <vector>

#include "external/envoy/source/common/stats/symbol_table_impl.h"

#include "common/request_source_impl.h"
#include "common/phase_impl.h"
#include "common/termination_predicate_impl.h"
#include "common/utility.h"

namespace Nighthawk {
namespace Client {

using namespace std::chrono_literals;

ClientWorkerImpl::ClientWorkerImpl(Envoy::Api::Api& api, Envoy::ThreadLocal::Instance& tls,
Envoy::Upstream::ClusterManagerPtr& cluster_manager,
const BenchmarkClientFactory& benchmark_client_factory,
Expand All @@ -17,34 +22,55 @@ ClientWorkerImpl::ClientWorkerImpl(Envoy::Api::Api& api, Envoy::ThreadLocal::Ins
Envoy::Stats::Store& store, const int worker_number,
const Envoy::MonotonicTime starting_time,
Envoy::Tracing::HttpTracerPtr& http_tracer)
: WorkerImpl(api, tls, store), worker_scope_(store_.createScope("cluster.")),
: WorkerImpl(api, tls, store), termination_predicate_factory_(termination_predicate_factory),
sequencer_factory_(sequencer_factory), worker_scope_(store_.createScope("cluster.")),
worker_number_scope_(worker_scope_->createScope(fmt::format("{}.", worker_number))),
worker_number_(worker_number), starting_time_(starting_time), http_tracer_(http_tracer),
request_generator_(request_generator_factory.create()),
benchmark_client_(benchmark_client_factory.create(
api, *dispatcher_, *worker_number_scope_, cluster_manager, http_tracer_,
fmt::format("{}", worker_number), *request_generator_)),
termination_predicate_(
termination_predicate_factory.create(time_source_, *worker_number_scope_, starting_time)),
sequencer_(sequencer_factory.create(time_source_, *dispatcher_, starting_time,
*benchmark_client_, *termination_predicate_,
*worker_number_scope_)) {}
fmt::format("{}", worker_number), *request_generator_)) {}

void ClientWorkerImpl::simpleWarmup() {
ENVOY_LOG(debug, "> worker {}: warmup start.", worker_number_);
if (benchmark_client_->tryStartRequest([this](bool, bool) { dispatcher_->exit(); })) {
dispatcher_->run(Envoy::Event::Dispatcher::RunType::RunUntilExit);
} else {
ENVOY_LOG(warn, "> worker {}: failed to initiate warmup request.", worker_number_);
}
ENVOY_LOG(debug, "> worker {}: warmup done.", worker_number_);
// We add a short warmup phase, which ends when either the first successful response
// is observed or two seconds have passed, whichever comes first. These warmup conditions are
// registered on top of the original configured predicates.
// TODO(oschaaf): allow configuration of this phase once the ramping rate limiters land,
// or a generic configuration of multiple phases (id, duration, termination predicates, rate

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

(for the future) Instinctively I lean towards generic configuration of multiple phases, rather than built-in phase types, but I haven't given it as much thought as you did. Generic configuration for phases will allow users to utilize phases in ways that we didn't account for.

On the other hand the warmup phase is very likely going to be fairly common. Maybe we could come up with providing some sort of templating for command line / service arguments that we could ship with the final version of phases. I.e. instead of building the warmup phase (or others) into the code we could bake it into config templates. WDYT?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

My instinct is the same; however because of the intermediate step performed here we're applying the generic phases concept in a very specific way to ensure we're doing a no-op. Hopefully we won't have hard coded build-in phases when we are done; I love the config templates idea.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Backed some of this out: we retain the old warmup call, and have a single phase for now to minimize changes that won't have future value.

// limiting options, etc).
TerminationPredicatePtr warmup_predicates =
termination_predicate_factory_.create(time_source_, *worker_number_scope_);
warmup_predicates
->appendToChain(std::make_unique<StatsCounterAbsoluteThresholdTerminationPredicateImpl>(
worker_number_scope_->counter("benchmark.http_2xx"), 0,
TerminationPredicate::Status::TERMINATE))
.appendToChain(std::make_unique<DurationTerminationPredicateImpl>(time_source_, 2s));
phases_.emplace_back(std::make_unique<PhaseImpl>(
"warmup",
sequencer_factory_.create(time_source_, *dispatcher_, *benchmark_client_,
std::move(warmup_predicates), *worker_number_scope_, true),
false));
}

void ClientWorkerImpl::work() {
simpleWarmup();
benchmark_client_->setMeasureLatencies(true);
sequencer_->start();
sequencer_->waitForCompletion();

phases_.emplace_back(std::make_unique<PhaseImpl>(
"main",
sequencer_factory_.create(
time_source_, *dispatcher_, *benchmark_client_,
termination_predicate_factory_.create(time_source_, *worker_number_scope_),
*worker_number_scope_, false),
true));

for (auto& phase : phases_) {
benchmark_client_->setMeasureLatencies(phase->measureLatencies());
phase->run();
if (worker_number_scope_->counter("sequencer.failed_terminations").value() != 0) {
break;
}
}

// Save a final snapshot of the worker-specific counter accumulations before
// we exit the thread.
for (const auto& stat : store_.counters()) {
Expand All @@ -69,7 +95,8 @@ void ClientWorkerImpl::shutdownThread() { benchmark_client_->terminate(); }
StatisticPtrMap ClientWorkerImpl::statistics() const {
StatisticPtrMap statistics;
StatisticPtrMap s1 = benchmark_client_->statistics();
StatisticPtrMap s2 = sequencer_->statistics();
auto& sequencer = phases_.back()->sequencer();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we prefer to mention the Sequencer type explicitly (instead of auto) unless it is too unwieldy?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done

StatisticPtrMap s2 = sequencer.statistics();
statistics.insert(s1.begin(), s1.end());
statistics.insert(s2.begin(), s2.end());
return statistics;
Expand Down
12 changes: 9 additions & 3 deletions source/client/client_worker_impl.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <vector>

#include "envoy/api/api.h"
#include "envoy/event/dispatcher.h"
#include "envoy/stats/store.h"
Expand All @@ -9,6 +11,7 @@
#include "nighthawk/client/benchmark_client.h"
#include "nighthawk/client/client_worker.h"
#include "nighthawk/client/factories.h"
#include "nighthawk/common/phase.h"
#include "nighthawk/common/request_source.h"
#include "nighthawk/common/sequencer.h"
#include "nighthawk/common/termination_predicate.h"
Expand All @@ -34,23 +37,26 @@ class ClientWorkerImpl : public WorkerImpl, virtual public ClientWorker {
const std::map<std::string, uint64_t>& thread_local_counter_values() override {
return thread_local_counter_values_;
}
const Sequencer& sequencer() const override { return *sequencer_; }

const std::vector<PhasePtr>& phases() const override { return phases_; }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we document the new public method?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This one is now renamed. Doc comments related to this reside in the interface class, does that suffice?


void shutdownThread() override;

protected:
void work() override;

private:
void simpleWarmup();
const TerminationPredicateFactory& termination_predicate_factory_;
const SequencerFactory& sequencer_factory_;
Envoy::Stats::ScopePtr worker_scope_;
Envoy::Stats::ScopePtr worker_number_scope_;
const int worker_number_;
const Envoy::MonotonicTime starting_time_;
Envoy::Tracing::HttpTracerPtr& http_tracer_;
RequestSourcePtr request_generator_;
BenchmarkClientPtr benchmark_client_;
TerminationPredicatePtr termination_predicate_;
const SequencerPtr sequencer_;
std::vector<PhasePtr> phases_;
Envoy::LocalInfo::LocalInfoPtr local_info_;
std::map<std::string, uint64_t> thread_local_counter_values_;
};
Expand Down
24 changes: 11 additions & 13 deletions source/client/factories_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,15 @@ SequencerFactoryImpl::SequencerFactoryImpl(const Options& options)

SequencerPtr SequencerFactoryImpl::create(Envoy::TimeSource& time_source,
Envoy::Event::Dispatcher& dispatcher,
Envoy::MonotonicTime start_time,
BenchmarkClient& benchmark_client,
TerminationPredicate& termination_predicate,
Envoy::Stats::Scope& scope) const {
TerminationPredicatePtr&& termination_predicate,
Envoy::Stats::Scope& scope, const bool warmup) const {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is there a way this can be refactored to not require a special set of extra code for warmup? Or to rename this variable to be more general purpose?
The two behaviors I seem to identify are avoiding the BurstRateLimiter and overriding frequency to be 1. Is it possible either of these behaviors might ever be desirable outside of the warmup phase?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

My goal is to wire in the phase concept as a no-op, with test coverage; my idea is that in a followup we can optionize this as generic as we want to, eg allow arbitrary phase names and configs.. so one step at a time

StatisticFactoryImpl statistic_factory(options_);
RateLimiterPtr rate_limiter =
std::make_unique<LinearRateLimiter>(time_source, Frequency(options_.requestsPerSecond()));
Frequency frequency(warmup ? 1 : options_.requestsPerSecond());
RateLimiterPtr rate_limiter = std::make_unique<LinearRateLimiter>(time_source, frequency);
const uint64_t burst_size = options_.burstSize();

if (burst_size) {
if (!warmup && burst_size) {
rate_limiter = std::make_unique<BurstingRateLimiter>(std::move(rate_limiter), burst_size);
}

Expand All @@ -71,9 +70,9 @@ SequencerPtr SequencerFactoryImpl::create(Envoy::TimeSource& time_source,
return benchmark_client.tryStartRequest(std::move(f));
};
return std::make_unique<SequencerImpl>(
platform_util_, dispatcher, time_source, start_time, std::move(rate_limiter),
sequencer_target, statistic_factory.create(), statistic_factory.create(),
options_.sequencerIdleStrategy(), termination_predicate, scope);
platform_util_, dispatcher, time_source, std::move(rate_limiter), sequencer_target,
statistic_factory.create(), statistic_factory.create(), options_.sequencerIdleStrategy(),
std::move(termination_predicate), scope);
}

StoreFactoryImpl::StoreFactoryImpl(const Options& options) : OptionBasedFactoryImpl(options) {}
Expand Down Expand Up @@ -144,11 +143,10 @@ RequestSourcePtr RequestSourceFactoryImpl::create() const {
TerminationPredicateFactoryImpl::TerminationPredicateFactoryImpl(const Options& options)
: OptionBasedFactoryImpl(options) {}

TerminationPredicatePtr
TerminationPredicateFactoryImpl::create(Envoy::TimeSource& time_source, Envoy::Stats::Scope& scope,
const Envoy::MonotonicTime start) const {
TerminationPredicatePtr TerminationPredicateFactoryImpl::create(Envoy::TimeSource& time_source,
Envoy::Stats::Scope& scope) const {
TerminationPredicatePtr duration_predicate =
std::make_unique<DurationTerminationPredicateImpl>(time_source, start, options_.duration());
std::make_unique<DurationTerminationPredicateImpl>(time_source, options_.duration());
TerminationPredicate* current_predicate = duration_predicate.get();
current_predicate = linkConfiguredPredicates(*current_predicate, options_.failurePredicates(),
TerminationPredicate::Status::FAIL, scope);
Expand Down
10 changes: 5 additions & 5 deletions source/client/factories_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ class SequencerFactoryImpl : public OptionBasedFactoryImpl, public SequencerFact
public:
SequencerFactoryImpl(const Options& options);
SequencerPtr create(Envoy::TimeSource& time_source, Envoy::Event::Dispatcher& dispatcher,
Envoy::MonotonicTime start_time, BenchmarkClient& benchmark_client,
TerminationPredicate& termination_predicate,
Envoy::Stats::Scope& scope) const override;
BenchmarkClient& benchmark_client,
TerminationPredicatePtr&& termination_predicate, Envoy::Stats::Scope& scope,
const bool warmup) const override;
};

class StoreFactoryImpl : public OptionBasedFactoryImpl, public StoreFactory {
Expand Down Expand Up @@ -76,8 +76,8 @@ class TerminationPredicateFactoryImpl : public OptionBasedFactoryImpl,
public TerminationPredicateFactory {
public:
TerminationPredicateFactoryImpl(const Options& options);
TerminationPredicatePtr create(Envoy::TimeSource& time_source, Envoy::Stats::Scope& scope,
const Envoy::MonotonicTime start) const override;
TerminationPredicatePtr create(Envoy::TimeSource& time_source,
Envoy::Stats::Scope& scope) const override;
TerminationPredicate* linkConfiguredPredicates(
TerminationPredicate& last_predicate, const TerminationPredicateMap& predicates,
const TerminationPredicate::Status termination_status, Envoy::Stats::Scope& scope) const;
Expand Down
2 changes: 1 addition & 1 deletion source/client/process_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ bool ProcessImpl::run(OutputCollector& collector) {
int i = 0;
std::chrono::nanoseconds total_execution_duration = 0ns;
for (auto& worker : workers_) {
auto sequencer_execution_duration = worker->sequencer().executionDuration();
auto sequencer_execution_duration = worker->phases().back()->sequencer().executionDuration();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you explain why it's safe to pick just the last phase for this? Why don't we need the duration of all of the phases?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See above; this is safe now because of some implicit knowledge abo this first step to land phases internally; in a follow up this has to change

// We don't write per-worker results if we only have a single worker, because the global results
// will be precisely the same.
if (workers_.size() > 1) {
Expand Down
2 changes: 2 additions & 0 deletions source/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ envoy_cc_library(
envoy_cc_library(
name = "nighthawk_common_lib",
srcs = [
"phase_impl.cc",
"rate_limiter_impl.cc",
"sequencer_impl.cc",
"statistic_impl.cc",
Expand All @@ -51,6 +52,7 @@ envoy_cc_library(
],
hdrs = [
"frequency.h",
"phase_impl.h",
"platform_util_impl.h",
"rate_limiter_impl.h",
"sequencer_impl.h",
Expand Down
18 changes: 18 additions & 0 deletions source/common/phase_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "common/phase_impl.h"

namespace Nighthawk {

absl::string_view PhaseImpl::id() const { return id_; }

Sequencer& PhaseImpl::sequencer() const { return *sequencer_; }

bool PhaseImpl::measureLatencies() const { return measure_latencies_; }

void PhaseImpl::run() const {
ENVOY_LOG(trace, "starting '{}' phase", id_);
sequencer().start();
sequencer().waitForCompletion();
ENVOY_LOG(trace, "finished '{}' phase", id_);
}

} // namespace Nighthawk
Loading