report file sizes in Eden's debugInodeStatus() call

Summary: Remove file size stat() call from cli and move logic into server-side code

Reviewed By: chadaustin

Differential Revision: D17080186

fbshipit-source-id: 882d1c01db841e7b13b7659f47091e6b5c46ab57
This commit is contained in:
Genevieve Helsel 2019-08-29 09:08:00 -07:00 committed by Facebook Github Bot
parent a8ef39078d
commit 7644433160
6 changed files with 45 additions and 15 deletions

View File

@ -404,14 +404,11 @@ class FileStatsCMD(Subcmd):
parser.add_argument("--sizes", action="store_true", help="Compute file sizes")
def make_file_entries(
self, base_path: str, paths: List[str], get_sizes: bool
self, base_path: str, paths: List[Tuple[str, Optional[int]]], get_sizes: bool
) -> List[Dict[str, Any]]:
if get_sizes:
return [
{"path": path, "size": os.path.getsize(os.path.join(base_path, path))}
for path in paths
]
return [{"path": path} for path in paths]
return [{"path": path, "size": file_size} for (path, file_size) in paths]
return [{"path": path} for (path, _) in paths]
def run(self, args: argparse.Namespace) -> int:
request_root = args.path

View File

@ -41,6 +41,7 @@ class UtilTest(unittest.TestCase):
loaded=True,
materialized=False,
hash=b"1abc",
fileSize=300,
),
TreeInodeEntryDebugInfo(
name=b"written_file",
@ -48,6 +49,7 @@ class UtilTest(unittest.TestCase):
mode=stat.S_IFREG,
loaded=True,
materialized=True,
fileSize=400,
),
],
refcount=0,
@ -58,8 +60,8 @@ class UtilTest(unittest.TestCase):
read_files, written_files = util.split_inodes_by_operation_type(
self.INODE_RESULTS_0
)
self.assertListEqual(read_files, ["some_path/d1/read_file"])
self.assertListEqual(written_files, ["some_path/d1/written_file"])
self.assertListEqual(read_files, [("some_path/d1/read_file", 300)])
self.assertListEqual(written_files, [("some_path/d1/written_file", 400)])
INODE_RESULTS_1 = [
TreeInodeDebugInfo(
@ -75,6 +77,7 @@ class UtilTest(unittest.TestCase):
loaded=True,
materialized=False,
hash=b"1abc",
fileSize=300,
),
TreeInodeEntryDebugInfo(
name=b"written_file",
@ -82,6 +85,7 @@ class UtilTest(unittest.TestCase):
mode=stat.S_IFDIR,
loaded=True,
materialized=True,
fileSize=400,
),
],
refcount=0,
@ -109,6 +113,7 @@ class UtilTest(unittest.TestCase):
loaded=True,
materialized=False,
hash=b"1abc",
fileSize=300,
)
],
refcount=0,
@ -125,6 +130,7 @@ class UtilTest(unittest.TestCase):
mode=stat.S_IFREG,
loaded=True,
materialized=True,
fileSize=400,
)
],
refcount=0,
@ -135,8 +141,8 @@ class UtilTest(unittest.TestCase):
read_files, written_files = util.split_inodes_by_operation_type(
self.INODE_RESULTS_2
)
self.assertListEqual(read_files, ["some_path/d1/read_file"])
self.assertListEqual(written_files, ["some_path/d1/written_file"])
self.assertListEqual(read_files, [("some_path/d1/read_file", 300)])
self.assertListEqual(written_files, [("some_path/d1/written_file", 400)])
INODE_RESULTS_3 = [
TreeInodeDebugInfo(
@ -152,6 +158,7 @@ class UtilTest(unittest.TestCase):
loaded=False,
materialized=False,
hash=b"1abc",
fileSize=300,
),
TreeInodeEntryDebugInfo(
name=b"written_file",
@ -159,6 +166,7 @@ class UtilTest(unittest.TestCase):
mode=stat.S_IFREG,
loaded=False,
materialized=True,
fileSize=400,
),
],
refcount=0,

View File

@ -511,21 +511,26 @@ def get_username() -> str:
class LoadedNode(typing.NamedTuple):
path: str
is_write: bool
file_size: Optional[int]
def split_inodes_by_operation_type(
inode_results: typing.Sequence[TreeInodeDebugInfo]
) -> typing.Tuple[typing.List[str], typing.List[str]]:
) -> typing.Tuple[
typing.List[typing.Tuple[str, Optional[int]]],
typing.List[typing.Tuple[str, Optional[int]]],
]:
loaded_node_info = [
LoadedNode(
path=os.path.join(os.fsdecode(tree.path), os.fsdecode(n.name)),
is_write=n.materialized or not n.hash,
file_size=n.fileSize,
)
for tree in inode_results
for n in tree.entries
if n.loaded and stat.S_IFMT(n.mode) == stat.S_IFREG
]
read_files = [o.path for o in loaded_node_info if not o.is_write]
written_files = [o.path for o in loaded_node_info if o.is_write]
read_files = [(o.path, o.file_size) for o in loaded_node_info if not o.is_write]
written_files = [(o.path, o.file_size) for o in loaded_node_info if o.is_write]
return read_files, written_files

View File

@ -223,6 +223,8 @@ class FileInode final : public InodeBaseMetadata<FileInodeState> {
void fsync(bool datasync);
folly::Future<struct stat> stat();
private:
using State = FileInodeState;
class LockedState;
@ -367,8 +369,6 @@ class FileInode final : public InodeBaseMetadata<FileInodeState> {
size_t numIovecs,
off_t off);
folly::Future<struct stat> stat();
/**
* Update the st_blocks field in a stat structure based on the st_size value.
*/

View File

@ -3274,6 +3274,7 @@ void TreeInode::getDebugStatus(vector<TreeInodeDebugInfo>& results) const {
}
}
std::vector<folly::Future<std::pair<size_t, uint64_t>>> futures;
for (const auto& childData : childInodes) {
info.entries.emplace_back();
auto& infoEntry = info.entries.back();
@ -3299,8 +3300,23 @@ void TreeInode::getDebugStatus(vector<TreeInodeDebugInfo>& results) const {
auto blobHash = childFile->getBlobHash();
infoEntry.materialized = !blobHash.has_value();
infoEntry.hash = thriftHash(blobHash);
futures.push_back(
childFile->stat().thenValue([i = info.entries.size() - 1](auto st) {
auto fileSize = st.st_size;
return std::make_pair(i, fileSize);
}));
}
}
auto fileSizeMappings = folly::collectAll(futures).get();
for (const auto& future : fileSizeMappings) {
auto [i, fileSize] = future.value();
auto& infoEntry = info.entries[i];
// We must use set_size here because size is
// optional and if it is set directly then it will
// not get serialized correctly.
infoEntry.set_fileSize(fileSize);
}
results.push_back(info);
// Recurse into all children directories after we finish building our own

View File

@ -390,6 +390,10 @@ struct TreeInodeEntryDebugInfo {
* control Blob or Tree.
*/
6: BinaryHash hash
/**
* Size of the file in bytes, won't be set for directories
*/
7: optional i64 fileSize
}
struct WorkingDirectoryParents {