Skip to content

Auto-configure httpclient5-observation connection pool gauges (CONN_POOL only) #50596

@gustavovnicius

Description

@gustavovnicius

Following up on #48655 (closed for inactivity). @snicoll raised valid concerns about scope — this is a narrower proposal based on implementation experience.

Problem

Spring Boot provides RED metrics for HTTP clients via http.client.requests, but connection pool utilization (USE metrics) is not observable. Pool exhaustion is only visible after the fact, when connectionRequestTimeout fires and surfaces as an error in http.client.requests. There is no way to see saturation building before requests start failing.

The httpclient5-observation module (5.6+) provides pool gauges (http.client.pool.leased, .available, .pending) that fill this gap.

Proposal: enable CONN_POOL only, not BASIC or IO

@snicoll's concern about duplicate traces is valid — BASIC adds a request timer (http.client.request) that overlaps with Spring Boot's http.client.requests observation. Enabling BASIC would produce two similar-looking timers that are hard to distinguish in dashboards. The same applies to the inflight gauge in BASIC, which overlaps with http.client.requests.active.

CONN_POOL is the only metric group that provides genuinely new information. It could be auto-configured with minimal surface area:

@Bean
@ConditionalOnBean(MeterRegistry.class)
@ConditionalOnProperty(name = "spring.http.client.pool-metrics.enabled", matchIfMissing = true)
ClientHttpRequestFactoryBuilderCustomizer<HttpComponentsClientHttpRequestFactoryBuilder>
poolMetricsCustomizer(MeterRegistry meterRegistry, ObjectProvider<ObservationRegistry> observationRegistry) {
    var options = ObservingOptions.builder()
            .metrics(EnumSet.of(ObservingOptions.MetricSet.CONN_POOL))
            .build();
    return builder -> builder.withHttpClientCustomizer(b ->
            HttpClientObservationSupport.enable(b, observationRegistry.getIfAvailable(),
                    meterRegistry, options, MetricConfig.DEFAULT));
}

Pitfall: client.name tag cardinality

ConnPoolMeters registers gauges with only the MetricConfig.commonTags — no client identifier by default. This works for a single pool but breaks with multiple RestClient beans: all pools register gauges under the same metric name and tags, silently overwriting each other in the registry.

For the ClientHttpRequestFactoryBuilderCustomizer path this is especially problematic because RestClient.Builder is prototype-scoped. Every injection creates a new pool with identical, indistinguishable gauges.

A useful auto-configuration would need to either:

  • Only register pool gauges when a client name is known (e.g., via the HTTP service group name in Spring Boot 4's RestClientHttpServiceGroupConfigurer)
  • Or add a client.name tag derived from the base URL host, matching the client.name tag on http.client.requests so pool and request metrics can be correlated on dashboards

Without sufficient cardinality on the client.name tag, the gauges are ambiguous in any service with more than one HTTP client.

Summary

  • Scope: CONN_POOL only (no trace duplication, no timer overlap)
  • Single kill switch property
  • Main challenge: ensuring each pool's gauges carry a distinguishing tag that correlates with http.client.requests

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions