eden/fs/store/LocalStore.cpp (187 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This software may be used and distributed according to the terms of the * GNU General Public License version 2. */ #include "eden/fs/store/LocalStore.h" #include <folly/String.h> #include <folly/futures/Future.h> #include <folly/io/Cursor.h> #include <folly/io/IOBuf.h> #include <folly/lang/Bits.h> #include <folly/logging/xlog.h> #include <array> #include "eden/fs/model/Blob.h" #include "eden/fs/model/Tree.h" #include "eden/fs/model/git/GitBlob.h" #include "eden/fs/model/git/GitTree.h" #include "eden/fs/store/SerializedBlobMetadata.h" #include "eden/fs/store/StoreResult.h" #include "eden/fs/store/TreeMetadata.h" using folly::ByteRange; using folly::IOBuf; using folly::StringPiece; using folly::io::Cursor; using std::optional; using std::string; namespace facebook::eden { void LocalStore::clearDeprecatedKeySpaces() { for (auto& ks : KeySpace::kAll) { if (ks->isDeprecated()) { clearKeySpace(ks); compactKeySpace(ks); } } } void LocalStore::clearCachesAndCompactAll() { for (auto& ks : KeySpace::kAll) { if (ks->isEphemeral()) { clearKeySpace(ks); } compactKeySpace(ks); } } void LocalStore::clearCaches() { for (auto& ks : KeySpace::kAll) { if (ks->isEphemeral()) { clearKeySpace(ks); } } } void LocalStore::compactStorage() { for (auto& ks : KeySpace::kAll) { compactKeySpace(ks); } } StoreResult LocalStore::get(KeySpace keySpace, const ObjectId& id) const { return get(keySpace, id.getBytes()); } // This is the fallback implementation for stores that don't have any // internal support for asynchronous fetches. This just performs the // fetch and wraps it in a future folly::Future<StoreResult> LocalStore::getFuture( KeySpace keySpace, folly::ByteRange key) const { return folly::makeFutureWith( [keySpace, key, this] { return get(keySpace, key); }); } folly::Future<std::vector<StoreResult>> LocalStore::getBatch( KeySpace keySpace, const std::vector<folly::ByteRange>& keys) const { return folly::makeFutureWith([keySpace, keys, this] { std::vector<StoreResult> results; for (auto& key : keys) { results.emplace_back(get(keySpace, key)); } return results; }); } folly::Future<std::unique_ptr<Tree>> LocalStore::getTree( const ObjectId& id) const { return getFuture(KeySpace::TreeFamily, id.getBytes()) .thenValue([id](StoreResult&& data) { if (!data.isValid()) { return std::unique_ptr<Tree>(nullptr); } auto try_tree = Tree::tryDeserialize(id, StringPiece{data.bytes()}); if (try_tree) { return std::make_unique<Tree>(*try_tree); } return deserializeGitTree(id, data.bytes()); }); } folly::Future<std::unique_ptr<Blob>> LocalStore::getBlob( const ObjectId& id) const { if (!enableBlobCaching) { return std::unique_ptr<Blob>(nullptr); } return getFuture(KeySpace::BlobFamily, id.getBytes()) .thenValue([id](StoreResult&& data) { if (!data.isValid()) { return std::unique_ptr<Blob>(nullptr); } auto buf = data.extractIOBuf(); return deserializeGitBlob(id, &buf); }); } folly::Future<optional<BlobMetadata>> LocalStore::getBlobMetadata( const ObjectId& id) const { return getFuture(KeySpace::BlobMetaDataFamily, id.getBytes()) .thenValue([id](StoreResult&& data) -> optional<BlobMetadata> { if (!data.isValid()) { return std::nullopt; } else { return SerializedBlobMetadata::parse(id, data); } }); } folly::IOBuf LocalStore::serializeTree(const Tree& tree) { if (!tree.isGitTreeCompatible()) { return tree.serialize(); } GitTreeSerializer serializer; for (auto& entry : tree.getTreeEntries()) { serializer.addEntry(std::move(entry)); } return serializer.finalize(); } bool LocalStore::hasKey(KeySpace keySpace, const ObjectId& id) const { return hasKey(keySpace, id.getBytes()); } void LocalStore::putTree(const Tree& tree) { auto serialized = LocalStore::serializeTree(tree); ByteRange treeData = serialized.coalesce(); put(KeySpace::TreeFamily, tree.getHash().getBytes(), treeData); } void LocalStore::WriteBatch::putTree(const Tree& tree) { auto serialized = LocalStore::serializeTree(tree); ByteRange treeData = serialized.coalesce(); put(KeySpace::TreeFamily, tree.getHash().getBytes(), treeData); } void LocalStore::putBlob(const ObjectId& id, const Blob* blob) { if (!enableBlobCaching) { XLOG(DBG8) << "Skipping caching " << id << " because blob cache is disabled via config"; } else { // Since blob serialization is moderately complex, just delegate // the immediate putBlob to the method on the WriteBatch. // Pre-allocate a buffer of approximately the right size; it // needs to hold the blob content plus have room for a couple of // hashes for the keys, plus some padding. auto batch = beginWrite(blob->getSize() + 64); batch->putBlob(id, blob); batch->flush(); } } BlobMetadata LocalStore::putBlobMetadata(const ObjectId& id, const Blob* blob) { BlobMetadata metadata{Hash20::sha1(blob->getContents()), blob->getSize()}; auto hashBytes = id.getBytes(); SerializedBlobMetadata metadataBytes(metadata); put(KeySpace::BlobMetaDataFamily, hashBytes, metadataBytes.slice()); return metadata; } void LocalStore::put( KeySpace keySpace, const ObjectId& id, folly::ByteRange value) { XCHECK(!keySpace->isDeprecated()) << "Write to deprecated keyspace " << keySpace->name; put(keySpace, id.getBytes(), value); } void LocalStore::WriteBatch::put( KeySpace keySpace, const ObjectId& id, folly::ByteRange value) { XCHECK(!keySpace->isDeprecated()) << "Write to deprecated keyspace " << keySpace->name; put(keySpace, id.getBytes(), value); } void LocalStore::WriteBatch::putBlob(const ObjectId& id, const Blob* blob) { const IOBuf& contents = blob->getContents(); auto hashSlice = id.getBytes(); // Add a git-style blob prefix auto prefix = folly::to<string>("blob ", blob->getSize()); prefix.push_back('\0'); std::vector<ByteRange> bodySlices; bodySlices.emplace_back(StringPiece(prefix)); // Add all of the IOBuf chunks Cursor cursor(&contents); while (true) { auto bytes = cursor.peekBytes(); if (bytes.empty()) { break; } bodySlices.push_back(bytes); cursor.skip(bytes.size()); } put(KeySpace::BlobFamily, hashSlice, bodySlices); } LocalStore::WriteBatch::~WriteBatch() {} void LocalStore::periodicManagementTask(const EdenConfig& /* config */) { // Individual store subclasses can provide their own implementations for // periodic management. } } // namespace facebook::eden