diff --git a/curvefs/src/client/filesystem/defer_sync.cpp b/curvefs/src/client/filesystem/defer_sync.cpp index 2b33e33ba4..3a91709f9d 100644 --- a/curvefs/src/client/filesystem/defer_sync.cpp +++ b/curvefs/src/client/filesystem/defer_sync.cpp @@ -30,14 +30,94 @@ namespace curvefs { namespace client { namespace filesystem { -DeferSync::DeferSync(DeferSyncOption option) - : option_(option), +using ::curve::common::LockGuard; +using ::curve::common::ReadLockGuard; +using ::curve::common::WriteLockGuard; +using ::curvefs::client::filesystem::AttrCtime; + +#define RETURN_FALSE_IF_CTO_ON() \ + do { \ + if (cto_) { \ + return false; \ + } \ + } while (0) + +DeferInodes::DeferInodes(bool cto) + : cto_(cto), + rwlock_(), + inodes_() {} + +bool DeferInodes::Add(const std::shared_ptr& inode) { + RETURN_FALSE_IF_CTO_ON(); + WriteLockGuard lk(rwlock_); + Ino ino = inode->GetInodeId(); + auto ret = inodes_.emplace(ino, inode); + auto iter = ret.first; + bool yes = ret.second; + if (!yes) { // already exists + iter->second = inode; + } + return true; +} + +bool DeferInodes::Get(Ino ino, std::shared_ptr* inode) { + RETURN_FALSE_IF_CTO_ON(); + ReadLockGuard lk(rwlock_); + auto iter = inodes_.find(ino); + if (iter == inodes_.end()) { + return false; + } + *inode = iter->second; + return true; +} + +bool DeferInodes::Remove(const std::shared_ptr& inode) { + RETURN_FALSE_IF_CTO_ON(); + WriteLockGuard lk(rwlock_); + InodeAttr attr; + inode->GetInodeAttrLocked(&attr); + auto iter = inodes_.find(attr.inodeid()); + if (iter == inodes_.end()) { + return false; + } + + InodeAttr defered; + iter->second->GetInodeAttrLocked(&defered); + if (AttrCtime(attr) < AttrCtime(defered)) { + // it means the old defered inode already replaced by the lastest one, + // so we can't remove it before it synced yet. + return false; + } + inodes_.erase(iter); + return true; +} + +size_t DeferInodes::Size() { + ReadLockGuard lk(rwlock_); + return inodes_.size(); +} + +SyncInodeClosure::SyncInodeClosure(const std::shared_ptr& inodes, + const std::shared_ptr& inode) + : inodes_(inodes), inode_(inode) {} + +void SyncInodeClosure::Run() { + std::unique_ptr self_guard(this); + MetaStatusCode rc = GetStatusCode(); + if (rc == MetaStatusCode::OK || rc == MetaStatusCode::NOT_FOUND) { + inodes_->Remove(inode_); + } +} + +DeferSync::DeferSync(bool cto, DeferSyncOption option) + : cto_(cto), + option_(option), mutex_(), running_(false), thread_(), sleeper_(), - inodes_() { -} + pending_(), + inodes_(std::make_shared(cto)) {} void DeferSync::Start() { if (!running_.exchange(true)) { @@ -55,20 +135,32 @@ void DeferSync::Stop() { } } +SyncInodeClosure* DeferSync::NewSyncInodeClosure( + const std::shared_ptr& inode) { + // NOTE: we only store the defer inodes in nocto scenario, + // which means we don't need to remove the inode from defer inodes + // even if the inode already synced done in cto scenario. + if (cto_) { + return nullptr; + } + return new SyncInodeClosure(inodes_, inode); +} + void DeferSync::SyncTask() { - std::vector> inodes; + std::vector> syncing; for ( ;; ) { bool running = sleeper_.wait_for(std::chrono::seconds(option_.delay)); { LockGuard lk(mutex_); - inodes.swap(inodes_); + syncing.swap(pending_); } - for (const auto& inode : inodes) { + for (const auto& inode : syncing) { + auto closure = NewSyncInodeClosure(inode); UniqueLock lk(inode->GetUniqueLock()); - inode->Async(nullptr, true); + inode->Async(closure, true); } - inodes.clear(); + syncing.clear(); if (!running) { break; @@ -78,18 +170,12 @@ void DeferSync::SyncTask() { void DeferSync::Push(const std::shared_ptr& inode) { LockGuard lk(mutex_); - inodes_.emplace_back(inode); + pending_.emplace_back(inode); + inodes_->Add(inode); } -bool DeferSync::IsDefered(Ino ino, InodeAttr* attr) { - LockGuard lk(mutex_); - for (const auto& inode : inodes_) { - if (inode->GetInodeId() == ino) { - inode->GetInodeAttr(attr); - return true; - } - } - return false; +bool DeferSync::IsDefered(Ino ino, std::shared_ptr* inode) { + return inodes_->Get(ino, inode); } } // namespace filesystem diff --git a/curvefs/src/client/filesystem/defer_sync.h b/curvefs/src/client/filesystem/defer_sync.h index 41264a7e00..17472ce11e 100644 --- a/curvefs/src/client/filesystem/defer_sync.h +++ b/curvefs/src/client/filesystem/defer_sync.h @@ -27,23 +27,56 @@ #include #include +#include "absl/container/btree_map.h" #include "src/common/interruptible_sleeper.h" #include "curvefs/src/client/common/config.h" +#include "curvefs/src/client/rpcclient/task_excutor.h" #include "curvefs/src/client/filesystem/meta.h" namespace curvefs { namespace client { namespace filesystem { -using ::curvefs::client::common::DeferSyncOption; - +using ::curve::common::RWLock; using ::curve::common::Mutex; -using ::curve::common::LockGuard; using ::curve::common::InterruptibleSleeper; +using ::curvefs::client::common::DeferSyncOption; +using ::curvefs::client::rpcclient::MetaServerClientDone; + +// NOTE: we only store the defer inodes in nocto scenario. +class DeferInodes { + public: + explicit DeferInodes(bool cto); + + bool Add(const std::shared_ptr& inode); + + bool Get(Ino ino, std::shared_ptr* inode); + + bool Remove(const std::shared_ptr& inode); + + size_t Size(); + + private: + bool cto_; + RWLock rwlock_; + absl::btree_map> inodes_; +}; + +class SyncInodeClosure : public MetaServerClientDone { + public: + explicit SyncInodeClosure(const std::shared_ptr& inodes, + const std::shared_ptr& inode); + + void Run() override; + + private: + std::shared_ptr inodes_; + std::shared_ptr inode_; +}; class DeferSync { public: - explicit DeferSync(DeferSyncOption option); + explicit DeferSync(bool cto, DeferSyncOption option); void Start(); @@ -51,18 +84,26 @@ class DeferSync { void Push(const std::shared_ptr& inode); - bool IsDefered(Ino ino, InodeAttr* attr); + bool IsDefered(Ino ino, std::shared_ptr* inode); private: + SyncInodeClosure* NewSyncInodeClosure( + const std::shared_ptr& inode); + void SyncTask(); private: + friend class SyncInodeClosure; + + private: + bool cto_; DeferSyncOption option_; Mutex mutex_; std::atomic running_; std::thread thread_; InterruptibleSleeper sleeper_; - std::vector> inodes_; + std::vector> pending_; + std::shared_ptr inodes_; }; } // namespace filesystem diff --git a/curvefs/src/client/filesystem/filesystem.cpp b/curvefs/src/client/filesystem/filesystem.cpp index 2aad563051..4546ade2af 100644 --- a/curvefs/src/client/filesystem/filesystem.cpp +++ b/curvefs/src/client/filesystem/filesystem.cpp @@ -31,7 +31,8 @@ namespace filesystem { FileSystem::FileSystem(FileSystemOption option, ExternalMember member) : option_(option), member(member) { - deferSync_ = std::make_shared(option.deferSyncOption); + deferSync_ = std::make_shared(option.cto, + option.deferSyncOption); negative_ = std::make_shared(option.lookupCacheOption); dirCache_ = std::make_shared(option.dirCacheOption); openFiles_ = std::make_shared(option_.openFilesOption, @@ -257,11 +258,6 @@ CURVEFS_ERROR FileSystem::Lookup(Ino parent, CURVEFS_ERROR FileSystem::GetAttr(Ino ino, AttrOut* attrOut) { InodeAttr attr; - if (!option_.cto && deferSync_->IsDefered(ino, &attr)) { - *attrOut = AttrOut(attr); - return CURVEFS_ERROR::OK; - } - auto rc = rpc_->GetAttr(ino, &attr); if (rc == CURVEFS_ERROR::OK) { *attrOut = AttrOut(attr); @@ -319,7 +315,7 @@ CURVEFS_ERROR FileSystem::Open(Ino ino, FileInfo* fi) { bool yes = openFiles_->IsOpened(ino, &inode); if (yes) { openFiles_->Open(ino, inode); - // fi->keep_cache = 1; + // fi->keep_cache = 1; // FIXME(Wine93): let it works. return CURVEFS_ERROR::OK; } diff --git a/curvefs/src/client/inode_cache_manager.cpp b/curvefs/src/client/inode_cache_manager.cpp index e8601c1526..b30dd4ee16 100644 --- a/curvefs/src/client/inode_cache_manager.cpp +++ b/curvefs/src/client/inode_cache_manager.cpp @@ -30,6 +30,8 @@ #include #include "curvefs/proto/metaserver.pb.h" #include "curvefs/src/client/filesystem/error.h" +#include "curvefs/src/client/filesystem/utils.h" +#include "curvefs/src/client/filesystem/defer_sync.h" #include "curvefs/src/client/inode_wrapper.h" using ::curvefs::metaserver::Inode; @@ -47,10 +49,73 @@ namespace curvefs { namespace client { using ::curvefs::client::filesystem::ToFSError; +using ::curvefs::client::filesystem::AttrCtime; using NameLockGuard = ::curve::common::GenericNameLockGuard; using curvefs::client::common::FLAGS_enableCto; +#define RETURN_IF_CTO_ON() \ + do { \ + if (cto_) { \ + return; \ + } \ + } while (0) + +DeferWatcher::DeferWatcher(bool cto, std::shared_ptr deferSync) + : cto_(cto), + deferSync_(deferSync), + deferAttrs_() {} + +void DeferWatcher::PreGetAttrs(const std::set& inos) { + RETURN_IF_CTO_ON(); + InodeAttr attr; + std::shared_ptr inode; + for (const auto& ino : inos) { + bool yes = deferSync_->IsDefered(ino, &inode); + if (!yes) { + continue; + } + inode->GetInodeAttr(&attr); + deferAttrs_.emplace(ino, attr); + } +} + +bool DeferWatcher::TryUpdate(InodeAttr* attr) { + Ino ino = attr->inodeid(); + auto iter = deferAttrs_.find(ino); + if (iter == deferAttrs_.end()) { + return false; + } + + auto& defered = iter->second; + if (AttrCtime(*attr) > AttrCtime(defered)) { + return false; + } + *attr = defered; + return true; +} + +void DeferWatcher::PostGetAttrs(std::list* attrs) { + RETURN_IF_CTO_ON(); + if (deferAttrs_.size() == 0) { + return; + } + for (auto& attr : *attrs) { + TryUpdate(&attr); + } +} + +void DeferWatcher::PostGetAttrs(std::map* attrs) { + RETURN_IF_CTO_ON(); + if (deferAttrs_.size() == 0) { + return; + } + for (auto& item : *attrs) { + auto& attr = item.second; + TryUpdate(&attr); + } +} + #define GET_INODE_REMOTE(FSID, INODEID, OUT, STREAMING) \ MetaStatusCode ret = metaClient_->GetInode(FSID, INODEID, OUT, STREAMING); \ if (ret != MetaStatusCode::OK) { \ @@ -76,6 +141,11 @@ InodeCacheManagerImpl::GetInode(uint64_t inodeId, return CURVEFS_ERROR::OK; } + bool cto = FLAGS_enableCto; + if (!cto && deferSync_->IsDefered(inodeId, &out)) { + return CURVEFS_ERROR::OK; + } + // get inode from metaserver Inode inode; bool streaming = false; @@ -97,6 +167,11 @@ CURVEFS_ERROR InodeCacheManagerImpl::GetInodeAttr(uint64_t inodeId, std::set inodeIds; std::list attrs; inodeIds.emplace(inodeId); + + bool cto = FLAGS_enableCto; + auto watcher = std::make_shared(cto, deferSync_); + watcher->PreGetAttrs(inodeIds); + MetaStatusCode ret = metaClient_->BatchGetInodeAttr(fsId_, inodeIds, &attrs); if (MetaStatusCode::OK != ret) { @@ -113,6 +188,7 @@ CURVEFS_ERROR InodeCacheManagerImpl::GetInodeAttr(uint64_t inodeId, return CURVEFS_ERROR::INTERNAL; } + watcher->PostGetAttrs(&attrs); *out = *attrs.begin(); return CURVEFS_ERROR::OK; } @@ -124,6 +200,10 @@ CURVEFS_ERROR InodeCacheManagerImpl::BatchGetInodeAttr( return CURVEFS_ERROR::OK; } + bool cto = FLAGS_enableCto; + auto watcher = std::make_shared(cto, deferSync_); + watcher->PreGetAttrs(*inodeIds); + MetaStatusCode ret = metaClient_->BatchGetInodeAttr(fsId_, *inodeIds, attrs); if (MetaStatusCode::OK != ret) { @@ -131,6 +211,7 @@ CURVEFS_ERROR InodeCacheManagerImpl::BatchGetInodeAttr( << ret << ", MetaStatusCode_Name = " << MetaStatusCode_Name(ret); } + watcher->PostGetAttrs(attrs); return ToFSError(ret); } @@ -144,6 +225,10 @@ CURVEFS_ERROR InodeCacheManagerImpl::BatchGetInodeAttrAsync( return CURVEFS_ERROR::OK; } + bool cto = FLAGS_enableCto; + auto watcher = std::make_shared(cto, deferSync_); + watcher->PreGetAttrs(*inodeIds); + // split inodeIds by partitionId and batch limit std::vector> inodeGroups; if (!metaClient_->SplitRequestInodes(fsId_, *inodeIds, &inodeGroups)) { @@ -168,6 +253,7 @@ CURVEFS_ERROR InodeCacheManagerImpl::BatchGetInodeAttrAsync( // wait for all sudrequest finished cond->Wait(); + watcher->PostGetAttrs(attrs); return CURVEFS_ERROR::OK; } diff --git a/curvefs/src/client/inode_cache_manager.h b/curvefs/src/client/inode_cache_manager.h index ce0ebb3d17..49c642c0e3 100644 --- a/curvefs/src/client/inode_cache_manager.h +++ b/curvefs/src/client/inode_cache_manager.h @@ -46,6 +46,7 @@ #include "curvefs/src/client/common/config.h" #include "curvefs/src/client/filesystem/openfile.h" #include "curvefs/src/client/filesystem/defer_sync.h" +#include "absl/container/btree_map.h" using ::curve::common::LRUCache; using ::curve::common::CacheMetrics; @@ -66,6 +67,7 @@ using rpcclient::BatchGetInodeAttrDone; using curve::common::CountDownEvent; using metric::S3ChunkInfoMetric; using common::RefreshDataOption; +using ::curvefs::client::filesystem::Ino; using ::curvefs::client::filesystem::OpenFiles; using ::curvefs::client::filesystem::DeferSync; @@ -116,6 +118,25 @@ class InodeCacheManager { uint32_t fsId_; }; +class DeferWatcher { + public: + DeferWatcher(bool cto, std::shared_ptr deferSync); + + void PreGetAttrs(const std::set& inos); + + void PostGetAttrs(std::list* attrs); + + void PostGetAttrs(std::map* attrs); + + private: + bool TryUpdate(InodeAttr* attr); + + private: + bool cto_; + std::shared_ptr deferSync_; + absl::btree_map deferAttrs_; +}; + class InodeCacheManagerImpl : public InodeCacheManager, public std::enable_shared_from_this { public: diff --git a/curvefs/test/client/BUILD b/curvefs/test/client/BUILD index 65bfb51d50..84852b8903 100644 --- a/curvefs/test/client/BUILD +++ b/curvefs/test/client/BUILD @@ -110,6 +110,7 @@ cc_test( "//curvefs/proto:mds_cc_proto", "//curvefs/proto:space_cc_proto", "//curvefs/test/volume/mock", + "//curvefs/test/client/filesystem/helper:helper", ], linkopts = ["-lfuse3", "-L/usr/local/lib/x86_64-linux-gnu"], diff --git a/curvefs/test/client/filesystem/defer_sync_test.cpp b/curvefs/test/client/filesystem/defer_sync_test.cpp index 0aabdac1df..fbd81fc470 100644 --- a/curvefs/test/client/filesystem/defer_sync_test.cpp +++ b/curvefs/test/client/filesystem/defer_sync_test.cpp @@ -27,6 +27,8 @@ namespace curvefs { namespace client { namespace filesystem { +class DeferInodesTest : public ::testing::Test {}; + class DeferSyncTest : public ::testing::Test { protected: void SetUp() override { @@ -41,9 +43,113 @@ class DeferSyncTest : public ::testing::Test { std::shared_ptr metaClient_; }; +TEST_F(DeferInodesTest, cto) { + // CASE 1: cto + bool cto = true; + auto deferInodes = std::make_shared(cto); + bool yes = deferInodes->Add(MkInode(100)); + ASSERT_FALSE(yes); + ASSERT_EQ(deferInodes->Size(), 0); + + // CASE 2: nocto + cto = false; + deferInodes = std::make_shared(cto); + yes = deferInodes->Add(MkInode(100)); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 1); +} + +TEST_F(DeferInodesTest, Add) { + auto deferInodes = std::make_shared(false); + + // CASE 1: add inode(100) success + bool yes = deferInodes->Add(MkInode(100, InodeOption().length(1024))); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 1); + + std::shared_ptr inode; + yes = deferInodes->Get(100, &inode); + ASSERT_TRUE(yes); + ASSERT_EQ(inode->GetLength(), 1024); + + // CASE 2: add inode(200) success + yes = deferInodes->Add(MkInode(200, InodeOption().length(2048))); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 2); + + yes = deferInodes->Get(200, &inode); + ASSERT_TRUE(yes); + ASSERT_EQ(inode->GetLength(), 2048); + + // CASE 3: add inode(200) which alreay exists + yes = deferInodes->Add(MkInode(200, InodeOption().length(2049))); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 2); + + yes = deferInodes->Get(200, &inode); + ASSERT_TRUE(yes); + ASSERT_EQ(inode->GetLength(), 2049); +} + +TEST_F(DeferInodesTest, Get) { + auto deferInodes = std::make_shared(false); + bool yes = deferInodes->Add(MkInode(100, InodeOption().length(1024))); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 1); + + // CASE 1: get exist inode + std::shared_ptr inode; + yes = deferInodes->Get(100, &inode); + ASSERT_TRUE(yes); + ASSERT_EQ(inode->GetLength(), 1024); + + // CASE 2: get non-exist inode + yes = deferInodes->Get(200, &inode); + ASSERT_FALSE(yes); +} + +TEST_F(DeferInodesTest, Remove_Basic) { + auto deferInodes = std::make_shared(false); + auto inode = MkInode(100, InodeOption().length(1024)); + bool yes = deferInodes->Add(inode); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 1); + + // CASE 1: remove exist inode + yes = deferInodes->Remove(inode); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 0); + + yes = deferInodes->Get(100, &inode); + ASSERT_FALSE(yes); + + // CASE 2: remove non-exist inode + yes = deferInodes->Remove(inode); + ASSERT_FALSE(yes); +} + +TEST_F(DeferInodesTest, Remove_CompareCtime) { + auto deferInodes = std::make_shared(false); + auto deferInode = MkInode(100, InodeOption().length(1024).ctime(123, 456)); + bool yes = deferInodes->Add(deferInode); + ASSERT_EQ(deferInodes->Size(), 1); + + // CASE 1: attr ctime < defered ctime => remove fail + auto inode = MkInode(100, InodeOption().length(1024).ctime(123, 455)); + yes = deferInodes->Remove(inode); + ASSERT_FALSE(yes); + ASSERT_EQ(deferInodes->Size(), 1); + + // CASE 2: attr ctime > defered ctime => remove fail + inode = MkInode(100, InodeOption().length(1024).ctime(123, 457)); + yes = deferInodes->Remove(inode); + ASSERT_TRUE(yes); + ASSERT_EQ(deferInodes->Size(), 0); +} + TEST_F(DeferSyncTest, Basic) { auto builder = DeferSyncBuilder(); - auto deferSync = builder.SetOption([&](DeferSyncOption* option){ + auto deferSync = builder.SetOption([&](bool* cto, DeferSyncOption* option) { option->delay = 3; }).Build(); deferSync->Start(); @@ -57,7 +163,7 @@ TEST_F(DeferSyncTest, Basic) { TEST_F(DeferSyncTest, Dirty) { auto builder = DeferSyncBuilder(); - auto deferSync = builder.SetOption([&](DeferSyncOption* option){ + auto deferSync = builder.SetOption([&](bool* cto, DeferSyncOption* option) { option->delay = 3; }).Build(); deferSync->Start(); @@ -68,6 +174,41 @@ TEST_F(DeferSyncTest, Dirty) { deferSync->Stop(); } +TEST_F(DeferSyncTest, IsDefered_cto) { + auto builder = DeferSyncBuilder(); + auto deferSync = builder.SetOption([&](bool* cto, DeferSyncOption* option) { + *cto = true; + option->delay = 3; + }).Build(); + deferSync->Start(); + + std::shared_ptr inode; + deferSync->Push(MkInode(100, InodeOption())); + bool yes = deferSync->IsDefered(100, &inode); + ASSERT_FALSE(yes); + deferSync->Stop(); +} + +TEST_F(DeferSyncTest, IsDefered_nocto) { + auto builder = DeferSyncBuilder(); + auto deferSync = builder.SetOption([&](bool* cto, DeferSyncOption* option) { + *cto = false; + option->delay = 3; + }).Build(); + deferSync->Start(); + + std::shared_ptr inode; + deferSync->Push(MkInode(100, InodeOption())); + bool yes = deferSync->IsDefered(100, &inode); + ASSERT_TRUE(yes); + + // wait inode synced and defer inode removed + std::this_thread::sleep_for(std::chrono::seconds(4)); + yes = deferSync->IsDefered(100, &inode); + ASSERT_FALSE(yes); + deferSync->Stop(); +} + } // namespace filesystem } // namespace client } // namespace curvefs diff --git a/curvefs/test/client/filesystem/helper/builder.h b/curvefs/test/client/filesystem/helper/builder.h index feece16aa0..2370dad311 100644 --- a/curvefs/test/client/filesystem/helper/builder.h +++ b/curvefs/test/client/filesystem/helper/builder.h @@ -46,7 +46,7 @@ using ::curvefs::client::common::KernelCacheOption; class DeferSyncBuilder { public: - using Callback = std::function; + using Callback = std::function; static DeferSyncOption DefaultOption() { return DeferSyncOption { @@ -57,17 +57,18 @@ class DeferSyncBuilder { public: DeferSyncBuilder() - : option_(DefaultOption()), + : cto_(true), + option_(DefaultOption()), dentryManager_(std::make_shared()), inodeManager_(std::make_shared()) {} DeferSyncBuilder SetOption(Callback callback) { - callback(&option_); + callback(&cto_, &option_); return *this; } std::shared_ptr Build() { - return std::make_shared(option_); + return std::make_shared(cto_, option_); } std::shared_ptr GetDentryManager() { @@ -79,6 +80,7 @@ class DeferSyncBuilder { } private: + bool cto_; DeferSyncOption option_; std::shared_ptr dentryManager_; std::shared_ptr inodeManager_; diff --git a/curvefs/test/client/filesystem/helper/meta.cpp b/curvefs/test/client/filesystem/helper/meta.cpp index 49d9540d61..3cab5b6720 100644 --- a/curvefs/test/client/filesystem/helper/meta.cpp +++ b/curvefs/test/client/filesystem/helper/meta.cpp @@ -76,11 +76,21 @@ AttrOption AttrOption::ctime(uint64_t seconds, uint32_t naoSeconds) { return *this; } +InodeOption InodeOption::ctime(uint64_t seconds, uint32_t naoSeconds) { + ctime_ = TimeSpec(seconds, naoSeconds); + return *this; +} + InodeOption InodeOption::mtime(uint64_t seconds, uint32_t naoSeconds) { mtime_ = TimeSpec(seconds, naoSeconds); return *this; } +InodeOption InodeOption::length(uint64_t length) { + length_ = length; + return *this; +} + InodeOption InodeOption::metaClient( std::shared_ptr metaClient) { metaClient_ = metaClient; @@ -109,8 +119,11 @@ InodeAttr MkAttr(Ino ino, AttrOption option) { std::shared_ptr MkInode(Ino ino, InodeOption option) { Inode inode; inode.set_inodeid(ino); + inode.set_ctime(option.ctime_.seconds); + inode.set_ctime_ns(option.ctime_.nanoSeconds); inode.set_mtime(option.mtime_.seconds); inode.set_mtime_ns(option.mtime_.nanoSeconds); + inode.set_length(option.length_); return std::make_shared(inode, option.metaClient_); } diff --git a/curvefs/test/client/filesystem/helper/meta.h b/curvefs/test/client/filesystem/helper/meta.h index ffa5899a67..a63b7297fc 100644 --- a/curvefs/test/client/filesystem/helper/meta.h +++ b/curvefs/test/client/filesystem/helper/meta.h @@ -71,14 +71,17 @@ struct AttrOption { class InodeOption { public: InodeOption() = default; + InodeOption ctime(uint64_t seconds, uint32_t naoSeconds); InodeOption mtime(uint64_t seconds, uint32_t naoSeconds); + InodeOption length(uint64_t length); InodeOption metaClient(std::shared_ptr metaClient); private: friend std::shared_ptr MkInode(Ino ino, InodeOption option); private: - TimeSpec mtime_; + TimeSpec ctime_, mtime_; + uint64_t length_; std::shared_ptr metaClient_; }; diff --git a/curvefs/test/client/test_inode_cache_manager.cpp b/curvefs/test/client/test_inode_cache_manager.cpp index b004acd62f..6617d3c390 100644 --- a/curvefs/test/client/test_inode_cache_manager.cpp +++ b/curvefs/test/client/test_inode_cache_manager.cpp @@ -35,6 +35,7 @@ #include "curvefs/src/client/filesystem/defer_sync.h" #include "curvefs/src/client/filesystem/openfile.h" #include "curvefs/src/client/filesystem/dir_cache.h" +#include "curvefs/test/client/filesystem/helper/helper.h" namespace curvefs { namespace client { @@ -66,6 +67,13 @@ using ::curvefs::client::common::OpenFilesOption; using ::curvefs::client::filesystem::DeferSync; using ::curvefs::client::filesystem::DirCache; using ::curvefs::client::filesystem::OpenFiles; +using ::curvefs::client::filesystem::DeferSyncBuilder; +using ::curvefs::client::filesystem::MkInode; +using ::curvefs::client::filesystem::MkAttr; +using ::curvefs::client::filesystem::InodeOption; +using ::curvefs::client::filesystem::AttrOption; + +class DeferWatcherTest : public ::testing::Test {}; class TestInodeCacheManager : public ::testing::Test { protected: @@ -80,7 +88,7 @@ class TestInodeCacheManager : public ::testing::Test { RefreshDataOption option; option.maxDataSize = 1; option.refreshDataIntervalSec = 0; - auto deferSync = std::make_shared(DeferSyncOption()); + auto deferSync = std::make_shared(true, DeferSyncOption()); auto openFiles = std::make_shared( OpenFilesOption(), deferSync); iCacheManager_->Init(option, openFiles, deferSync); @@ -98,6 +106,87 @@ class TestInodeCacheManager : public ::testing::Test { uint32_t timeout_ = 3; }; +TEST_F(DeferWatcherTest, Basic_cto) { + auto builder = DeferSyncBuilder(); + auto deferSync = builder.SetOption([&](bool* cto, DeferSyncOption* option) { + *cto = true; + option->delay = 3; + }).Build(); + deferSync->Start(); + deferSync->Push(MkInode(100, InodeOption().length(1024).ctime(123, 456))); + + auto watcher = std::make_shared(false, deferSync); + std::set inos { 100 }; + watcher->PreGetAttrs(inos); + + InodeAttr attr = MkAttr(100, AttrOption().length(0).ctime(123, 455)); + std::list attrs; + attrs.emplace_back(attr); // mock get attr from remote + watcher->PostGetAttrs(&attrs); + + InodeAttr out = attrs.front(); + ASSERT_EQ(out.length(), 0); + ASSERT_EQ(out.ctime(), 123); + ASSERT_EQ(out.ctime_ns(), 455); + + deferSync->Stop(); +} + +TEST_F(DeferWatcherTest, Basic) { + auto builder = DeferSyncBuilder(); + auto deferSync = builder.SetOption([&](bool* cto, DeferSyncOption* option) { + *cto = false; + option->delay = 3; + }).Build(); + deferSync->Start(); + deferSync->Push(MkInode(100, InodeOption().length(1024).ctime(123, 456))); + + auto watcher = std::make_shared(false, deferSync); + std::set inos { 100 }; + watcher->PreGetAttrs(inos); + + // CASE 1: attr ctime < defered ctime => update success + { + InodeAttr attr = MkAttr(100, AttrOption().length(0).ctime(123, 455)); + std::list attrs; + attrs.emplace_back(attr); // mock get attr from remote + watcher->PostGetAttrs(&attrs); + + InodeAttr out = attrs.front(); + ASSERT_EQ(out.length(), 1024); + ASSERT_EQ(out.ctime(), 123); + ASSERT_EQ(out.ctime_ns(), 456); + } + + // CASE 2: attr ctime > defered ctime => update failed + { + InodeAttr attr = MkAttr(100, AttrOption().length(0).ctime(123, 457)); + std::list attrs; + attrs.emplace_back(attr); // mock get attr from remote + watcher->PostGetAttrs(&attrs); + + InodeAttr out = attrs.front(); + ASSERT_EQ(out.length(), 0); + ASSERT_EQ(out.ctime(), 123); + ASSERT_EQ(out.ctime_ns(), 457); + } + + // CASE 3: another interface + { + InodeAttr attr = MkAttr(100, AttrOption().length(0).ctime(123, 455)); + std::map attrs; + attrs.emplace(100, attr); // mock get attr from remote + watcher->PostGetAttrs(&attrs); + + InodeAttr out = attrs[100]; + ASSERT_EQ(out.length(), 1024); + ASSERT_EQ(out.ctime(), 123); + ASSERT_EQ(out.ctime_ns(), 456); + } + + deferSync->Stop(); +} + TEST_F(TestInodeCacheManager, GetInode) { uint64_t inodeId = 100; uint64_t fileLength = 100;