Skip to content

Commit 9da7839

Browse files
committed
Avoid Stream API in performance critical tracing code
Injection and extraction of the tracing context happens on each request. For example, on each call to a Controller or HTTP client. The Stream API used here was showing up on CPU profiling under heavy load. With these changes, the CPU profiling looks better.
1 parent 8861304 commit 9da7839

File tree

2 files changed

+47
-22
lines changed

2 files changed

+47
-22
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/CompositePropagationFactory.java

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Collection;
2020
import java.util.Collections;
2121
import java.util.List;
22-
import java.util.function.Predicate;
2322
import java.util.stream.Stream;
2423

2524
import brave.propagation.B3Propagation;
@@ -76,11 +75,19 @@ public Propagation<String> get() {
7675

7776
@Override
7877
public TraceContext decorate(TraceContext context) {
79-
return Stream.concat(this.injectors.stream(), this.extractors.stream())
80-
.map((factory) -> factory.decorate(context))
81-
.filter((decorated) -> decorated != context)
82-
.findFirst()
83-
.orElse(context);
78+
for (Propagation.Factory factory : this.injectors.factories) {
79+
TraceContext decorated = factory.decorate(context);
80+
if (decorated != context) {
81+
return decorated;
82+
}
83+
}
84+
for (Propagation.Factory factory : this.extractors.factories) {
85+
TraceContext decorated = factory.decorate(context);
86+
if (decorated != context) {
87+
return decorated;
88+
}
89+
}
90+
return context;
8491
}
8592

8693
/**
@@ -179,11 +186,21 @@ private static class PropagationFactories {
179186
}
180187

181188
boolean requires128BitTraceId() {
182-
return stream().anyMatch(Propagation.Factory::requires128BitTraceId);
189+
for (Propagation.Factory factory : this.factories) {
190+
if (factory.requires128BitTraceId()) {
191+
return true;
192+
}
193+
}
194+
return false;
183195
}
184196

185197
boolean supportsJoin() {
186-
return stream().allMatch(Propagation.Factory::supportsJoin);
198+
for (Propagation.Factory factory : this.factories) {
199+
if (!factory.supportsJoin()) {
200+
return false;
201+
}
202+
}
203+
return true;
187204
}
188205

189206
List<Propagation<String>> get() {
@@ -224,19 +241,24 @@ public List<String> keys() {
224241

225242
@Override
226243
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
227-
return (traceContext, request) -> this.injectors.stream()
228-
.map((propagation) -> propagation.injector(setter))
229-
.forEach((injector) -> injector.inject(traceContext, request));
244+
return (traceContext, request) -> {
245+
for (Propagation<String> propagation : this.injectors) {
246+
propagation.injector(setter).inject(traceContext, request);
247+
}
248+
};
230249
}
231250

232251
@Override
233252
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
234-
return (request) -> this.extractors.stream()
235-
.map((propagation) -> propagation.extractor(getter))
236-
.map((extractor) -> extractor.extract(request))
237-
.filter(Predicate.not(TraceContextOrSamplingFlags.EMPTY::equals))
238-
.findFirst()
239-
.orElse(TraceContextOrSamplingFlags.EMPTY);
253+
return (request) -> {
254+
for (Propagation<String> propagation : this.extractors) {
255+
TraceContextOrSamplingFlags extracted = propagation.extractor(getter).extract(request);
256+
if (!TraceContextOrSamplingFlags.EMPTY.equals(extracted)) {
257+
return extracted;
258+
}
259+
}
260+
return TraceContextOrSamplingFlags.EMPTY;
261+
};
240262
}
241263

242264
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/CompositeTextMapPropagator.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,14 @@ public <C> Context extract(Context context, C carrier, TextMapGetter<C> getter)
106106
if (getter == null) {
107107
return context;
108108
}
109-
Context result = this.extractors.stream()
110-
.map((extractor) -> extractor.extract(context, carrier, getter))
111-
.filter((extracted) -> extracted != context)
112-
.findFirst()
113-
.orElse(context);
109+
Context result = context;
110+
for (TextMapPropagator extractor : this.extractors) {
111+
Context extracted = extractor.extract(context, carrier, getter);
112+
if (extracted != context) {
113+
result = extracted;
114+
break;
115+
}
116+
}
114117
if (this.baggagePropagator != null) {
115118
result = this.baggagePropagator.extract(result, carrier, getter);
116119
}

0 commit comments

Comments
 (0)