Don't bother getting the not-real FileStateValue for a path whose parent doesn't exist. In addition to saving a filesystem operation, this removes a source of a potential filesystem inconsistency. -- MOS_MIGRATED_REVID=136355008
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java index 8404f53..617baad 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
@@ -18,6 +18,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; +import com.google.devtools.build.lib.skyframe.FileStateValue.Type; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.Path; @@ -80,34 +81,36 @@ } realRootedPath = resolvedState.getFirst(); realFileStateValue = resolvedState.getSecond(); + if (realFileStateValue.getType() == Type.NONEXISTENT) { + return FileValue.value( + rootedPath, + FileStateValue.NONEXISTENT_FILE_STATE_NODE, + realRootedPath, + realFileStateValue); + } } - FileStateValue fileStateValue = null; - - try { - fileStateValue = - (FileStateValue) - env.getValueOrThrow( - FileStateValue.key(rootedPath), FileOutsidePackageRootsException.class); - } catch (FileOutsidePackageRootsException e) { - throw new FileFunctionException( - new FileOutsidePackageRootsException(rootedPath), Transience.PERSISTENT); + FileStateValue fileStateValue; + if (rootedPath.equals(realRootedPath)) { + fileStateValue = Preconditions.checkNotNull(realFileStateValue, rootedPath); + } else { + try { + fileStateValue = + (FileStateValue) + env.getValueOrThrow( + FileStateValue.key(rootedPath), FileOutsidePackageRootsException.class); + } catch (FileOutsidePackageRootsException e) { + throw new FileFunctionException( + new FileOutsidePackageRootsException(rootedPath), Transience.PERSISTENT); + } + if (fileStateValue == null) { + return null; + } } - if (fileStateValue == null) { - return null; - } if (realFileStateValue == null) { realRootedPath = rootedPath; realFileStateValue = fileStateValue; - } else if (rootedPath.equals(realRootedPath) && !fileStateValue.equals(realFileStateValue)) { - String message = String.format( - "Some filesystem operations implied %s was a %s but others made us think it was a %s", - rootedPath.asPath().getPathString(), - fileStateValue.prettyPrint(), - realFileStateValue.prettyPrint()); - throw new FileFunctionException(new InconsistentFilesystemException(message), - Transience.TRANSIENT); } ArrayList<RootedPath> symlinkChain = new ArrayList<>();
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java index 5257324..6308953 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
@@ -888,22 +888,13 @@ } @Test - public void testFilesystemInconsistencies_ParentDoesntExistAndChildIsSymlink() throws Exception { - symlink("a/b", "doesntmatter"); - // Our custom filesystem says "a/b" exists but "a" does not exist. + public void testDoesntStatChildIfParentDoesntExist() throws Exception { + // Our custom filesystem says "a" does not exist, so FileFunction shouldn't bother trying to + // think about "a/b". Test for this by having a stat of "a/b" fail with an io error, and + // observing that we don't encounter the error. fs.stubStat(path("a"), null); - SequentialBuildDriver driver = makeDriver(); - SkyKey skyKey = skyKey("a/b"); - EvaluationResult<FileValue> result = - driver.evaluate( - ImmutableList.of(skyKey), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE); - assertTrue(result.hasError()); - ErrorInfo errorInfo = result.getError(skyKey); - assertThat(errorInfo.getException()).isInstanceOf(InconsistentFilesystemException.class); - assertThat(errorInfo.getException().getMessage()) - .contains( - "/root/a/b was a symlink to doesntmatter but others made us think it was a " - + "nonexistent path"); + fs.stubStatError(path("a/b"), new IOException("ouch!")); + assertThat(valueForPath(path("a/b")).exists()).isFalse(); } @Test @@ -1639,8 +1630,9 @@ private class CustomInMemoryFs extends InMemoryFileSystem { - private Map<Path, FileStatus> stubbedStats = Maps.newHashMap(); - private Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap(); + private final Map<Path, FileStatus> stubbedStats = Maps.newHashMap(); + private final Map<Path, IOException> stubbedStatErrors = Maps.newHashMap(); + private final Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap(); public CustomInMemoryFs(ManualClock manualClock) { super(manualClock); @@ -1667,8 +1659,15 @@ stubbedStats.put(path, stubbedResult); } + public void stubStatError(Path path, IOException error) { + stubbedStatErrors.put(path, error); + } + @Override public FileStatus stat(Path path, boolean followSymlinks) throws IOException { + if (stubbedStatErrors.containsKey(path)) { + throw stubbedStatErrors.get(path); + } if (stubbedStats.containsKey(path)) { return stubbedStats.get(path); }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java index 9175145..3c960df 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
@@ -252,7 +252,6 @@ Path bazDir = bazFile.getParentDirectory(); Path barDir = bazDir.getParentDirectory(); - long bazFileNodeId = bazFile.stat().getNodeId(); // Our custom filesystem says "foo/bar/baz" does not exist but it also says that "foo/bar" // has a child directory "baz". fs.stubStat(bazDir, null); @@ -265,9 +264,7 @@ new Dirent("baz", Dirent.Type.DIRECTORY)))); differencer.inject(ImmutableMap.of(DirectoryListingValue.key(barDirRootedPath), barDirListing)); SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo")); - String expectedMessage = "Some filesystem operations implied /workspace/foo/bar/baz/baz.sh was " - + "a regular file with size of 0 and mtime of 0 and nodeId of " + bazFileNodeId + " and " - + "mtime of 0 but others made us think it was a nonexistent path"; + String expectedMessage = "/workspace/foo/bar/baz is no longer an existing directory"; EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate( getSkyframeExecutor(), skyKey, /*keepGoing=*/false, reporter); assertTrue(result.hasError());