47 #include "BESInternalError.h"
53 #include "BESFileLockingCache.h"
57 #define LOCK "cache-lock"
58 #define LOCK_STATUS "cache-lock-status"
60 #define CACHE_CONTROL "cache_control"
62 #define prolog std::string("BESFileLockingCache::").append(__func__).append("() - ")
67 static const unsigned long long BYTES_PER_MEG = 1048576ULL;
71 static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
94 BESFileLockingCache::BESFileLockingCache(
const string &cache_dir,
const string &prefix,
unsigned long long size) :
95 d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size), d_target_size(0), d_cache_info(
""),
98 m_initialize_cache_info();
118 d_cache_dir = cache_dir;
120 d_max_cache_size_in_bytes = size;
122 m_initialize_cache_info();
125 static inline string get_errno()
127 char *s_err = strerror(errno);
131 return "Unknown error.";
135 static inline struct flock *lock(
int type)
137 static struct flock lock;
139 lock.l_whence = SEEK_SET;
142 lock.l_pid = getpid();
147 inline void BESFileLockingCache::m_record_descriptor(
const string &file,
int fd)
150 "BESFileLockingCache::m_record_descriptor() - Recording descriptor: " << file <<
", " << fd << endl);
152 d_locks.insert(std::pair<string, int>(file, fd));
155 inline int BESFileLockingCache::m_remove_descriptor(
const string &file)
157 BESDEBUG(LOCK,
"BESFileLockingCache::m_remove_descriptor(): d_locks size: " << d_locks.size() << endl);
159 FilesAndLockDescriptors::iterator i = d_locks.find(file);
160 if (i == d_locks.end())
return -1;
166 "BESFileLockingCache::m_remove_descriptor(): Found file descriptor [" << fd <<
"] for file: " << file << endl);
171 #if USE_GET_SHARED_LOCK
172 inline int BESFileLockingCache::m_find_descriptor(
const string &file)
174 BESDEBUG(LOCK,
"BESFileLockingCache::m_find_descriptor(): d_locks size: " << d_locks.size() << endl);
176 FilesAndLockDescriptors::iterator i = d_locks.find(file);
177 if (i == d_locks.end())
return -1;
180 "BESFileLockingCache::m_find_descriptor(): Found file descriptor [" << i->second <<
"] for file: " << file << endl);
191 static string lockStatus(
const int fd)
193 struct flock lock_query;
195 lock_query.l_type = F_WRLCK;
196 lock_query.l_start = 0;
197 lock_query.l_whence = SEEK_SET;
198 lock_query.l_len = 0;
199 lock_query.l_pid = 0;
201 int ret = fcntl(fd, F_GETLK, &lock_query);
206 ss <<
"fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret <<
" errno[" << errno <<
"]: "
207 << strerror(errno) << endl;
210 ss <<
"fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret << endl;
213 ss <<
"lock_info.l_len: " << lock_query.l_len << endl;
214 ss <<
"lock_info.l_pid: " << lock_query.l_pid << endl;
215 ss <<
"lock_info.l_start: " << lock_query.l_start << endl;
218 switch (lock_query.l_type) {
231 ss <<
"lock_info.l_type: " << type << endl;
232 ss <<
"lock_info.l_whence: " << lock_query.l_whence << endl;
242 static void unlock(
int fd)
244 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
245 throw BESInternalError(
"An error occurred trying to unlock the file: " + get_errno(), __FILE__, __LINE__);
248 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::unlock() - lock status: " << lockStatus(fd) << endl);
250 if (close(fd) == -1)
throw BESInternalError(
"Could not close the (just) unlocked file.", __FILE__, __LINE__);
252 BESDEBUG(LOCK,
"BESFileLockingCache::unlock() - File Closed. fd: " << fd << endl);
274 bool BESFileLockingCache::m_check_ctor_params()
287 BESDEBUG(CACHE,
"BESFileLockingCache::" <<__func__ <<
"() - BEGIN" << endl);
289 if (d_cache_dir.empty()) {
290 BESDEBUG(CACHE,
"BESFileLockingCache::" <<__func__ <<
"() - " <<
291 "The cache directory was not specified. CACHE IS DISABLED." << endl);
298 int status = mkdir(d_cache_dir.c_str(), 0775);
301 if (status == -1 && errno != EEXIST) {
302 string err =
"The cache directory " + d_cache_dir +
" could not be created: " + strerror(errno);
303 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
306 if (d_prefix.empty()) {
307 string err =
"The cache file prefix was not specified, must not be empty";
308 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
318 if (d_max_cache_size_in_bytes < 0) {
319 string err =
"The cache size was not specified, must be greater than zero";
320 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
325 "BESFileLockingCache::" << __func__ <<
"() -" <<
326 " d_cache_dir: " << d_cache_dir <<
327 " d_prefix: " << d_prefix <<
328 " d_max_cache_size_in_bytes: " << d_max_cache_size_in_bytes << endl);
343 static bool createLockedFile(
string file_name,
int &ref_fd)
345 BESDEBUG(LOCK,
"createLockedFile() - filename: " << file_name <<endl);
348 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
358 struct flock *l = lock(F_WRLCK);
360 if (fcntl(fd, F_SETLKW, l) == -1) {
363 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
367 BESDEBUG(LOCK,
"createLockedFile exit: " << file_name <<endl);
383 bool BESFileLockingCache::m_initialize_cache_info()
385 BESDEBUG(CACHE,
"BESFileLockingCache::m_initialize_cache_info() - BEGIN" << endl);
389 d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
390 d_max_cache_size_in_bytes *= BYTES_PER_MEG;
391 d_target_size = d_max_cache_size_in_bytes * 0.8;
394 "BESFileLockingCache::m_initialize_cache_info() - d_max_cache_size_in_bytes: "
395 << d_max_cache_size_in_bytes <<
" d_target_size: "<<d_target_size<< endl);
397 bool status = m_check_ctor_params();
401 BESDEBUG(CACHE,
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info: " << d_cache_info << endl);
405 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
407 unsigned long long size = 0;
408 if (write(d_cache_info_fd, &size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
409 throw BESInternalError(
"Could not write size info to the cache info file `" + d_cache_info +
"`",
417 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
423 "BESFileLockingCache::m_initialize_cache_info() - d_cache_info_fd: " << d_cache_info_fd << endl);
427 "BESFileLockingCache::m_initialize_cache_info() - END [" <<
"CACHE IS " << (
cache_enabled()?
"ENABLED]":
"DISABLED]") << endl);
432 static const string chars_excluded_from_filenames =
"<>=,/()\\\"\':? []()$";
452 BESDEBUG(CACHE, __FUNCTION__ <<
" - src: '" << src <<
"' mangle: "<< mangle << endl);
457 string::size_type pos = target.find_first_of(chars_excluded_from_filenames);
458 while (pos != string::npos) {
459 target.replace(pos, 1,
"#", 1);
460 pos = target.find_first_of(chars_excluded_from_filenames);
464 if (target.length() > 254) {
466 msg <<
"Cache filename is longer than 254 characters (name length: ";
467 msg << target.length() <<
", name: " << target;
473 BESDEBUG(CACHE, __FUNCTION__ <<
" - target: '" << target <<
"'" << endl);
478 #if USE_GET_SHARED_LOCK
492 static bool getSharedLock(
const string &file_name,
int &ref_fd)
494 BESDEBUG(LOCK,
"getSharedLock(): Acquiring cache read lock for " << file_name <<endl);
497 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
507 struct flock *l = lock(F_RDLCK);
508 if (fcntl(fd, F_SETLKW, l) == -1) {
511 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
512 BESDEBUG(LOCK,
"getSharedLock(): FAIL Could not get read lock for " << file_name <<
": " << oss.str() << endl);
516 BESDEBUG(LOCK,
"getSharedLock(): SUCCESS Read Lock Acquired For " << file_name <<endl);
548 #if USE_GET_SHARED_LOCK
549 status = getSharedLock(target, fd);
551 if (status) m_record_descriptor(target, fd);
553 fd = m_find_descriptor(target);
555 if ((fd == -1) && (fd = open(target.c_str(), O_RDONLY)) < 0) {
567 struct flock *l = lock(F_RDLCK);
568 if (fcntl(fd, F_SETLKW, l) == -1) {
572 m_record_descriptor(target, fd);
601 bool status = createLockedFile(target, fd);
604 "BESFileLockingCache::create_and_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
606 if (status) m_record_descriptor(target, fd);
631 lock.l_type = F_RDLCK;
632 lock.l_whence = SEEK_SET;
635 lock.l_pid = getpid();
637 if (fcntl(fd, F_SETLKW, &lock) == -1) {
641 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::exclusive_to_shared_lock() - lock status: " << lockStatus(fd) << endl);
654 BESDEBUG(LOCK,
"BESFileLockingCache::lock_cache_write() - d_cache_info_fd: " << d_cache_info_fd << endl);
656 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
657 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
661 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::lock_cache_write() - lock status: " << lockStatus(d_cache_info_fd) << endl);
669 BESDEBUG(LOCK,
"BESFileLockingCache::lock_cache_read() - d_cache_info_fd: " << d_cache_info_fd << endl);
671 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
672 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
676 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::lock_cache_read() - lock status: " << lockStatus(d_cache_info_fd) << endl);
686 BESDEBUG(LOCK,
"BESFileLockingCache::unlock_cache() - d_cache_info_fd: " << d_cache_info_fd << endl);
688 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
689 throw BESInternalError(
"An error occurred trying to unlock the cache-control file" + get_errno(), __FILE__,
693 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::unlock_cache() - lock status: " << lockStatus(d_cache_info_fd) << endl);
713 BESDEBUG(LOCK,
"BESFileLockingCache::unlock_and_close() - BEGIN file: " << file_name << endl);
715 int fd = m_remove_descriptor(file_name);
718 fd = m_remove_descriptor(file_name);
721 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::unlock_and_close() - lock status: " << lockStatus(d_cache_info_fd) << endl);
722 BESDEBUG(LOCK,
"BESFileLockingCache::unlock_and_close() - END"<< endl);
737 unsigned long long current_size;
741 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
742 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
745 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
746 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
749 int statret = stat(target.c_str(), &buf);
751 current_size += buf.st_size;
753 throw BESInternalError(
"Could not read the size of the new file: " + target +
" : " + get_errno(), __FILE__,
756 BESDEBUG(CACHE,
"BESFileLockingCache::update_cache_info() - cache size updated to: " << current_size << endl);
758 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
759 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
761 if (write(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
762 throw BESInternalError(
"Could not write size info from the cache info file!", __FILE__, __LINE__);
780 return current_size > d_max_cache_size_in_bytes;
792 unsigned long long current_size;
796 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
797 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
799 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
800 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
814 return e1.time < e2.time;
818 unsigned long long BESFileLockingCache::m_collect_cache_dir_info(CacheFiles &contents)
820 DIR *dip = opendir(d_cache_dir.c_str());
821 if (!dip)
throw BESInternalError(
"Unable to open cache directory " + d_cache_dir, __FILE__, __LINE__);
824 vector<string> files;
827 while ((dit = readdir(dip)) != NULL) {
828 string dirEntry = dit->d_name;
829 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0 && dirEntry != d_cache_info) {
830 files.push_back(d_cache_dir +
"/" + dirEntry);
836 unsigned long long current_size = 0;
838 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
839 if (stat(file->c_str(), &buf) == 0) {
840 current_size += buf.st_size;
843 entry.size = buf.st_size;
844 entry.time = buf.st_atime;
848 throw BESInternalError(
"Zero-byte file found in cache. " + *file, __FILE__, __LINE__);
850 contents.push_back(entry);
855 contents.sort(entry_op);
876 static bool getExclusiveLockNB(
string file_name,
int &ref_fd)
878 BESDEBUG(LOCK,
"getExclusiveLock_nonblocking: " << file_name <<endl);
881 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
891 struct flock *l = lock(F_WRLCK);
892 if (fcntl(fd, F_SETLK, l) == -1) {
897 "getExclusiveLockNB exit (false): " << file_name <<
" by: " << l->l_pid << endl);
904 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
910 BESDEBUG(LOCK,
"getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
934 BESDEBUG(CACHE,
"purge - starting the purge" << endl);
937 BESDEBUG(CACHE,
"purge - unlimited so no need to purge." << endl);
945 unsigned long long computed_size = m_collect_cache_dir_info(contents);
947 if (BESISDEBUG(
"cache_contents" )) {
948 BESDEBUG(CACHE,
"BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
949 CacheFiles::iterator ti = contents.begin();
950 CacheFiles::iterator te = contents.end();
951 for (; ti != te; ti++) {
952 BESDEBUG(CACHE, (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
957 "BESFileLockingCache::update_and_purge() - current and target size (in MB) "
958 << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
965 CacheFiles::iterator i = contents.begin();
966 while (i != contents.end() && computed_size > d_target_size) {
971 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
972 BESDEBUG(CACHE,
"purge: " << i->name <<
" removed." << endl);
974 if (unlink(i->name.c_str()) != 0)
976 "Unable to purge the file " + i->name +
" from the cache: " + get_errno(), __FILE__,
980 computed_size -= i->size;
985 "BESFileLockingCache::update_and_purge() - current and target size (in MB) "
986 << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
990 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
991 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
993 if (write(d_cache_info_fd, &computed_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
994 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
996 if (BESISDEBUG(
"cache_contents" )) {
998 computed_size = m_collect_cache_dir_info(contents);
999 BESDEBUG(CACHE,
"AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
1000 CacheFiles::iterator ti = contents.begin();
1001 CacheFiles::iterator te = contents.end();
1002 for (; ti != te; ti++) {
1003 BESDEBUG(CACHE, (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
1031 static bool getExclusiveLock(
string file_name,
int &ref_fd)
1033 BESDEBUG(LOCK,
"BESFileLockingCache::getExclusiveLock() - " << file_name <<endl);
1036 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
1042 BESDEBUG(LOCK, __func__ <<
"() - FAILED to open file: " << file_name << endl);
1047 struct flock *l = lock(F_WRLCK);
1048 if (fcntl(fd, F_SETLKW, l) == -1) {
1051 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
1055 BESDEBUG(LOCK,
"BESFileLockingCache::getExclusiveLock() - exit: " << file_name <<endl);
1074 BESDEBUG(CACHE,
"BESFileLockingCache::purge_file() - starting the purge" << endl);
1081 if (getExclusiveLock(file, cfile_fd)) {
1083 unsigned long long size = 0;
1085 if (stat(file.c_str(), &buf) == 0) {
1089 BESDEBUG(CACHE,
"BESFileLockingCache::purge_file() - " << file <<
" removed." << endl);
1091 if (unlink(file.c_str()) != 0)
1092 throw BESInternalError(
"Unable to purge the file " + file +
" from the cache: " + get_errno(), __FILE__,
1099 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
1100 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
1102 if (write(d_cache_info_fd, &cache_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
1103 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
1127 return (stat(dir.c_str(), &buf) == 0) && (buf.st_mode & S_IFDIR);
1140 strm << BESIndent::LMarg <<
"BESFileLockingCache::dump - (" << (
void *)
this <<
")" << endl;
1141 BESIndent::Indent();
1142 strm << BESIndent::LMarg <<
"cache dir: " << d_cache_dir << endl;
1143 strm << BESIndent::LMarg <<
"prefix: " << d_prefix << endl;
1144 strm << BESIndent::LMarg <<
"size (bytes): " << d_max_cache_size_in_bytes << endl;
1145 BESIndent::UnIndent();