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

Dispatcher: keeps a stack of tracked objects. #14573

Merged
merged 16 commits into from
Jan 19, 2021
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 docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ New Features
* compression: the :ref:`compressor <envoy_v3_api_msg_extensions.filters.http.compressor.v3.Compressor>` filter adds support for compressing request payloads. Its configuration is unified with the :ref:`decompressor <envoy_v3_api_msg_extensions.filters.http.decompressor.v3.Decompressor>` filter with two new fields for different directions - :ref:`requests <envoy_v3_api_field_extensions.filters.http.compressor.v3.Compressor.request_direction_config>` and :ref:`responses <envoy_v3_api_field_extensions.filters.http.compressor.v3.Compressor.response_direction_config>`. The latter deprecates the old response-specific fields and, if used, roots the response-specific stats in `<stat_prefix>.compressor.<compressor_library.name>.<compressor_library_stat_prefix>.response.*` instead of `<stat_prefix>.compressor.<compressor_library.name>.<compressor_library_stat_prefix>.*`.
* config: added ability to flush stats when the admin's :ref:`/stats endpoint <operations_admin_interface_stats>` is hit instead of on a timer via :ref:`stats_flush_on_admin <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.stats_flush_on_admin>`.
* config: added new runtime feature `envoy.features.enable_all_deprecated_features` that allows the use of all deprecated features.
* dispatcher: supports a stack of `Envoy::ScopeTrackedObject` instead of a single tracked object. This will allow us to dump more debug information on crash.
KBaichoo marked this conversation as resolved.
Show resolved Hide resolved
* formatter: added new :ref:`text_format_source <envoy_v3_api_field_config.core.v3.SubstitutionFormatString.text_format_source>` field to support format strings both inline and from a file.
* grpc: implemented header value syntax support when defining :ref:`initial metadata <envoy_v3_api_field_config.core.v3.GrpcService.initial_metadata>` for gRPC-based `ext_authz` :ref:`HTTP <envoy_v3_api_field_extensions.filters.http.ext_authz.v3.ExtAuthz.grpc_service>` and :ref:`network <envoy_v3_api_field_extensions.filters.network.ext_authz.v3.ExtAuthz.grpc_service>` filters, and :ref:`ratelimit <envoy_v3_api_field_config.ratelimit.v3.RateLimitServiceConfig.grpc_service>` filters.
* grpc-json: added support for configuring :ref:`unescaping behavior <envoy_v3_api_field_extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder.url_unescape_spec>` for path components.
Expand Down
17 changes: 10 additions & 7 deletions include/envoy/event/dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,18 @@ class DispatcherBase {
virtual Event::SchedulableCallbackPtr createSchedulableCallback(std::function<void()> cb) PURE;

/**
* Sets a tracked object, which is currently operating in this Dispatcher.
* This should be cleared with another call to setTrackedObject() when the object is done doing
* work. Calling setTrackedObject(nullptr) results in no object being tracked.
* Appends a tracked object to the current stack of tracked objects operating
* in the dispatcher.
*
* This is optimized for performance, to avoid allocation where we do scoped object tracking.
*
* @return The previously tracked object or nullptr if there was none.
* It's recommended to use ScopeTrackerScopeState to manage the object's tracking. If directly
* invoking, there needs to be a subsequent call to popTrackedObject().
*/
virtual void appendTrackedObject(const ScopeTrackedObject* object) PURE;
Copy link
Contributor

Choose a reason for hiding this comment

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

For naming consistency with the stack pattern we are going for, I suggest pushTrackedObject

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.


/**
* Removes the top of the stack of tracked object and asserts that it was expected.
*/
virtual const ScopeTrackedObject* setTrackedObject(const ScopeTrackedObject* object) PURE;
virtual void popTrackedObject(const ScopeTrackedObject* expected_object) PURE;

/**
* Validates that an operation is thread-safe with respect to this dispatcher; i.e. that the
Expand Down
6 changes: 3 additions & 3 deletions include/envoy/server/fatal_action_config.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <memory>
#include <vector>

#include "envoy/common/pure.h"
#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
Expand All @@ -18,10 +19,9 @@ class FatalAction {
virtual ~FatalAction() = default;
/**
* Callback function to run when we are crashing.
* @param current_object the object we were working on when we started
* crashing.
* @param objects a vector of objects we were working on when we started crashing.
KBaichoo marked this conversation as resolved.
Show resolved Hide resolved
*/
virtual void run(const ScopeTrackedObject* current_object) PURE;
virtual void run(const std::vector<const ScopeTrackedObject*>& tracked_objects) PURE;
Copy link
Contributor

Choose a reason for hiding this comment

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

virtual void run(const absl::Span<const ScopeTrackedObject*>& tracked_objects) PURE;

To avoid tieing us to a vector implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. Done.


/**
* @return whether the action is async-signal-safe.
Expand Down
25 changes: 18 additions & 7 deletions source/common/common/scope_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,35 @@
#include "envoy/common/scope_tracker.h"
#include "envoy/event/dispatcher.h"

#include "common/common/assert.h"

namespace Envoy {

// A small class for tracking the scope of the object which is currently having
// A small class for tracking the managing the scope of a tracked object which is currently having
KBaichoo marked this conversation as resolved.
Show resolved Hide resolved
// work done in this thread.
//
// When created, it sets the tracked object in the dispatcher, and when destroyed it points the
// dispatcher at the previously tracked object.
// When created, it appends the tracked object to the dispatcher's stack of tracked objects, and
// when destroyed it pops the dispatcher's stack of tracked object, which should be the object it
// registered.
class ScopeTrackerScopeState {
public:
ScopeTrackerScopeState(const ScopeTrackedObject* object, Event::Dispatcher& dispatcher)
: dispatcher_(dispatcher) {
latched_object_ = dispatcher_.setTrackedObject(object);
: registered_object_(object), dispatcher_(dispatcher) {
dispatcher_.appendTrackedObject(registered_object_);
}

~ScopeTrackerScopeState() {
// If ScopeTrackerScopeState is always used for managing tracked objects,
// then the object popped off should be the object we registered.
dispatcher_.popTrackedObject(registered_object_);
}

~ScopeTrackerScopeState() { dispatcher_.setTrackedObject(latched_object_); }
// Make this object stack-only, it doesn't make sense for it
// to be on the heap since it's tracking a stack of active operations.
void* operator new(std::size_t) = delete;

private:
const ScopeTrackedObject* latched_object_;
const ScopeTrackedObject* registered_object_;
Event::Dispatcher& dispatcher_;
};

Expand Down
38 changes: 37 additions & 1 deletion source/common/event/dispatcher_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
#include <vector>

#include "envoy/api/api.h"
#include "envoy/common/scope_tracker.h"
#include "envoy/network/listen_socket.h"
#include "envoy/network/listener.h"

#include "common/buffer/buffer_impl.h"
#include "common/common/assert.h"
#include "common/common/lock_guard.h"
#include "common/common/thread.h"
#include "common/event/file_event_impl.h"
Expand All @@ -36,6 +38,12 @@

namespace Envoy {
namespace Event {
namespace {
// Our tracked object stack likely won't grow larger than this initial
KBaichoo marked this conversation as resolved.
Show resolved Hide resolved
// reservation; this should make appends constant time since we shouldn't
// have to grow the stack larger.
constexpr size_t ExpectedMaxTrackedObjectStackDepth = 10;
} // namespace

DispatcherImpl::DispatcherImpl(const std::string& name, Api::Api& api,
Event::TimeSystem& time_system,
Expand All @@ -49,6 +57,7 @@ DispatcherImpl::DispatcherImpl(const std::string& name, Api::Api& api,
post_cb_(base_scheduler_.createSchedulableCallback([this]() -> void { runPostCallbacks(); })),
current_to_delete_(&to_delete_1_) {
ASSERT(!name_.empty());
tracked_object_stack_.reserve(ExpectedMaxTrackedObjectStackDepth);
FatalErrorHandler::registerFatalErrorHandler(*this);
updateApproximateMonotonicTimeInternal();
base_scheduler_.registerOnPrepareCallback(
Expand Down Expand Up @@ -287,6 +296,16 @@ void DispatcherImpl::runPostCallbacks() {
}
}

void DispatcherImpl::onFatalError(std::ostream& os) const {
// Dump the state of the tracked objects in the dispatcher if thread safe. This generally
// results in dumping the active state only for the thread which caused the fatal error.
if (isThreadSafe()) {
for (auto iter = tracked_object_stack_.rbegin(); iter != tracked_object_stack_.rend(); ++iter) {
(*iter)->dumpState(os);
Copy link
Contributor

Choose a reason for hiding this comment

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

Test that covers dumping of multiple tracked objects, including relative ordering of the dumps.

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.

}
}
}

void DispatcherImpl::runFatalActionsOnTrackedObject(
const FatalAction::FatalActionPtrList& actions) const {
// Only run if this is the dispatcher of the current thread and
Expand All @@ -296,7 +315,7 @@ void DispatcherImpl::runFatalActionsOnTrackedObject(
}

for (const auto& action : actions) {
action->run(current_object_);
action->run(tracked_object_stack_);
}
}

Expand All @@ -306,5 +325,22 @@ void DispatcherImpl::touchWatchdog() {
}
}

void DispatcherImpl::appendTrackedObject(const ScopeTrackedObject* object) {
tracked_object_stack_.push_back(object);
Copy link
Contributor

Choose a reason for hiding this comment

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

ASSERT(tracked_object_stack_.size() <= ExpectedMaxTrackedObjectStackDepth)?

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.

}

void DispatcherImpl::popTrackedObject(const ScopeTrackedObject* expected_object) {
antoniovicente marked this conversation as resolved.
Show resolved Hide resolved
if (tracked_object_stack_.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should probably be RELEASE_ASSERT(!tracked_object_stack.empty()); It is undefined behavior to pop_back() if the container is empty, so I think we should halt even in release builds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before on release it'd just end up returning and suppressing, but this is a good point. Done.

ASSERT(!expected_object,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why !expected_object? You just asserted that it isn't nullptr.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Done.

"Tracked object stack is empty yet we expected a non-null tracked object on the top.");
return;
}

const ScopeTrackedObject* top = tracked_object_stack_.back();
tracked_object_stack_.pop_back();
ASSERT(top == expected_object,
"Popped the top of the tracked object stack, but it wasn't the expected object!");
}

} // namespace Event
} // namespace Envoy
20 changes: 4 additions & 16 deletions source/common/event/dispatcher_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,25 +74,13 @@ class DispatcherImpl : Logger::Loggable<Logger::Id::main>,
void post(std::function<void()> callback) override;
void run(RunType type) override;
Buffer::WatermarkFactory& getWatermarkFactory() override { return *buffer_factory_; }
const ScopeTrackedObject* setTrackedObject(const ScopeTrackedObject* object) override {
const ScopeTrackedObject* return_object = current_object_;
current_object_ = object;
return return_object;
}
void appendTrackedObject(const ScopeTrackedObject* object) override;
void popTrackedObject(const ScopeTrackedObject* expected_object) override;
MonotonicTime approximateMonotonicTime() const override;
void updateApproximateMonotonicTime() override;

// FatalErrorInterface
void onFatalError(std::ostream& os) const override {
// Dump the state of the tracked object if it is in the current thread. This generally results
// in dumping the active state only for the thread which caused the fatal error.
if (isThreadSafe()) {
if (current_object_) {
current_object_->dumpState(os);
}
}
}

void onFatalError(std::ostream& os) const override;
void
runFatalActionsOnTrackedObject(const FatalAction::FatalActionPtrList& actions) const override;

Expand Down Expand Up @@ -150,7 +138,7 @@ class DispatcherImpl : Logger::Loggable<Logger::Id::main>,
std::vector<DeferredDeletablePtr>* current_to_delete_;
Thread::MutexBasicLockable post_lock_;
std::list<std::function<void()>> post_callbacks_ ABSL_GUARDED_BY(post_lock_);
const ScopeTrackedObject* current_object_{};
std::vector<const ScopeTrackedObject*> tracked_object_stack_;
Copy link
Contributor

Choose a reason for hiding this comment

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

absl::InlineVector?

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.

bool deferred_deleting_{};
MonotonicTime approximate_monotonic_time_;
WatchdogRegistrationPtr watchdog_registration_;
Expand Down
4 changes: 3 additions & 1 deletion test/common/event/dispatcher_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,9 @@ TEST_F(DispatcherImplTest, IsThreadSafe) {

class TestFatalAction : public Server::Configuration::FatalAction {
public:
void run(const ScopeTrackedObject* /*current_object*/) override { ++times_ran_; }
void run(const std::vector<const ScopeTrackedObject*>& /*tracked_objects*/) override {
++times_ran_;
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 add some tests that cover 0, 1, >1 entries in tracked_objects ?

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.

}
bool isAsyncSignalSafe() const override { return true; }
int getNumTimesRan() { return times_ran_; }

Expand Down
10 changes: 8 additions & 2 deletions test/common/event/scaled_range_timer_manager_impl_test.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <chrono>

#include "envoy/common/scope_tracker.h"
#include "envoy/event/timer.h"

#include "common/event/dispatcher_impl.h"
Expand All @@ -24,9 +25,14 @@ class ScopeTrackingDispatcher : public WrappedDispatcher {
ScopeTrackingDispatcher(DispatcherPtr dispatcher)
: WrappedDispatcher(*dispatcher), dispatcher_(std::move(dispatcher)) {}

const ScopeTrackedObject* setTrackedObject(const ScopeTrackedObject* object) override {
void appendTrackedObject(const ScopeTrackedObject* object) override {
scope_ = object;
return impl_.setTrackedObject(object);
return impl_.appendTrackedObject(object);
}

void popTrackedObject(const ScopeTrackedObject* expected_object) override {
scope_ = nullptr;
return impl_.popTrackedObject(expected_object);
}

const ScopeTrackedObject* scope_{nullptr};
Expand Down
6 changes: 4 additions & 2 deletions test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2654,7 +2654,8 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutCallbackDisarmsAndReturns408
EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body));

conn_manager_->newStream(response_encoder_);
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, setTrackedObject(_)).Times(2);
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, appendTrackedObject(_));
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_));
request_timer->invokeCallback();
return Http::okStatus();
}));
Expand Down Expand Up @@ -2884,7 +2885,8 @@ TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutCallbackDisarmsAndRetu
EXPECT_CALL(*request_header_timer, enableTimer(request_headers_timeout_, _));

conn_manager_->newStream(response_encoder_);
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, setTrackedObject(_)).Times(2);
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, appendTrackedObject(_));
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_));
return Http::okStatus();
}));

Expand Down
23 changes: 11 additions & 12 deletions test/common/http/conn_manager_impl_test_2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2348,19 +2348,19 @@ TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) {
{
RequestHeaderMapPtr headers{
new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "POST"}}};
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, setTrackedObject(_))
.Times(2)
.WillOnce(Invoke([](const ScopeTrackedObject* object) -> const ScopeTrackedObject* {

EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, appendTrackedObject(_))
.Times(1)
.WillOnce(Invoke([](const ScopeTrackedObject* object) -> void {
ASSERT(object != nullptr); // On the first call, this should be the active stream.
std::stringstream out;
object->dumpState(out);
std::string state = out.str();
EXPECT_THAT(state,
testing::HasSubstr("filter_manager_callbacks_.requestHeaders(): null"));
EXPECT_THAT(state, testing::HasSubstr("protocol_: 1"));
return nullptr;
}))
.WillRepeatedly(Return(nullptr));
}));
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_));
EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false))
.WillOnce(Invoke([](HeaderMap&, bool) -> FilterHeadersStatus {
return FilterHeadersStatus::StopIteration;
Expand All @@ -2371,19 +2371,18 @@ TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) {
// Send trailers to that stream, and verify by this point headers are in logged state.
{
RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}};
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, setTrackedObject(_))
.Times(2)
.WillOnce(Invoke([](const ScopeTrackedObject* object) -> const ScopeTrackedObject* {
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, appendTrackedObject(_))
.Times(1)
.WillOnce(Invoke([](const ScopeTrackedObject* object) -> void {
ASSERT(object != nullptr); // On the first call, this should be the active stream.
std::stringstream out;
object->dumpState(out);
std::string state = out.str();
EXPECT_THAT(state, testing::HasSubstr("filter_manager_callbacks_.requestHeaders(): \n"));
EXPECT_THAT(state, testing::HasSubstr("':authority', 'host'\n"));
EXPECT_THAT(state, testing::HasSubstr("protocol_: 1"));
return nullptr;
}))
.WillRepeatedly(Return(nullptr));
}));
EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_));
EXPECT_CALL(*decoder_filters_[0], decodeComplete());
EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_))
.WillOnce(Return(FilterTrailersStatus::StopIteration));
Expand Down
5 changes: 3 additions & 2 deletions test/common/http/filter_manager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ TEST_F(FilterManagerTest, MatchTreeSkipActionDecodingHeaders) {
TEST_F(FilterManagerTest, MatchTreeSkipActionRequestAndResponseHeaders) {
initialize();

EXPECT_CALL(dispatcher_, setTrackedObject(_)).Times(2);
EXPECT_CALL(dispatcher_, appendTrackedObject(_));
EXPECT_CALL(dispatcher_, popTrackedObject(_));

// This stream filter will skip further callbacks once it sees both the request and response
// header. As such, it should see the decoding callbacks but none of the encoding callbacks.
Expand Down Expand Up @@ -251,4 +252,4 @@ TEST_F(FilterManagerTest, MatchTreeSkipActionRequestAndResponseHeaders) {
}
} // namespace
} // namespace Http
} // namespace Envoy
} // namespace Envoy
30 changes: 17 additions & 13 deletions test/common/router/router_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ class RouterTestBase : public testing::Test {
// Make the "system time" non-zero, because 0 is considered invalid by DateUtil.
test_time_.setMonotonicTime(std::chrono::milliseconds(50));

// Allow any number of setTrackedObject calls for the dispatcher strict mock.
EXPECT_CALL(callbacks_.dispatcher_, setTrackedObject(_)).Times(AnyNumber());
// Allow any number of (append|pop)TrackedObject calls for the dispatcher strict mock.
EXPECT_CALL(callbacks_.dispatcher_, appendTrackedObject(_)).Times(AnyNumber());
EXPECT_CALL(callbacks_.dispatcher_, popTrackedObject(_)).Times(AnyNumber());
}

void expectResponseTimerCreate() {
Expand Down Expand Up @@ -289,15 +290,16 @@ class RouterTestBase : public testing::Test {
EXPECT_CALL(callbacks_.dispatcher_, createTimer_(_));
}
EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _))
.WillOnce(Invoke(
[&](Http::ResponseDecoder& decoder,
Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* {
response_decoder_ = &decoder;
EXPECT_CALL(callbacks_.dispatcher_, setTrackedObject(_)).Times(testing::AtLeast(2));
callbacks.onPoolReady(original_encoder_, cm_.thread_local_cluster_.conn_pool_.host_,
upstream_stream_info_, Http::Protocol::Http10);
return nullptr;
}));
.WillOnce(Invoke([&](Http::ResponseDecoder& decoder,
Http::ConnectionPool::Callbacks& callbacks)
-> Http::ConnectionPool::Cancellable* {
response_decoder_ = &decoder;
EXPECT_CALL(callbacks_.dispatcher_, appendTrackedObject(_)).Times(testing::AtLeast(1));
EXPECT_CALL(callbacks_.dispatcher_, popTrackedObject(_)).Times(testing::AtLeast(1));
callbacks.onPoolReady(original_encoder_, cm_.thread_local_cluster_.conn_pool_.host_,
upstream_stream_info_, Http::Protocol::Http10);
return nullptr;
}));
HttpTestUtility::addDefaultHeaders(default_request_headers_);
router_.decodeHeaders(default_request_headers_, end_stream);
}
Expand Down Expand Up @@ -2189,15 +2191,17 @@ TEST_F(RouterTest, GrpcOk) {
EXPECT_EQ(1U,
callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value());

EXPECT_CALL(callbacks_.dispatcher_, setTrackedObject(_)).Times(2);
EXPECT_CALL(callbacks_.dispatcher_, appendTrackedObject(_));
EXPECT_CALL(callbacks_.dispatcher_, popTrackedObject(_));
Http::ResponseHeaderMapPtr response_headers(
new Http::TestResponseHeaderMapImpl{{":status", "200"}});
EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_,
putHttpResponseCode(200));
response_decoder->decodeHeaders(std::move(response_headers), false);
EXPECT_TRUE(verifyHostUpstreamStats(0, 0));

EXPECT_CALL(callbacks_.dispatcher_, setTrackedObject(_)).Times(2);
EXPECT_CALL(callbacks_.dispatcher_, appendTrackedObject(_));
EXPECT_CALL(callbacks_.dispatcher_, popTrackedObject(_));
Http::ResponseTrailerMapPtr response_trailers(
new Http::TestResponseTrailerMapImpl{{"grpc-status", "0"}});
response_decoder->decodeTrailers(std::move(response_trailers));
Expand Down
Loading