diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java b/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java index 8c936db5eb7059..810fa7c5b33f95 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java +++ b/src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java @@ -18,6 +18,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.flogger.GoogleLogger; import com.google.devtools.build.lib.actions.FileArtifactValue.RemoteFileArtifactValue; @@ -253,9 +254,37 @@ private static CompactPersistentActionCache create( } misses.put(reason, new AtomicInteger(0)); } + deleteUnrecognizedFiles(cacheRoot); return new CompactPersistentActionCache(indexer, map, Maps.immutableEnumMap(misses)); } + /** + * Deletes unrecognized files from the action cache on-disk directory. + * + *

This works around an incrementality bug when building a runfiles tree while alternating + * between Bazel versions. Specifically, because the runfiles output manifest is a symlink to the + * input manifest (and therefore always appears to have the contents of the latter), one can get a + * spurious cache hit for a stale runfiles tree if the tree was updated in an intervening build + * without the action cache being updated accordingly. + * + *

Backported from https://github.com/bazelbuild/bazel/pull/27525 (Bazel 9.0.0). + */ + private static void deleteUnrecognizedFiles(Path cacheRoot) throws IOException { + Path indexFile = cacheRoot.getChild("filename_index_v" + VERSION + ".blaze"); + Path indexJournalFile = cacheRoot.getChild("filename_index_v" + VERSION + ".journal"); + ImmutableSet knownFiles = + ImmutableSet.of( + cacheFile(cacheRoot), + journalFile(cacheRoot), + indexFile, + indexJournalFile); + for (Path child : cacheRoot.getDirectoryEntries()) { + if (!knownFiles.contains(child)) { + child.delete(); + } + } + } + private static CompactPersistentActionCache logAndThrowOrRecurse( Path cacheRoot, Clock clock, diff --git a/src/test/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCacheTest.java b/src/test/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCacheTest.java index 838af5eda54dee..3c6eafd6ccb347 100644 --- a/src/test/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCacheTest.java +++ b/src/test/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCacheTest.java @@ -451,6 +451,15 @@ public void putAndGet_treeMetadata_savesMaterializationExecPath() { Optional.of(materializationExecPath))); } + @Test + public void testDeleteUnrecognizedFiles() throws Exception { + Path unrecognizedFile = dataRoot.getChild("unrecognized_file"); + FileSystemUtils.writeContentAsLatin1(unrecognizedFile, "content"); + assertThat(unrecognizedFile.exists()).isTrue(); + CompactPersistentActionCache.create(dataRoot, clock, NullEventHandler.INSTANCE); + assertThat(unrecognizedFile.exists()).isFalse(); + } + private static void assertKeyEquals(ActionCache cache1, ActionCache cache2, String key) { Object entry = cache1.get(key); assertThat(entry).isNotNull();