Skip to content

Commit d370783

Browse files
olix0rhawkw
andauthored
Box all stack modules (#1011)
Building a release-mode proxy currently consumes significant resources: Elapsed (wall clock) time (h:mm:ss or m:ss): 4:23.84 Maximum resident set size (kbytes): 23751132 With this, change we reduce memory consumption by >50%: Elapsed (wall clock) time (h:mm:ss or m:ss): 2:18.08 Maximum resident set size (kbytes): 10144624 This is accomplished by boxing each top-level module. This is made explicit by modifying the signature of these stack builders to explicitly reference boxed types (instead of using `impl`). Furthermore, we add a `BoxNewService` wherever the inner new service is cloned into a returned `Service`. This most commonly happens with layers like `http::NewDetectService`` `NewDetectTls`, and `NewRouter`. Co-authored-by: Eliza Weisman <eliza@buoyant.io>
1 parent 251fa8b commit d370783

File tree

20 files changed

+202
-214
lines changed

20 files changed

+202
-214
lines changed

.github/workflows/advisory.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Optional build
2+
name: Advisory
3+
4+
on:
5+
pull_request: {}
6+
7+
jobs:
8+
# Prevent sudden announcement of a new advisory from failing Ci.
9+
advisories:
10+
timeout-minutes: 5
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v2
14+
- uses: EmbarkStudios/cargo-deny-action@202e2b23300c1ef90c9d7389a0bc5b34c05c0fe4
15+
with:
16+
command: check advisories
17+
18+
# Ensure that all of the fuzzers continue to build.
19+
#
20+
# This is optional because it depends on the nightly toolchain.
21+
fuzzers:
22+
timeout-minutes: 40
23+
runs-on: ubuntu-latest
24+
container:
25+
image: docker://rust:1.52.1-buster
26+
strategy:
27+
matrix:
28+
dir:
29+
- addr
30+
- app/inbound
31+
- dns
32+
- proxy/http
33+
- tls
34+
- transport-header
35+
steps:
36+
- uses: actions/checkout@v2
37+
- run: rustup toolchain add nightly
38+
- run: cargo install cargo-fuzz
39+
# Iterate through all fuzz crates to ensure each compiles independently.
40+
- run: cd linkerd/${{matrix.dir}}/fuzz && cargo +nightly fuzz build
41+
# Error if the repo isn't clean (i.e. because lock files were modified).
42+
- run: git status && git diff-index --quiet HEAD
43+
44+
# It's easy to make changes that are innocuous in dev builds that end up ballooning resources
45+
# needed for release builds. This job builds the proxy in release-mode to ensure the build isn't
46+
# OOM-killed.
47+
release-binary:
48+
timeout-minutes: 15
49+
runs-on: ubuntu-latest
50+
steps:
51+
- uses: actions/checkout@v2
52+
- run: make build
53+
env:
54+
CARGO_RELEASE: true

.github/workflows/rust.yml

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,20 @@ on:
44
pull_request: {}
55

66
jobs:
7-
fmt:
8-
timeout-minutes: 5
9-
runs-on: ubuntu-latest
10-
container:
11-
image: docker://rust:1.52.1-buster
12-
steps:
13-
- uses: actions/checkout@v2
14-
- run: rustup component add rustfmt
15-
- run: make check-fmt
167

8+
## Required builds
9+
10+
# Ensures we don't take unintended dependencies.
1711
audit:
1812
timeout-minutes: 5
1913
runs-on: ubuntu-latest
20-
strategy:
21-
matrix:
22-
checks:
23-
- advisories
24-
- bans licenses sources
25-
# Prevent sudden announcement of a new advisory from failing Ci.
26-
continue-on-error: ${{ matrix.checks == 'advisories' }}
2714
steps:
2815
- uses: actions/checkout@v2
2916
- uses: EmbarkStudios/cargo-deny-action@202e2b23300c1ef90c9d7389a0bc5b34c05c0fe4
3017
with:
31-
command: check ${{ matrix.checks }}
18+
command: check bans licenses sources
3219

20+
# Linting
3321
clippy:
3422
timeout-minutes: 5
3523
runs-on: ubuntu-latest
@@ -40,42 +28,31 @@ jobs:
4028
- run: rustup component add clippy
4129
- run: make lint
4230

31+
# Iterate through all (non-fuzzer) sub-crates to ensure each compiles independently.
4332
check:
4433
timeout-minutes: 20
4534
runs-on: ubuntu-latest
4635
container:
4736
image: docker://rust:1.52.1-buster
4837
steps:
4938
- uses: actions/checkout@v2
50-
# Iterate through all (non-fuzzer) sub-crates to ensure each compiles independently.
5139
- run: for d in $(for toml in $(find . -mindepth 2 -name Cargo.toml -not -path '*/fuzz/*') ; do echo ${toml%/*} ; done | sort -r ) ; do echo "# $d" ; (cd $d ; cargo check --all-targets) ; done
5240

53-
test:
54-
timeout-minutes: 15
41+
# Enforce automated formatting.
42+
fmt:
43+
timeout-minutes: 5
5544
runs-on: ubuntu-latest
45+
container:
46+
image: docker://rust:1.52.1-buster
5647
steps:
5748
- uses: actions/checkout@v2
58-
- run: make test
49+
- run: rustup component add rustfmt
50+
- run: make check-fmt
5951

60-
fuzzers:
61-
timeout-minutes: 40
52+
# Run all tests.
53+
test:
54+
timeout-minutes: 15
6255
runs-on: ubuntu-latest
63-
container:
64-
image: docker://rust:1.52.1-buster
65-
strategy:
66-
matrix:
67-
dir:
68-
- addr
69-
- app/inbound
70-
- dns
71-
- proxy/http
72-
- tls
73-
- transport-header
7456
steps:
7557
- uses: actions/checkout@v2
76-
- run: rustup toolchain add nightly
77-
- run: cargo install cargo-fuzz
78-
# Iterate through all fuzz crates to ensure each compiles independently.
79-
- run: cd linkerd/${{matrix.dir}}/fuzz && cargo +nightly fuzz build
80-
# Error if the repo isn't clean (i.e. because lock files were modified).
81-
- run: git status && git diff-index --quiet HEAD
58+
- run: make test

linkerd/app/gateway/src/lib.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,14 @@ struct RefusedNoTarget(());
6767
#[error("the provided address could not be resolved: {}", self.0)]
6868
struct RefusedNotResolved(NameAddr);
6969

70-
#[allow(clippy::clippy::too_many_arguments)]
70+
#[allow(clippy::too_many_arguments)]
7171
pub fn stack<I, O, P, R>(
7272
Config { allow_discovery }: Config,
7373
inbound: Inbound<()>,
7474
outbound: Outbound<O>,
7575
profiles: P,
7676
resolve: R,
77-
) -> impl svc::NewService<
78-
GatewayConnection,
79-
Service = impl svc::Service<I, Response = (), Error = impl Into<Error>, Future = impl Send>
80-
+ Send
81-
+ 'static,
82-
> + Clone
83-
+ Send
77+
) -> svc::BoxNewService<GatewayConnection, svc::BoxService<I, (), Error>>
8478
where
8579
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr + fmt::Debug + Send + Sync + Unpin + 'static,
8680
O: Clone + Send + Sync + Unpin + 'static,
@@ -199,7 +193,8 @@ where
199193
svc::layers()
200194
.push(http::Retain::layer())
201195
.push(http::BoxResponse::layer()),
202-
);
196+
)
197+
.push(svc::BoxNewService::layer());
203198

204199
// When handling gateway connections from older clients that do not
205200
// support the transport header, do protocol detection and route requests
@@ -216,6 +211,7 @@ where
216211
.push_http_server()
217212
.into_stack()
218213
.push(svc::Filter::<ClientInfo, _>::layer(HttpLegacy::try_from))
214+
.push(svc::BoxNewService::layer())
219215
.push(detect::NewDetectService::layer(
220216
detect_protocol_timeout,
221217
http::DetectHttp::default(),
@@ -259,11 +255,10 @@ where
259255
GatewayConnection::TransportHeader(t) => Ok::<_, Never>(svc::Either::A(t)),
260256
GatewayConnection::Legacy(c) => Ok(svc::Either::B(c)),
261257
},
262-
legacy_http
263-
.push_on_response(svc::BoxService::layer())
264-
.push(svc::BoxNewService::layer())
265-
.into_inner(),
258+
legacy_http.into_inner(),
266259
)
260+
.push_on_response(svc::BoxService::layer())
261+
.push(svc::BoxNewService::layer())
267262
.into_inner()
268263
}
269264

linkerd/app/inbound/src/direct.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,7 @@ impl<N> Inbound<N> {
6767
pub fn push_direct<T, I, NSvc, G, GSvc>(
6868
self,
6969
gateway: G,
70-
) -> Inbound<
71-
impl svc::NewService<
72-
T,
73-
Service = impl svc::Service<I, Response = (), Error = Error, Future = impl Send>,
74-
> + Clone,
75-
>
70+
) -> Inbound<svc::BoxNewService<T, svc::BoxService<I, (), Error>>>
7671
where
7772
T: Param<Remote<ClientAddr>> + Param<OrigDstAddr> + Clone + Send + 'static,
7873
I: io::AsyncRead + io::AsyncWrite + io::Peek + io::PeerAddr,
@@ -158,11 +153,14 @@ impl<N> Inbound<N> {
158153
// Build a ClientInfo target for each accepted connection. Refuse the
159154
// connection if it doesn't include an mTLS identity.
160155
.push_request_filter(ClientInfo::try_from)
156+
.push(svc::BoxNewService::layer())
161157
.push(tls::NewDetectTls::layer(
162158
rt.identity.clone().map(WithTransportHeaderAlpn),
163159
detect_timeout,
164160
))
165-
.check_new_service::<T, I>();
161+
.check_new_service::<T, I>()
162+
.push_on_response(svc::BoxService::layer())
163+
.push(svc::BoxNewService::layer());
166164

167165
Inbound {
168166
config,

linkerd/app/inbound/src/http/mod.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ impl<H> Inbound<H> {
2727
pub fn push_http_server<T, I, HSvc>(
2828
self,
2929
) -> Inbound<
30-
impl svc::NewService<
31-
T,
32-
Service = impl svc::Service<I, Response = (), Error = Error, Future = impl Send> + Clone,
33-
> + Clone,
30+
svc::BoxNewService<
31+
T,
32+
impl svc::Service<I, Response = (), Error = Error, Future = impl Send> + Clone,
33+
>,
3434
>
3535
where
3636
T: Param<Version>
@@ -89,7 +89,8 @@ impl<H> Inbound<H> {
8989
)
9090
.check_new_service::<T, http::Request<_>>()
9191
.instrument(|t: &T| debug_span!("http", v=%Param::<Version>::param(t)))
92-
.push(http::NewServeHttp::layer(h2_settings, rt.drain.clone()));
92+
.push(http::NewServeHttp::layer(h2_settings, rt.drain.clone()))
93+
.push(svc::BoxNewService::layer());
9394

9495
Inbound {
9596
config,
@@ -110,15 +111,15 @@ where
110111
self,
111112
profiles: P,
112113
) -> Inbound<
113-
impl svc::NewService<
114-
HttpAccept,
115-
Service = impl svc::Service<
114+
svc::BoxNewService<
115+
HttpAccept,
116+
impl svc::Service<
116117
http::Request<http::BoxBody>,
117118
Response = http::Response<http::BoxBody>,
118119
Error = Error,
119120
Future = impl Send,
120121
> + Clone,
121-
> + Clone,
122+
>,
122123
>
123124
where
124125
P: profiles::GetProfile<profiles::LookupAddr> + Clone + Send + Sync + 'static,
@@ -225,19 +226,18 @@ where
225226
.push(http::Retain::layer())
226227
.push(http::BoxResponse::layer()),
227228
)
228-
// Boxing is necessary purely to limit the link-time overhead of
229-
// having enormous types.
230-
.push(svc::BoxNewService::layer())
231229
.check_new_service::<Target, http::Request<http::BoxBody>>()
232230
// Removes the override header after it has been used to
233231
// determine a request target.
234232
.push_on_response(strip_header::request::layer(DST_OVERRIDE_HEADER))
235233
// Routes each request to a target, obtains a service for that
236234
// target, and dispatches the request.
237235
.instrument_from_target()
236+
.push(svc::BoxNewService::layer())
238237
.push(svc::NewRouter::layer(RequestTarget::from))
239238
// Used by tap.
240-
.push_http_insert_target::<HttpAccept>();
239+
.push_http_insert_target::<HttpAccept>()
240+
.push(svc::BoxNewService::layer());
241241

242242
Inbound {
243243
config,

linkerd/app/inbound/src/http/tests.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ fn build_server<I>(
2222
rt: ProxyRuntime,
2323
profiles: resolver::Profiles,
2424
connect: Connect<Remote<ServerAddr>>,
25-
) -> impl svc::NewService<
25+
) -> svc::BoxNewService<
2626
HttpAccept,
27-
Service = impl tower::Service<
28-
I,
29-
Response = (),
30-
Error = impl Into<linkerd_app_core::Error>,
31-
Future = impl Send + 'static,
32-
> + Send
33-
+ Clone,
34-
> + Clone
27+
impl tower::Service<
28+
I,
29+
Response = (),
30+
Error = impl Into<linkerd_app_core::Error>,
31+
Future = impl Send + 'static,
32+
> + Send
33+
+ Clone,
34+
>
3535
where
3636
I: io::AsyncRead + io::AsyncWrite + io::PeerAddr + Send + Unpin + 'static,
3737
{

0 commit comments

Comments
 (0)