Skip to content

Commit

Permalink
listener: create per network filter chain stats scope (#17931)
Browse files Browse the repository at this point in the history
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
  • Loading branch information
lambdai authored Sep 2, 2021
1 parent 9d3cf43 commit c361b05
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 18 deletions.
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Minor Behavior Changes
to false. As part of this change, the use of reuse_port for TCP listeners on both macOS and
Windows has been disabled due to suboptimal behavior. See the field documentation for more
information.
* listener: destroy per network filter chain stats when a network filter chain is removed during the listener in place update.

Bug Fixes
---------
Expand Down
10 changes: 5 additions & 5 deletions source/server/filter_chain_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ Network::Address::InstanceConstSharedPtr fakeAddress() {

PerFilterChainFactoryContextImpl::PerFilterChainFactoryContextImpl(
Configuration::FactoryContext& parent_context, Init::Manager& init_manager)
: parent_context_(parent_context), init_manager_(init_manager) {}
: parent_context_(parent_context), scope_(parent_context_.scope().createScope("")),
filter_chain_scope_(parent_context_.listenerScope().createScope("")),
init_manager_(init_manager) {}

bool PerFilterChainFactoryContextImpl::drainClose() const {
return is_draining_.load() || parent_context_.drainDecision().drainClose();
Expand Down Expand Up @@ -101,7 +103,7 @@ Envoy::Runtime::Loader& PerFilterChainFactoryContextImpl::runtime() {
return parent_context_.runtime();
}

Stats::Scope& PerFilterChainFactoryContextImpl::scope() { return parent_context_.scope(); }
Stats::Scope& PerFilterChainFactoryContextImpl::scope() { return *scope_; }

Singleton::Manager& PerFilterChainFactoryContextImpl::singletonManager() {
return parent_context_.singletonManager();
Expand Down Expand Up @@ -135,9 +137,7 @@ PerFilterChainFactoryContextImpl::getTransportSocketFactoryContext() const {
return parent_context_.getTransportSocketFactoryContext();
}

Stats::Scope& PerFilterChainFactoryContextImpl::listenerScope() {
return parent_context_.listenerScope();
}
Stats::Scope& PerFilterChainFactoryContextImpl::listenerScope() { return *filter_chain_scope_; }

bool PerFilterChainFactoryContextImpl::isQuicListener() const {
return parent_context_.isQuicListener();
Expand Down
4 changes: 4 additions & 0 deletions source/server/filter_chain_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class PerFilterChainFactoryContextImpl : public Configuration::FilterChainFactor

private:
Configuration::FactoryContext& parent_context_;
// The scope that has empty prefix.
Stats::ScopePtr scope_;
// filter_chain_scope_ has the same prefix as listener owners scope.
Stats::ScopePtr filter_chain_scope_;
Init::Manager& init_manager_;
std::atomic<bool> is_draining_{false};
};
Expand Down
5 changes: 5 additions & 0 deletions test/integration/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,11 @@ class IntegrationTestServer : public Logger::Loggable<Logger::Id::testing>,
notifyingStatsAllocator().waitForCounterExists(name);
}

// TODO(#17956): Add Gauge type to NotifyingAllocator and adopt it in this method.
void waitForGaugeDestroyed(const std::string& name) override {
ASSERT_TRUE(TestUtility::waitForGaugeDestroyed(statStore(), name, time_system_));
}

void waitUntilHistogramHasSamples(
const std::string& name,
std::chrono::milliseconds timeout = std::chrono::milliseconds::zero()) override {
Expand Down
6 changes: 6 additions & 0 deletions test/integration/server_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ class IntegrationTestServerStats {
waitForGaugeEq(const std::string& name, uint64_t value,
std::chrono::milliseconds timeout = std::chrono::milliseconds::zero()) PURE;

/**
* Wait for a gauge to be destroyed. Note that MockStatStore does not destroy stat.
* @param name gauge name.
*/
virtual void waitForGaugeDestroyed(const std::string& name) PURE;

/**
* Counter lookup. This is not thread safe, since we don't get a consistent
* snapshot, uses counters() instead for this behavior.
Expand Down
39 changes: 26 additions & 13 deletions test/integration/xds_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ class LdsInplaceUpdateHttpIntegrationTest
std::string tls_inspector_config = ConfigHelper::tlsInspectorFilter();
config_helper_.addListenerFilter(tls_inspector_config);
config_helper_.addSslConfig();
config_helper_.addConfigModifier(
[&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
hcm) { hcm.mutable_stat_prefix()->assign("hcm0"); });
config_helper_.addConfigModifier([this, add_default_filter_chain](
envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
if (!use_default_balancer_) {
Expand Down Expand Up @@ -335,6 +338,7 @@ class LdsInplaceUpdateHttpIntegrationTest
->mutable_routes(0)
->mutable_route()
->set_cluster("cluster_1");
hcm_config.mutable_stat_prefix()->assign("hcm1");
config_blob->PackFrom(hcm_config);
bootstrap.mutable_static_resources()->mutable_clusters()->Add()->MergeFrom(
*bootstrap.mutable_static_resources()->mutable_clusters(0));
Expand Down Expand Up @@ -381,7 +385,7 @@ class LdsInplaceUpdateHttpIntegrationTest
}
}

void expectConnenctionServed(std::string alpn = "alpn0") {
void expectConnectionServed(std::string alpn = "alpn0") {
auto codec_client_after_config_update = createHttpCodec(alpn);
expectResponseHeaderConnectionClose(*codec_client_after_config_update, false);
codec_client_after_config_update->close();
Expand All @@ -395,20 +399,14 @@ class LdsInplaceUpdateHttpIntegrationTest
};

// Verify that http response on filter chain 1 and default filter chain have "Connection: close"
// header when these 2 filter chains are deleted during the listener update.
// header when these 2 filter chains are deleted during the listener update.
TEST_P(LdsInplaceUpdateHttpIntegrationTest, ReloadConfigDeletingFilterChain) {
inplaceInitialize(/*add_default_filter_chain=*/true);

auto codec_client_1 = createHttpCodec("alpn1");
auto codec_client_0 = createHttpCodec("alpn0");
auto codec_client_default = createHttpCodec("alpndefault");

Cleanup cleanup([c1 = codec_client_1.get(), c0 = codec_client_0.get(),
c_default = codec_client_default.get()]() {
c1->close();
c0->close();
c_default->close();
});
ConfigHelper new_config_helper(
version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap()));
new_config_helper.addConfigModifier(
Expand All @@ -422,12 +420,20 @@ TEST_P(LdsInplaceUpdateHttpIntegrationTest, ReloadConfigDeletingFilterChain) {
test_server_->waitForCounterGe("listener_manager.listener_in_place_updated", 1);
test_server_->waitForGaugeGe("listener_manager.total_filter_chains_draining", 1);

test_server_->waitForGaugeGe("http.hcm0.downstream_cx_active", 1);
test_server_->waitForGaugeGe("http.hcm1.downstream_cx_active", 1);

expectResponseHeaderConnectionClose(*codec_client_1, true);
expectResponseHeaderConnectionClose(*codec_client_default, true);

test_server_->waitForGaugeGe("listener_manager.total_filter_chains_draining", 0);
expectResponseHeaderConnectionClose(*codec_client_0, false);
expectConnenctionServed();
expectConnectionServed();

codec_client_1->close();
test_server_->waitForGaugeDestroyed("http.hcm1.downstream_cx_active");
codec_client_0->close();
codec_client_default->close();
}

// Verify that http clients of filter chain 0 survives if new listener config adds new filter
Expand All @@ -438,15 +444,19 @@ TEST_P(LdsInplaceUpdateHttpIntegrationTest, ReloadConfigAddingFilterChain) {

auto codec_client_0 = createHttpCodec("alpn0");
Cleanup cleanup0([c0 = codec_client_0.get()]() { c0->close(); });
test_server_->waitForGaugeGe("http.hcm0.downstream_cx_active", 1);

ConfigHelper new_config_helper(
version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap()));
new_config_helper.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap)
-> void {
auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0);
listener->mutable_filter_chains()->Add()->MergeFrom(*listener->mutable_filter_chains(1));
// Note that HCM2 copies the stats prefix from HCM0
listener->mutable_filter_chains()->Add()->MergeFrom(*listener->mutable_filter_chains(0));
*listener->mutable_filter_chains(2)
->mutable_filter_chain_match()
->mutable_application_protocols(0) = "alpn2";

auto default_filter_chain =
bootstrap.mutable_static_resources()->mutable_listeners(0)->mutable_default_filter_chain();
default_filter_chain->MergeFrom(*listener->mutable_filter_chains(1));
Expand All @@ -458,14 +468,17 @@ TEST_P(LdsInplaceUpdateHttpIntegrationTest, ReloadConfigAddingFilterChain) {
auto codec_client_2 = createHttpCodec("alpn2");
auto codec_client_default = createHttpCodec("alpndefault");

// 1 connection from filter chain 0 and 1 connection from filter chain 2.
test_server_->waitForGaugeGe("http.hcm0.downstream_cx_active", 2);

Cleanup cleanup2([c2 = codec_client_2.get(), c_default = codec_client_default.get()]() {
c2->close();
c_default->close();
});
expectResponseHeaderConnectionClose(*codec_client_2, false);
expectResponseHeaderConnectionClose(*codec_client_default, false);
expectResponseHeaderConnectionClose(*codec_client_0, false);
expectConnenctionServed();
expectConnectionServed();
}

// Verify that http clients of default filter chain is drained and recreated if the default filter
Expand Down Expand Up @@ -493,7 +506,7 @@ TEST_P(LdsInplaceUpdateHttpIntegrationTest, ReloadConfigUpdatingDefaultFilterCha
Cleanup cleanup2([c_default_v3 = codec_client_default_v3.get()]() { c_default_v3->close(); });
expectResponseHeaderConnectionClose(*codec_client_default, true);
expectResponseHeaderConnectionClose(*codec_client_default_v3, false);
expectConnenctionServed();
expectConnectionServed();
}

// Verify that balancer is inherited. Test only default balancer because ExactConnectionBalancer
Expand All @@ -515,7 +528,7 @@ TEST_P(LdsInplaceUpdateHttpIntegrationTest, OverlappingFilterChainServesNewConne
new_config_helper.setLds("1");
test_server_->waitForCounterGe("listener_manager.listener_in_place_updated", 1);
expectResponseHeaderConnectionClose(*codec_client_0, false);
expectConnenctionServed();
expectConnectionServed();
}

// Verify default filter chain update is filter chain only update.
Expand Down
8 changes: 8 additions & 0 deletions test/test_common/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,14 @@ AssertionResult TestUtility::waitForGaugeEq(Stats::Store& store, const std::stri
return AssertionSuccess();
}

AssertionResult TestUtility::waitForGaugeDestroyed(Stats::Store& store, const std::string& name,
Event::TestTimeSystem& time_system) {
while (findGauge(store, name) == nullptr) {
time_system.advanceTimeWait(std::chrono::milliseconds(10));
}
return AssertionSuccess();
}

AssertionResult TestUtility::waitUntilHistogramHasSamples(Stats::Store& store,
const std::string& name,
Event::TestTimeSystem& time_system,
Expand Down
11 changes: 11 additions & 0 deletions test/test_common/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,17 @@ class TestUtility {
Event::TestTimeSystem& time_system,
std::chrono::milliseconds timeout = std::chrono::milliseconds::zero());

/**
* Wait for a gauge to be destroyed.
* @param store supplies the stats store.
* @param name gauge name.
* @param time_system the time system to use for waiting.
* @return AssertionSuccess() if the gauge was == to the value within the timeout, else
* AssertionFailure().
*/
static AssertionResult waitForGaugeDestroyed(Stats::Store& store, const std::string& name,
Event::TestTimeSystem& time_system);

/**
* Wait for a histogram to have samples.
* @param store supplies the stats store.
Expand Down

0 comments on commit c361b05

Please sign in to comment.