mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
5b36617f84
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
108 lines
3.7 KiB
C++
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());
|
|
}
|
|
}
|