In memory cache for softlinks with cache out

This commit is contained in:
Takeshi Nakatani 2019-11-26 13:42:44 +00:00 committed by Andrew Gaul
parent bdfb9ee815
commit 23945a0130
5 changed files with 284 additions and 53 deletions

View File

@ -156,13 +156,13 @@ time to wait between read/write activity before giving up.
specify the maximum number of keys returned by S3 list object API. The default is 1000. you can set this value to 1000 or more.
.TP
\fB\-o\fR max_stat_cache_size (default="100,000" entries (about 40MB))
maximum number of entries in the stat cache
maximum number of entries in the stat cache and symbolic link cache.
.TP
\fB\-o\fR stat_cache_expire (default is no expire)
specify expire time (seconds) for entries in the stat cache. This expire time indicates the time since stat cached.
specify expire time (seconds) for entries in the stat cache and symbolic link cache. This expire time indicates the time since cached.
.TP
\fB\-o\fR stat_cache_interval_expire (default is no expire)
specify expire time (seconds) for entries in the stat cache. This expire time is based on the time from the last access time of the stat cache.
specify expire time (seconds) for entries in the stat cache and symbolic link cache. This expire time is based on the time from the last access time of those cache.
This option is exclusive with stat_cache_expire, and is left for compatibility with older versions.
.TP
\fB\-o\fR enable_noobj_cache (default is disable)

View File

@ -114,7 +114,7 @@ inline bool IsExpireStatCacheTime(const struct timespec& ts, const time_t& expir
}
//
// For cache out
// For stats cache out
//
typedef std::vector<stat_cache_t::iterator> statiterlist_t;
@ -132,6 +132,25 @@ struct sort_statiterlist{
}
};
//
// For symbolic link cache out
//
typedef std::vector<symlink_cache_t::iterator> symlinkiterlist_t;
struct sort_symlinkiterlist{
// ascending order
bool operator()(const symlink_cache_t::iterator& src1, const symlink_cache_t::iterator& src2) const
{
int result = CompareStatCacheTime(src1->second->cache_date, src2->second->cache_date); // use the same as Stats
if(0 == result){
if(src1->second->hit_count < src2->second->hit_count){
result = -1;
}
}
return (result < 0);
}
};
//-------------------------------------------------------------------
// Static
//-------------------------------------------------------------------
@ -344,7 +363,7 @@ bool StatCache::IsNoObjectCache(const string& key, bool overcheck)
return false;
}
bool StatCache::AddStat(std::string& key, headers_t& meta, bool forcedir, bool no_truncate)
bool StatCache::AddStat(const std::string& key, headers_t& meta, bool forcedir, bool no_truncate)
{
if(!no_truncate && CacheSize< 1){
return true;
@ -408,10 +427,18 @@ bool StatCache::AddStat(std::string& key, headers_t& meta, bool forcedir, bool n
}
stat_cache[key] = ent;
// check symbolic link cache
if(!S_ISLNK(ent->stbuf.st_mode)){
if(symlink_cache.end() != symlink_cache.find(key)){
// if symbolic link cache has key, thus remove it.
DelSymlink(key.c_str(), true);
}
}
return true;
}
bool StatCache::AddNoObjectCache(string& key)
bool StatCache::AddNoObjectCache(const string& key)
{
if(!IsCacheNoObject){
return true; // pretend successful
@ -459,6 +486,12 @@ bool StatCache::AddNoObjectCache(string& key)
}
stat_cache[key] = ent;
// check symbolic link cache
if(symlink_cache.end() != symlink_cache.find(key)){
// if symbolic link cache has key, thus remove it.
DelSymlink(key.c_str(), true);
}
return true;
}
@ -571,6 +604,151 @@ bool StatCache::DelStat(const char* key, bool lock_already_held)
return true;
}
bool StatCache::GetSymlink(const string& key, string& value)
{
bool is_delete_cache = false;
string strpath = key;
AutoLock lock(&StatCache::stat_cache_lock);
symlink_cache_t::iterator iter = symlink_cache.find(strpath);
if(iter != symlink_cache.end() && iter->second){
symlink_cache_entry* ent = iter->second;
if(!IsExpireTime || !IsExpireStatCacheTime(ent->cache_date, ExpireTime)){ // use the same as Stats
// found
S3FS_PRN_DBG("symbolic link cache hit [path=%s][time=%lld.%09ld][hit count=%lu]",
strpath.c_str(), static_cast<long long>(ent->cache_date.tv_sec), ent->cache_date.tv_nsec, ent->hit_count);
value = ent->link;
ent->hit_count++;
if(IsExpireIntervalType){
SetStatCacheTime(ent->cache_date);
}
return true;
}else{
// timeout
is_delete_cache = true;
}
}
if(is_delete_cache){
DelSymlink(strpath.c_str(), /*lock_already_held=*/ true);
}
return false;
}
bool StatCache::AddSymlink(const string& key, const string& value)
{
if(CacheSize< 1){
return true;
}
S3FS_PRN_INFO3("add symbolic link cache entry[path=%s, value=%s]", key.c_str(), value.c_str());
bool found;
bool do_truncate;
{
AutoLock lock(&StatCache::stat_cache_lock);
found = symlink_cache.end() != symlink_cache.find(key);
do_truncate = symlink_cache.size() > CacheSize;
}
if(found){
DelSymlink(key.c_str());
}else{
if(do_truncate){
if(!TruncateSymlink()){
return false;
}
}
}
// make new
symlink_cache_entry* ent = new symlink_cache_entry();
ent->link = value;
ent->hit_count = 0;
SetStatCacheTime(ent->cache_date); // Set time(use the same as Stats).
// add
AutoLock lock(&StatCache::stat_cache_lock);
symlink_cache_t::iterator iter = symlink_cache.find(key); // recheck for same key exists
if(symlink_cache.end() != iter){
delete iter->second;
symlink_cache.erase(iter);
}
symlink_cache[key] = ent;
return true;
}
bool StatCache::TruncateSymlink()
{
AutoLock lock(&StatCache::stat_cache_lock);
if(symlink_cache.empty()){
return true;
}
// 1) erase over expire time
if(IsExpireTime){
for(symlink_cache_t::iterator iter = symlink_cache.begin(); iter != symlink_cache.end(); ){
symlink_cache_entry* entry = iter->second;
if(!entry || IsExpireStatCacheTime(entry->cache_date, ExpireTime)){ // use the same as Stats
delete entry;
symlink_cache.erase(iter++);
}else{
++iter;
}
}
}
// 2) check stat cache count
if(symlink_cache.size() < CacheSize){
return true;
}
// 3) erase from the old cache in order
size_t erase_count= symlink_cache.size() - CacheSize + 1;
symlinkiterlist_t erase_iters;
for(symlink_cache_t::iterator iter = symlink_cache.begin(); iter != symlink_cache.end(); ++iter){
erase_iters.push_back(iter);
sort(erase_iters.begin(), erase_iters.end(), sort_symlinkiterlist());
if(erase_count < erase_iters.size()){
erase_iters.pop_back();
}
}
for(symlinkiterlist_t::iterator iiter = erase_iters.begin(); iiter != erase_iters.end(); ++iiter){
symlink_cache_t::iterator siter = *iiter;
S3FS_PRN_DBG("truncate symbolic link cache[path=%s]", siter->first.c_str());
delete siter->second;
symlink_cache.erase(siter);
}
S3FS_MALLOCTRIM(0);
return true;
}
bool StatCache::DelSymlink(const char* key, bool lock_already_held)
{
if(!key){
return false;
}
S3FS_PRN_INFO3("delete symbolic link cache entry[path=%s]", key);
AutoLock lock(&StatCache::stat_cache_lock, lock_already_held ? AutoLock::ALREADY_LOCKED : AutoLock::NONE);
symlink_cache_t::iterator iter;
if(symlink_cache.end() != (iter = symlink_cache.find(string(key)))){
delete iter->second;
symlink_cache.erase(iter);
}
S3FS_MALLOCTRIM(0);
return true;
}
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------

View File

@ -24,7 +24,7 @@
#include "common.h"
//
// Struct
// Struct for stats cache
//
struct stat_cache_entry {
struct stat stbuf;
@ -45,20 +45,46 @@ struct stat_cache_entry {
typedef std::map<std::string, stat_cache_entry*> stat_cache_t; // key=path
//
// Struct for symbolic link cache
//
struct symlink_cache_entry {
std::string link;
unsigned long hit_count;
struct timespec cache_date; // The function that operates timespec uses the same as Stats
symlink_cache_entry() : link(""), hit_count(0) {
cache_date.tv_sec = 0;
cache_date.tv_nsec = 0;
}
};
typedef std::map<std::string, symlink_cache_entry*> symlink_cache_t;
//
// Class
//
// [NOTE] About Symbolic link cache
// The Stats cache class now also has a symbolic link cache.
// It is possible to take out the Symbolic link cache in another class,
// but the cache out etc. should be synchronized with the Stats cache
// and implemented in this class.
// Symbolic link cache size and timeout use the same settings as Stats
// cache. This simplifies user configuration, and from a user perspective,
// the symbolic link cache appears to be included in the Stats cache.
//
class StatCache
{
private:
static StatCache singleton;
static pthread_mutex_t stat_cache_lock;
stat_cache_t stat_cache;
bool IsExpireTime;
bool IsExpireIntervalType; // if this flag is true, cache data is updated at last access time.
time_t ExpireTime;
unsigned long CacheSize;
bool IsCacheNoObject;
stat_cache_t stat_cache;
bool IsExpireTime;
bool IsExpireIntervalType; // if this flag is true, cache data is updated at last access time.
time_t ExpireTime;
unsigned long CacheSize;
bool IsCacheNoObject;
symlink_cache_t symlink_cache;
private:
StatCache();
@ -68,6 +94,8 @@ class StatCache
bool GetStat(const std::string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag, bool* pisforce);
// Truncate stat cache
bool TruncateCache(void);
// Truncate symbolic link cache
bool TruncateSymlink(void);
public:
// Reference singleton
@ -111,10 +139,10 @@ class StatCache
// Cache For no object
bool IsNoObjectCache(const std::string& key, bool overcheck = true);
bool AddNoObjectCache(std::string& key);
bool AddNoObjectCache(const std::string& key);
// Add stat cache
bool AddStat(std::string& key, headers_t& meta, bool forcedir = false, bool no_truncate = false);
bool AddStat(const std::string& key, headers_t& meta, bool forcedir = false, bool no_truncate = false);
// Change no truncate flag
void ChangeNoTruncateFlag(const std::string& key, bool no_truncate);
@ -124,6 +152,11 @@ class StatCache
bool DelStat(std::string& key, bool lock_already_held = false) {
return DelStat(key.c_str(), lock_already_held);
}
// Cache for symbolic link
bool GetSymlink(const std::string& key, std::string& value);
bool AddSymlink(const std::string& key, const std::string& value);
bool DelSymlink(const char* key, bool lock_already_held = false);
};
//

View File

@ -910,40 +910,54 @@ static int s3fs_readlink(const char* _path, char* buf, size_t size)
return 0;
}
WTF8_ENCODE(path)
// Open
FdEntity* ent;
if(NULL == (ent = get_local_fent(path))){
S3FS_PRN_ERR("could not get fent(file=%s)", path);
return -EIO;
}
// Get size
off_t readsize;
if(!ent->GetSize(readsize)){
S3FS_PRN_ERR("could not get file size(file=%s)", path);
FdManager::get()->Close(ent);
return -EIO;
}
if(static_cast<off_t>(size) <= readsize){
readsize = size - 1;
}
// Read
ssize_t ressize;
if(0 > (ressize = ent->Read(buf, 0, readsize))){
S3FS_PRN_ERR("could not read file(file=%s, ressize=%jd)", path, (intmax_t)ressize);
FdManager::get()->Close(ent);
return static_cast<int>(ressize);
}
buf[ressize] = '\0';
string strValue;
// check buf if it has space words.
string strTmp = trim(string(buf));
// decode wtf8. This will always be shorter
if(use_wtf8){
strTmp = s3fs_wtf8_decode(strTmp);
}
strncpy(buf, strTmp.c_str(), size);
// check symblic link cache
if(!StatCache::getStatCacheData()->GetSymlink(string(path), strValue)){
// not found in cache, then open the path
FdEntity* ent;
if(NULL == (ent = get_local_fent(path))){
S3FS_PRN_ERR("could not get fent(file=%s)", path);
return -EIO;
}
// Get size
off_t readsize;
if(!ent->GetSize(readsize)){
S3FS_PRN_ERR("could not get file size(file=%s)", path);
FdManager::get()->Close(ent);
return -EIO;
}
if(static_cast<off_t>(size) <= readsize){
readsize = size - 1;
}
// Read
ssize_t ressize;
if(0 > (ressize = ent->Read(buf, 0, readsize))){
S3FS_PRN_ERR("could not read file(file=%s, ressize=%jd)", path, (intmax_t)ressize);
FdManager::get()->Close(ent);
return static_cast<int>(ressize);
}
buf[ressize] = '\0';
// close
FdManager::get()->Close(ent);
// check buf if it has space words.
strValue = trim(string(buf));
// decode wtf8. This will always be shorter
if(use_wtf8){
strValue = s3fs_wtf8_decode(strValue);
}
// add symblic link cache
if(!StatCache::getStatCacheData()->AddSymlink(string(path), strValue)){
S3FS_PRN_ERR("failed to add symbolic link cache for %s", path);
}
}
// copy result
strncpy(buf, strValue.c_str(), size);
FdManager::get()->Close(ent);
S3FS_MALLOCTRIM(0);
return 0;
@ -1150,6 +1164,7 @@ static int s3fs_unlink(const char* _path)
result = s3fscurl.DeleteRequest(path);
FdManager::DeleteCacheFile(path);
StatCache::getStatCacheData()->DelStat(path);
StatCache::getStatCacheData()->DelSymlink(path);
S3FS_MALLOCTRIM(0);
return result;
@ -1279,6 +1294,9 @@ static int s3fs_symlink(const char* _from, const char* _to)
FdManager::get()->Close(ent);
StatCache::getStatCacheData()->DelStat(to);
if(!StatCache::getStatCacheData()->AddSymlink(string(to), strFrom)){
S3FS_PRN_ERR("failed to add symbolic link cache for %s", to);
}
S3FS_MALLOCTRIM(0);
return result;

View File

@ -1199,18 +1199,20 @@ void show_help ()
" API. The default is 1000. you can set this value to 1000 or more.\n"
"\n"
" max_stat_cache_size (default=\"100,000\" entries (about 40MB))\n"
" - maximum number of entries in the stat cache\n"
" - maximum number of entries in the stat cache, and this maximum is\n"
" also treated as the number of symbolic link cache.\n"
"\n"
" stat_cache_expire (default is no expire)\n"
" - specify expire time (seconds) for entries in the stat cache.\n"
" This expire time indicates the time since stat cached.\n"
" This expire time indicates the time since stat cached. and this\n"
" is also set to the expire time of the symbolic link cache.\n"
"\n"
" stat_cache_interval_expire (default is no expire)\n"
" - specify expire time (seconds) for entries in the stat cache.\n"
" - specify expire time (seconds) for entries in the stat cache(and\n"
" symbolic link cache).\n"
" This expire time is based on the time from the last access time\n"
" of the stat cache. This option is exclusive with\n"
" stat_cache_expire, and is left for compatibility with older\n"
" versions.\n"
" of the stat cache. This option is exclusive with stat_cache_expire,\n"
" and is left for compatibility with older versions.\n"
"\n"
" enable_noobj_cache (default is disable)\n"
" - enable cache entries for the object which does not exist.\n"