Use locks when checking/cleaning repository
This commit is contained in:
parent
556a749362
commit
cbfa8d8298
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue