mirror of
https://github.com/s3fs-fuse/s3fs-fuse.git
synced 2025-02-02 10:38:25 +00:00
In memory cache for softlinks with cache out
This commit is contained in:
parent
bdfb9ee815
commit
23945a0130
@ -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)
|
||||
|
184
src/cache.cpp
184
src/cache.cpp
@ -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
|
||||
//-------------------------------------------------------------------
|
||||
|
51
src/cache.h
51
src/cache.h
@ -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);
|
||||
};
|
||||
|
||||
//
|
||||
|
82
src/s3fs.cpp
82
src/s3fs.cpp
@ -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;
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user