Skip to content

Commit 4ef63e5

Browse files
committed
Allow multiple cache entries per checksum
1 parent c2e4c0b commit 4ef63e5

22 files changed

+482
-155
lines changed

src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java

+38-19
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424

2525
import java.io.File;
2626
import java.nio.file.Path;
27+
import java.util.Comparator;
2728
import java.util.HashSet;
29+
import java.util.LinkedHashMap;
2830
import java.util.List;
2931
import java.util.Map;
3032
import java.util.Set;
@@ -106,7 +108,7 @@ public void execute(
106108

107109
// execute clean bound goals before restoring to not interfere/slowdown clean
108110
CacheState cacheState = DISABLED;
109-
CacheResult result = CacheResult.empty();
111+
Map<Zone, CacheResult> results = new LinkedHashMap<>();
110112
boolean skipCache = cacheConfig.isSkipCache() || MavenProjectInput.isSkipCache(project);
111113
boolean cacheIsDisabled = MavenProjectInput.isCacheDisabled(project);
112114
// Forked execution should be thought as a part of originating mojo internal implementation
@@ -125,15 +127,21 @@ public void execute(
125127
"Cache is explicitly disabled on project level for {}", getVersionlessProjectKey(project));
126128
}
127129
if (cacheState == INITIALIZED || skipCache) {
128-
result = cacheController.findCachedBuild(session, project, mojoExecutions, skipCache);
130+
for (Zone zone : cacheConfig.getInputZones()) {
131+
results.put(
132+
zone,
133+
cacheController.findCachedBuild(session, project, mojoExecutions, zone, skipCache));
134+
}
129135
}
130136
}
131137

132-
boolean restorable = result.isSuccess() || result.isPartialSuccess();
133138
boolean restored = false; // if partially restored need to save increment
134-
if (restorable) {
139+
CacheResult bestResult = results.values().stream()
140+
.max(Comparator.comparing(CacheResult::isRestorable))
141+
.orElseGet(CacheResult::empty);
142+
if (bestResult.isRestorable()) {
135143
CacheRestorationStatus cacheRestorationStatus =
136-
restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig);
144+
restoreProject(bestResult, mojoExecutions, mojoExecutionRunner, cacheConfig);
137145
restored = CacheRestorationStatus.SUCCESS == cacheRestorationStatus;
138146
executeExtraCleanPhaseIfNeeded(cacheRestorationStatus, cleanPhase, mojoExecutionRunner);
139147
}
@@ -147,21 +155,32 @@ public void execute(
147155
}
148156
}
149157

150-
if (cacheState == INITIALIZED && (!result.isSuccess() || !restored)) {
151-
if (cacheConfig.isSkipSave()) {
152-
LOGGER.info("Cache saving is disabled.");
153-
} else if (cacheConfig.isMandatoryClean()
154-
&& lifecyclePhasesHelper
155-
.getCleanSegment(project, mojoExecutions)
156-
.isEmpty()) {
157-
LOGGER.info("Cache storing is skipped since there was no \"clean\" phase.");
158-
} else {
159-
final Map<String, MojoExecutionEvent> executionEvents = mojoListener.getProjectExecutions(project);
160-
cacheController.save(result, mojoExecutions, executionEvents);
158+
if (cacheState == INITIALIZED) {
159+
final Map<String, MojoExecutionEvent> executionEvents = mojoListener.getProjectExecutions(project);
160+
for (Zone outputZone : cacheConfig.getOutputZones()) {
161+
CacheResult zoneResult = results.get(outputZone);
162+
if (bestResult.isSuccess()
163+
&& restored
164+
&& (bestResult.getInputZone().equals(outputZone)
165+
|| zoneResult != null && zoneResult.isSuccess())) {
166+
continue;
167+
}
168+
if (cacheConfig.isSkipSave()) {
169+
LOGGER.info("Cache saving is disabled.");
170+
break;
171+
}
172+
if (cacheConfig.isMandatoryClean()
173+
&& lifecyclePhasesHelper
174+
.getCleanSegment(project, mojoExecutions)
175+
.isEmpty()) {
176+
LOGGER.info("Cache storing is skipped since there was no \"clean\" phase.");
177+
break;
178+
}
179+
cacheController.save(bestResult, mojoExecutions, executionEvents, outputZone);
161180
}
162181
}
163182

164-
if (cacheConfig.isFailFast() && !result.isSuccess() && !skipCache && !forkedExecution) {
183+
if (cacheConfig.isFailFast() && !bestResult.isSuccess() && !skipCache && !forkedExecution) {
165184
throw new LifecycleExecutionException(
166185
"Failed to restore project[" + getVersionlessProjectKey(project)
167186
+ "] from cache, failing build.",
@@ -178,8 +197,8 @@ public void execute(
178197
* we execute an extra clean phase to remove any potential partially restored files
179198
*
180199
* @param cacheRestorationStatus the restoration status
181-
* @param cleanPhase clean phase mojos
182-
* @param mojoExecutionRunner mojo runner
200+
* @param cleanPhase clean phase mojos
201+
* @param mojoExecutionRunner mojo runner
183202
* @throws LifecycleExecutionException
184203
*/
185204
private void executeExtraCleanPhaseIfNeeded(

src/main/java/org/apache/maven/buildcache/CacheController.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,19 @@
3333
public interface CacheController {
3434

3535
CacheResult findCachedBuild(
36-
MavenSession session, MavenProject project, List<MojoExecution> mojoExecutions, boolean skipCache);
36+
MavenSession session,
37+
MavenProject project,
38+
List<MojoExecution> mojoExecutions,
39+
Zone inputZone,
40+
boolean skipCache);
3741

3842
ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult);
3943

4044
void save(
4145
CacheResult cacheResult,
4246
List<MojoExecution> mojoExecutions,
43-
Map<String, MojoExecutionEvent> executionEvents);
47+
Map<String, MojoExecutionEvent> executionEvents,
48+
Zone outputZone);
4449

4550
boolean isForcedExecution(MavenProject project, MojoExecution execution);
4651

src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

+48-35
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ public CacheControllerImpl(
168168
@Override
169169
@Nonnull
170170
public CacheResult findCachedBuild(
171-
MavenSession session, MavenProject project, List<MojoExecution> mojoExecutions, boolean skipCache) {
171+
MavenSession session,
172+
MavenProject project,
173+
List<MojoExecution> mojoExecutions,
174+
Zone inputZone,
175+
boolean skipCache) {
172176
final String highestPhase = lifecyclePhasesHelper.resolveHighestLifecyclePhase(project, mojoExecutions);
173177

174178
if (!lifecyclePhasesHelper.isLaterPhaseThanClean(highestPhase)) {
@@ -177,30 +181,33 @@ public CacheResult findCachedBuild(
177181

178182
String projectName = getVersionlessProjectKey(project);
179183

180-
ProjectsInputInfo inputInfo = projectInputCalculator.calculateInput(project);
184+
ProjectsInputInfo inputInfo = projectInputCalculator.calculateInput(project, inputZone);
181185

182186
final CacheContext context = new CacheContext(project, inputInfo, session);
183187

184-
CacheResult result = empty(context);
188+
CacheResult result = empty(context, inputZone);
185189
if (!skipCache) {
186190

187-
LOGGER.info("Attempting to restore project {} from build cache", projectName);
191+
LOGGER.info("Attempting to restore project {} from build cache zone {}", projectName, inputZone);
188192

189193
// remote build first
190194
if (cacheConfig.isRemoteCacheEnabled()) {
191-
result = findCachedBuild(mojoExecutions, context);
195+
result = findCachedBuild(mojoExecutions, context, inputZone);
192196
if (!result.isSuccess() && result.getContext() != null) {
193197
LOGGER.info("Remote cache is incomplete or missing, trying local build for {}", projectName);
194198
}
195199
}
196200

197201
if (!result.isSuccess() && result.getContext() != null) {
198-
CacheResult localBuild = findLocalBuild(mojoExecutions, context);
202+
CacheResult localBuild = findLocalBuild(mojoExecutions, context, inputZone);
199203
if (localBuild.isSuccess() || (localBuild.isPartialSuccess() && !result.isPartialSuccess())) {
200204
result = localBuild;
201205
} else {
202206
LOGGER.info(
203-
"Local build was not found by checksum {} for {}", inputInfo.getChecksum(), projectName);
207+
"Local build was not found by checksum {} for {} and zone {}",
208+
inputInfo.getChecksum(),
209+
projectName,
210+
inputZone);
204211
}
205212
}
206213
} else {
@@ -212,39 +219,43 @@ public CacheResult findCachedBuild(
212219
return result;
213220
}
214221

215-
private CacheResult findCachedBuild(List<MojoExecution> mojoExecutions, CacheContext context) {
222+
private CacheResult findCachedBuild(List<MojoExecution> mojoExecutions, CacheContext context, Zone inputZone) {
216223
Optional<Build> cachedBuild = Optional.empty();
217224
try {
218-
cachedBuild = localCache.findBuild(context);
225+
cachedBuild = localCache.findBuild(context, inputZone);
219226
if (cachedBuild.isPresent()) {
220-
return analyzeResult(context, mojoExecutions, cachedBuild.get());
227+
return analyzeResult(context, mojoExecutions, inputZone, cachedBuild.get());
221228
}
222229
} catch (Exception e) {
223230
LOGGER.error("Cannot read cached remote build", e);
224231
}
225-
return cachedBuild.map(build -> failure(build, context)).orElseGet(() -> empty(context));
232+
return cachedBuild.map(build -> failure(build, context, inputZone)).orElseGet(() -> empty(context, inputZone));
226233
}
227234

228-
private CacheResult findLocalBuild(List<MojoExecution> mojoExecutions, CacheContext context) {
235+
private CacheResult findLocalBuild(List<MojoExecution> mojoExecutions, CacheContext context, Zone inputZone) {
229236
Optional<Build> localBuild = Optional.empty();
230237
try {
231-
localBuild = localCache.findLocalBuild(context);
238+
localBuild = localCache.findLocalBuild(context, inputZone);
232239
if (localBuild.isPresent()) {
233-
return analyzeResult(context, mojoExecutions, localBuild.get());
240+
return analyzeResult(context, mojoExecutions, inputZone, localBuild.get());
234241
}
235242
} catch (Exception e) {
236243
LOGGER.error("Cannot read local build", e);
237244
}
238-
return localBuild.map(build -> failure(build, context)).orElseGet(() -> empty(context));
245+
return localBuild.map(build -> failure(build, context, inputZone)).orElseGet(() -> empty(context, inputZone));
239246
}
240247

241-
private CacheResult analyzeResult(CacheContext context, List<MojoExecution> mojoExecutions, Build build) {
248+
private CacheResult analyzeResult(
249+
CacheContext context, List<MojoExecution> mojoExecutions, Zone inputZone, Build build) {
242250
try {
243251
final ProjectsInputInfo inputInfo = context.getInputInfo();
244252
String projectName = getVersionlessProjectKey(context.getProject());
245253

246254
LOGGER.info(
247-
"Found cached build, restoring {} from cache by checksum {}", projectName, inputInfo.getChecksum());
255+
"Found cached build, restoring {} from cache zone {} by checksum {}",
256+
projectName,
257+
inputZone,
258+
inputInfo.getChecksum());
248259
LOGGER.debug("Cached build details: {}", build);
249260

250261
final String cacheImplementationVersion = build.getCacheImplementationVersion();
@@ -263,12 +274,12 @@ private CacheResult analyzeResult(CacheContext context, List<MojoExecution> mojo
263274
"Cached build doesn't contains all requested plugin executions "
264275
+ "(missing: {}), cannot restore",
265276
missingMojos);
266-
return failure(build, context);
277+
return failure(build, context, inputZone);
267278
}
268279

269280
if (!isCachedSegmentPropertiesPresent(context.getProject(), build, cachedSegment)) {
270281
LOGGER.info("Cached build violates cache rules, cannot restore");
271-
return failure(build, context);
282+
return failure(build, context, inputZone);
272283
}
273284

274285
final String highestRequestPhase =
@@ -281,15 +292,15 @@ private CacheResult analyzeResult(CacheContext context, List<MojoExecution> mojo
281292
projectName,
282293
build.getHighestCompletedGoal(),
283294
highestRequestPhase);
284-
return partialSuccess(build, context);
295+
return partialSuccess(build, context, inputZone);
285296
}
286297

287-
return success(build, context);
298+
return success(build, context, inputZone);
288299

289300
} catch (Exception e) {
290301
LOGGER.error("Failed to restore project", e);
291-
localCache.clearCache(context);
292-
return failure(build, context);
302+
localCache.clearCache(context, inputZone);
303+
return failure(build, context, inputZone);
293304
}
294305
}
295306

@@ -389,8 +400,8 @@ public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult
389400
// Set this value before trying the restoration, to keep a trace of the attempt if it fails
390401
restorationReport.setRestoredFilesInProjectDirectory(true);
391402
// generated sources artifact
392-
final Path attachedArtifactFile =
393-
localCache.getArtifactFile(context, cacheResult.getSource(), attachedArtifactInfo);
403+
final Path attachedArtifactFile = localCache.getArtifactFile(
404+
context, cacheResult.getSource(), cacheResult.getInputZone(), attachedArtifactInfo);
394405
restoreGeneratedSources(attachedArtifactInfo, attachedArtifactFile, project);
395406
}
396407
} else {
@@ -462,7 +473,8 @@ private Future<File> createDownloadTask(
462473
String originalVersion) {
463474
final FutureTask<File> downloadTask = new FutureTask<>(() -> {
464475
LOGGER.debug("Downloading artifact {}", artifact.getArtifactId());
465-
final Path artifactFile = localCache.getArtifactFile(context, cacheResult.getSource(), artifact);
476+
final Path artifactFile =
477+
localCache.getArtifactFile(context, cacheResult.getSource(), cacheResult.getInputZone(), artifact);
466478

467479
if (!Files.exists(artifactFile)) {
468480
throw new FileNotFoundException("Missing file for cached build, cannot restore. File: " + artifactFile);
@@ -482,7 +494,8 @@ private Future<File> createDownloadTask(
482494
public void save(
483495
CacheResult cacheResult,
484496
List<MojoExecution> mojoExecutions,
485-
Map<String, MojoExecutionEvent> executionEvents) {
497+
Map<String, MojoExecutionEvent> executionEvents,
498+
Zone outputZone) {
486499
CacheContext context = cacheResult.getContext();
487500

488501
if (context == null || context.getInputInfo() == null) {
@@ -526,19 +539,19 @@ public void save(
526539
build.getDto().set_final(cacheConfig.isSaveToRemoteFinal());
527540
cacheResults.put(getVersionlessProjectKey(project), rebuilded(cacheResult, build));
528541

529-
localCache.beforeSave(context);
542+
localCache.beforeSave(context, outputZone);
530543

531544
// if package phase presence means new artifacts were packaged
532545
if (project.hasLifecyclePhase("package")) {
533546
if (projectArtifact.getFile() != null) {
534-
localCache.saveArtifactFile(cacheResult, projectArtifact);
547+
localCache.saveArtifactFile(cacheResult, outputZone, projectArtifact);
535548
}
536549
for (org.apache.maven.artifact.Artifact attachedArtifact : attachedArtifacts) {
537550
if (attachedArtifact.getFile() != null) {
538551
boolean storeArtifact =
539552
isOutputArtifact(attachedArtifact.getFile().getName());
540553
if (storeArtifact) {
541-
localCache.saveArtifactFile(cacheResult, attachedArtifact);
554+
localCache.saveArtifactFile(cacheResult, outputZone, attachedArtifact);
542555
} else {
543556
LOGGER.debug(
544557
"Skipping attached project artifact '{}' = "
@@ -549,7 +562,7 @@ public void save(
549562
}
550563
}
551564

552-
localCache.saveBuildInfo(cacheResult, build);
565+
localCache.saveBuildInfo(cacheResult, outputZone, build);
553566

554567
if (cacheConfig.isBaselineDiffEnabled()) {
555568
produceDiffReport(cacheResult, build);
@@ -558,7 +571,7 @@ public void save(
558571
} catch (Exception e) {
559572
LOGGER.error("Failed to save project, cleaning cache. Project: {}", project, e);
560573
try {
561-
localCache.clearCache(context);
574+
localCache.clearCache(context, outputZone);
562575
} catch (Exception ex) {
563576
LOGGER.error("Failed to clean cache due to unexpected error:", ex);
564577
}
@@ -567,7 +580,7 @@ public void save(
567580

568581
public void produceDiffReport(CacheResult cacheResult, Build build) {
569582
MavenProject project = cacheResult.getContext().getProject();
570-
Optional<Build> baselineHolder = remoteCache.findBaselineBuild(project);
583+
Optional<Build> baselineHolder = remoteCache.findBaselineBuild(project, cacheResult.getInputZone());
571584
if (baselineHolder.isPresent()) {
572585
Build baseline = baselineHolder.get();
573586
String outputDirectory = project.getBuild().getDirectory();
@@ -841,10 +854,10 @@ public void saveCacheReport(MavenSession session) {
841854
projectReport.setLifecycleMatched(checksumMatched && result.isSuccess());
842855
projectReport.setSource(String.valueOf(result.getSource()));
843856
if (result.getSource() == CacheSource.REMOTE) {
844-
projectReport.setUrl(remoteCache.getResourceUrl(context, BUILDINFO_XML));
857+
projectReport.setUrl(remoteCache.getResourceUrl(context, result.getInputZone(), BUILDINFO_XML));
845858
} else if (result.getSource() == CacheSource.BUILD && cacheConfig.isSaveToRemote()) {
846859
projectReport.setSharedToRemote(true);
847-
projectReport.setUrl(remoteCache.getResourceUrl(context, BUILDINFO_XML));
860+
projectReport.setUrl(remoteCache.getResourceUrl(context, result.getInputZone(), BUILDINFO_XML));
848861
}
849862
cacheReport.addProject(projectReport);
850863
}

src/main/java/org/apache/maven/buildcache/CacheRepository.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
public interface CacheRepository {
3535

3636
@Nonnull
37-
Optional<Build> findBuild(CacheContext context) throws IOException;
37+
Optional<Build> findBuild(CacheContext context, Zone inputZone) throws IOException;
3838

39-
void saveBuildInfo(CacheResult cacheResult, Build build) throws IOException;
39+
void saveBuildInfo(CacheResult cacheResult, Zone outputZone, Build build) throws IOException;
4040

41-
void saveArtifactFile(CacheResult cacheResult, Artifact artifact) throws IOException;
41+
void saveArtifactFile(CacheResult cacheResult, Zone outputZone, Artifact artifact) throws IOException;
4242

4343
void saveCacheReport(String buildId, MavenSession session, CacheReport cacheReport) throws IOException;
4444
}

0 commit comments

Comments
 (0)