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.";
138 static inline struct flock *lock(
int type)
140 static struct flock lock;
142 lock.l_whence = SEEK_SET;
145 lock.l_pid = getpid();
150 inline void BESFileLockingCache::m_record_descriptor(
const string &file,
int fd)
153 "BESFileLockingCache::m_record_descriptor() - Recording descriptor: " << file <<
", " << fd << endl);
155 d_locks.insert(std::pair<string, int>(file, fd));
158 inline int BESFileLockingCache::m_remove_descriptor(
const string &file)
160 BESDEBUG(LOCK,
"BESFileLockingCache::m_remove_descriptor(): d_locks size: " << d_locks.size() << endl);
162 FilesAndLockDescriptors::iterator i = d_locks.find(file);
163 if (i == d_locks.end())
return -1;
169 "BESFileLockingCache::m_remove_descriptor(): Found file descriptor [" << fd <<
"] for file: " << file << endl);
174 #if USE_GET_SHARED_LOCK
175 inline int BESFileLockingCache::m_find_descriptor(
const string &file)
177 BESDEBUG(LOCK,
"BESFileLockingCache::m_find_descriptor(): d_locks size: " << d_locks.size() << endl);
179 FilesAndLockDescriptors::iterator i = d_locks.find(file);
180 if (i == d_locks.end())
return -1;
183 "BESFileLockingCache::m_find_descriptor(): Found file descriptor [" << i->second <<
"] for file: " << file << endl);
194 static string lockStatus(
const int fd)
196 struct flock lock_query;
198 lock_query.l_type = F_WRLCK;
199 lock_query.l_start = 0;
200 lock_query.l_whence = SEEK_SET;
201 lock_query.l_len = 0;
202 lock_query.l_pid = 0;
204 int ret = fcntl(fd, F_GETLK, &lock_query);
209 ss <<
"fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret <<
" errno[" << errno <<
"]: "
210 << strerror(errno) << endl;
213 ss <<
"fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret << endl;
216 ss <<
"lock_info.l_len: " << lock_query.l_len << endl;
217 ss <<
"lock_info.l_pid: " << lock_query.l_pid << endl;
218 ss <<
"lock_info.l_start: " << lock_query.l_start << endl;
221 switch (lock_query.l_type) {
234 ss <<
"lock_info.l_type: " << type << endl;
235 ss <<
"lock_info.l_whence: " << lock_query.l_whence << endl;
245 static void unlock(
int fd)
247 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
248 throw BESInternalError(
"An error occurred trying to unlock the file: " + get_errno(), __FILE__, __LINE__);
251 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::unlock() - lock status: " << lockStatus(fd) << endl);
253 if (close(fd) == -1)
throw BESInternalError(
"Could not close the (just) unlocked file.", __FILE__, __LINE__);
255 BESDEBUG(LOCK,
"BESFileLockingCache::unlock() - File Closed. fd: " << fd << endl);
277 bool BESFileLockingCache::m_check_ctor_params()
290 BESDEBUG(CACHE,
"BESFileLockingCache::" <<__func__ <<
"() - BEGIN" << endl);
292 if (d_cache_dir.empty()) {
293 BESDEBUG(CACHE,
"BESFileLockingCache::" <<__func__ <<
"() - " <<
294 "The cache directory was not specified. CACHE IS DISABLED." << endl);
301 int status = mkdir(d_cache_dir.c_str(), 0775);
304 if (status == -1 && errno != EEXIST) {
305 string err =
"The cache directory " + d_cache_dir +
" could not be created: " + strerror(errno);
306 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
309 if (d_prefix.empty()) {
310 string err =
"The cache file prefix was not specified, must not be empty";
311 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
321 if (d_max_cache_size_in_bytes < 0) {
322 string err =
"The cache size was not specified, must be greater than zero";
323 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
328 "BESFileLockingCache::" << __func__ <<
"() -" <<
329 " d_cache_dir: " << d_cache_dir <<
330 " d_prefix: " << d_prefix <<
331 " d_max_cache_size_in_bytes: " << d_max_cache_size_in_bytes << endl);
346 static bool createLockedFile(
const string &file_name,
int &ref_fd)
348 BESDEBUG(LOCK,
"createLockedFile() - filename: " << file_name <<endl);
351 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
361 struct flock *l = lock(F_WRLCK);
363 if (fcntl(fd, F_SETLKW, l) == -1) {
366 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error for '" << file_name <<
"': " << get_errno();
370 BESDEBUG(LOCK,
"createLockedFile exit: " << file_name <<endl);
386 bool BESFileLockingCache::m_initialize_cache_info()
388 BESDEBUG(CACHE,
"BESFileLockingCache::m_initialize_cache_info() - BEGIN" << endl);
392 d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
393 d_max_cache_size_in_bytes *= BYTES_PER_MEG;
394 d_target_size = d_max_cache_size_in_bytes * 0.8;
397 "BESFileLockingCache::m_initialize_cache_info() - d_max_cache_size_in_bytes: "
398 << d_max_cache_size_in_bytes <<
" d_target_size: "<<d_target_size<< endl);
400 bool status = m_check_ctor_params();
404 BESDEBUG(CACHE,
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info: " << d_cache_info << endl);
408 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
410 unsigned long long size = 0;
411 if (write(d_cache_info_fd, &size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
412 throw BESInternalError(
"Could not write size info to the cache info file `" + d_cache_info +
"`",
420 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
426 "BESFileLockingCache::m_initialize_cache_info() - d_cache_info_fd: " << d_cache_info_fd << endl);
430 "BESFileLockingCache::m_initialize_cache_info() - END [" <<
"CACHE IS " << (
cache_enabled()?
"ENABLED]":
"DISABLED]") << endl);
435 static const string chars_excluded_from_filenames =
"<>=,/()\\\"\':? []()$";
455 BESDEBUG(CACHE, __FUNCTION__ <<
" - src: '" << src <<
"' mangle: "<< mangle << endl);
460 string::size_type pos = target.find_first_of(chars_excluded_from_filenames);
461 while (pos != string::npos) {
462 target.replace(pos, 1,
"#", 1);
463 pos = target.find_first_of(chars_excluded_from_filenames);
467 if (target.length() > 254) {
469 msg <<
"Cache filename is longer than 254 characters (name length: ";
470 msg << target.length() <<
", name: " << target;
476 BESDEBUG(CACHE, __FUNCTION__ <<
" - target: '" << target <<
"'" << endl);
481 #if USE_GET_SHARED_LOCK
495 static bool getSharedLock(
const string &file_name,
int &ref_fd)
497 BESDEBUG(LOCK,
"getSharedLock(): Acquiring cache read lock for " << file_name <<endl);
500 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
510 struct flock *l = lock(F_RDLCK);
511 if (fcntl(fd, F_SETLKW, l) == -1) {
514 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error for '" << file_name <<
"': " << get_errno();
518 BESDEBUG(LOCK,
"getSharedLock(): SUCCESS Read Lock Acquired For " << file_name <<endl);
550 #if USE_GET_SHARED_LOCK
551 status = getSharedLock(target, fd);
553 if (status) m_record_descriptor(target, fd);
555 fd = m_find_descriptor(target);
557 if ((fd == -1) && (fd = open(target.c_str(), O_RDONLY)) < 0) {
569 struct flock *l = lock(F_RDLCK);
570 if (fcntl(fd, F_SETLKW, l) == -1) {
574 m_record_descriptor(target, fd);
603 bool status = createLockedFile(target, fd);
606 "BESFileLockingCache::create_and_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
608 if (status) m_record_descriptor(target, fd);
633 lock.l_type = F_RDLCK;
634 lock.l_whence = SEEK_SET;
637 lock.l_pid = getpid();
639 if (fcntl(fd, F_SETLKW, &lock) == -1) {
643 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::exclusive_to_shared_lock() - lock status: " << lockStatus(fd) << endl);
656 BESDEBUG(LOCK,
"BESFileLockingCache::lock_cache_write() - d_cache_info_fd: " << d_cache_info_fd << endl);
658 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
659 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
663 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::lock_cache_write() - lock status: " << lockStatus(d_cache_info_fd) << endl);
671 BESDEBUG(LOCK,
"BESFileLockingCache::lock_cache_read() - d_cache_info_fd: " << d_cache_info_fd << endl);
673 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
674 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
678 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::lock_cache_read() - lock status: " << lockStatus(d_cache_info_fd) << endl);
688 BESDEBUG(LOCK,
"BESFileLockingCache::unlock_cache() - d_cache_info_fd: " << d_cache_info_fd << endl);
690 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
691 throw BESInternalError(
"An error occurred trying to unlock the cache-control file" + get_errno(), __FILE__,
695 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::unlock_cache() - lock status: " << lockStatus(d_cache_info_fd) << endl);
715 BESDEBUG(LOCK,
"BESFileLockingCache::unlock_and_close() - BEGIN file: " << file_name << endl);
717 int fd = m_remove_descriptor(file_name);
720 fd = m_remove_descriptor(file_name);
723 BESDEBUG(LOCK_STATUS,
"BESFileLockingCache::unlock_and_close() - lock status: " << lockStatus(d_cache_info_fd) << endl);
724 BESDEBUG(LOCK,
"BESFileLockingCache::unlock_and_close() - END"<< endl);
739 unsigned long long current_size;
743 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
744 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
747 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
748 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
751 int statret = stat(target.c_str(), &buf);
753 current_size += buf.st_size;
755 throw BESInternalError(
"Could not read the size of the new file: " + target +
" : " + get_errno(), __FILE__,
758 BESDEBUG(CACHE,
"BESFileLockingCache::update_cache_info() - cache size updated to: " << current_size << endl);
760 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
761 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
763 if (write(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
764 throw BESInternalError(
"Could not write size info from the cache info file!", __FILE__, __LINE__);
782 return current_size > d_max_cache_size_in_bytes;
794 unsigned long long current_size;
798 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
799 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
801 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
802 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
816 return e1.time < e2.time;
820 unsigned long long BESFileLockingCache::m_collect_cache_dir_info(CacheFiles &contents)
822 DIR *dip = opendir(d_cache_dir.c_str());
823 if (!dip)
throw BESInternalError(
"Unable to open cache directory " + d_cache_dir, __FILE__, __LINE__);
826 vector<string> files;
829 while ((dit = readdir(dip)) != NULL) {
830 string dirEntry = dit->d_name;
831 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0 && dirEntry != d_cache_info) {
832 files.push_back(d_cache_dir +
"/" + dirEntry);
838 unsigned long long current_size = 0;
840 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
841 if (stat(file->c_str(), &buf) == 0) {
842 current_size += buf.st_size;
845 entry.size = buf.st_size;
846 entry.time = buf.st_atime;
850 throw BESInternalError(
"Zero-byte file found in cache. " + *file, __FILE__, __LINE__);
852 contents.push_back(entry);
857 contents.sort(entry_op);
878 static bool getExclusiveLockNB(
const string &file_name,
int &ref_fd)
880 BESDEBUG(LOCK,
"getExclusiveLock_nonblocking: " << file_name <<endl);
883 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
893 struct flock *l = lock(F_WRLCK);
894 if (fcntl(fd, F_SETLK, l) == -1) {
899 "getExclusiveLockNB exit (false): " << file_name <<
" by: " << l->l_pid << endl);
906 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error for '" << file_name <<
"': " << get_errno();
912 BESDEBUG(LOCK,
"getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
919 bool BESFileLockingCache::get_exclusive_lock_nb(
const string &target,
int &fd)
921 return getExclusiveLockNB(target, fd);
942 BESDEBUG(CACHE,
"purge - starting the purge" << endl);
945 BESDEBUG(CACHE,
"purge - unlimited so no need to purge." << endl);
953 unsigned long long computed_size = m_collect_cache_dir_info(contents);
955 if (BESISDEBUG(
"cache_contents" )) {
956 BESDEBUG(CACHE,
"BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
957 CacheFiles::iterator ti = contents.begin();
958 CacheFiles::iterator te = contents.end();
959 for (; ti != te; ti++) {
960 BESDEBUG(CACHE, (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
965 "BESFileLockingCache::update_and_purge() - current and target size (in MB) "
966 << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
973 CacheFiles::iterator i = contents.begin();
974 while (i != contents.end() && computed_size > d_target_size) {
979 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
980 BESDEBUG(CACHE,
"purge: " << i->name <<
" removed." << endl);
982 if (unlink(i->name.c_str()) != 0)
984 "Unable to purge the file " + i->name +
" from the cache: " + get_errno(), __FILE__,
988 computed_size -= i->size;
993 "BESFileLockingCache::update_and_purge() - current and target size (in MB) "
994 << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
998 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
999 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
1001 if (write(d_cache_info_fd, &computed_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
1002 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
1004 if (BESISDEBUG(
"cache_contents" )) {
1006 computed_size = m_collect_cache_dir_info(contents);
1007 BESDEBUG(CACHE,
"AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
1008 CacheFiles::iterator ti = contents.begin();
1009 CacheFiles::iterator te = contents.end();
1010 for (; ti != te; ti++) {
1011 BESDEBUG(CACHE, (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
1039 static bool getExclusiveLock(
const string &file_name,
int &ref_fd)
1041 BESDEBUG(LOCK,
"BESFileLockingCache::getExclusiveLock() - " << file_name <<endl);
1044 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
1050 BESDEBUG(LOCK, __func__ <<
"() - FAILED to open file: " << file_name << endl);
1055 struct flock *l = lock(F_WRLCK);
1056 if (fcntl(fd, F_SETLKW, l) == -1) {
1059 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error for '" << file_name <<
"': " << get_errno();
1063 BESDEBUG(LOCK,
"BESFileLockingCache::getExclusiveLock() - exit: " << file_name <<endl);
1070 bool BESFileLockingCache::get_exclusive_lock(
const string &target,
int &fd)
1072 return getExclusiveLock(target, fd);
1087 BESDEBUG(CACHE,
"BESFileLockingCache::purge_file() - starting the purge" << endl);
1094 if (getExclusiveLock(file, cfile_fd)) {
1096 unsigned long long size = 0;
1098 if (stat(file.c_str(), &buf) == 0) {
1102 BESDEBUG(CACHE,
"BESFileLockingCache::purge_file() - " << file <<
" removed." << endl);
1104 if (unlink(file.c_str()) != 0)
1105 throw BESInternalError(
"Unable to purge the file " + file +
" from the cache: " + get_errno(), __FILE__,
1112 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
1113 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
1115 if (write(d_cache_info_fd, &cache_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
1116 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
1140 return (stat(dir.c_str(), &buf) == 0) && (buf.st_mode & S_IFDIR);
1153 strm << BESIndent::LMarg <<
"BESFileLockingCache::dump - (" << (
void *)
this <<
")" << endl;
1154 BESIndent::Indent();
1155 strm << BESIndent::LMarg <<
"cache dir: " << d_cache_dir << endl;
1156 strm << BESIndent::LMarg <<
"prefix: " << d_prefix << endl;
1157 strm << BESIndent::LMarg <<
"size (bytes): " << d_max_cache_size_in_bytes << endl;
1158 BESIndent::UnIndent();