/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2016 CERN/Switzerland * * * * 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 .* ************************************************************************/ #include "namespace/ns_quarkdb/persistency/FileMDSvc.hh" #include "namespace/ns_quarkdb/Constants.hh" #include "namespace/ns_quarkdb/FileMD.hh" #include "namespace/ns_quarkdb/accounting/QuotaStats.hh" #include "namespace/ns_quarkdb/persistency/ContainerMDSvc.hh" #include "namespace/ns_quarkdb/flusher/MetadataFlusher.hh" #include "namespace/ns_quarkdb/persistency/MetadataFetcher.hh" #include "namespace/ns_quarkdb/persistency/MetadataProvider.hh" #include "namespace/ns_quarkdb/persistency/RequestBuilder.hh" #include "namespace/ns_quarkdb/ConfigurationParser.hh" #include "namespace/ns_quarkdb/QdbContactDetails.hh" #include "namespace/utils/StringConvertion.hh" #include "common/StacktraceHere.hh" #include "namespace/utils/IMDLockHelper.hh" #include EOSNSNAMESPACE_BEGIN std::chrono::seconds QuarkFileMDSvc::sFlushInterval(5); //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ QuarkFileMDSvc::QuarkFileMDSvc(qclient::QClient* qcl, MetadataFlusher* flusher) : pQuotaStats(nullptr), pContSvc(nullptr), pFlusher(flusher), pQcl(qcl), mMetaMap(), mNumFiles(0ull), mMetadataProvider(nullptr) {} //------------------------------------------------------------------------------ // Destructor //------------------------------------------------------------------------------ QuarkFileMDSvc::~QuarkFileMDSvc() { if (pFlusher) { pFlusher->synchronize(); } } //------------------------------------------------------------------------------ // Configure the file service //------------------------------------------------------------------------------ void QuarkFileMDSvc::configure(const std::map& config) { std::string qdb_cluster; std::string qdb_flusher_id; const std::string key_flusher = "qdb_flusher_md"; if (config.find(key_flusher) != config.end()) { // This should only be called once during booting but then the rest of the // config values can be updated also while running QdbContactDetails contactDetails = ConfigurationParser::parse(config); mMetaMap.setKey(constants::sMapMetaInfoKey); mMetaMap.setClient(*pQcl); mUnifiedInodeProvider.configure(mMetaMap); mMetadataProvider.reset(new MetadataProvider(contactDetails, pContSvc, this)); static_cast(pContSvc)->setMetadataProvider (mMetadataProvider.get()); static_cast(pContSvc)->setInodeProvider (&mUnifiedInodeProvider); } // Refresh the inode provider with the latest inode values from QDB if (config.find(constants::sKeyInodeRefresh) != config.end()) { mUnifiedInodeProvider.configure(mMetaMap); } if (config.find(constants::sMaxNumCacheFiles) != config.end()) { std::string val = config.at(constants::sMaxNumCacheFiles); mMetadataProvider->setFileMDCacheNum(std::stoull(val)); } } //------------------------------------------------------------------------------ // Initialize the file service //------------------------------------------------------------------------------ void QuarkFileMDSvc::initialize() { if (pContSvc == nullptr) { MDException e(EINVAL); e.getMessage() << __FUNCTION__ << " FileMDSvc: container service not set"; throw e; } if ((pQcl == nullptr) || (pFlusher == nullptr)) { MDException e(EINVAL); e.getMessage() << __FUNCTION__ << " No qclient/flusher initialized for " << "the container metadata service"; throw e; } SafetyCheck(); mNumFiles.store(pQcl->execute( RequestBuilder::getNumberOfFiles()).get()->integer); } //------------------------------------------------------------------------------ // Safety check to make sure there are no file entries in the backend with // ids bigger than the max file id. //------------------------------------------------------------------------------ void QuarkFileMDSvc::SafetyCheck() { std::string blob; IFileMD::id_t free_id = getFirstFreeId(); std::vector offsets {1, 10, 50, 100, 501, 1001, 11000, 50000, 100000, 150199, 200001, 1000002, 2000123 }; std::vector> futs; for (auto incr : offsets) { IFileMD::id_t check_id = free_id + incr; futs.emplace_back(MetadataFetcher::getFileFromId(*pQcl, FileIdentifier(check_id))); } for (size_t i = 0; i < futs.size(); i++) { try { std::move(futs[i]).get(); } catch (eos::MDException& qdb_err) { // All is good, we didn't find any file, as expected continue; } // Uh-oh, this is bad. MDException e(EEXIST); e.getMessage() << __FUNCTION__ << " FATAL: Risk of data loss, found " << "file (" << free_id + offsets[i] << ") with id bigger than max file id (" << free_id << ")"; throw e; } } //------------------------------------------------------------------------------ // Get the file metadata information for the given file id - asynchronous API. //------------------------------------------------------------------------------ folly::Future QuarkFileMDSvc::getFileMDFut(IFileMD::id_t id) { return mMetadataProvider->retrieveFileMD(FileIdentifier(id)); } //------------------------------------------------------------------------------ // Get the file metadata information for the given file id //------------------------------------------------------------------------------ std::shared_ptr QuarkFileMDSvc::getFileMD(IFileMD::id_t id, uint64_t* clock) { IFileMDPtr file = mMetadataProvider->retrieveFileMD(FileIdentifier(id)).get(); if (file && clock) { *clock = file->getClock(); } return file; } //------------------------------------------------------------------------ //! Get the file metadata information for the given file ID and read lock it //------------------------------------------------------------------------ IFileMD::IFileMDReadLockerPtr QuarkFileMDSvc::getFileMDReadLocked(IFileMD::id_t id) { return IMDLockHelper::lock(getFileMD(id,0)); } //------------------------------------------------------------------------ //! Get the file metadata information for the given file ID and write lock it //------------------------------------------------------------------------ IFileMD::IFileMDWriteLockerPtr QuarkFileMDSvc::getFileMDWriteLocked(IFileMD::id_t id) { return IMDLockHelper::lock(getFileMD(id,0)); } //------------------------------------------------------------------------------ //! Check if a FileMD with a given identifier exists //------------------------------------------------------------------------------ folly::Future QuarkFileMDSvc::hasFileMD(const eos::FileIdentifier id) { return mMetadataProvider->hasFileMD(id); } //---------------------------------------------------------------------------- // Drop cached FileMD - return true if found //---------------------------------------------------------------------------- bool QuarkFileMDSvc::dropCachedFileMD(FileIdentifier id) { return mMetadataProvider->dropCachedFileID(id); } //------------------------------------------------------------------------------ // Create new file metadata object //------------------------------------------------------------------------------ std::shared_ptr QuarkFileMDSvc::createFile(IFileMD::id_t id) { uint64_t free_id; if (id > 0) { mUnifiedInodeProvider.blacklistFileId(id); free_id = id; } else { free_id = mUnifiedInodeProvider.reserveFileId(); } std::shared_ptr file{new QuarkFileMD(free_id, this)}; mMetadataProvider->insertFileMD(file->getIdentifier(), file); IFileMDChangeListener::Event e(file.get(), IFileMDChangeListener::Created); notifyListeners(&e); ++mNumFiles; return file; } //------------------------------------------------------------------------------ // Update backend store and notify all the listeners //------------------------------------------------------------------------------ void QuarkFileMDSvc::updateStore(IFileMD* obj) { if (obj->getName() == "") { eos_static_crit("updateFileStore called on file with empty name; id=%llu, parent=%llu, trace=%s", obj->getId(), obj->getContainerId(), common::getStacktrace().c_str()); // eventually throw, once we understand how this happens // TODO: @ccaffy - this should throw an exception, but // all the code calling this method should handle it properly // by surrounding the calls with try/catch blocks return; } pFlusher->execute(RequestBuilder::writeFileProto(obj)); // If file is detached then add it to the list of orphans if (obj->getContainerId() == 0) { pFlusher->sadd(constants::sOrphanFiles, stringify(obj->getId())); } } //------------------------------------------------------------------------------ // Remove object from the store //------------------------------------------------------------------------------ void QuarkFileMDSvc::removeFile(IFileMD* obj) { std::string sid = stringify(obj->getId()); pFlusher->execute(RequestBuilder::deleteFileProto(FileIdentifier( obj->getId()))); pFlusher->srem(constants::sOrphanFiles, sid); IFileMDChangeListener::Event e(obj, IFileMDChangeListener::Deleted); notifyListeners(&e); obj->setDeleted(); if (mNumFiles) { --mNumFiles; } } //------------------------------------------------------------------------------ // Get number of files //------------------------------------------------------------------------------ uint64_t QuarkFileMDSvc::getNumFiles() { return mNumFiles.load(); } //------------------------------------------------------------------------------ // Add file listener //------------------------------------------------------------------------------ void QuarkFileMDSvc::addChangeListener(IFileMDChangeListener* listener) { pListeners.push_back(listener); } //------------------------------------------------------------------------------ // Notify the listeners about the change //------------------------------------------------------------------------------ void QuarkFileMDSvc::notifyListeners(IFileMDChangeListener::Event* event) { for (const auto& elem : pListeners) { elem->fileMDChanged(event); } } //------------------------------------------------------------------------------ // Set container service //------------------------------------------------------------------------------ void QuarkFileMDSvc::setContMDService(IContainerMDSvc* cont_svc) { QuarkContainerMDSvc* impl_cont_svc = dynamic_cast (cont_svc); if (!impl_cont_svc) { MDException e(EFAULT); e.getMessage() << __FUNCTION__ << " ContainerMDSvc dynamic cast failed"; throw e; } pContSvc = impl_cont_svc; } //------------------------------------------------------------------------------ // Get first free file id //------------------------------------------------------------------------------ IFileMD::id_t QuarkFileMDSvc::getFirstFreeId() { return mUnifiedInodeProvider.getFirstFreeFileId(); } //------------------------------------------------------------------------------ //! Retrieve MD cache statistics. //------------------------------------------------------------------------------ CacheStatistics QuarkFileMDSvc::getCacheStatistics() { return mMetadataProvider->getFileMDCacheStats(); } //------------------------------------------------------------------------------ // Blacklist IDs below the given threshold //------------------------------------------------------------------------------ void QuarkFileMDSvc::blacklistBelow(FileIdentifier id) { mUnifiedInodeProvider.blacklistFileId(id.getUnderlyingUInt64()); } //------------------------------------------------------------------------------ // Get pointer to metadata provider //------------------------------------------------------------------------------ MetadataProvider* QuarkFileMDSvc::getMetadataProvider() { return mMetadataProvider.get(); } EOSNSNAMESPACE_END