Skip to content

Commit de5d42f

Browse files
hawkwolix0r
andauthored
Prepare for Rust 1.50+ by boxing large futures (#1003)
Version 1.50 of the Rust compiler introduced a regression (rust-lang/rust#84873) that results in the compiler using extremely large amounts of memory (and eventually getting OOM killed) when compiling code involving very large nested types. This regression is triggered by a number of future types in the proxy. This change adds several `BoxService` layers--primarily within `switch` layers--to reduce the size of future types so that the proxy can successfully be compiled on Rust 1.50+. This change does not update the Rust toolchain version. Co-authored-by: Oliver Gould <[email protected]>
1 parent 660f0e8 commit de5d42f

File tree

11 files changed

+67
-22
lines changed

11 files changed

+67
-22
lines changed

linkerd/app/gateway/src/lib.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,7 @@ where
218218
.push(detect::NewDetectService::layer(
219219
detect_protocol_timeout,
220220
http::DetectHttp::default(),
221-
))
222-
.into_inner();
221+
));
223222

224223
// When a transported connection is received, use the header's target to
225224
// drive routing.
@@ -232,6 +231,8 @@ where
232231
)
233232
.push_http_server()
234233
.into_stack()
234+
.push_on_response(svc::BoxService::layer())
235+
.push(svc::BoxNewService::layer())
235236
.push_switch(
236237
|GatewayTransportHeader {
237238
target,
@@ -248,14 +249,19 @@ where
248249
})),
249250
None => Ok::<_, Never>(svc::Either::B(target)),
250251
},
251-
tcp.into_inner(),
252+
tcp.push_on_response(svc::BoxService::layer())
253+
.push(svc::BoxNewService::layer())
254+
.into_inner(),
252255
)
253256
.push_switch(
254257
|gw| match gw {
255258
GatewayConnection::TransportHeader(t) => Ok::<_, Never>(svc::Either::A(t)),
256259
GatewayConnection::Legacy(c) => Ok(svc::Either::B(c)),
257260
},
258-
legacy_http,
261+
legacy_http
262+
.push_on_response(svc::BoxService::layer())
263+
.push(svc::BoxNewService::layer())
264+
.into_inner(),
259265
)
260266
.into_inner()
261267
}

linkerd/app/inbound/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ where
234234
.push_http_router(profiles)
235235
.push_http_server()
236236
.stack
237+
.push_on_response(svc::BoxService::layer())
237238
.push_map_target(HttpAccept::from)
238239
.push(svc::UnwrapOr::layer(
239240
// When HTTP detection fails, forward the connection to the
@@ -242,6 +243,8 @@ where
242243
.push_tcp_forward(server_port)
243244
.stack
244245
.push_map_target(TcpEndpoint::from)
246+
.push_on_response(svc::BoxService::layer())
247+
.push(svc::BoxNewService::layer())
245248
.into_inner(),
246249
))
247250
.push_map_target(detect::allow_timeout)
@@ -257,6 +260,7 @@ where
257260
config.detect_protocol_timeout,
258261
))
259262
.instrument(|_: &_| debug_span!("proxy"))
263+
.push_on_response(svc::BoxService::layer())
260264
.push_switch(
261265
disable_detect,
262266
self.clone()
@@ -267,6 +271,8 @@ where
267271
.push_map_target(TcpAccept::port_skipped)
268272
.check_new_service::<T, _>()
269273
.instrument(|_: &T| debug_span!("forward"))
274+
.push_on_response(svc::BoxService::layer())
275+
.push(svc::BoxNewService::layer())
270276
.into_inner(),
271277
)
272278
.check_new_service::<T, I>()
@@ -276,6 +282,8 @@ where
276282
.push_direct(gateway)
277283
.stack
278284
.instrument(|_: &_| debug_span!("direct"))
285+
.push_on_response(svc::BoxService::layer())
286+
.push(svc::BoxNewService::layer())
279287
.into_inner(),
280288
)
281289
.instrument(|a: &T| {

linkerd/app/outbound/src/discover.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ impl<N> Outbound<N> {
2727
N: svc::NewService<(Option<profiles::Receiver>, tcp::Accept), Service = NSvc>
2828
+ Clone
2929
+ Send
30+
+ Sync
3031
+ 'static,
3132
NSvc: svc::Service<SensorIo<I>, Response = (), Error = Error> + Send + 'static,
3233
NSvc::Future: Send,
33-
P: profiles::GetProfile<profiles::LookupAddr> + Clone + Send + 'static,
34+
P: profiles::GetProfile<profiles::LookupAddr> + Clone + Send + Sync + 'static,
3435
P::Future: Send,
3536
P::Error: Send,
3637
{
@@ -80,6 +81,9 @@ impl<N> Outbound<N> {
8081
.push_cache(config.proxy.cache_max_idle_age)
8182
.instrument(|a: &tcp::Accept| info_span!("server", orig_dst = %a.orig_dst))
8283
.push_request_filter(|t: T| tcp::Accept::try_from(t.param()))
84+
// Boxing is necessary purely to limit the link-time overhead of
85+
// having enormous types.
86+
.push(svc::BoxNewService::layer())
8387
.check_new_service::<T, I>();
8488

8589
Outbound {

linkerd/app/outbound/src/endpoint.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ impl<S> Outbound<S> {
205205
S::Response:
206206
tls::HasNegotiatedProtocol + io::AsyncRead + io::AsyncWrite + Send + Unpin + 'static,
207207
S::Future: Send + Unpin,
208-
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr + fmt::Debug + Send + Unpin + 'static,
208+
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr,
209+
I: fmt::Debug + Send + Sync + Unpin + 'static,
209210
{
210211
let http = self
211212
.clone()

linkerd/app/outbound/src/http/detect.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ impl<N> Outbound<N> {
2121
> + Clone,
2222
>
2323
where
24-
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr + std::fmt::Debug + Send + Unpin + 'static,
25-
N: svc::NewService<T, Service = NSvc> + Clone + Send + 'static,
26-
NSvc: svc::Service<io::EitherIo<I, io::PrefixedIo<I>>, Response = ()> + Send + 'static,
24+
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr,
25+
I: std::fmt::Debug + Send + Sync + Unpin + 'static,
26+
N: svc::NewService<T, Service = NSvc> + Clone + Send + Sync + 'static,
27+
NSvc:
28+
svc::Service<io::EitherIo<I, io::PrefixedIo<I>>, Response = ()> + Send + Sync + 'static,
2729
NSvc::Error: Into<Error>,
2830
NSvc::Future: Send,
29-
H: svc::NewService<U, Service = HSvc> + Clone + Send + 'static,
31+
H: svc::NewService<U, Service = HSvc> + Clone + Send + Sync + 'static,
3032
HSvc: svc::Service<http::Request<http::BoxBody>, Response = http::Response<http::BoxBody>>,
3133
HSvc: Clone + Send + Sync + Unpin + 'static,
3234
HSvc::Error: Into<Error>,
@@ -82,7 +84,10 @@ impl<N> Outbound<N> {
8284
},
8385
skipped,
8486
)
85-
.check_new_service::<T, _>();
87+
.check_new_service::<T, _>()
88+
// Boxing is necessary purely to limit the link-time overhead of
89+
// having enormous types.
90+
.push(svc::BoxNewService::layer());
8691

8792
Outbound {
8893
config,

linkerd/app/outbound/src/http/endpoint.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ impl<C> Outbound<C> {
1919
Error = Error,
2020
Future = impl Send,
2121
>,
22-
> + Clone,
22+
> + Clone
23+
+ Send
24+
+ Sync
25+
+ 'static,
2326
>
2427
where
2528
T: Clone + Send + Sync + 'static,
@@ -33,7 +36,7 @@ impl<C> Outbound<C> {
3336
C: svc::Service<T> + Clone + Send + Sync + Unpin + 'static,
3437
C::Response: io::AsyncRead + io::AsyncWrite + Send + Unpin,
3538
C::Error: Into<Error>,
36-
C::Future: Send + Unpin,
39+
C::Future: Send + Unpin + 'static,
3740
{
3841
let Self {
3942
config,

linkerd/app/outbound/src/http/logical.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ impl<E> Outbound<E> {
3030
where
3131
B: http::HttpBody<Error = Error> + std::fmt::Debug + Default + Send + 'static,
3232
B::Data: Send + 'static,
33-
E: svc::NewService<Endpoint, Service = ESvc> + Clone + Send + 'static,
33+
E: svc::NewService<Endpoint, Service = ESvc> + Clone + Send + Sync + 'static,
3434
ESvc: svc::Service<http::Request<http::BoxBody>, Response = http::Response<http::BoxBody>>
3535
+ Send
3636
+ 'static,
3737
ESvc::Error: Into<Error>,
3838
ESvc::Future: Send,
39-
R: Resolve<ConcreteAddr, Error = Error, Endpoint = Metadata> + Clone + Send + 'static,
39+
R: Resolve<ConcreteAddr, Error = Error, Endpoint = Metadata>,
40+
R: Clone + Send + Sync + 'static,
4041
R::Resolution: Send,
4142
R::Future: Send + Unpin,
4243
{
@@ -152,7 +153,11 @@ impl<E> Outbound<E> {
152153
.push(http::strip_header::request::layer(DST_OVERRIDE_HEADER))
153154
.push(http::BoxResponse::layer()),
154155
)
155-
.instrument(|l: &Logical| debug_span!("logical", dst = %l.logical_addr));
156+
.instrument(|l: &Logical| debug_span!("logical", dst = %l.logical_addr))
157+
// Boxing is necessary purely to limit the link-time overhead of
158+
// having enormous types.
159+
.push(svc::BoxNewService::layer())
160+
.push_on_response(svc::BoxService::layer());
156161

157162
Outbound {
158163
config,

linkerd/app/outbound/src/http/server.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl<N> Outbound<N> {
1717
>
1818
where
1919
T: svc::Param<http::normalize_uri::DefaultAuthority>,
20-
N: svc::NewService<T, Service = NSvc> + Clone + Send + 'static,
20+
N: svc::NewService<T, Service = NSvc> + Clone + Send + Sync + 'static,
2121
NSvc: svc::Service<http::Request<http::BoxBody>, Response = http::Response<http::BoxBody>>,
2222
NSvc: Send + 'static,
2323
NSvc::Error: Into<Error>,
@@ -62,7 +62,10 @@ impl<N> Outbound<N> {
6262
.push(http::NewNormalizeUri::layer())
6363
// Record when a HTTP/1 URI originated in absolute form
6464
.push_on_response(http::normalize_uri::MarkAbsoluteForm::layer())
65-
.check_new_service::<T, http::Request<http::BoxBody>>();
65+
.check_new_service::<T, http::Request<http::BoxBody>>()
66+
// Boxing is necessary purely to limit the link-time overhead of
67+
// having enormous types.
68+
.push(svc::BoxNewService::layer());
6669

6770
Outbound {
6871
config,

linkerd/app/outbound/src/ingress.rs

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ impl<H> Outbound<H> {
161161
// Boxing is necessary purely to limit the link-time overhead of
162162
// having enormous types.
163163
.push(svc::BoxNewService::layer())
164+
.push_on_response(svc::BoxService::layer())
164165
.check_new_service::<T, I>()
165166
.into_inner()
166167
}

linkerd/app/outbound/src/logical.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ impl<C> Outbound<C> {
131131
tls::HasNegotiatedProtocol + io::AsyncRead + io::AsyncWrite + Send + Unpin + 'static,
132132
C::Future: Send + Unpin,
133133
R: Clone + Send + 'static,
134-
R: Resolve<ConcreteAddr, Endpoint = Metadata, Error = Error>,
134+
R: Resolve<ConcreteAddr, Endpoint = Metadata, Error = Error> + Sync,
135135
R::Resolution: Send,
136136
R::Future: Send + Unpin,
137-
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr + fmt::Debug + Send + Unpin + 'static,
137+
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr,
138+
I: fmt::Debug + Send + Sync + Unpin + 'static,
138139
{
139140
let http = self
140141
.clone()

linkerd/app/outbound/src/tcp/logical.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ where
3131
>
3232
where
3333
I: io::AsyncRead + io::AsyncWrite + std::fmt::Debug + Send + Unpin + 'static,
34-
R: Resolve<ConcreteAddr, Endpoint = Metadata, Error = Error> + Clone + Send + 'static,
34+
R: Resolve<ConcreteAddr, Endpoint = Metadata, Error = Error>
35+
+ Clone
36+
+ Send
37+
+ Sync
38+
+ 'static,
3539
R::Resolution: Send,
3640
R::Future: Send + Unpin,
41+
C: Send + Sync + 'static,
3742
{
3843
let Self {
3944
config,
@@ -101,7 +106,10 @@ where
101106
.push_cache(cache_max_idle_age)
102107
.check_new_service::<Logical, I>()
103108
.instrument(|_: &Logical| debug_span!("tcp"))
104-
.check_new_service::<Logical, I>();
109+
.check_new_service::<Logical, I>()
110+
// Boxing is necessary purely to limit the link-time overhead of
111+
// having enormous types.
112+
.push(svc::BoxNewService::layer());
105113

106114
Outbound {
107115
config,

0 commit comments

Comments
 (0)