/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2018 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 .* ************************************************************************/ //------------------------------------------------------------------------------ //! @author Georgios Bitzes //! @brief Class handling caching and access of individual filesystems //------------------------------------------------------------------------------ #ifndef EOS_NS_FILESYSTEM_HANDLER_HH #define EOS_NS_FILESYSTEM_HANDLER_HH #include "namespace/Namespace.hh" #include "namespace/interface/IFsView.hh" #include "namespace/interface/IFileMD.hh" #include "namespace/ns_quarkdb/accounting/SetChangeList.hh" #include "qclient/structures/QSet.hh" #include #include #include "common/Assert.hh" #include "common/SteadyClock.hh" namespace qclient { class QClient; } EOSNSNAMESPACE_BEGIN class MetadataFlusher; struct IsNoReplicaListTag {}; //------------------------------------------------------------------------------ //! Iterator to go through the contents of a FileSystemHandler. Keeps //! the corresponding list read-locked during its lifetime. //------------------------------------------------------------------------------ class FileListIterator : public ICollectionIterator { public: //---------------------------------------------------------------------------- //! Constructor. //---------------------------------------------------------------------------- FileListIterator(const IFsView::FileList& fileList, std::shared_timed_mutex& mtx) : pFileList(fileList), mLock(mtx) { mIterator = pFileList.begin(); } //---------------------------------------------------------------------------- //! Destructor. //---------------------------------------------------------------------------- virtual ~FileListIterator() {} //---------------------------------------------------------------------------- //! Check whether the iterator is still valid. //---------------------------------------------------------------------------- virtual bool valid() override { return mIterator != pFileList.end(); } //---------------------------------------------------------------------------- //! Get current element. //---------------------------------------------------------------------------- virtual IFileMD::id_t getElement() override { return *mIterator; } //---------------------------------------------------------------------------- //! Progress iterator. //---------------------------------------------------------------------------- virtual void next() override { mIterator++; } private: const IFsView::FileList& pFileList; std::shared_lock mLock; IFsView::FileList::const_iterator mIterator; }; //------------------------------------------------------------------------------ //! Streaming iterator to go through the contents of a FileSystemHandler. //! //! Elements which are added, or deleted while iteration is ongoing, may or //! may not be in the results. //! //! Also, watch out for races related to the flusher.. Use only if a weakly //! consistent view is acceptable. //------------------------------------------------------------------------------ class StreamingFileListIterator : public ICollectionIterator { public: //---------------------------------------------------------------------------- //! Constructor. //---------------------------------------------------------------------------- StreamingFileListIterator(qclient::QClient& qcl, const std::string& key) : mQSet(qcl, key), it(mQSet.getIterator()) {} //---------------------------------------------------------------------------- //! Destructor. //---------------------------------------------------------------------------- virtual ~StreamingFileListIterator() {} //---------------------------------------------------------------------------- //! Check whether the iterator is still valid. //---------------------------------------------------------------------------- virtual bool valid() override { return it.valid(); } //---------------------------------------------------------------------------- //! Get current element. //---------------------------------------------------------------------------- virtual IFileMD::id_t getElement() override { return std::stoull(it.getElement()); } //---------------------------------------------------------------------------- //! Progress iterator. //---------------------------------------------------------------------------- virtual void next() override { return it.next(); } private: qclient::QSet mQSet; qclient::QSet::Iterator it; }; class FileSystemHandler { public: //---------------------------------------------------------------------------- //! Constructor. //! //! @param location file system ID //! @param qcl QClient object to use for loading the view from QDB //! @param flusher Flusher object for propagating updates to the backend //! @param unlinked whether we want the unlinked file list, or the regular one //! @param fake_clock if true is fake clock implementation for tests //---------------------------------------------------------------------------- FileSystemHandler(IFileMD::location_t location, folly::Executor* pExecutor, qclient::QClient* qcl, MetadataFlusher* flusher, bool unlinked, bool fake_clock = false); //---------------------------------------------------------------------------- //! Constructor for the special case of "no replica list". //! //! @param location file system ID //! @param qcl QClient object to use for loading the view from QDB //! @param flusher Flusher object for propagating updates to the backend //! @param Tag for dispatching to this constructor overload //---------------------------------------------------------------------------- FileSystemHandler(folly::Executor* pExecutor, qclient::QClient* qcl, MetadataFlusher* flusher, IsNoReplicaListTag tag); //---------------------------------------------------------------------------- //! Ensure contents have been loaded into the cache. If so, returns //! immediatelly. Otherwise, does requests to QDB to retrieve its contents. //! Return value: "this" pointer. //---------------------------------------------------------------------------- FileSystemHandler* ensureContentsLoaded(); //---------------------------------------------------------------------------- //! Async version of ensureContentsLoaded. //---------------------------------------------------------------------------- folly::Future ensureContentsLoadedAsync(); //---------------------------------------------------------------------------- //! Insert item. //---------------------------------------------------------------------------- void insert(FileIdentifier identifier); //---------------------------------------------------------------------------- //! Erase item. //---------------------------------------------------------------------------- void erase(FileIdentifier identifier); //---------------------------------------------------------------------------- //! Get size. Careful with calling this function, it'll load all contents if //! not already there. //---------------------------------------------------------------------------- uint64_t size(); //---------------------------------------------------------------------------- //! Return redis key holding our target filesystem list. //---------------------------------------------------------------------------- std::string getRedisKey() const; //---------------------------------------------------------------------------- //! Return iterator for this file system. Note that the iterator keeps //! this filesystem read-locked during its entire lifetime. //---------------------------------------------------------------------------- std::shared_ptr> getFileList(); //---------------------------------------------------------------------------- //! Retrieve streaming iterator to go through the contents of a //! FileSystemHandler. //! //! Elements which are added, or deleted while iteration is ongoing, may or //! may not be in the results. //! //! Also, watch out for races related to the flusher.. Use only if a weakly //! consistent view is acceptable. //---------------------------------------------------------------------------- std::shared_ptr> getStreamingFileList(); //---------------------------------------------------------------------------- //! Delete the entire filelist. //---------------------------------------------------------------------------- void nuke(); //---------------------------------------------------------------------------- //! Get an approximately random file in the filelist. //---------------------------------------------------------------------------- bool getApproximatelyRandomFile(IFileMD::id_t& res); //---------------------------------------------------------------------------- //! Check whether a given id_t is contained in this filelist //---------------------------------------------------------------------------- bool hasFileId(IFileMD::id_t file); //---------------------------------------------------------------------------- //! Clear cache if given timeout is exceeded //! //! @param inactive_timeout timeout in seconds since the last time there was //! a call that required the entries to be actually loaded in memory. //! If inactive timeout is 0 then the cache is cleared immediately. By //! default once every 30 minutes. //---------------------------------------------------------------------------- void clearCache(std::chrono::seconds inactive_timeout = std::chrono::seconds(30 * 60)); private: #ifdef IN_TEST_HARNESS public: #endif //---------------------------------------------------------------------------- //! Cache status states //---------------------------------------------------------------------------- enum class CacheStatus { kNotLoaded, kInFlight, kLoaded }; CacheStatus mCacheStatus = CacheStatus::kNotLoaded; ///< Stores caching status for this fs enum class Target { kRegular, kUnlinked, kNoReplicaList }; Target target; ///< The filesystem list type this class is targetting. IFileMD::location_t location; ///< Filesystem ID, if available folly::Executor* pExecutor; ///< Folly executor qclient::QClient* pQcl; ///< QClient object MetadataFlusher* pFlusher; ///< Metadata flusher object mutable std::shared_timed_mutex mMutex; ///< Object mutex //! Actual contents. May be incomplete if mCacheStatus != kLoaded. IFsView::FileList mContents; //! ChangeList for what happens when cache loading is in progress. SetChangeList mChangeList; folly::FutureSplitter mSplitter; //! Timestamp of the last mandatory cache load attempt. This value will be //! used to decide when the cache contents can be dropped. std::atomic mLastCacheLoadTS; eos::common::SteadyClock mClock; //---------------------------------------------------------------------------- //! Trigger cache load. Must only be called once. //---------------------------------------------------------------------------- FileSystemHandler* triggerCacheLoad(); //---------------------------------------------------------------------------- //! Get cache status //---------------------------------------------------------------------------- inline CacheStatus getCacheStatus() const { std::unique_lock lock(mMutex); return mCacheStatus; } }; EOSNSNAMESPACE_END #endif