Add a new abstraction layer UnifiedOnDiskCache that adds new functions of disk space management and data validation that builds on top of OnDiskGraphDB and OnDiskKeyValueDB. Build upon UnifiedOnDiskCache, it is OnDiskCAS that implements ObjectStore and ActionCache interface for LLVM tools to interact with CAS storage.
212 lines
6.6 KiB
C++
212 lines
6.6 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "BuiltinCAS.h"
|
|
#include "llvm/CAS/BuiltinCASContext.h"
|
|
#include "llvm/CAS/BuiltinObjectHasher.h"
|
|
#include "llvm/CAS/OnDiskGraphDB.h"
|
|
#include "llvm/CAS/UnifiedOnDiskCache.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Error.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::cas;
|
|
using namespace llvm::cas::builtin;
|
|
|
|
namespace {
|
|
|
|
class OnDiskCAS : public BuiltinCAS {
|
|
public:
|
|
Expected<ObjectRef> storeImpl(ArrayRef<uint8_t> ComputedHash,
|
|
ArrayRef<ObjectRef> Refs,
|
|
ArrayRef<char> Data) final;
|
|
|
|
Expected<std::optional<ObjectHandle>> loadIfExists(ObjectRef Ref) final;
|
|
|
|
CASID getID(ObjectRef Ref) const final;
|
|
|
|
std::optional<ObjectRef> getReference(const CASID &ID) const final;
|
|
|
|
Expected<bool> isMaterialized(ObjectRef Ref) const final;
|
|
|
|
ArrayRef<char> getDataConst(ObjectHandle Node) const final;
|
|
|
|
void print(raw_ostream &OS) const final;
|
|
Error validate(bool CheckHash) const final;
|
|
|
|
static Expected<std::unique_ptr<OnDiskCAS>> open(StringRef Path);
|
|
|
|
OnDiskCAS(std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB)
|
|
: UnifiedDB(std::move(UniDB)), DB(&UnifiedDB->getGraphDB()) {}
|
|
|
|
private:
|
|
ObjectHandle convertHandle(ondisk::ObjectHandle Node) const {
|
|
return makeObjectHandle(Node.getOpaqueData());
|
|
}
|
|
|
|
ondisk::ObjectHandle convertHandle(ObjectHandle Node) const {
|
|
return ondisk::ObjectHandle(Node.getInternalRef(*this));
|
|
}
|
|
|
|
ObjectRef convertRef(ondisk::ObjectID Ref) const {
|
|
return makeObjectRef(Ref.getOpaqueData());
|
|
}
|
|
|
|
ondisk::ObjectID convertRef(ObjectRef Ref) const {
|
|
return ondisk::ObjectID::fromOpaqueData(Ref.getInternalRef(*this));
|
|
}
|
|
|
|
size_t getNumRefs(ObjectHandle Node) const final {
|
|
auto RefsRange = DB->getObjectRefs(convertHandle(Node));
|
|
return std::distance(RefsRange.begin(), RefsRange.end());
|
|
}
|
|
|
|
ObjectRef readRef(ObjectHandle Node, size_t I) const final {
|
|
auto RefsRange = DB->getObjectRefs(convertHandle(Node));
|
|
return convertRef(RefsRange.begin()[I]);
|
|
}
|
|
|
|
Error forEachRef(ObjectHandle Node,
|
|
function_ref<Error(ObjectRef)> Callback) const final;
|
|
|
|
Error setSizeLimit(std::optional<uint64_t> SizeLimit) final;
|
|
Expected<std::optional<uint64_t>> getStorageSize() const final;
|
|
Error pruneStorageData() final;
|
|
|
|
OnDiskCAS(std::unique_ptr<ondisk::OnDiskGraphDB> GraphDB)
|
|
: OwnedDB(std::move(GraphDB)), DB(OwnedDB.get()) {}
|
|
|
|
std::unique_ptr<ondisk::OnDiskGraphDB> OwnedDB;
|
|
std::shared_ptr<ondisk::UnifiedOnDiskCache> UnifiedDB;
|
|
ondisk::OnDiskGraphDB *DB;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
void OnDiskCAS::print(raw_ostream &OS) const { DB->print(OS); }
|
|
Error OnDiskCAS::validate(bool CheckHash) const {
|
|
auto Hasher = [](ArrayRef<ArrayRef<uint8_t>> Refs, ArrayRef<char> Data,
|
|
SmallVectorImpl<uint8_t> &Result) {
|
|
auto Hash = BuiltinObjectHasher<llvm::cas::builtin::HasherT>::hashObject(
|
|
Refs, Data);
|
|
Result.assign(Hash.begin(), Hash.end());
|
|
};
|
|
|
|
if (auto E = DB->validate(CheckHash, Hasher))
|
|
return E;
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
CASID OnDiskCAS::getID(ObjectRef Ref) const {
|
|
ArrayRef<uint8_t> Hash = DB->getDigest(convertRef(Ref));
|
|
return CASID::create(&getContext(), toStringRef(Hash));
|
|
}
|
|
|
|
std::optional<ObjectRef> OnDiskCAS::getReference(const CASID &ID) const {
|
|
std::optional<ondisk::ObjectID> ObjID =
|
|
DB->getExistingReference(ID.getHash());
|
|
if (!ObjID)
|
|
return std::nullopt;
|
|
return convertRef(*ObjID);
|
|
}
|
|
|
|
Expected<bool> OnDiskCAS::isMaterialized(ObjectRef ExternalRef) const {
|
|
return DB->isMaterialized(convertRef(ExternalRef));
|
|
}
|
|
|
|
ArrayRef<char> OnDiskCAS::getDataConst(ObjectHandle Node) const {
|
|
return DB->getObjectData(convertHandle(Node));
|
|
}
|
|
|
|
Expected<std::optional<ObjectHandle>>
|
|
OnDiskCAS::loadIfExists(ObjectRef ExternalRef) {
|
|
Expected<std::optional<ondisk::ObjectHandle>> ObjHnd =
|
|
DB->load(convertRef(ExternalRef));
|
|
if (!ObjHnd)
|
|
return ObjHnd.takeError();
|
|
if (!*ObjHnd)
|
|
return std::nullopt;
|
|
return convertHandle(**ObjHnd);
|
|
}
|
|
|
|
Expected<ObjectRef> OnDiskCAS::storeImpl(ArrayRef<uint8_t> ComputedHash,
|
|
ArrayRef<ObjectRef> Refs,
|
|
ArrayRef<char> Data) {
|
|
SmallVector<ondisk::ObjectID, 64> IDs;
|
|
IDs.reserve(Refs.size());
|
|
for (ObjectRef Ref : Refs) {
|
|
IDs.push_back(convertRef(Ref));
|
|
}
|
|
|
|
auto StoredID = DB->getReference(ComputedHash);
|
|
if (LLVM_UNLIKELY(!StoredID))
|
|
return StoredID.takeError();
|
|
if (Error E = DB->store(*StoredID, IDs, Data))
|
|
return std::move(E);
|
|
return convertRef(*StoredID);
|
|
}
|
|
|
|
Error OnDiskCAS::forEachRef(ObjectHandle Node,
|
|
function_ref<Error(ObjectRef)> Callback) const {
|
|
auto RefsRange = DB->getObjectRefs(convertHandle(Node));
|
|
for (ondisk::ObjectID Ref : RefsRange) {
|
|
if (Error E = Callback(convertRef(Ref)))
|
|
return E;
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Error OnDiskCAS::setSizeLimit(std::optional<uint64_t> SizeLimit) {
|
|
UnifiedDB->setSizeLimit(SizeLimit);
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<std::optional<uint64_t>> OnDiskCAS::getStorageSize() const {
|
|
return UnifiedDB->getStorageSize();
|
|
}
|
|
|
|
Error OnDiskCAS::pruneStorageData() { return UnifiedDB->collectGarbage(); }
|
|
|
|
Expected<std::unique_ptr<OnDiskCAS>> OnDiskCAS::open(StringRef AbsPath) {
|
|
Expected<std::unique_ptr<ondisk::OnDiskGraphDB>> DB =
|
|
ondisk::OnDiskGraphDB::open(AbsPath, BuiltinCASContext::getHashName(),
|
|
sizeof(HashType));
|
|
if (!DB)
|
|
return DB.takeError();
|
|
return std::unique_ptr<OnDiskCAS>(new OnDiskCAS(std::move(*DB)));
|
|
}
|
|
|
|
bool cas::isOnDiskCASEnabled() {
|
|
#if LLVM_ENABLE_ONDISK_CAS
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
Expected<std::unique_ptr<ObjectStore>> cas::createOnDiskCAS(const Twine &Path) {
|
|
#if LLVM_ENABLE_ONDISK_CAS
|
|
// FIXME: An absolute path isn't really good enough. Should open a directory
|
|
// and use openat() for files underneath.
|
|
SmallString<256> AbsPath;
|
|
Path.toVector(AbsPath);
|
|
sys::fs::make_absolute(AbsPath);
|
|
|
|
return OnDiskCAS::open(AbsPath);
|
|
#else
|
|
return createStringError(inconvertibleErrorCode(), "OnDiskCAS is disabled");
|
|
#endif /* LLVM_ENABLE_ONDISK_CAS */
|
|
}
|
|
|
|
std::unique_ptr<ObjectStore>
|
|
cas::builtin::createObjectStoreFromUnifiedOnDiskCache(
|
|
std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB) {
|
|
return std::make_unique<OnDiskCAS>(std::move(UniDB));
|
|
}
|