Skip to content

Commit 435d881

Browse files
authored
fix cloning detecthttp blowing away state (#700)
The `AcceptHttp` service is stateful. It builds the underlying HTTP, HTTP/2, and TCP service stacks lazily the first time they're needed. This interacts badly with the derived implementation of `Clone`: if it's cloned before the state is set, the state will only be set for the clone, rather than for the original instance in the cache. Since `Prefix` clones and oneshots the underlying service (`AcceptHttp`), this means that we rebuild the HTTP, HTTP/2, and TCP stacks on every connection to the same orig dest. This basically breaks caching for everything other than service profile discovery, causing us to do new destination discovery for every connection to the same original destination. Which...isn't great. This branch fixes this issue by removing the `Clone` impl from `AcceptHttp` and putting it behind a buffer instead. In the future, it may be possible to fix this in a nicer way, but this resolves the issue for now.
1 parent b9046e4 commit 435d881

File tree

3 files changed

+18
-7
lines changed

3 files changed

+18
-7
lines changed

linkerd/app/inbound/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ impl Config {
319319
dispatch_timeout,
320320
max_in_flight_requests,
321321
detect_protocol_timeout,
322+
buffer_capacity,
322323
..
323324
} = self.proxy.clone();
324325

@@ -373,9 +374,11 @@ impl Config {
373374
.into_inner(),
374375
drain.clone(),
375376
))
376-
.push_on_response(transport::Prefix::layer(
377-
http::Version::DETECT_BUFFER_CAPACITY,
378-
detect_protocol_timeout,
377+
.push_on_response(svc::layers().push_spawn_buffer(buffer_capacity).push(
378+
transport::Prefix::layer(
379+
http::Version::DETECT_BUFFER_CAPACITY,
380+
detect_protocol_timeout,
381+
),
379382
))
380383
.into_inner()
381384
}

linkerd/app/outbound/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,11 @@ impl Config {
403403
endpoint::TcpLogical,
404404
transport::io::PrefixedIo<transport::metrics::SensorIo<I>>,
405405
>()
406-
.push_on_response(transport::Prefix::layer(
406+
.push_on_response(
407+
svc::layers().push_spawn_buffer(buffer_capacity).push(transport::Prefix::layer(
407408
http::Version::DETECT_BUFFER_CAPACITY,
408409
detect_protocol_timeout,
409-
))
410+
)))
410411
.check_new_service::<endpoint::TcpLogical, transport::metrics::SensorIo<I>>()
411412
.into_inner();
412413

linkerd/proxy/http/src/detect.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::{
1515
task::{Context, Poll},
1616
};
1717
use tower::{util::ServiceExt, Service};
18-
use tracing::debug;
18+
use tracing::{debug, trace};
1919

2020
type Server = hyper::server::conn::Http<trace::Executor>;
2121

@@ -35,7 +35,7 @@ pub struct DetectHttp<F, H> {
3535
///
3636
/// Otherwise, the `F` type forwarding service is used to handle the TCP
3737
/// connection.
38-
#[derive(Clone, Debug)]
38+
#[derive(Debug)]
3939
pub struct AcceptHttp<T, F: NewService<T>, H: NewService<(HttpVersion, T)>> {
4040
target: T,
4141
new_tcp: F,
@@ -92,6 +92,7 @@ where
9292
H: NewService<(HttpVersion, T)>,
9393
{
9494
pub fn new(target: T, server: Server, new_http: H, new_tcp: F, drain: drain::Watch) -> Self {
95+
tracing::trace!("new AcceptHttp");
9596
Self {
9697
target,
9798
server,
@@ -136,8 +137,10 @@ where
136137
Some(HttpVersion::Http1) => {
137138
debug!("Handling as HTTP");
138139
let http1 = if let Some(svc) = self.http1.clone() {
140+
trace!("HTTP service already exists");
139141
svc
140142
} else {
143+
trace!("Building new HTTP service");
141144
let svc = self
142145
.new_http
143146
.new_service((HttpVersion::Http1, self.target.clone()));
@@ -167,8 +170,10 @@ where
167170
Some(HttpVersion::H2) => {
168171
debug!("Handling as H2");
169172
let h2 = if let Some(svc) = self.h2.clone() {
173+
trace!("H2 service already exists");
170174
svc
171175
} else {
176+
trace!("Building new H2 service");
172177
let svc = self
173178
.new_http
174179
.new_service((HttpVersion::H2, self.target.clone()));
@@ -193,8 +198,10 @@ where
193198
None => {
194199
debug!("Forwarding TCP");
195200
let tcp = if let Some(svc) = self.tcp.clone() {
201+
trace!("TCP service already exists");
196202
svc
197203
} else {
204+
trace!("Building new TCP service");
198205
let svc = self.new_tcp.new_service(self.target.clone());
199206
self.tcp = Some(svc.clone());
200207
svc

0 commit comments

Comments
 (0)