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

quic: add quic downstream stats in hcm #16625

Closed
wants to merge 4 commits into from

Conversation

RenjieTang
Copy link
Contributor

@RenjieTang RenjieTang commented May 21, 2021

Commit Message: Introduce QuicStats and add Quic connection close stats for downstream connections.
Risk Level: Low
Testing: unit tests
Docs Changes: n/a
Release Notes: n/a.
Platform Specific Features: n/a.

Signed-off-by: Renjie Tang <renjietang@google.com>
@RenjieTang
Copy link
Contributor Author

/wait

@RenjieTang RenjieTang changed the title quic:: add quic downstream stats in hcm quic: add quic downstream stats in hcm May 21, 2021
Copy link
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

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

This organization does look cleaner. Go for it.

Were there some unit tests for QuicStats in the other PR? In any case we should have some.

@@ -167,6 +170,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase,
absl::optional<std::reference_wrapper<Http::Http3::CodecStats>> codec_stats_;
absl::optional<std::reference_wrapper<const envoy::config::core::v3::Http3ProtocolOptions>>
http3_options_;
absl::optional<std::reference_wrapper<QuicStats>> quic_stats_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Use OptRef as a shortcut to this.

@@ -82,6 +82,8 @@ void EnvoyQuicServerSession::setUpRequestDecoder(EnvoyQuicServerStream& stream)
void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
quic::ConnectionCloseSource source) {
quic::QuicServerSessionBase::OnConnectionClosed(frame, source);
ASSERT(quic_stats_.has_value());
quic_stats_->get().chargeQuicConnectionCloseStats(frame.quic_error_code, source);
Copy link
Contributor

Choose a reason for hiding this comment

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

quic_stats_->chargeQuicConnectionCloseStats if you use OptRef (per below).

@jmarantz jmarantz self-assigned this May 22, 2021
Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Yeah, I'd be inclined to close 16584 off in favor of this - when/if we have QUIC "tcp proxying" we can do a similar thing passing quic stats in via that network filter.

@alyssawilk alyssawilk self-assigned this May 24, 2021
Signed-off-by: Renjie Tang <renjietang@google.com>
@@ -82,6 +82,9 @@ void EnvoyQuicServerSession::setUpRequestDecoder(EnvoyQuicServerStream& stream)
void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
quic::ConnectionCloseSource source) {
quic::QuicServerSessionBase::OnConnectionClosed(frame, source);
if (quic_stats_.has_value()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Under what circumstances would quic_stats_ not be set?

We do have other examples where there are optional blocks of stats, but those are runtime-feature enabled for extra stats detail.

I think in your usage model this will always be set except for tests. Can we have more consistent behavior where we always set the stats-ref? In other words, make QuicStats be a required ctor arg for EnvoyQuicServerSession?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just like other Optref stats, this field is not initialized when the object is created. It will be initialized in QuicHttpServerConnectionImpl constructor.
From the current flow, QuicStats can't be in the constructor and be initialized at the very beginning.

There have been assertions that those other OptRef stats must be initialized before creating a new stream and invoke Envoy bug if the field is not initialized.

I intended to do the same in EnvoyQuicServerSession::OnConnectionClosed(). But unlike the assertions in CreateIncomingStream() where the session is guaranteed to be initialized by HCM, OnConnectionClosed() could be called before the HCM setup.

One example is that in EnvoyQuicDispatcherTest.CloseConnectionDuringFilterInstallation, the connection is closed during processing CHLO.
I'm not sure if that is a valid case in production. But this does mean that if we let the HCM own quic_stats_, we will miss out logging stats when the connection is closed before HCM filter is initialized. Tagging @alyssawilk as Alyssa knows more on this QUIC integration.

Copy link
Contributor

Choose a reason for hiding this comment

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

There's a difference. The other OptRef stats are optional due to config. These are not. I'm trying to suss out why you need a required object to be declared as Optional, as I suspect it's an indication something else is not quite right (see my other comment).

I see that EnvoyQuicConnection is instantiated by EnvoyQuicDispatcher. I'm inferring there's one of those per thread, so it's not a good place for QuicStats to be owned, though it's fine for it to have a reference.

I also see that it is created in EnvoyQuicDIspatcher::CreateQuicSession. EnvoyQuicDispatcher has member variables so I think we can supply QuicStats as a ctor arg. Let's go up-stack again.

That gets created from an ActiveQuicListener, which gets created from ActiveQuicListenerFactory, and that's created by Server::ListenerImpl::buildUdpListenerFactory. Cool.

ListenerImpl is created from ListenerManagerFactory. I think ListenerManagerFactory can own the QuicImpl, and pass it down through the ctor/chain so it's available where EnvoyQuicConnection is instantiated.

I think this is a better place because I want there to be exactly one of them in a Server, and it should be available unconditionally whenever a EnvoyQuicConnection is instantiated.

The reason is that there can be many different listener scopes, for different ports that Envoy is listening on. But there should be only one QuicStats object (actually a better name for that structure would be QuicStatNames), since it doesn't actually hold any counters. It just holds their lazily symbolized names.

Maybe what I'm saying is that HCM is not quite right because there's one of them for each listener? Not sure. In any case the order-of-construction seems wrong.

The reason the plumbing needs to be a little more complex than what you have here is to get the ownership/sharing model right.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the clarification! I now realized that there should only be one QuicStats object in the whole server instance.

I think it makes a lot of sense for ListenerManager to own QuicStats. This way we guaranteed that a QuicStats singleton is initiated during server startup and subsequent Quic connections can use it right away.
I updated #16584 instead of this one since I pursued the listener route on the other branch and thus the change was more gradual.

PTAL.

Signed-off-by: Renjie Tang <renjietang@google.com>
@RenjieTang
Copy link
Contributor Author

/wait

Signed-off-by: Renjie Tang <renjietang@google.com>
namespace Quic {

QuicStats::QuicStats(Stats::Scope& scope, bool is_upstream)
: scope_(scope), stat_name_pool_(scope.symbolTable()), symbol_table_(scope.symbolTable()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't pass the scope to the ctor; pass it to incCounter instead. Otherwise you are going to wind up replicating the construction of all these StatNames in each scope.

@@ -82,6 +82,9 @@ void EnvoyQuicServerSession::setUpRequestDecoder(EnvoyQuicServerStream& stream)
void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
quic::ConnectionCloseSource source) {
quic::QuicServerSessionBase::OnConnectionClosed(frame, source);
if (quic_stats_.has_value()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a difference. The other OptRef stats are optional due to config. These are not. I'm trying to suss out why you need a required object to be declared as Optional, as I suspect it's an indication something else is not quite right (see my other comment).

I see that EnvoyQuicConnection is instantiated by EnvoyQuicDispatcher. I'm inferring there's one of those per thread, so it's not a good place for QuicStats to be owned, though it's fine for it to have a reference.

I also see that it is created in EnvoyQuicDIspatcher::CreateQuicSession. EnvoyQuicDispatcher has member variables so I think we can supply QuicStats as a ctor arg. Let's go up-stack again.

That gets created from an ActiveQuicListener, which gets created from ActiveQuicListenerFactory, and that's created by Server::ListenerImpl::buildUdpListenerFactory. Cool.

ListenerImpl is created from ListenerManagerFactory. I think ListenerManagerFactory can own the QuicImpl, and pass it down through the ctor/chain so it's available where EnvoyQuicConnection is instantiated.

I think this is a better place because I want there to be exactly one of them in a Server, and it should be available unconditionally whenever a EnvoyQuicConnection is instantiated.

The reason is that there can be many different listener scopes, for different ports that Envoy is listening on. But there should be only one QuicStats object (actually a better name for that structure would be QuicStatNames), since it doesn't actually hold any counters. It just holds their lazily symbolized names.

Maybe what I'm saying is that HCM is not quite right because there's one of them for each listener? Not sure. In any case the order-of-construction seems wrong.

The reason the plumbing needs to be a little more complex than what you have here is to get the ownership/sharing model right.

@jmarantz
Copy link
Contributor

Closing this in favor of the other PR

@jmarantz jmarantz closed this May 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants