Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 36 additions & 18 deletions fs/cache/full_file_cache/cache_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,31 @@ int FileCachePool::stat(CacheStat* stat, std::string_view pathname) {
}

int FileCachePool::evict(std::string_view filename) {
errno = ENOSYS;
return -1;
auto fileIter = fileIndex_.find(filename);
if (fileIter == fileIndex_.end()) {
LOG_ERROR("Evict no such file , name: `", filename);
return 0;
}

const auto& filePath = fileIter->first;
auto lruEntry = fileIter->second.get();
if (lruEntry->openCount == 0) {
lru_.mark_key_cleared(lruEntry->lruIter);
}
int err = 0;
{
photon::scoped_rwlock rl(lruEntry->rw_lock_, photon::WLOCK);
err = mediaFs_->truncate(filePath.data(), 0);
lruEntry->truncate_done = false;
}
if (err) {
ERRNO e;
LOG_ERROR("truncate(0) failed, name: `, ret: `, error code: `", filePath,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simply

LOG_ERRNO_RETURN("truncate(0) failed, name: ", filePath);

(note its ERRNO, not ERROR)
and it will output errno and error message as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just output the error log here, no need to return.

err, e);
// If truncate fails, we can attempt to remove the file directly
// in the afterFtrucate function.
}
return afterFtrucate(fileIter) ? 0 : -1;
}

int FileCachePool::evict(size_t size) {
Expand Down Expand Up @@ -241,13 +264,6 @@ void FileCachePool::eviction() {
if (err) {
ERRNO e;
LOG_ERROR("truncate(0) failed, name : `, ret : `, error code : `", fileName, err, e);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG_ERRNO_RETURN

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to return.

// truncate to 0 failed means unable to free the file, it should not consider as a part
// of cache. Deal as it already release.
// The only exception is errno EINTR, means truncate interrupted by signal, should try
// again
if (e.no == EINTR) {
continue;
}
}
afterFtrucate(fileIter);
actualEvict -= fileSize;
Expand All @@ -269,16 +285,18 @@ bool FileCachePool::afterFtrucate(FileNameMap::iterator iter) {
}
if (0 == iter->second->openCount) {
auto err = mediaFs_->unlink(iter->first.data());
ERRNO e;
LOG_ERROR("unlink failed, name : `, ret : `, error code : `", iter->first, err, e);
// unlik failed may caused by multiple reasons
// only EBUSY should may be able to trying to unlink again
// other reason should never try to clean it.
if (err && (e.no == EBUSY)) {
return false;
if (err) {
ERRNO e;
LOG_ERROR("unlink failed, name : `, ret : `, error code : `", iter->first, err, e);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG_ERRNO_RETURN

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to return.

// unlink failed may caused by multiple reasons
// only EBUSY should may be able to trying to unlink again
// other reason should never try to clean it.
if (err && (e.no == EBUSY)) {
return false;
}
lru_.remove(iter->second->lruIter);
fileIndex_.erase(iter);
}
lru_.remove(iter->second->lruIter);
fileIndex_.erase(iter);
}
return true;
}
Expand Down
142 changes: 142 additions & 0 deletions fs/cache/test/cache_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ namespace fs {

// Cleanup and recreate the test dir
inline void SetupTestDir(const std::string& dir) {
if (::access(dir.c_str(), F_OK) != 0) {
::mkdir(dir.c_str(), 0755);
}
std::string cmd = std::string("rm -r ") + dir;
EXPECT_NE(-1, system(cmd.c_str()));
cmd = std::string("mkdir -p ") + dir;
Expand Down Expand Up @@ -534,6 +537,145 @@ TEST(CachedFS, fn_trans_func) {
auto cs2 = cachedFile2->get_store();
EXPECT_EQ(cs1, cs2);
}

TEST(CachePool, evict_file) {
std::string root = "/tmp/ease/cache/evict_file_test/";
SetupTestDir(root);
auto mediaFs = new_localfs_adaptor(root.c_str(), ioengine_libaio);
auto alignFs = new_aligned_fs_adaptor(mediaFs, 4 * 1024, true, true);
auto cacheAllocator = new AlignedAlloc(4 * 1024);
auto roCachedFs = new_full_file_cached_fs(nullptr, alignFs, 1024 * 1024,
1, 1000 * 1000 * 1, 128ul * 1024 * 1024, cacheAllocator, 0);
auto cachePool = roCachedFs->get_pool();
DEFER({ delete cacheAllocator; delete roCachedFs; });

auto fileName = "/file_to_evict";
auto cacheStore = cachePool->open(fileName, O_CREAT | O_RDWR, 0644);
ASSERT_NE(nullptr, cacheStore);

const size_t bufSize = 1024 * 1024;
IOVector buffer(*cacheAllocator);
buffer.push_back(bufSize);

auto ret = cacheStore->do_pwritev2(buffer.iovec(), buffer.iovcnt(), 0, 0);
EXPECT_EQ(ret, (ssize_t)bufSize);

ret = cacheStore->do_preadv2(buffer.iovec(), buffer.iovcnt(), 0, 0);
EXPECT_EQ(ret, (ssize_t)bufSize);

struct stat stBefore;
std::string fullPath = root + fileName;
EXPECT_EQ(0, ::stat(fullPath.c_str(), &stBefore));
EXPECT_EQ(stBefore.st_size, (off_t)bufSize);

EXPECT_EQ(0, cachePool->evict(fileName));

ret = cacheStore->do_preadv2(buffer.iovec(), buffer.iovcnt(), 0, 0);
EXPECT_EQ(ret, 0);

struct stat stAfter;
EXPECT_EQ(0, ::stat(fullPath.c_str(), &stAfter));
EXPECT_EQ(stAfter.st_size, 0);

cacheStore->release();

cacheStore = cachePool->open(fileName, O_CREAT | O_RDWR, 0644);
ASSERT_NE(nullptr, cacheStore);

auto tres = cacheStore->try_preadv2(buffer.iovec(), buffer.iovcnt(), bufSize, 0);
EXPECT_EQ(tres.refill_offset, (ssize_t)bufSize);
EXPECT_EQ(tres.refill_size, bufSize);

ret = cacheStore->do_pwritev2(buffer.iovec(), buffer.iovcnt(), 0, 0);
EXPECT_EQ(ret, (ssize_t)bufSize);

ret = cacheStore->do_preadv2(buffer.iovec(), buffer.iovcnt(), 0, 0);
EXPECT_EQ(ret, (ssize_t)bufSize);

cacheStore->release();
}

TEST(CachePool, random_evict_file) {
std::string root = "/tmp/ease/cache/random_evict_file_test/";
SetupTestDir(root);
auto mediaFs = new_localfs_adaptor(root.c_str(), ioengine_libaio);
auto alignFs = new_aligned_fs_adaptor(mediaFs, 4 * 1024, true, true);
auto cacheAllocator = new AlignedAlloc(4 * 1024);
auto roCachedFs = new_full_file_cached_fs(nullptr, alignFs, 1024 * 1024,
1, 1000 * 1000 * 1, 128ul * 1024 * 1024, cacheAllocator, 0);
auto cachePool = roCachedFs->get_pool();
DEFER({ delete cacheAllocator; delete roCachedFs; });

const size_t bufSize = 1024 * 1024;
IOVector buffer(*cacheAllocator);
buffer.push_back(bufSize);
auto fileName = "/huge_file";

auto cacheStore = cachePool->open(fileName, O_CREAT | O_RDWR, 0644);
cacheStore->set_actual_size(200 * bufSize);

off_t last_evict_offset = 0;
off_t written = 0;
auto random_release_evict_open = [&]() {
if (rand() % 5 == 0) {
cacheStore->release();
cacheStore = nullptr;
}
if (rand() % 3 == 0) {
cachePool->evict(fileName);
last_evict_offset = written;
}
if (rand() % 5 == 0 && cacheStore) {
cacheStore->release();
cacheStore = nullptr;
}
if (cacheStore == nullptr) {
cacheStore = cachePool->open(fileName, O_CREAT | O_RDWR, 0644);
}
};

for (int i = 0; i < 100; i++) {
random_release_evict_open();
auto ret = cacheStore->do_pwritev2(buffer.iovec(), buffer.iovcnt(), written, 0);
EXPECT_EQ(ret, (ssize_t)bufSize);
written += ret;

if (rand() % 4 == 0) {
// write again
random_release_evict_open();
auto ret = cacheStore->do_pwritev2(buffer.iovec(), buffer.iovcnt(), written, 0);
EXPECT_EQ(ret, (ssize_t)bufSize);
written += ret;
}

for (int j = 0; j < 10; j++) {
random_release_evict_open();
off_t qoffset = (rand() % ((i + 1) * 3)) * bufSize;
size_t qsize = (rand() % 10 + 1) * bufSize;
auto qres = cacheStore->queryRefillRange(qoffset, qsize);
if (qoffset + qsize <= (size_t)last_evict_offset || qoffset >= written ||
(qoffset < last_evict_offset && qoffset + qsize > (size_t)written)) {
EXPECT_EQ(qres.first, qoffset);
EXPECT_EQ(qres.second, qsize);
} else if (qoffset < last_evict_offset && qoffset + qsize <= (size_t)written) {
EXPECT_EQ(qres.first, qoffset);
EXPECT_EQ(qres.second, size_t(last_evict_offset - qoffset));
} else if (qoffset >= last_evict_offset && qoffset + qsize > (size_t)written) {
EXPECT_EQ(qres.first, written);
EXPECT_EQ(qres.second, size_t(qoffset + qsize - written));
} else {
EXPECT_EQ(qres.second, 0UL);
IOVector read_buffer(*cacheAllocator);
read_buffer.push_back(qsize);
ret = cacheStore->do_preadv2(read_buffer.iovec(), read_buffer.iovcnt(), qoffset, 0);
EXPECT_EQ(ret, (ssize_t)qsize);
}
}
}

if (cacheStore) cacheStore->release();
}

}
}
int main(int argc, char** argv) {
Expand Down
Loading