Use locks when checking/cleaning repository

This commit is contained in:
Martchus 2021-03-04 20:15:19 +01:00
parent 556a749362
commit cbfa8d8298
2 changed files with 54 additions and 11 deletions

View File

@ -313,8 +313,8 @@ protected:
ServiceSetup &m_setup; ServiceSetup &m_setup;
std::shared_ptr<BuildAction> m_buildAction; std::shared_ptr<BuildAction> m_buildAction;
std::set<LibPkg::Database *> m_sourceDbs; std::set<LibPkg::Database *> m_sourceDbs; // ordering important to prevent deadlocks when acquiring locks for DBs
std::set<LibPkg::Database *> m_destinationDbs; std::set<LibPkg::Database *> m_destinationDbs; // ordering important to prevent deadlocks when acquiring locks for DBs
bool m_fromAur = false; bool m_fromAur = false;
bool m_toAur = false; bool m_toAur = false;
bool m_hasError = false; bool m_hasError = false;

View File

@ -2,6 +2,8 @@
#include "../logging.h" #include "../logging.h"
#include "../libpkg/parser/utils.h"
#include <c++utilities/chrono/datetime.h> #include <c++utilities/chrono/datetime.h>
#include <c++utilities/conversion/stringbuilder.h> #include <c++utilities/conversion/stringbuilder.h>
#include <c++utilities/conversion/stringconversion.h> #include <c++utilities/conversion/stringconversion.h>
@ -13,6 +15,7 @@
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <ranges>
using namespace std; using namespace std;
using namespace std::literals::string_view_literals; using namespace std::literals::string_view_literals;
@ -401,6 +404,11 @@ void CheckForProblems::run()
auto result = std::unordered_map<std::string, std::vector<RepositoryProblem>>(); auto result = std::unordered_map<std::string, std::vector<RepositoryProblem>>();
for (auto *const db : m_destinationDbs) { for (auto *const db : m_destinationDbs) {
// acquire locks
const auto archLock = m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(*db));
const auto anyLock = m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db->name, "any"));
const auto srcLock = m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db->name, "src"));
// check whether files exist // check whether files exist
auto &problems = result[db->name]; auto &problems = result[db->name];
try { try {
@ -432,6 +440,7 @@ void CheckForProblems::run()
} catch (const std::filesystem::filesystem_error &e) { } catch (const std::filesystem::filesystem_error &e) {
problems.emplace_back(RepositoryProblem{ .desc = argsToString("unable to check presence of files: ", e.what()) }); problems.emplace_back(RepositoryProblem{ .desc = argsToString("unable to check presence of files: ", e.what()) });
} }
// check for unresolved dependencies and missing libraries // check for unresolved dependencies and missing libraries
checkForUnresolvedPackages: checkForUnresolvedPackages:
auto unresolvedPackages = db->detectUnresolvedPackages( auto unresolvedPackages = db->detectUnresolvedPackages(
@ -471,7 +480,9 @@ void CleanRepository::run()
return; return;
} }
// find relevant repository directories // find relevant repository directories and acquire locks
// note: Only using a shared lock here because the cleanup isn't supposed to touch any files which actually still belong to
// the repository.
enum class RepoDirType { enum class RepoDirType {
New, New,
ArchSpecific, ArchSpecific,
@ -483,6 +494,7 @@ void CleanRepository::run()
std::vector<std::pair<std::filesystem::path, std::string>> toArchive; // old packages not belonging to the DB anymore std::vector<std::pair<std::filesystem::path, std::string>> toArchive; // old packages not belonging to the DB anymore
std::vector<std::filesystem::path> toDelete; // non-package junk files std::vector<std::filesystem::path> toDelete; // non-package junk files
std::unordered_set<LibPkg::Database *> relevantDbs; std::unordered_set<LibPkg::Database *> relevantDbs;
std::variant<std::monostate, SharedLoggingLock, UniqueLoggingLock> lock;
RepoDirType type = RepoDirType::New; RepoDirType type = RepoDirType::New;
}; };
std::unordered_map<std::string, RepoDir> repoDirs; std::unordered_map<std::string, RepoDir> repoDirs;
@ -494,6 +506,8 @@ void CleanRepository::run()
auto &anyDir = repoDirs[anyPath.string()]; auto &anyDir = repoDirs[anyPath.string()];
if (anyDir.type == RepoDirType::New) { if (anyDir.type == RepoDirType::New) {
anyDir.type = RepoDirType::Any; anyDir.type = RepoDirType::Any;
anyDir.lock.emplace<SharedLoggingLock>(
m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db.name, "any")));
anyDir.canonicalPath = std::move(anyPath); anyDir.canonicalPath = std::move(anyPath);
} }
anyDir.relevantDbs.emplace(&db); anyDir.relevantDbs.emplace(&db);
@ -506,6 +520,8 @@ void CleanRepository::run()
auto &srcDir = repoDirs[srcPath.string()]; auto &srcDir = repoDirs[srcPath.string()];
if (srcDir.type == RepoDirType::New) { if (srcDir.type == RepoDirType::New) {
srcDir.type = RepoDirType::Src; srcDir.type = RepoDirType::Src;
srcDir.lock.emplace<SharedLoggingLock>(
m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db.name, "src")));
srcDir.canonicalPath = std::move(srcPath); srcDir.canonicalPath = std::move(srcPath);
} }
srcDir.relevantDbs.emplace(&db); srcDir.relevantDbs.emplace(&db);
@ -522,10 +538,20 @@ void CleanRepository::run()
auto parentPath = std::filesystem::path(); auto parentPath = std::filesystem::path();
try { try {
auto archSpecificPath = std::filesystem::canonical(db->localPkgDir); auto archSpecificPath = std::filesystem::canonical(db->localPkgDir);
const auto dbFile = argsToString(archSpecificPath, '/', db->name + ".db");
const auto lastModified = LibPkg::lastModified(dbFile);
if (lastModified != db->lastUpdate) {
m_messages.errors.emplace_back("The db file's last modification (" % lastModified.toString() % ") does not match the last db update ("
% db->lastUpdate.toString()
+ ").");
fatalError = true;
}
auto &archSpecificDir = repoDirs[archSpecificPath.string()]; auto &archSpecificDir = repoDirs[archSpecificPath.string()];
parentPath = archSpecificPath.parent_path(); parentPath = archSpecificPath.parent_path();
if (archSpecificDir.type == RepoDirType::New) { if (archSpecificDir.type == RepoDirType::New) {
archSpecificDir.type = RepoDirType::ArchSpecific; archSpecificDir.type = RepoDirType::ArchSpecific;
archSpecificDir.lock.emplace<SharedLoggingLock>(
m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(*db)));
archSpecificDir.canonicalPath = std::move(archSpecificPath); archSpecificDir.canonicalPath = std::move(archSpecificPath);
} }
archSpecificDir.relevantDbs.emplace(db); archSpecificDir.relevantDbs.emplace(db);
@ -559,12 +585,12 @@ void CleanRepository::run()
} }
// find relevant databases for repo dirs discovered in "find other directories next to …" step // find relevant databases for repo dirs discovered in "find other directories next to …" step
std::vector<std::unique_ptr<LibPkg::Database>> otherDbs; auto otherDbs = std::vector<std::unique_ptr<LibPkg::Database>>();
for (auto &[dirName, dirInfo] : repoDirs) { for (auto &[dirName, dirInfo] : repoDirs) {
if (dirInfo.type != RepoDirType::New) { if (dirInfo.type != RepoDirType::New) {
continue; continue;
} }
std::vector<std::string> dbFileNames; auto dbFilePaths = std::vector<std::filesystem::path>();
try { try {
// find the database file // find the database file
dirInfo.canonicalPath = std::filesystem::canonical(dirName); dirInfo.canonicalPath = std::filesystem::canonical(dirName);
@ -573,20 +599,36 @@ void CleanRepository::run()
continue; continue;
} }
if (repoItem.path().extension() == ".db") { if (repoItem.path().extension() == ".db") {
dbFileNames.emplace_back(repoItem.path().filename()); dbFilePaths.emplace_back(repoItem.path());
} }
} }
if (dbFileNames.empty()) { if (dbFilePaths.empty()) {
throw std::runtime_error("no *.db file present"); throw std::runtime_error("no *.db file present");
} }
if (dbFileNames.size() > 1) { if (dbFilePaths.size() > 1) {
throw std::runtime_error("multiple/ambigous *.db files present: " + joinStrings(dbFileNames, ", ")); auto dbFileNames
#if defined(__GNUC__) && !defined(__clang__)
= dbFilePaths | std::views::transform([](const std::filesystem::path &path) { return std::string(path.filename()); });
#else
= [&dbFilePaths] {
auto res = std::vector<std::string>();
res.reserve(dbFilePaths.size());
for (const auto &path : dbFilePaths) {
res.emplace_back(path.filename());
}
return res;
}();
#endif
throw std::runtime_error(
"multiple/ambigous *.db files present: " + joinStrings<decltype(dbFileNames), std::string>(dbFileNames, ", "));
} }
// initialize temporary database object for the repository // initialize temporary database object for the repository
auto &db auto &db = otherDbs.emplace_back(std::make_unique<LibPkg::Database>(dbFilePaths.front().stem(), dbFilePaths.front()));
= otherDbs.emplace_back(std::make_unique<LibPkg::Database>(dirName, argsToString(dirInfo.canonicalPath, '/', dbFileNames.front()))); db->arch = dirInfo.canonicalPath.stem();
db->loadPackages(); db->loadPackages();
dirInfo.relevantDbs.emplace(db.get()); dirInfo.relevantDbs.emplace(db.get());
// acquire lock for db directory
dirInfo.lock.emplace<SharedLoggingLock>(m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(*db)));
// find the "any" and "src" directory // find the "any" and "src" directory
db->localPkgDir = dirInfo.canonicalPath.string(); db->localPkgDir = dirInfo.canonicalPath.string();
addAnyAndSrcDir(*db); addAnyAndSrcDir(*db);
@ -729,6 +771,7 @@ void CleanRepository::run()
} }
m_buildAction->appendOutput(Phrases::InfoMessage, "Archived/deleted ", processesItems, " files in \"", dirName, '\"', '\n'); m_buildAction->appendOutput(Phrases::InfoMessage, "Archived/deleted ", processesItems, " files in \"", dirName, '\"', '\n');
} }
repoDirs.clear();
const auto buildLock = m_setup.building.lockToWrite(); const auto buildLock = m_setup.building.lockToWrite();
m_buildAction->resultData = std::move(m_messages); m_buildAction->resultData = std::move(m_messages);