diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp index 94efb5edd7de0897fea2010e97440751e19b6fa1..0b84b9c426471568d680fe2b219525fc073eca1b 100644 --- a/catalogue/Catalogue.hpp +++ b/catalogue/Catalogue.hpp @@ -64,6 +64,7 @@ #include "common/exception/FileSizeMismatch.hpp" #include "common/exception/TapeFseqMismatch.hpp" #include "common/exception/UserError.hpp" +#include "common/exception/UserErrorWithCacheInfo.hpp" #include "common/log/LogContext.hpp" #include "common/log/Logger.hpp" #include "common/optional.hpp" @@ -164,6 +165,7 @@ public: * used by the Catalogue to determine the mount policy to be used when * archiving the file. * @return The new archive file identifier. + * @throw UserErrorWithCacheInfo if there was a user error. */ virtual uint64_t checkAndGetNextArchiveFileId( const std::string &diskInstanceName, diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp index cd94aa12cd9d0b993910c85acae531b1b2416907..22939e944fbe61000fc875207809b65c88f23447 100644 --- a/catalogue/CatalogueTest.cpp +++ b/catalogue/CatalogueTest.cpp @@ -21,6 +21,7 @@ #include "common/Constants.hpp" #include "common/exception/Exception.hpp" #include "common/exception/UserError.hpp" +#include "common/exception/UserErrorWithCacheInfo.hpp" #include "common/make_unique.hpp" #include "common/threading/Thread.hpp" #include "common/threading/Mutex.hpp" @@ -7567,7 +7568,7 @@ TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_no_mount_rules) requesterIdentity.name = requesterName; requesterIdentity.group = "group"; - ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstance, m_storageClassSingleCopy.name, requesterIdentity), exception::UserError); + ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstance, m_storageClassSingleCopy.name, requesterIdentity), exception::UserErrorWithCacheInfo); } TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_after_cached_and_then_deleted_requester_mount_rule) { @@ -7644,7 +7645,7 @@ TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_after_cached_an // Try to get an archive ID which should now fail because there is no user // mount rule and the invalidated user mount rule cache should not hide this // fact - ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserError); + ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserErrorWithCacheInfo); } TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_requester_mount_rule) { @@ -7860,7 +7861,7 @@ TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_after_cached_an // Try to get an archive ID which should now fail because there is no group // mount rule and the invalidated group mount rule cache should not hide this // fact - ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserError); + ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserErrorWithCacheInfo); } TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_requester_mount_rule_overide) { diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp index 2d61efebb818b38c229287b00b8a487daec42f9c..4db898a66bb1dcd41471ebc6cab0c07c9de163d1 100644 --- a/catalogue/RdbmsCatalogue.cpp +++ b/catalogue/RdbmsCatalogue.cpp @@ -560,7 +560,7 @@ common::dataStructures::VirtualOrganization RdbmsCatalogue::getCachedVirtualOrga auto conn = m_connPool.getConn(); return getVirtualOrganizationOfTapepool(conn,tapepoolName); }; - return m_tapepoolVirtualOrganizationCache.getCachedValue(tapepoolName,getNonCachedValue); + return m_tapepoolVirtualOrganizationCache.getCachedValue(tapepoolName,getNonCachedValue).value; } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { @@ -5538,7 +5538,7 @@ void RdbmsCatalogue::createRequesterGroupMountRule( //------------------------------------------------------------------------------ // getCachedRequesterGroupMountPolicy //------------------------------------------------------------------------------ -optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedRequesterGroupMountPolicy(const Group &group) +ValueAndTimeBasedCacheInfo<optional<common::dataStructures::MountPolicy> > RdbmsCatalogue::getCachedRequesterGroupMountPolicy(const Group &group) const { try { auto getNonCachedValue = [&] { @@ -5764,7 +5764,7 @@ bool RdbmsCatalogue::requesterMountRuleExists(rdbms::Conn &conn, const std::stri //------------------------------------------------------------------------------ // getCachedRequesterMountPolicy //------------------------------------------------------------------------------ -optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedRequesterMountPolicy(const User &user) const { +ValueAndTimeBasedCacheInfo <optional <common::dataStructures::MountPolicy> > RdbmsCatalogue::getCachedRequesterMountPolicy(const User &user) const { try { auto getNonCachedValue = [&] { auto conn = m_connPool.getConn(); @@ -5992,7 +5992,7 @@ std::list<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedMountPol auto conn = m_connPool.getConn(); return getMountPolicies(conn); }; - return m_allMountPoliciesCache.getCachedValue("all",getNonCachedValue); + return m_allMountPoliciesCache.getCachedValue("all",getNonCachedValue).value; } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { @@ -7375,17 +7375,20 @@ uint64_t RdbmsCatalogue::checkAndGetNextArchiveFileId(const std::string &diskIns throw ue; } - auto mountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, user.name)); + const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name)); + const auto userMountPolicy = userMountPolicyAndCacheInfo.value; // Only consider the requester's group if there is no user mount policy - if(!mountPolicy) { - const auto groupMountPolicy = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group)); + if(!userMountPolicy) { + const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group)); + const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value; if(!groupMountPolicy) { - const auto defaultMountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, "default")); + const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default")); + const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value; - if(!defaultMountPolicy) { - exception::UserError ue; + if(!defaultUserMountPolicy) { + exception::UserErrorWithCacheInfo ue(defaultUserMountPolicyAndCacheInfo.cacheInfo); ue.getMessage() << "Failed to check and get next archive file ID: No mount rules: storageClass=" << storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group; throw ue; @@ -7399,6 +7402,13 @@ uint64_t RdbmsCatalogue::checkAndGetNextArchiveFileId(const std::string &diskIns auto conn = m_connPool.getConn(); return getNextArchiveFileId(conn); } + } catch(exception::UserErrorWithCacheInfo &ue) { + log::LogContext lc(m_log); + log::ScopedParamContainer spc(lc); + spc.add("cacheInfo", ue.cacheInfo) + .add("userError", ue.getMessage().str()); + lc.log(log::INFO, "Catalogue::checkAndGetNextArchiveFileId caught a UserErrorWithCacheInfo"); + throw; } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { @@ -7433,23 +7443,31 @@ common::dataStructures::ArchiveFileQueueCriteria RdbmsCatalogue::getArchiveFileQ } // Get the mount policy - user mount policies overrule group ones - auto mountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, user.name)); - if(!mountPolicy) { - mountPolicy = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group)); + const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name)); + const auto userMountPolicy = userMountPolicyAndCacheInfo.value; - if(!mountPolicy) { - mountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, "default")); + if(userMountPolicy) { + return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *userMountPolicy); + } else { + const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group)); + const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value; - if(!mountPolicy) { - exception::UserError ue; + if(groupMountPolicy) { + return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *groupMountPolicy); + } else { + const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default")); + const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value; + + if(defaultUserMountPolicy) { + return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *defaultUserMountPolicy); + } else { + exception::UserErrorWithCacheInfo ue(defaultUserMountPolicyAndCacheInfo.cacheInfo); ue.getMessage() << "Failed to get archive file queue criteria: No mount rules: storageClass=" << storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group; throw ue; } } } - - return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *mountPolicy); } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { @@ -7468,7 +7486,7 @@ common::dataStructures::TapeCopyToPoolMap RdbmsCatalogue::getCachedTapeCopyToPoo auto conn = m_connPool.getConn(); return getTapeCopyToPoolMap(conn, storageClass); }; - return m_tapeCopyToPoolCache.getCachedValue(storageClass, getNonCachedValue); + return m_tapeCopyToPoolCache.getCachedValue(storageClass, getNonCachedValue).value; } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { @@ -7523,7 +7541,7 @@ uint64_t RdbmsCatalogue::getCachedExpectedNbArchiveRoutes(const StorageClass &st auto conn = m_connPool.getConn(); return getExpectedNbArchiveRoutes(conn, storageClass); }; - return m_expectedNbArchiveRoutesCache.getCachedValue(storageClass, getNonCachedValue); + return m_expectedNbArchiveRoutesCache.getCachedValue(storageClass, getNonCachedValue).value; } catch (exception::LostDatabaseConnection &le) { throw exception::LostDatabaseConnection(std::string(__FUNCTION__) + " failed: " + le.getMessage().str()); } catch(exception::UserError &) { @@ -7793,7 +7811,7 @@ bool RdbmsCatalogue::isCachedAdmin(const common::dataStructures::SecurityIdentit auto getNonCachedValue = [&] { return isNonCachedAdmin(admin); }; - return m_isAdminCache.getCachedValue(admin, getNonCachedValue); + return m_isAdminCache.getCachedValue(admin, getNonCachedValue).value; } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { @@ -8164,7 +8182,7 @@ RdbmsCatalogue::getCachedActivitiesWeights(const std::string& diskInstance) cons auto conn = m_connPool.getConn(); return getActivitiesWeights(conn, diskInstance); }; - return m_activitiesFairShareWeights.getCachedValue(diskInstance, getNonCachedValue); + return m_activitiesFairShareWeights.getCachedValue(diskInstance, getNonCachedValue).value; } catch(exception::UserError &) { throw; } catch(exception::Exception &ex) { diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp index 0c3bcf6d58ccc782cc33b6e27e18c2721936ac20..9b025b28c92c91850c314857fed9ced35f4bff3b 100644 --- a/catalogue/RdbmsCatalogue.hpp +++ b/catalogue/RdbmsCatalogue.hpp @@ -117,6 +117,7 @@ public: * used by the Catalogue to determine the mount policy to be used when * archiving the file. * @return The new archive file identifier. + * @throw UserErrorWithTimeBasedCacheInfo if there was a user error. */ uint64_t checkAndGetNextArchiveFileId( const std::string &diskInstanceName, @@ -1230,8 +1231,9 @@ protected: * @param user The fully qualified user, in other words the name of the disk * instance and the name of the group. * @return The mount policy or nullopt if one does not exists. + * @throw UserErrorWithTimeBasedCacheInfo if there was a user error. */ - optional<common::dataStructures::MountPolicy> getCachedRequesterMountPolicy(const User &user) const; + ValueAndTimeBasedCacheInfo<optional<common::dataStructures::MountPolicy> > getCachedRequesterMountPolicy(const User &user) const; /** * Returns the specified requester mount-policy or nullopt if one does not @@ -1305,7 +1307,7 @@ protected: * instance and the name of the group. * @return The cached mount policy or nullopt if one does not exists. */ - optional<common::dataStructures::MountPolicy> getCachedRequesterGroupMountPolicy(const Group &group) const; + ValueAndTimeBasedCacheInfo<optional<common::dataStructures::MountPolicy> > getCachedRequesterGroupMountPolicy(const Group &group) const; /** * Returns the specified requester-group mount-policy or nullptr if one does diff --git a/catalogue/TimeBasedCache.hpp b/catalogue/TimeBasedCache.hpp index 32064a3539efe0c65aa7efa999026ab1d32ba7f1..437cd38708108e6f69bad5353a4462d42fc6d050 100644 --- a/catalogue/TimeBasedCache.hpp +++ b/catalogue/TimeBasedCache.hpp @@ -17,6 +17,7 @@ #pragma once +#include "catalogue/ValueAndTimeBasedCacheInfo.hpp" #include "common/make_unique.hpp" #include "common/threading/Mutex.hpp" #include "common/threading/MutexLocker.hpp" @@ -40,11 +41,11 @@ public: } /** - * Get the cached value corresponing to the specified key. + * Get the cached value corresponding to the specified key. * * This method updates the cache when necessary. */ - template<typename Callable> Value getCachedValue(const Key &key, const Callable &getNonCachedValue) { + template<typename Callable> ValueAndTimeBasedCacheInfo<Value> getCachedValue(const Key &key, const Callable &getNonCachedValue) { const time_t now = time(nullptr); threading::MutexLocker cacheLock(m_mutex); @@ -56,16 +57,17 @@ public: const time_t ageSecs = now - cachedValue.timestamp; if (m_maxAgeSecs >= ageSecs) { // Cached value is fresh - return cachedValue.value; + return ValueAndTimeBasedCacheInfo<Value>(cachedValue.value, "Fresh value found in cache"); } else { // Cached value is stale cachedValue.value = getNonCachedValue(); cachedValue.timestamp = ::time(nullptr); - return cachedValue.value; + + return ValueAndTimeBasedCacheInfo<Value>(cachedValue.value, "Stale value found and replaced in cache"); } } else { // No cache hit const auto emplaceResult = m_cache.emplace(std::make_pair(key, cta::make_unique<TimestampedValue>(now, getNonCachedValue()))); - return emplaceResult.first->second->value; + return ValueAndTimeBasedCacheInfo<Value>(emplaceResult.first->second->value, "First time value entered into cache"); } } diff --git a/catalogue/ValueAndTimeBasedCacheInfo.hpp b/catalogue/ValueAndTimeBasedCacheInfo.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bc3680d13cff9856460d9eefbfd00914aea5a651 --- /dev/null +++ b/catalogue/ValueAndTimeBasedCacheInfo.hpp @@ -0,0 +1,36 @@ +/* + * @project The CERN Tape Archive (CTA) + * @copyright Copyright(C) 2015-2021 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <string> +#include <time.h> + +namespace cta { +namespace catalogue { + +template <typename Value> struct ValueAndTimeBasedCacheInfo { + Value value; + std::string cacheInfo; + + ValueAndTimeBasedCacheInfo(const Value &v, const std::string &cInfo): + value(v), cacheInfo(cInfo) { + } +}; // class ValueAndTimeBasedCacheInfo + +} // namespace catalogue +} // namespace cta diff --git a/common/exception/UserErrorWithCacheInfo.hpp b/common/exception/UserErrorWithCacheInfo.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c7649d337f8776fd2f9678818da213c8b529be7 --- /dev/null +++ b/common/exception/UserErrorWithCacheInfo.hpp @@ -0,0 +1,50 @@ +/* + * @project The CERN Tape Archive (CTA) + * @copyright Copyright(C) 2015-2021 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "common/exception/UserError.hpp" + +namespace cta { +namespace exception { + +/** + * A user error together with information about how the respective cached + * value was obtained. + */ +class UserErrorWithCacheInfo : public exception::UserError { +public: + std::string cacheInfo; + + /** + * Constructor. + * + * @param cInfo Information about how the respective cached value was + * obtained. + * @param context optional context string added to the message + * at initialisation time. + * @param embedBacktrace whether to embed a backtrace of where the + * exception was throw in the message + */ + UserErrorWithCacheInfo(const std::string &cInfo, const std::string &context = "", const bool embedBacktrace = true): + UserError(context, embedBacktrace), + cacheInfo(cInfo) { + } +}; // class UserErrorWithTimeBasedCacheInfo + +} // namespace exception +} // namespace cta