Skip to content

Commit e078e8a

Browse files
Parallelize CI Visibility settings requests (#8299)
1 parent bcddb29 commit e078e8a

File tree

6 files changed

+124
-68
lines changed

6 files changed

+124
-68
lines changed

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public CiVisibilitySettings getSettings(TracerEnvironment tracerEnvironment) {
1818

1919
@Override
2020
public SkippableTests getSkippableTests(TracerEnvironment tracerEnvironment) {
21-
return new SkippableTests(null, Collections.emptyMap(), null);
21+
return SkippableTests.EMPTY;
2222
}
2323

2424
@Override

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Base64;
3030
import java.util.BitSet;
3131
import java.util.Collection;
32+
import java.util.Collections;
3233
import java.util.HashMap;
3334
import java.util.HashSet;
3435
import java.util.List;
@@ -191,7 +192,9 @@ public SkippableTests getSkippableTests(TracerEnvironment tracerEnvironment) thr
191192

192193
String correlationId = response.meta != null ? response.meta.correlation_id : null;
193194
Map<String, BitSet> coveredLinesByRelativeSourcePath =
194-
response.meta != null ? response.meta.coverage : null;
195+
response.meta != null && response.meta.coverage != null
196+
? response.meta.coverage
197+
: Collections.emptyMap();
195198
return new SkippableTests(
196199
correlationId, testIdentifiersByModule, coveredLinesByRelativeSourcePath);
197200
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class ExecutionSettings {
4040
@Nonnull private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings;
4141
@Nullable private final String itrCorrelationId;
4242
@Nonnull private final Map<TestIdentifier, TestMetadata> skippableTests;
43-
@Nullable private final Map<String, BitSet> skippableTestsCoverage;
43+
@Nonnull private final Map<String, BitSet> skippableTestsCoverage;
4444
@Nullable private final Collection<TestIdentifier> flakyTests;
4545
@Nullable private final Collection<TestIdentifier> knownTests;
4646
@Nonnull private final Diff pullRequestDiff;
@@ -54,7 +54,7 @@ public ExecutionSettings(
5454
@Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings,
5555
@Nullable String itrCorrelationId,
5656
@Nonnull Map<TestIdentifier, TestMetadata> skippableTests,
57-
@Nullable Map<String, BitSet> skippableTestsCoverage,
57+
@Nonnull Map<String, BitSet> skippableTestsCoverage,
5858
@Nullable Collection<TestIdentifier> flakyTests,
5959
@Nullable Collection<TestIdentifier> knownTests,
6060
@Nonnull Diff pullRequestDiff) {
@@ -107,7 +107,7 @@ public String getItrCorrelationId() {
107107
}
108108

109109
/** A bit vector of covered lines by relative source file path. */
110-
@Nullable
110+
@Nonnull
111111
public Map<String, BitSet> getSkippableTestsCoverage() {
112112
return skippableTestsCoverage;
113113
}
@@ -126,6 +126,10 @@ public Collection<TestIdentifier> getKnownTests() {
126126
return knownTests;
127127
}
128128

129+
/**
130+
* @return the list of flaky tests for the given module (can be empty), or {@code null} if flaky
131+
* tests could not be obtained
132+
*/
129133
@Nullable
130134
public Collection<TestIdentifier> getFlakyTests() {
131135
return flakyTests;

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java

Lines changed: 102 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import datadog.trace.api.civisibility.CIConstants;
55
import datadog.trace.api.civisibility.CiVisibilityWellKnownTags;
66
import datadog.trace.api.civisibility.config.TestIdentifier;
7-
import datadog.trace.api.civisibility.config.TestMetadata;
87
import datadog.trace.api.git.GitInfo;
98
import datadog.trace.api.git.GitInfoProvider;
109
import datadog.trace.civisibility.ci.PullRequestInfo;
@@ -14,15 +13,19 @@
1413
import datadog.trace.civisibility.git.tree.GitClient;
1514
import datadog.trace.civisibility.git.tree.GitDataUploader;
1615
import datadog.trace.civisibility.git.tree.GitRepoUnshallow;
17-
import java.nio.file.Path;
1816
import java.nio.file.Paths;
19-
import java.util.BitSet;
2017
import java.util.Collection;
2118
import java.util.Collections;
2219
import java.util.HashMap;
2320
import java.util.HashSet;
2421
import java.util.Map;
22+
import java.util.Objects;
2523
import java.util.Set;
24+
import java.util.concurrent.ExecutionException;
25+
import java.util.concurrent.ExecutorService;
26+
import java.util.concurrent.Executors;
27+
import java.util.concurrent.Future;
28+
import java.util.concurrent.ThreadFactory;
2629
import java.util.concurrent.TimeUnit;
2730
import java.util.function.Function;
2831
import javax.annotation.Nonnull;
@@ -33,8 +36,11 @@
3336
public class ExecutionSettingsFactoryImpl implements ExecutionSettingsFactory {
3437

3538
private static final Logger LOGGER = LoggerFactory.getLogger(ExecutionSettingsFactoryImpl.class);
39+
3640
private static final String TEST_CONFIGURATION_TAG_PREFIX = "test.configuration.";
3741

42+
private static final ThreadFactory THREAD_FACTORY = r -> new Thread(null, r, "dd-ci-vis-config");
43+
3844
/**
3945
* A workaround for bulk-requesting module settings. For any module that has no settings that are
4046
* exclusive to it (i.e. that has no skippable/flaky/known tests), the settings will be under this
@@ -113,9 +119,31 @@ private TracerEnvironment buildTracerEnvironment(
113119
.build();
114120
}
115121

116-
private @Nonnull Map<String, ExecutionSettings> create(TracerEnvironment tracerEnvironment) {
122+
@Nonnull
123+
private Map<String, ExecutionSettings> create(TracerEnvironment tracerEnvironment) {
117124
CiVisibilitySettings settings = getCiVisibilitySettings(tracerEnvironment);
125+
ExecutorService settingsExecutor = Executors.newCachedThreadPool(THREAD_FACTORY);
126+
try {
127+
return doCreate(tracerEnvironment, settings, settingsExecutor);
128+
129+
} catch (InterruptedException e) {
130+
Thread.currentThread().interrupt();
131+
LOGGER.error("Interrupted while creating execution settings");
132+
return Collections.singletonMap(DEFAULT_SETTINGS, ExecutionSettings.EMPTY);
118133

134+
} catch (ExecutionException e) {
135+
LOGGER.error("Error while creating execution settings", e);
136+
return Collections.singletonMap(DEFAULT_SETTINGS, ExecutionSettings.EMPTY);
137+
138+
} finally {
139+
settingsExecutor.shutdownNow();
140+
}
141+
}
142+
143+
@Nonnull
144+
private Map<String, ExecutionSettings> doCreate(
145+
TracerEnvironment tracerEnvironment, CiVisibilitySettings settings, ExecutorService executor)
146+
throws InterruptedException, ExecutionException {
119147
boolean itrEnabled =
120148
isFeatureEnabled(
121149
settings, CiVisibilitySettings::isItrEnabled, Config::isCiVisibilityItrEnabled);
@@ -134,7 +162,7 @@ private TracerEnvironment buildTracerEnvironment(
134162
settings,
135163
CiVisibilitySettings::isFlakyTestRetriesEnabled,
136164
Config::isCiVisibilityFlakyRetryEnabled);
137-
boolean impactedTestsDetectionEnabled =
165+
boolean impactedTestsEnabled =
138166
isFeatureEnabled(
139167
settings,
140168
CiVisibilitySettings::isImpactedTestsDetectionEnabled,
@@ -167,46 +195,27 @@ private TracerEnvironment buildTracerEnvironment(
167195
codeCoverageEnabled,
168196
testSkippingEnabled,
169197
earlyFlakeDetectionEnabled,
170-
impactedTestsDetectionEnabled,
198+
impactedTestsEnabled,
171199
knownTestsRequest,
172200
flakyTestRetriesEnabled);
173201

174-
String itrCorrelationId = null;
175-
Map<String, Map<TestIdentifier, TestMetadata>> skippableTestIdentifiers =
176-
Collections.emptyMap();
177-
Map<String, BitSet> skippableTestsCoverage = null;
178-
if (itrEnabled && repositoryRoot != null) {
179-
SkippableTests skippableTests =
180-
getSkippableTests(Paths.get(repositoryRoot), tracerEnvironment);
181-
if (skippableTests != null) {
182-
itrCorrelationId = skippableTests.getCorrelationId();
183-
skippableTestIdentifiers = skippableTests.getIdentifiersByModule();
184-
skippableTestsCoverage = skippableTests.getCoveredLinesByRelativeSourcePath();
185-
}
186-
}
187-
188-
Map<String, Collection<TestIdentifier>> flakyTestsByModule =
189-
flakyTestRetriesEnabled && config.isCiVisibilityFlakyRetryOnlyKnownFlakes()
190-
|| CIConstants.FAIL_FAST_TEST_ORDER.equalsIgnoreCase(
191-
config.getCiVisibilityTestOrder())
192-
? getFlakyTestsByModule(tracerEnvironment)
193-
: null;
194-
195-
Map<String, Collection<TestIdentifier>> knownTestsByModule =
196-
knownTestsRequest ? getKnownTestsByModule(tracerEnvironment) : null;
197-
198-
Set<String> moduleNames = new HashSet<>(Collections.singleton(DEFAULT_SETTINGS));
199-
moduleNames.addAll(skippableTestIdentifiers.keySet());
200-
if (flakyTestsByModule != null) {
201-
moduleNames.addAll(flakyTestsByModule.keySet());
202-
}
203-
if (knownTestsByModule != null) {
204-
moduleNames.addAll(knownTestsByModule.keySet());
205-
}
202+
Future<SkippableTests> skippableTestsFuture =
203+
executor.submit(() -> getSkippableTests(tracerEnvironment, itrEnabled));
204+
Future<Map<String, Collection<TestIdentifier>>> flakyTestsFuture =
205+
executor.submit(() -> getFlakyTestsByModule(tracerEnvironment, flakyTestRetriesEnabled));
206+
Future<Map<String, Collection<TestIdentifier>>> knownTestsFuture =
207+
executor.submit(() -> getKnownTestsByModule(tracerEnvironment, knownTestsRequest));
208+
Future<Diff> pullRequestDiffFuture =
209+
executor.submit(() -> getPullRequestDiff(tracerEnvironment, impactedTestsEnabled));
206210

207-
Diff pullRequestDiff = getPullRequestDiff(impactedTestsDetectionEnabled, tracerEnvironment);
211+
SkippableTests skippableTests = skippableTestsFuture.get();
212+
Map<String, Collection<TestIdentifier>> flakyTestsByModule = flakyTestsFuture.get();
213+
Map<String, Collection<TestIdentifier>> knownTestsByModule = knownTestsFuture.get();
214+
Diff pullRequestDiff = pullRequestDiffFuture.get();
208215

209216
Map<String, ExecutionSettings> settingsByModule = new HashMap<>();
217+
Set<String> moduleNames =
218+
getModuleNames(skippableTests, flakyTestsByModule, knownTestsByModule);
210219
for (String moduleName : moduleNames) {
211220
settingsByModule.put(
212221
moduleName,
@@ -215,13 +224,15 @@ private TracerEnvironment buildTracerEnvironment(
215224
codeCoverageEnabled,
216225
testSkippingEnabled,
217226
flakyTestRetriesEnabled,
218-
impactedTestsDetectionEnabled,
227+
impactedTestsEnabled,
219228
earlyFlakeDetectionEnabled
220229
? settings.getEarlyFlakeDetectionSettings()
221230
: EarlyFlakeDetectionSettings.DEFAULT,
222-
itrCorrelationId,
223-
skippableTestIdentifiers.getOrDefault(moduleName, Collections.emptyMap()),
224-
skippableTestsCoverage,
231+
skippableTests.getCorrelationId(),
232+
skippableTests
233+
.getIdentifiersByModule()
234+
.getOrDefault(moduleName, Collections.emptyMap()),
235+
skippableTests.getCoveredLinesByRelativeSourcePath(),
225236
flakyTestsByModule != null
226237
? flakyTestsByModule.getOrDefault(moduleName, Collections.emptyList())
227238
: null,
@@ -258,47 +269,66 @@ private boolean isFeatureEnabled(
258269
return remoteSetting.apply(ciVisibilitySettings) && killSwitch.apply(config);
259270
}
260271

261-
@Nullable
272+
@Nonnull
262273
private SkippableTests getSkippableTests(
263-
Path repositoryRoot, TracerEnvironment tracerEnvironment) {
274+
TracerEnvironment tracerEnvironment, boolean itrEnabled) {
275+
if (!itrEnabled || repositoryRoot == null) {
276+
return SkippableTests.EMPTY;
277+
}
264278
try {
265279
// ensure git data upload is finished before asking for tests
266280
gitDataUploader
267281
.startOrObserveGitDataUpload()
268282
.get(config.getCiVisibilityGitUploadTimeoutMillis(), TimeUnit.MILLISECONDS);
269283

270284
SkippableTests skippableTests = configurationApi.getSkippableTests(tracerEnvironment);
271-
LOGGER.debug(
272-
"Received {} skippable tests in total for {}",
273-
skippableTests.getIdentifiersByModule().size(),
274-
repositoryRoot);
285+
286+
if (LOGGER.isDebugEnabled()) {
287+
int totalSkippableTests =
288+
skippableTests.getIdentifiersByModule().values().stream()
289+
.filter(Objects::nonNull)
290+
.mapToInt(Map::size)
291+
.sum();
292+
LOGGER.debug(
293+
"Received {} skippable tests in total for {}",
294+
totalSkippableTests,
295+
Paths.get(repositoryRoot));
296+
}
275297

276298
return skippableTests;
277299

278300
} catch (InterruptedException e) {
279301
Thread.currentThread().interrupt();
280302
LOGGER.error("Interrupted while waiting for git data upload", e);
281-
return null;
303+
return SkippableTests.EMPTY;
282304

283305
} catch (Exception e) {
284306
LOGGER.error("Could not obtain list of skippable tests, will proceed without skipping", e);
285-
return null;
307+
return SkippableTests.EMPTY;
286308
}
287309
}
288310

311+
@Nullable
289312
private Map<String, Collection<TestIdentifier>> getFlakyTestsByModule(
290-
TracerEnvironment tracerEnvironment) {
313+
TracerEnvironment tracerEnvironment, boolean flakyTestRetriesEnabled) {
314+
if (!(flakyTestRetriesEnabled && config.isCiVisibilityFlakyRetryOnlyKnownFlakes())
315+
&& !CIConstants.FAIL_FAST_TEST_ORDER.equalsIgnoreCase(config.getCiVisibilityTestOrder())) {
316+
return null;
317+
}
291318
try {
292319
return configurationApi.getFlakyTestsByModule(tracerEnvironment);
293-
294320
} catch (Exception e) {
295321
LOGGER.error("Could not obtain list of flaky tests", e);
296-
return Collections.emptyMap();
322+
return null;
297323
}
298324
}
299325

326+
@Nullable
300327
private Map<String, Collection<TestIdentifier>> getKnownTestsByModule(
301-
TracerEnvironment tracerEnvironment) {
328+
TracerEnvironment tracerEnvironment, boolean knownTestsRequest) {
329+
if (!knownTestsRequest) {
330+
return null;
331+
}
302332
try {
303333
return configurationApi.getKnownTestsByModule(tracerEnvironment);
304334

@@ -310,7 +340,7 @@ private Map<String, Collection<TestIdentifier>> getKnownTestsByModule(
310340

311341
@Nonnull
312342
private Diff getPullRequestDiff(
313-
boolean impactedTestsDetectionEnabled, TracerEnvironment tracerEnvironment) {
343+
TracerEnvironment tracerEnvironment, boolean impactedTestsDetectionEnabled) {
314344
if (!impactedTestsDetectionEnabled) {
315345
return LineDiff.EMPTY;
316346
}
@@ -362,4 +392,20 @@ private Diff getPullRequestDiff(
362392

363393
return LineDiff.EMPTY;
364394
}
395+
396+
@Nonnull
397+
private static Set<String> getModuleNames(
398+
SkippableTests skippableTests,
399+
Map<String, Collection<TestIdentifier>> flakyTestsByModule,
400+
Map<String, Collection<TestIdentifier>> knownTestsByModule) {
401+
Set<String> moduleNames = new HashSet<>(Collections.singleton(DEFAULT_SETTINGS));
402+
moduleNames.addAll(skippableTests.getIdentifiersByModule().keySet());
403+
if (flakyTestsByModule != null) {
404+
moduleNames.addAll(flakyTestsByModule.keySet());
405+
}
406+
if (knownTestsByModule != null) {
407+
moduleNames.addAll(knownTestsByModule.keySet());
408+
}
409+
return moduleNames;
410+
}
365411
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/SkippableTests.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,24 @@
33
import datadog.trace.api.civisibility.config.TestIdentifier;
44
import datadog.trace.api.civisibility.config.TestMetadata;
55
import java.util.BitSet;
6+
import java.util.Collections;
67
import java.util.Map;
8+
import javax.annotation.Nonnull;
79
import javax.annotation.Nullable;
810

911
public class SkippableTests {
1012

13+
public static final SkippableTests EMPTY =
14+
new SkippableTests(null, Collections.emptyMap(), Collections.emptyMap());
15+
1116
private final String correlationId;
1217
private final Map<String, Map<TestIdentifier, TestMetadata>> identifiersByModule;
1318
private final Map<String, BitSet> coveredLinesByRelativeSourcePath;
1419

1520
public SkippableTests(
1621
@Nullable String correlationId,
17-
Map<String, Map<TestIdentifier, TestMetadata>> identifiersByModule,
18-
@Nullable Map<String, BitSet> coveredLinesByRelativeSourcePath) {
22+
@Nonnull Map<String, Map<TestIdentifier, TestMetadata>> identifiersByModule,
23+
@Nonnull Map<String, BitSet> coveredLinesByRelativeSourcePath) {
1924
this.correlationId = correlationId;
2025
this.identifiersByModule = identifiersByModule;
2126
this.coveredLinesByRelativeSourcePath = coveredLinesByRelativeSourcePath;
@@ -26,11 +31,12 @@ public String getCorrelationId() {
2631
return correlationId;
2732
}
2833

34+
@Nonnull
2935
public Map<String, Map<TestIdentifier, TestMetadata>> getIdentifiersByModule() {
3036
return identifiersByModule;
3137
}
3238

33-
@Nullable
39+
@Nonnull
3440
public Map<String, BitSet> getCoveredLinesByRelativeSourcePath() {
3541
return coveredLinesByRelativeSourcePath;
3642
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/coverage/percentage/JacocoCoverageCalculator.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,7 @@ private void addModuleLayout(BuildModuleLayout moduleLayout) {
165165
}
166166

167167
/** Handles skipped tests' coverage data received from the backend */
168-
private void addBackendCoverageData(@Nullable Map<String, BitSet> skippableTestsCoverage) {
169-
if (skippableTestsCoverage == null) {
170-
return;
171-
}
168+
private void addBackendCoverageData(@Nonnull Map<String, BitSet> skippableTestsCoverage) {
172169
synchronized (coverageDataLock) {
173170
for (Map.Entry<String, BitSet> e : skippableTestsCoverage.entrySet()) {
174171
backendCoverageData.merge(e.getKey(), e.getValue(), JacocoCoverageCalculator::mergeBitSets);

0 commit comments

Comments
 (0)