Files
gps/tools/local/circt/include/llvm/CAS/UnifiedOnDiskCache.h
T
2026-07-12 11:01:51 +03:00

185 lines
8.1 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CAS_UNIFIEDONDISKCACHE_H
#define LLVM_CAS_UNIFIEDONDISKCACHE_H
#include "llvm/CAS/OnDiskGraphDB.h"
#include "llvm/CAS/ValidationResult.h"
#include <atomic>
namespace llvm::cas::ondisk {
class OnDiskKeyValueDB;
/// A unified CAS nodes and key-value database, using on-disk storage for both.
/// It manages storage growth and provides APIs for garbage collection.
///
/// High-level properties:
/// * While \p UnifiedOnDiskCache is open on a directory, by any process, the
/// storage size in that directory will keep growing unrestricted. For data to
/// become eligible for garbage-collection there should be no open instances
/// of \p UnifiedOnDiskCache for that directory, by any process.
/// * Garbage-collection needs to be triggered explicitly by the client. It can
/// be triggered on a directory concurrently, at any time and by any process,
/// without affecting any active readers/writers, in the same process or other
/// processes.
///
/// Usage patterns should be that an instance of \p UnifiedOnDiskCache is open
/// for a limited period of time, e.g. for the duration of a build operation.
/// For long-living processes that need periodic access to a
/// \p UnifiedOnDiskCache, the client should devise a scheme where access is
/// performed within some defined period. For example, if a service is designed
/// to continuously wait for requests that access a \p UnifiedOnDiskCache, it
/// could keep the instance alive while new requests are coming in but close it
/// after a time period in which there are no new requests.
class UnifiedOnDiskCache {
public:
/// The \p OnDiskGraphDB instance for the open directory.
OnDiskGraphDB &getGraphDB() { return *PrimaryGraphDB; }
/// The \p OnDiskGraphDB instance for the open directory.
const OnDiskGraphDB &getGraphDB() const { return *PrimaryGraphDB; }
/// The \p OnDiskGraphDB instance for the open directory.
OnDiskKeyValueDB &getKeyValueDB() { return *PrimaryKVDB; }
/// The \p OnDiskGraphDB instance for the open directory.
const OnDiskKeyValueDB &getKeyValueDB() const { return *PrimaryKVDB; }
/// Open a \p UnifiedOnDiskCache instance for a directory.
///
/// \param Path directory for the on-disk database. The directory will be
/// created if it doesn't exist.
/// \param SizeLimit Optional size for limiting growth. This has an effect for
/// when the instance is closed.
/// \param HashName Identifier name for the hashing algorithm that is going to
/// be used.
/// \param HashByteSize Size for the object digest hash bytes.
/// \param FaultInPolicy Controls how nodes are copied to primary store. This
/// is recorded at creation time and subsequent opens need to pass the same
/// policy otherwise the \p open will fail.
LLVM_ABI_FOR_TEST static Expected<std::unique_ptr<UnifiedOnDiskCache>>
open(StringRef Path, std::optional<uint64_t> SizeLimit, StringRef HashName,
unsigned HashByteSize,
OnDiskGraphDB::FaultInPolicy FaultInPolicy =
OnDiskGraphDB::FaultInPolicy::FullTree);
/// Validate the data in \p Path, if needed to ensure correctness.
///
/// Note: if invalid data is detected and \p AllowRecovery is true, then
/// recovery requires exclusive access to the CAS and it is an error to
/// attempt recovery if there is concurrent use of the CAS.
///
/// \param Path directory for the on-disk database.
/// \param HashName Identifier name for the hashing algorithm that is going to
/// be used.
/// \param HashByteSize Size for the object digest hash bytes.
/// \param CheckHash Whether to validate hashes match the data.
/// \param AllowRecovery Whether to automatically recover from invalid data by
/// marking the files for garbage collection.
/// \param ForceValidation Whether to force validation to occur even if it
/// should not be necessary.
/// \param LLVMCasBinary If provided, validation is performed out-of-process
/// using the given \c llvm-cas executable which protects against crashes
/// during validation. Otherwise validation is performed in-process.
///
/// \returns \c Valid if the data is already valid, \c Recovered if data
/// was invalid but has been cleared, \c Skipped if validation is not needed,
/// or an \c Error if validation cannot be performed or if the data is left
/// in an invalid state because \p AllowRecovery is false.
LLVM_ABI static Expected<ValidationResult>
validateIfNeeded(StringRef Path, StringRef HashName, unsigned HashByteSize,
bool CheckHash, OnDiskGraphDB::HashingFuncT HashFn,
bool AllowRecovery, bool ForceValidation,
std::optional<StringRef> LLVMCasBinary);
/// Validate the action cache only.
LLVM_ABI_FOR_TEST Error validateActionCache() const;
/// This is called implicitly at destruction time, so it is not required for a
/// client to call this. After calling \p close the only method that is valid
/// to call is \p needsGarbageCollection.
///
/// \param CheckSizeLimit if true it will check whether the primary store has
/// exceeded its intended size limit. If false the check is skipped even if a
/// \p SizeLimit was passed to the \p open call.
LLVM_ABI_FOR_TEST Error close(bool CheckSizeLimit = true);
/// Set the size for limiting growth. This has an effect for when the instance
/// is closed.
LLVM_ABI_FOR_TEST void setSizeLimit(std::optional<uint64_t> SizeLimit);
/// \returns the storage size of the cache data.
LLVM_ABI_FOR_TEST uint64_t getStorageSize() const;
/// \returns whether the primary store has exceeded the intended size limit.
/// This can return false even if the overall size of the opened directory is
/// over the \p SizeLimit passed to \p open. To know whether garbage
/// collection needs to be triggered or not, call \p needsGarbaseCollection.
LLVM_ABI_FOR_TEST bool hasExceededSizeLimit() const;
/// \returns whether there are unused data that can be deleted using a
/// \p collectGarbage call.
bool needsGarbageCollection() const { return NeedsGarbageCollection; }
/// Remove any unused data from the directory at \p Path. If there are no such
/// data the operation is a no-op.
///
/// This can be called concurrently, regardless of whether there is an open
/// \p UnifiedOnDiskCache instance or not; it has no effect on readers/writers
/// in the same process or other processes.
///
/// It is recommended that garbage-collection is triggered concurrently in the
/// background, so that it has minimal effect on the workload of the process.
LLVM_ABI_FOR_TEST static Error
collectGarbage(StringRef Path, ondisk::OnDiskCASLogger *Logger = nullptr);
/// Remove unused data from the current UnifiedOnDiskCache.
LLVM_ABI Error collectGarbage();
/// Helper function to convert the value stored in KeyValueDB and ObjectID.
LLVM_ABI_FOR_TEST static ObjectID getObjectIDFromValue(ArrayRef<char> Value);
using ValueBytes = std::array<char, sizeof(uint64_t)>;
LLVM_ABI_FOR_TEST static ValueBytes getValueFromObjectID(ObjectID ID);
LLVM_ABI_FOR_TEST ~UnifiedOnDiskCache();
private:
friend class OnDiskGraphDB;
friend class OnDiskKeyValueDB;
UnifiedOnDiskCache();
Expected<std::optional<ArrayRef<char>>>
faultInFromUpstreamKV(ArrayRef<uint8_t> Key);
/// \returns the storage size of the primary directory.
uint64_t getPrimaryStorageSize() const;
std::string RootPath;
std::atomic<uint64_t> SizeLimit;
int LockFD = -1;
std::atomic<bool> NeedsGarbageCollection;
std::string PrimaryDBDir;
std::unique_ptr<OnDiskGraphDB> UpstreamGraphDB;
std::unique_ptr<OnDiskGraphDB> PrimaryGraphDB;
std::unique_ptr<OnDiskKeyValueDB> UpstreamKVDB;
std::unique_ptr<OnDiskKeyValueDB> PrimaryKVDB;
std::shared_ptr<ondisk::OnDiskCASLogger> Logger = nullptr;
};
} // namespace llvm::cas::ondisk
#endif // LLVM_CAS_UNIFIEDONDISKCACHE_H