Skip to content

router: scope upstream HTTP filter chain to the connection manager stat prefix#45666

Closed
fl0Lec wants to merge 2 commits into
envoyproxy:mainfrom
DataDog:fl/router-upstream-filter-stats-scope
Closed

router: scope upstream HTTP filter chain to the connection manager stat prefix#45666
fl0Lec wants to merge 2 commits into
envoyproxy:mainfrom
DataDog:fl/router-upstream-filter-stats-scope

Conversation

@fl0Lec

@fl0Lec fl0Lec commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Commit Message: router: scope upstream HTTP filter chain to the connection manager stat prefix
Additional Description:

Generative AI (Claude) was used to assist with this change; I have reviewed and understand all of the code.

The router builds its upstream_http_filters chain at the root stats scope: in FilterConfig's constructor the upstream FilterChainHelper is given context.scope() (the root scope) and a prefix derived from context.scope().prefix() (empty). As a result, stats emitted by upstream HTTP filters configured via Router.upstream_http_filters have no downstream context.

By contrast, the cluster-level upstream filter chain (HttpProtocolOptions.http_filters, built in upstream_impl.cc) is scoped to cluster.<name>..

This change builds the router's upstream filter chain under a stats scope that inherits the connection manager's stat prefix (http.<stat_prefix>.) — which the router already receives as the stat_prefix StatName — mirroring the cluster path. Filters that read their stats scope now emit stats under http.<stat_prefix>. instead of the root scope.

Open question for maintainers: this changes the stat path for existing upstream_http_filters users, so a runtime guard would normally be expected. However, the scope is chosen once at config-construction time (not on the request path), so a reloadable_features guard does not map cleanly. Happy to gate it differently if preferred.

Risk Level: Low (only affects the stats scope of upstream_http_filters, a rarely-used feature; no data-plane behavior change).
Testing: //test/common/router:router_upstream_filter_test passes; //source/common/router:router_lib builds.
Docs Changes: n/a
Release Notes: added (minor_behavior_changes).
Platform Specific Features: n/a

@repokitteh-read-only

Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #45666 was opened by fl0Lec.

see: more, trace.

@fl0Lec
fl0Lec force-pushed the fl/router-upstream-filter-stats-scope branch 3 times, most recently from 4413b6a to f2f5ed0 Compare June 16, 2026 16:02
@fl0Lec
fl0Lec marked this pull request as ready for review June 16, 2026 16:09
@fl0Lec

fl0Lec commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@botengyao botengyao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks for working on it.

/wait

Comment thread source/common/router/router.cc Outdated
upstream_filter_scope_ = context.scope().createScope(prefix);
upstream_ctx_ = std::make_unique<Upstream::UpstreamFactoryContextImpl>(
server_factory_ctx, context.initManager(), context.scope());
server_factory_ctx, context.initManager(), *upstream_filter_scope_);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a behavior change, could you please add a runtime guard on it?

@botengyao botengyao self-assigned this Jun 23, 2026
fl0Lec added 2 commits June 23, 2026 06:32
…at prefix

The router builds its upstream_http_filters chain at the root stats scope,
so upstream filters (e.g. the upstream RBAC filter) emit metrics with no
downstream context. The cluster-level upstream filter chain, by contrast,
is scoped to "cluster.<name>.".

Build the router's upstream filter chain under a stats scope that inherits
the connection manager's stat prefix ("http.<stat_prefix>."), which the
router already receives as stat_prefix, mirroring the cluster path. Upstream
filters configured via Router.upstream_http_filters now emit stats under
"http.<stat_prefix>." instead of the root scope.

Signed-off-by: Florent Lecoultre <florent.lecoultre@datadoghq.com>
Add runtime guard envoy.reloadable_features.router_upstream_filters_scoped_stat_prefix
(enabled by default) around the new upstream filter chain stat scoping. When disabled,
reverts to the legacy root-scope behavior for operators who need to preserve existing
dashboard/alert configuration.

Signed-off-by: Florent Lecoultre <florent.lecoultre@datadoghq.com>
@fl0Lec
fl0Lec force-pushed the fl/router-upstream-filter-stats-scope branch from f2f5ed0 to 878ba8b Compare June 23, 2026 06:33
@repokitteh-read-only

Copy link
Copy Markdown

CC @envoyproxy/runtime-guard-changes: FYI only for changes made to (source/common/runtime/runtime_features.cc).

🐱

Caused by: #45666 was synchronize by fl0Lec.

see: more, trace.

@fl0Lec

fl0Lec commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@fl0Lec
fl0Lec requested a review from botengyao June 23, 2026 12:09
@fl0Lec

fl0Lec commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

@botengyao gentle bump on this one

@botengyao botengyao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm, thanks!

defer to @wbpcode for a second eye, I think this is simple to go with the runtime guard.

@wbpcode wbpcode left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for working on this.

First, the current context.scope() is not root scope, it's filter chain scope with empty prefix.
Second, the current router's implementation actually correctly matches the downstream HTTP filters' implementation. The scope self has an empty prefix, and the parent's prefix is propagated to the filter factory by a string and the filter could optionally use it or not. (I actually agree that letting the parent create a prefixed scope is better design, then the sub filters needn't to care about the stat prefix. However, previous design works from the start of this project, even if we want to migrate, it should be done in a more smooth way).

It's more like the cluster's HTTP filter chain has an unexpected implementation.

Note, most of the HTTP filters will concatenate the parent prefix string http.example. to the own prefix and stat names and create the stats under the factory context's scope.

With these changes, it may result in unexpected stats names like: http.example.http.example.foo or cluster.example.cluster.example.foo when these filters are used as upstream filters.

That's say, we may need to update the cluster's implementation to match the router rather to update router.

cc @botengyao

@fl0Lec

fl0Lec commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing this in favor of #45987, which takes the approach suggested in review: instead of creating a sub-scope in the router, the fix is applied at the stats_prefix string level — passing the correct prefix to FilterChainHelper from both router.cc and upstream_impl.cc, and removing the EMPTY_STRING workaround in the upstream RBAC filter config. Thanks for the feedback that pointed in the right direction.

@fl0Lec fl0Lec closed this Jul 8, 2026
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