-
Notifications
You must be signed in to change notification settings - Fork 89
Introduce Phases #219
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
Introduce Phases #219
Changes from all commits
a76a706
4a348eb
7883d59
e458ea5
e7b65c1
db1c468
731736b
79bdf33
ebf82a3
658bd48
bff40ac
d12d0d5
b5b5fd2
fb85732
3ea8055
ca49ab5
9528d92
281a4e3
8a268fe
34f649b
b31f03e
cdb10e5
3298929
62914e4
07fa643
c32ba0e
0fd8271
4dcb33b
efd8a51
128b633
c927a6e
be3f183
0bd9cfd
b81f4c5
0ab72fd
49c9344
fc998a2
29872a7
e8300c9
f3df58c
f3f3271
ba832c3
b75803e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
|
|
||
| #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 shouldMeasureLatencies() const PURE; | ||
|
|
||
| /** | ||
| * Runs the sequencer associated to this phase and blocks until completion, which means this phase | ||
| * has ended as well. | ||
| * Execution failure can be observed via the sequencer.failed_terminations counter. | ||
| */ | ||
| virtual void run() const PURE; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,12 +2,15 @@ | |
|
|
||
| #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, | ||
|
|
@@ -17,20 +20,23 @@ 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), | ||
| worker_number_(worker_number), http_tracer_(http_tracer), | ||
| request_generator_( | ||
| request_generator_factory.create(cluster_manager, *dispatcher_, *worker_number_scope_, | ||
| fmt::format("{}.requestsource", worker_number))), | ||
| 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_)) {} | ||
| phase_(std::make_unique<PhaseImpl>( | ||
| "main", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is hardcoding this to main going to make it harder to allow arbitrary configuration of these later?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No this shouldn’t matter for now, it will only show up in logs but otherwise is not visible externally.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to clarify, does that mean this is temporary code that will be replaced later? Or that the use of main here is a secret phase that outside users won't need to know about? If it's the latter, I'm concerned about name collision, but if it's the first, then agree there's no concern here.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's temporary code. This will be replaced with a generic mechanism that translates phases into configuration. So the id chosen here can be an arbitrary value, perhaps I should have better called it |
||
| sequencer_factory_.create( | ||
| time_source_, *dispatcher_, *benchmark_client_, | ||
| termination_predicate_factory_.create(time_source_, *worker_number_scope_), | ||
| *worker_number_scope_, starting_time), | ||
| true)) {} | ||
|
|
||
| void ClientWorkerImpl::simpleWarmup() { | ||
| ENVOY_LOG(debug, "> worker {}: warmup start.", worker_number_); | ||
|
|
@@ -43,11 +49,12 @@ void ClientWorkerImpl::simpleWarmup() { | |
| } | ||
|
|
||
| void ClientWorkerImpl::work() { | ||
| benchmark_client_->setShouldMeasureLatencies(false); | ||
| request_generator_->initOnThread(); | ||
| simpleWarmup(); | ||
| benchmark_client_->setMeasureLatencies(true); | ||
| sequencer_->start(); | ||
| sequencer_->waitForCompletion(); | ||
| benchmark_client_->setShouldMeasureLatencies(phase_->shouldMeasureLatencies()); | ||
| phase_->run(); | ||
|
|
||
| // Save a final snapshot of the worker-specific counter accumulations before | ||
| // we exit the thread. | ||
| for (const auto& stat : store_.counters()) { | ||
|
|
@@ -72,7 +79,8 @@ void ClientWorkerImpl::shutdownThread() { benchmark_client_->terminate(); } | |
| StatisticPtrMap ClientWorkerImpl::statistics() const { | ||
| StatisticPtrMap statistics; | ||
| StatisticPtrMap s1 = benchmark_client_->statistics(); | ||
| StatisticPtrMap s2 = sequencer_->statistics(); | ||
| Sequencer& sequencer = phase_->sequencer(); | ||
| StatisticPtrMap s2 = sequencer.statistics(); | ||
| statistics.insert(s1.begin(), s1.end()); | ||
| statistics.insert(s2.begin(), s2.end()); | ||
| return statistics; | ||
|
|
||
| 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::shouldMeasureLatencies() const { return should_measure_latencies_; } | ||
|
|
||
| void PhaseImpl::run() const { | ||
| ENVOY_LOG(trace, "starting '{}' phase", id()); | ||
| sequencer().start(); | ||
| sequencer().waitForCompletion(); | ||
| ENVOY_LOG(trace, "finished '{}' phase", id()); | ||
| } | ||
|
|
||
| } // namespace Nighthawk |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #268