Skip to content

Commit bb1bc5d

Browse files
authored
Preserve span order (#8017)
On jdk8 tests occasionally fail when spans starting on the same millisecond get reordered. As we eventually sort the spans by start time we can't get the initial oder back when the start time is the same for these spans. Hopefully preserving the initial order reduces such flakiness.
1 parent 67b6b6c commit bb1bc5d

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

testing-common/src/main/java/io/opentelemetry/instrumentation/testing/util/TelemetryDataUtil.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import java.util.ArrayList;
1515
import java.util.Arrays;
1616
import java.util.Comparator;
17-
import java.util.HashMap;
17+
import java.util.LinkedHashMap;
1818
import java.util.List;
1919
import java.util.Map;
2020
import java.util.concurrent.TimeUnit;
@@ -37,7 +37,9 @@ public static Comparator<List<SpanData>> orderByRootSpanName(String... names) {
3737
public static List<List<SpanData>> groupTraces(List<SpanData> spans) {
3838
List<List<SpanData>> traces =
3939
new ArrayList<>(
40-
spans.stream().collect(Collectors.groupingBy(SpanData::getTraceId)).values());
40+
spans.stream()
41+
.collect(Collectors.groupingBy(SpanData::getTraceId, LinkedHashMap::new, toList()))
42+
.values());
4143
sortTraces(traces);
4244
for (int i = 0; i < traces.size(); i++) {
4345
List<SpanData> trace = traces.get(i);
@@ -107,7 +109,7 @@ private static long getMinSpanOrder(List<SpanData> spans) {
107109
@SuppressWarnings("UnstableApiUsage")
108110
private static List<SpanData> sort(List<SpanData> trace) {
109111

110-
Map<String, Node> lookup = new HashMap<>();
112+
Map<String, Node> lookup = new LinkedHashMap<>();
111113
for (SpanData span : trace) {
112114
lookup.put(span.getSpanId(), new Node(span));
113115
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.testing.util;
7+
8+
import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat;
9+
10+
import io.opentelemetry.api.trace.SpanContext;
11+
import io.opentelemetry.api.trace.SpanId;
12+
import io.opentelemetry.api.trace.SpanKind;
13+
import io.opentelemetry.api.trace.TraceFlags;
14+
import io.opentelemetry.api.trace.TraceId;
15+
import io.opentelemetry.api.trace.TraceState;
16+
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
17+
import io.opentelemetry.sdk.testing.assertj.TraceAssert;
18+
import io.opentelemetry.sdk.testing.trace.TestSpanData;
19+
import io.opentelemetry.sdk.trace.data.SpanData;
20+
import io.opentelemetry.sdk.trace.data.StatusData;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.function.Consumer;
24+
import org.junit.jupiter.api.Test;
25+
26+
public class TelemetryDataUtilTest {
27+
28+
@Test
29+
void testTraceOrderPreserved() {
30+
List<SpanData> spans = new ArrayList<>();
31+
for (int i = 1; i <= 10; i++) {
32+
spans.add(
33+
TestSpanData.builder()
34+
.setName("trace" + i)
35+
.setSpanContext(
36+
SpanContext.create(
37+
TraceId.fromLongs(0, i),
38+
SpanId.fromLong(i),
39+
TraceFlags.getDefault(),
40+
TraceState.getDefault()))
41+
.setKind(SpanKind.CLIENT)
42+
.setStatus(StatusData.unset())
43+
.setHasEnded(true)
44+
.setStartEpochNanos(1678338770194000000L)
45+
.setEndEpochNanos(1678338770196419884L)
46+
.build());
47+
}
48+
49+
List<Consumer<TraceAssert>> asserts = new ArrayList<>();
50+
for (int i = 1; i <= 10; i++) {
51+
String spanName = "trace" + i;
52+
asserts.add(trace -> trace.hasSpansSatisfyingExactly(span -> span.hasName(spanName)));
53+
}
54+
55+
List<List<SpanData>> result = TelemetryDataUtil.groupTraces(spans);
56+
assertThat(result).hasTracesSatisfyingExactly(asserts);
57+
}
58+
59+
@Test
60+
void testSpanOrderPreserved() {
61+
List<SpanData> spans = new ArrayList<>();
62+
for (int i = 1; i <= 10; i++) {
63+
spans.add(
64+
TestSpanData.builder()
65+
.setName("span" + i)
66+
.setSpanContext(
67+
SpanContext.create(
68+
TraceId.fromLongs(0, 1),
69+
SpanId.fromLong(i),
70+
TraceFlags.getDefault(),
71+
TraceState.getDefault()))
72+
.setKind(SpanKind.CLIENT)
73+
.setStatus(StatusData.unset())
74+
.setHasEnded(true)
75+
.setStartEpochNanos(1678338770194000000L)
76+
.setEndEpochNanos(1678338770196419884L)
77+
.build());
78+
}
79+
80+
List<Consumer<SpanDataAssert>> asserts = new ArrayList<>();
81+
for (int i = 1; i <= 10; i++) {
82+
String spanName = "span" + i;
83+
asserts.add(span -> span.hasName(spanName));
84+
}
85+
86+
List<List<SpanData>> result = TelemetryDataUtil.groupTraces(spans);
87+
assertThat(result)
88+
.hasTracesSatisfyingExactly(trace -> trace.hasSpansSatisfyingExactly(asserts));
89+
}
90+
}

0 commit comments

Comments
 (0)