sapling/eden/fs/inodes/test/InodeLoaderTest.cpp
Wez Furlong 5b36617f84 add InodeLoader to improve getFileInformation perf
Summary:
Some of the heavier watchman->eden queries in www today
trigger calls to `getFileInformation` with very large numbers
of paths.

For a set of paths with a lot of common prefixes there is a lot
of repeated work to load same inodes over and over again.

eg: `a/b/c/d/A` and `a/b/c/d/B` both have to load `a -> b -> c -> d` before
they can load the leaf node.

This diff pre-processes the list of paths and builds a tree-shaped plan
so that we won't need to load any inode referenced by any of the paths
more than once.

The `getSHA1` method could benefit from a similar change; I'll do that in
a follow-up diff.

Reviewed By: strager

Differential Revision: D8578261

fbshipit-source-id: e899640a2ef6dcdb6b903d2a2735e9240496d74b
2018-07-12 11:53:54 -07:00

108 lines
3.7 KiB
C++

/*
* Copyright (c) 2018-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "eden/fs/inodes/InodeLoader.h"
#include <folly/Exception.h>
#include <folly/experimental/TestUtil.h>
#include <folly/test/TestUtils.h>
#include <gtest/gtest.h>
#include "eden/fs/inodes/TreeInode.h"
#include "eden/fs/testharness/FakeBackingStore.h"
#include "eden/fs/testharness/FakeTreeBuilder.h"
#include "eden/fs/testharness/TestChecks.h"
#include "eden/fs/testharness/TestMount.h"
using namespace facebook;
using namespace facebook::eden;
TEST(InodeLoader, load) {
FakeTreeBuilder builder;
builder.setFiles({{"dir/a.txt", ""}, {"dir/sub/b.txt", ""}});
TestMount mount(builder);
auto rootInode = mount.getTreeInode(RelativePathPiece());
{
auto results =
collectAllSemiFuture(
applyToInodes(
rootInode,
std::vector<std::string>{
"dir/a.txt", "not/exist/a", "not/exist/b", "dir/sub/b.txt"},
[](InodePtr inode) { return inode->getPath(); }))
.get();
EXPECT_EQ("dir/a.txt"_relpath, results[0].value());
EXPECT_THROW_ERRNO(results[1].value(), ENOENT);
EXPECT_THROW_ERRNO(results[2].value(), ENOENT);
EXPECT_EQ("dir/sub/b.txt"_relpath, results[3].value());
}
{
auto results = collectAllSemiFuture(
applyToInodes(
rootInode,
std::vector<std::string>{"dir/sub/b.txt",
"dir/a.txt",
"not/exist/a",
"not/exist/b",
"dir/sub/b.txt"},
[](InodePtr inode) { return inode->getPath(); }))
.get();
EXPECT_EQ("dir/sub/b.txt"_relpath, results[0].value());
EXPECT_EQ("dir/a.txt"_relpath, results[1].value());
EXPECT_THROW_ERRNO(results[2].value(), ENOENT);
EXPECT_THROW_ERRNO(results[3].value(), ENOENT);
EXPECT_EQ(results[0].value(), results[4].value())
<< "dir/sub/b.txt was requested twice and both entries are the same";
}
{
auto results =
collectAllSemiFuture(
applyToInodes(
rootInode,
std::vector<std::string>{"dir/a.txt", "/invalid///exist/a"},
[](InodePtr inode) { return inode->getPath(); }))
.get();
EXPECT_EQ("dir/a.txt"_relpath, results[0].value());
EXPECT_THROW_RE(results[1].value(), std::domain_error, "absolute path");
}
}
TEST(InodeLoader, notReady) {
FakeTreeBuilder builder;
builder.setFiles({{"dir/a.txt", ""}, {"dir/sub/b.txt", ""}});
TestMount mount(builder, /* startReady= */ false);
auto rootInode = mount.getTreeInode(RelativePathPiece());
{
auto future = collectAllSemiFuture(applyToInodes(
rootInode,
std::vector<std::string>{
"dir/a.txt", "not/exist/a", "not/exist/b", "dir/sub/b.txt"},
[](InodePtr inode) { return inode->getPath(); }));
builder.setReady("dir");
builder.setReady("dir/sub");
builder.setReady("dir/a.txt");
builder.setReady("dir/sub/b.txt");
auto results = future.wait().value();
EXPECT_EQ("dir/a.txt"_relpath, results[0].value());
EXPECT_THROW_ERRNO(results[1].value(), ENOENT);
EXPECT_THROW_ERRNO(results[2].value(), ENOENT);
EXPECT_EQ("dir/sub/b.txt"_relpath, results[3].value());
}
}