Merge pull request #350 from ggtakec/master

Changed cache out logic for stat - #340
This commit is contained in:
Takeshi Nakatani 2016-02-06 18:37:58 +09:00
commit 0ac2f7cded
2 changed files with 114 additions and 35 deletions

View File

@ -29,6 +29,7 @@
#include <syslog.h>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <list>
@ -39,6 +40,66 @@
using namespace std;
//-------------------------------------------------------------------
// Utility
//-------------------------------------------------------------------
inline void SetStatCacheTime(struct timespec& ts)
{
if(-1 == clock_gettime(CLOCK_MONOTONIC_COARSE, &ts)){
ts.tv_sec = time(NULL);
ts.tv_nsec = 0;
}
}
inline void InitStatCacheTime(struct timespec& ts)
{
ts.tv_sec = 0;
ts.tv_nsec = 0;
}
inline int CompareStatCacheTime(struct timespec& ts1, struct timespec& ts2)
{
// return -1: ts1 < ts2
// 0: ts1 == ts2
// 1: ts1 > ts2
if(ts1.tv_sec < ts2.tv_sec){
return -1;
}else if(ts1.tv_sec > ts2.tv_sec){
return 1;
}else{
if(ts1.tv_nsec < ts2.tv_nsec){
return -1;
}else if(ts1.tv_nsec > ts2.tv_nsec){
return 1;
}
}
return 0;
}
inline bool IsExpireStatCacheTime(const struct timespec& ts, const time_t& expire)
{
return ((ts.tv_sec + expire) < time(NULL));
}
//
// For cache out
//
typedef std::vector<stat_cache_t::iterator> statiterlist_t;
struct sort_statiterlist{
// ascending order
bool operator()(const stat_cache_t::iterator& src1, const stat_cache_t::iterator& src2) const
{
int result = CompareStatCacheTime(src1->second->cache_date, src2->second->cache_date);
if(0 == result){
if(src1->second->hit_count < src2->second->hit_count){
result = -1;
}
}
return (result < 0);
}
};
//-------------------------------------------------------------------
// Static
//-------------------------------------------------------------------
@ -144,7 +205,7 @@ bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool ove
if(iter != stat_cache.end() && (*iter).second){
stat_cache_entry* ent = (*iter).second;
if(!IsExpireTime|| (ent->cache_date + ExpireTime) >= time(NULL)){
if(!IsExpireTime || !IsExpireStatCacheTime(ent->cache_date, ExpireTime)){
if(ent->noobjcache){
pthread_mutex_unlock(&StatCache::stat_cache_lock);
if(!IsCacheNoObject){
@ -164,11 +225,12 @@ bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool ove
}
if(is_delete_cache){
// not hit by different ETag
S3FS_PRN_DBG("stat cache not hit by ETag[path=%s][time=%jd][hit count=%lu][ETag(%s)!=(%s)]",
strpath.c_str(), (intmax_t)(ent->cache_date), ent->hit_count, petag ? petag : "null", ent->meta["ETag"].c_str());
S3FS_PRN_DBG("stat cache not hit by ETag[path=%s][time=%jd.%09ld][hit count=%lu][ETag(%s)!=(%s)]",
strpath.c_str(), (intmax_t)(ent->cache_date.tv_sec), ent->cache_date.tv_nsec, ent->hit_count, petag ? petag : "null", ent->meta["ETag"].c_str());
}else{
// hit
S3FS_PRN_DBG("stat cache hit [path=%s][time=%jd][hit count=%lu]", strpath.c_str(), (intmax_t)(ent->cache_date), ent->hit_count);
S3FS_PRN_DBG("stat cache hit [path=%s][time=%jd.%09ld][hit count=%lu]",
strpath.c_str(), (intmax_t)(ent->cache_date.tv_sec), ent->cache_date.tv_nsec, ent->hit_count);
if(pst!= NULL){
*pst= ent->stbuf;
@ -180,7 +242,7 @@ bool StatCache::GetStat(string& key, struct stat* pst, headers_t* meta, bool ove
(*pisforce) = ent->isforce;
}
ent->hit_count++;
ent->cache_date = time(NULL);
SetStatCacheTime(ent->cache_date);
pthread_mutex_unlock(&StatCache::stat_cache_lock);
return true;
}
@ -220,10 +282,10 @@ bool StatCache::IsNoObjectCache(string& key, bool overcheck)
}
if(iter != stat_cache.end() && (*iter).second) {
if(!IsExpireTime|| ((*iter).second->cache_date + ExpireTime) >= time(NULL)){
if(!IsExpireTime || !IsExpireStatCacheTime((*iter).second->cache_date, ExpireTime)){
if((*iter).second->noobjcache){
// noobjcache = true means no object.
(*iter).second->cache_date = time(NULL);
SetStatCacheTime((*iter).second->cache_date);
pthread_mutex_unlock(&StatCache::stat_cache_lock);
return true;
}
@ -271,10 +333,10 @@ bool StatCache::AddStat(std::string& key, headers_t& meta, bool forcedir)
return false;
}
ent->hit_count = 0;
ent->cache_date = time(NULL); // Set time.
ent->isforce = forcedir;
ent->noobjcache = false;
ent->meta.clear();
SetStatCacheTime(ent->cache_date); // Set time.
//copy only some keys
for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){
string tag = lower(iter->first);
@ -330,10 +392,10 @@ bool StatCache::AddNoObjectCache(string& key)
stat_cache_entry* ent = new stat_cache_entry();
memset(&(ent->stbuf), 0, sizeof(struct stat));
ent->hit_count = 0;
ent->cache_date = time(NULL); // Set time.
ent->isforce = false;
ent->noobjcache = true;
ent->meta.clear();
SetStatCacheTime(ent->cache_date); // Set time.
// add
pthread_mutex_lock(&StatCache::stat_cache_lock);
stat_cache[key] = ent;
@ -344,33 +406,47 @@ bool StatCache::AddNoObjectCache(string& key)
bool StatCache::TruncateCache(void)
{
if(stat_cache.empty()){
return true;
}
pthread_mutex_lock(&StatCache::stat_cache_lock);
if(stat_cache.empty()){
// 1) erase over expire time
if(IsExpireTime){
for(stat_cache_t::iterator iter = stat_cache.begin(); iter != stat_cache.end(); ){
stat_cache_entry* entry = iter->second;
if(!entry || IsExpireStatCacheTime(entry->cache_date, ExpireTime)){
stat_cache.erase(iter++);
}else{
++iter;
}
}
}
// 2) check stat cache count
if(stat_cache.size() < CacheSize){
pthread_mutex_unlock(&StatCache::stat_cache_lock);
return true;
}
time_t lowest_time = time(NULL) + 1;
stat_cache_t::iterator iter_to_delete = stat_cache.end();
stat_cache_t::iterator iter;
// 3) erase from the old cache in order
size_t erase_count= stat_cache.size() - CacheSize + 1;
statiterlist_t erase_iters;
for(stat_cache_t::iterator iter = stat_cache.begin(); iter != stat_cache.end(); ++iter){
erase_iters.push_back(iter);
sort(erase_iters.begin(), erase_iters.end(), sort_statiterlist());
if(erase_count < erase_iters.size()){
erase_iters.pop_back();
}
}
for(statiterlist_t::iterator iiter = erase_iters.begin(); iiter != erase_iters.end(); ++iiter){
stat_cache_t::iterator siter = *iiter;
for(iter = stat_cache.begin(); iter != stat_cache.end(); ++iter) {
if((*iter).second){
if(lowest_time > (*iter).second->cache_date){
lowest_time = (*iter).second->cache_date;
iter_to_delete = iter;
S3FS_PRN_DBG("truncate stat cache[path=%s]", siter->first.c_str());
stat_cache.erase(siter);
}
}
}
if(stat_cache.end() != iter_to_delete){
S3FS_PRN_DBG("truncate stat cache[path=%s]", (*iter_to_delete).first.c_str());
if((*iter_to_delete).second){
delete (*iter_to_delete).second;
}
stat_cache.erase(iter_to_delete);
S3FS_MALLOCTRIM(0);
}
pthread_mutex_unlock(&StatCache::stat_cache_lock);

View File

@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_CACHE_H_
#define S3FS_CACHE_H_
@ -28,13 +29,15 @@
struct stat_cache_entry {
struct stat stbuf;
unsigned long hit_count;
time_t cache_date;
struct timespec cache_date;
headers_t meta;
bool isforce;
bool noobjcache; // Flag: cache is no object for no listing.
stat_cache_entry() : hit_count(0), cache_date(0), isforce(false), noobjcache(false) {
stat_cache_entry() : hit_count(0), isforce(false), noobjcache(false) {
memset(&stbuf, 0, sizeof(struct stat));
cache_date.tv_sec = 0;
cache_date.tv_nsec = 0;
meta.clear();
}
};