diff options
Diffstat (limited to 'src/buildtool/storage/garbage_collector.cpp')
-rw-r--r-- | src/buildtool/storage/garbage_collector.cpp | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/buildtool/storage/garbage_collector.cpp b/src/buildtool/storage/garbage_collector.cpp new file mode 100644 index 00000000..3e486672 --- /dev/null +++ b/src/buildtool/storage/garbage_collector.cpp @@ -0,0 +1,155 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BOOTSTRAP_BUILD_TOOL + +#include "src/buildtool/storage/garbage_collector.hpp" + +#include <vector> + +#include <nlohmann/json.hpp> + +#include "src/buildtool/common/artifact.hpp" +#include "src/buildtool/common/bazel_types.hpp" +#include "src/buildtool/compatibility/compatibility.hpp" +#include "src/buildtool/compatibility/native_support.hpp" +#include "src/buildtool/file_system/file_storage.hpp" +#include "src/buildtool/file_system/file_system_manager.hpp" +#include "src/buildtool/file_system/git_repo.hpp" +#include "src/buildtool/file_system/object_type.hpp" +#include "src/buildtool/logging/log_level.hpp" +#include "src/buildtool/logging/logger.hpp" +#include "src/buildtool/storage/config.hpp" +#include "src/buildtool/storage/storage.hpp" +#include "src/buildtool/storage/target_cache_entry.hpp" +#include "src/utils/cpp/hex_string.hpp" + +auto GarbageCollector::GlobalUplinkBlob(bazel_re::Digest const& digest, + bool is_executable) noexcept -> bool { + // Try to find blob in all generations. + auto const& latest_cas = Storage::Generation(0).CAS(); + for (std::size_t i = 0; i < StorageConfig::NumGenerations(); ++i) { + // Note that we uplink with _skip_sync_ as we want to prefer hard links + // from older generations over copies from the companion file/exec CAS. + if (Storage::Generation(i).CAS().LocalUplinkBlob( + latest_cas, digest, is_executable, /*skip_sync=*/true)) { + return true; + } + } + return false; +} + +auto GarbageCollector::GlobalUplinkTree(bazel_re::Digest const& digest) noexcept + -> bool { + // Try to find tree in all generations. + auto const& latest_cas = Storage::Generation(0).CAS(); + for (std::size_t i = 0; i < StorageConfig::NumGenerations(); ++i) { + if (Storage::Generation(i).CAS().LocalUplinkTree(latest_cas, digest)) { + return true; + } + } + return false; +} + +auto GarbageCollector::GlobalUplinkActionCacheEntry( + bazel_re::Digest const& action_id) noexcept -> bool { + // Try to find action-cache entry in all generations. + auto const& latest_ac = Storage::Generation(0).ActionCache(); + for (std::size_t i = 0; i < StorageConfig::NumGenerations(); ++i) { + if (Storage::Generation(i).ActionCache().LocalUplinkEntry(latest_ac, + action_id)) { + return true; + } + } + return false; +} + +auto GarbageCollector::GlobalUplinkTargetCacheEntry( + TargetCacheKey const& key) noexcept -> bool { + // Try to find target-cache entry in all generations. + auto const& latest_tc = Storage::Generation(0).TargetCache(); + for (std::size_t i = 0; i < StorageConfig::NumGenerations(); ++i) { + if (Storage::Generation(i).TargetCache().LocalUplinkEntry(latest_tc, + key)) { + return true; + } + } + return false; +} + +auto GarbageCollector::SharedLock() noexcept -> std::optional<LockFile> { + return LockFile::Acquire(LockFilePath(), /*is_shared=*/true); +} + +auto GarbageCollector::ExclusiveLock() noexcept -> std::optional<LockFile> { + return LockFile::Acquire(LockFilePath(), /*is_shared=*/false); +} + +auto GarbageCollector::LockFilePath() noexcept -> std::filesystem::path { + return StorageConfig::CacheRoot() / "gc.lock"; +} + +auto GarbageCollector::TriggerGarbageCollection() noexcept -> bool { + auto pid = CreateProcessUniqueId(); + if (not pid) { + return false; + } + auto remove_me = std::string{"remove-me-"} + *pid; + auto remove_me_dir = StorageConfig::CacheRoot() / remove_me; + if (FileSystemManager::IsDirectory(remove_me_dir)) { + if (not FileSystemManager::RemoveDirectory(remove_me_dir, + /*recursively=*/true)) { + Logger::Log(LogLevel::Error, + "Failed to remove directory {}", + remove_me_dir.string()); + return false; + } + } + { // Create scope for critical renaming section protected by advisory lock. + auto lock = ExclusiveLock(); + if (not lock) { + Logger::Log(LogLevel::Error, + "Failed to exclusively lock the local build root"); + return false; + } + for (std::size_t i = StorageConfig::NumGenerations(); i > 0; --i) { + auto cache_root = StorageConfig::GenerationCacheRoot(i - 1); + if (FileSystemManager::IsDirectory(cache_root)) { + auto new_cache_root = + (i == StorageConfig::NumGenerations()) + ? remove_me_dir + : StorageConfig::GenerationCacheRoot(i); + if (not FileSystemManager::Rename(cache_root, new_cache_root)) { + Logger::Log(LogLevel::Error, + "Failed to rename {} to {}.", + cache_root.string(), + new_cache_root.string()); + return false; + } + } + } + } + if (FileSystemManager::IsDirectory(remove_me_dir)) { + if (not FileSystemManager::RemoveDirectory(remove_me_dir, + /*recursively=*/true)) { + Logger::Log(LogLevel::Warning, + "Failed to remove directory {}", + remove_me_dir.string()); + return false; + } + } + return true; +} + +#endif // BOOTSTRAP_BUILD_TOOL |