47 #include "BESInternalError.h" 53 #include "BESFileLockingCache.h" 58 static const unsigned long long BYTES_PER_MEG = 1048576ULL;
62 static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
77 BESFileLockingCache::BESFileLockingCache(
const string &cache_dir,
const string &prefix,
unsigned long long size) :
78 d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size), d_target_size(0), d_cache_info(
""),
81 m_initialize_cache_info();
84 void BESFileLockingCache::initialize(
const string &cache_dir,
const string &prefix,
unsigned long long size)
86 d_cache_dir = cache_dir;
88 d_max_cache_size_in_bytes = size;
90 m_initialize_cache_info();
93 static inline string get_errno()
95 char *s_err = strerror(errno);
99 return "Unknown error.";
103 static inline struct flock *lock(
int type)
105 static struct flock lock;
107 lock.l_whence = SEEK_SET;
110 lock.l_pid = getpid();
115 inline void BESFileLockingCache::m_record_descriptor(
const string &file,
int fd)
118 "BESFileLockingCache::m_record_descriptor() - Recording descriptor: " << file <<
", " << fd << endl);
120 d_locks.insert(std::pair<string, int>(file, fd));
123 inline int BESFileLockingCache::m_remove_descriptor(
const string &file)
125 BESDEBUG(
"cache",
"BESFileLockingCache::m_remove_descriptor(): d_locks size: " << d_locks.size() << endl);
127 FilesAndLockDescriptors::iterator i = d_locks.find(file);
128 if (i == d_locks.end())
return -1;
134 "BESFileLockingCache::m_remove_descriptor(): Found file descriptor [" << fd <<
"] for file: " << file << endl);
139 #if USE_GET_SHARED_LOCK 140 inline int BESFileLockingCache::m_find_descriptor(
const string &file)
142 BESDEBUG(
"cache",
"BESFileLockingCache::m_find_descriptor(): d_locks size: " << d_locks.size() << endl);
144 FilesAndLockDescriptors::iterator i = d_locks.find(file);
145 if (i == d_locks.end())
return -1;
148 "BESFileLockingCache::m_find_descriptor(): Found file descriptor [" << i->second <<
"] for file: " << file << endl);
159 string lockStatus(
const int fd)
161 struct flock lock_query;
163 lock_query.l_type = F_WRLCK;
164 lock_query.l_start = 0;
165 lock_query.l_whence = SEEK_SET;
166 lock_query.l_len = 0;
167 lock_query.l_pid = 0;
169 int ret = fcntl(fd, F_GETLK, &lock_query);
174 ss <<
"ERROR! fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret <<
" errno[" << errno <<
"]: " 175 << strerror(errno) << endl;
178 ss <<
"SUCCESS. fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret << endl;
181 ss <<
"lock_info.l_len: " << lock_query.l_len << endl;
182 ss <<
"lock_info.l_pid: " << lock_query.l_pid << endl;
183 ss <<
"lock_info.l_start: " << lock_query.l_start << endl;
186 switch (lock_query.l_type) {
199 ss <<
"lock_info.l_type: " << type << endl;
200 ss <<
"lock_info.l_whence: " << lock_query.l_whence << endl;
210 static void unlock(
int fd)
212 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
213 throw BESInternalError(
"An error occurred trying to unlock the file: " + get_errno(), __FILE__, __LINE__);
216 BESDEBUG(
"cache",
"BESFileLockingCache::unlock() - lock status: " << lockStatus(fd) << endl);
218 if (close(fd) == -1)
throw BESInternalError(
"Could not close the (just) unlocked file.", __FILE__, __LINE__);
220 BESDEBUG(
"cache",
"BESFileLockingCache::unlock() - File Closed. fd: " << fd << endl);
231 bool BESFileLockingCache::m_check_ctor_params()
244 BESDEBUG(
"cache",
"BESFileLockingCache::" <<__func__ <<
"() - " <<
245 "d_cache_dir: '" << d_cache_dir <<
"'" << endl);
247 if (d_cache_dir.empty()) {
248 BESDEBUG(
"cache",
"BESFileLockingCache::" <<__func__ <<
"() - " <<
249 "The cache directory was not specified. CACHE IS DISABLED." << endl);
254 int status = mkdir(d_cache_dir.c_str(), 0775);
257 if (status == -1 && errno != EEXIST) {
258 string err =
"The cache directory " + d_cache_dir +
" could not be created: " + strerror(errno);
259 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
262 if (d_prefix.empty()) {
263 string err =
"The cache file prefix was not specified, must not be empty";
264 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
267 if (d_max_cache_size_in_bytes <= 0) {
268 string err =
"The cache size was not specified, must be greater than zero";
269 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
275 "BESFileLockingCache::" << __func__ <<
"() -" <<
276 " d_cache_dir: " << d_cache_dir <<
277 " d_prefix: " << d_prefix <<
278 " d_max_cache_size_in_bytes: " << d_max_cache_size_in_bytes <<
279 " (The cache has been " << (
cache_enabled()?
"enabled)":
"disabled)") << endl);
293 static bool createLockedFile(
string file_name,
int &ref_fd)
295 BESDEBUG(
"cache2",
"createLockedFile() - filename: " << file_name <<endl);
298 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
308 struct flock *l = lock(F_WRLCK);
310 if (fcntl(fd, F_SETLKW, l) == -1) {
313 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
317 BESDEBUG(
"cache2",
"createLockedFile exit: " << file_name <<endl);
325 bool BESFileLockingCache::m_initialize_cache_info()
327 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - BEGIN" << endl);
331 d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
332 d_max_cache_size_in_bytes *= BYTES_PER_MEG;
333 d_target_size = d_max_cache_size_in_bytes * 0.8;
336 "BESFileLockingCache::m_initialize_cache_info() - d_max_cache_size_in_bytes: " << d_max_cache_size_in_bytes <<
" d_target_size: "<<d_target_size<< endl);
338 bool status = m_check_ctor_params();
342 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info: " << d_cache_info << endl);
346 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
348 unsigned long long size = 0;
349 if (write(d_cache_info_fd, &size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
350 throw BESInternalError(
"Could not write size info to the cache info file `" + d_cache_info +
"`",
358 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
363 "BESFileLockingCache::m_initialize_cache_info() - d_cache_info_fd: " << d_cache_info_fd << endl);
366 "BESFileLockingCache::m_initialize_cache_info() - END [" <<
"CACHE IS " << (
cache_enabled()?
"ENABLED]":
"DISABLED]") << endl);
371 const string chars_excluded_from_filenames =
"<>=,/()\\\"\':? []()$";
391 BESDEBUG(
"cache", __FUNCTION__ <<
" - src: '" << src <<
"' mangle: "<< mangle << endl);
396 string::size_type pos = target.find_first_of(chars_excluded_from_filenames);
397 while (pos != string::npos) {
398 target.replace(pos, 1,
"#", 1);
399 pos = target.find_first_of(chars_excluded_from_filenames);
403 if (target.length() > 254) {
405 msg <<
"Cache filename is longer than 254 characters (name length: ";
406 msg << target.length() <<
", name: " << target;
412 BESDEBUG(
"cache", __FUNCTION__ <<
" - target: '" << target <<
"'" << endl);
417 #if USE_GET_SHARED_LOCK 430 static bool getSharedLock(
const string &file_name,
int &ref_fd)
432 BESDEBUG(
"cache2",
"getSharedLock(): Acquiring cache read lock for " << file_name <<endl);
435 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
445 struct flock *l = lock(F_RDLCK);
446 if (fcntl(fd, F_SETLKW, l) == -1) {
449 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
453 BESDEBUG(
"cache2",
"getSharedLock(): SUCCESS Read Lock Acquired For " << file_name <<endl);
485 #if USE_GET_SHARED_LOCK 486 status = getSharedLock(target, fd);
488 if (status) m_record_descriptor(target, fd);
490 fd = m_find_descriptor(target);
492 if ((fd == -1) && (fd = open(target.c_str(), O_RDONLY)) < 0) {
504 struct flock *l = lock(F_RDLCK);
505 if (fcntl(fd, F_SETLKW, l) == -1) {
509 m_record_descriptor(target, fd);
538 bool status = createLockedFile(target, fd);
541 "BESFileLockingCache::create_and_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
543 if (status) m_record_descriptor(target, fd);
568 lock.l_type = F_RDLCK;
569 lock.l_whence = SEEK_SET;
572 lock.l_pid = getpid();
574 if (fcntl(fd, F_SETLKW, &lock) == -1) {
578 BESDEBUG(
"cache",
"BESFileLockingCache::exclusive_to_shared_lock() - lock status: " << lockStatus(fd) << endl);
591 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_write() - d_cache_info_fd: " << d_cache_info_fd << endl);
593 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
594 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
598 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_write() - lock status: " << lockStatus(d_cache_info_fd) << endl);
606 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_read() - d_cache_info_fd: " << d_cache_info_fd << endl);
608 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
609 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
613 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_read() - lock status: " << lockStatus(d_cache_info_fd) << endl);
623 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_cache() - d_cache_info_fd: " << d_cache_info_fd << endl);
625 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
626 throw BESInternalError(
"An error occurred trying to unlock the cache-control file" + get_errno(), __FILE__,
630 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_cache() - lock status: " << lockStatus(d_cache_info_fd) << endl);
650 BESDEBUG(
"cache2",
"BESFileLockingCache::unlock_and_close() - BEGIN file: " << file_name << endl);
652 int fd = m_remove_descriptor(file_name);
655 fd = m_remove_descriptor(file_name);
658 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_and_close() - lock status: " << lockStatus(d_cache_info_fd) << endl);
659 BESDEBUG(
"cache2",
"BESFileLockingCache::unlock_and_close() - END"<< endl);
674 unsigned long long current_size;
678 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
679 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
682 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
683 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
686 int statret = stat(target.c_str(), &buf);
688 current_size += buf.st_size;
690 throw BESInternalError(
"Could not read the size of the new file: " + target +
" : " + get_errno(), __FILE__,
693 BESDEBUG(
"cache",
"BESFileLockingCache::update_cache_info() - cache size updated to: " << current_size << endl);
695 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
696 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
698 if (write(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
699 throw BESInternalError(
"Could not write size info from the cache info file!", __FILE__, __LINE__);
717 return current_size > d_max_cache_size_in_bytes;
729 unsigned long long current_size;
733 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
734 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
736 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
737 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
751 return e1.time < e2.time;
755 unsigned long long BESFileLockingCache::m_collect_cache_dir_info(CacheFiles &contents)
757 DIR *dip = opendir(d_cache_dir.c_str());
758 if (!dip)
throw BESInternalError(
"Unable to open cache directory " + d_cache_dir, __FILE__, __LINE__);
761 vector<string> files;
764 while ((dit = readdir(dip)) != NULL) {
765 string dirEntry = dit->d_name;
766 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0 && dirEntry != d_cache_info) {
767 files.push_back(d_cache_dir +
"/" + dirEntry);
773 unsigned long long current_size = 0;
775 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
776 if (stat(file->c_str(), &buf) == 0) {
777 current_size += buf.st_size;
780 entry.size = buf.st_size;
781 entry.time = buf.st_atime;
785 throw BESInternalError(
"Zero-byte file found in cache. " + *file, __FILE__, __LINE__);
787 contents.push_back(entry);
792 contents.sort(entry_op);
813 static bool getExclusiveLockNB(
string file_name,
int &ref_fd)
815 BESDEBUG(
"cache2",
"getExclusiveLock_nonblocking: " << file_name <<endl);
818 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
828 struct flock *l = lock(F_WRLCK);
829 if (fcntl(fd, F_SETLK, l) == -1) {
834 "getExclusiveLockNB exit (false): " << file_name <<
" by: " << l->l_pid << endl);
841 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
847 BESDEBUG(
"cache2",
"getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
867 BESDEBUG(
"cache",
"purge - starting the purge" << endl);
873 unsigned long long computed_size = m_collect_cache_dir_info(contents);
875 if (BESISDEBUG(
"cache_contents" )) {
876 BESDEBUG(
"cache",
"BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
877 CacheFiles::iterator ti = contents.begin();
878 CacheFiles::iterator te = contents.end();
879 for (; ti != te; ti++) {
880 BESDEBUG(
"cache", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
885 "BESFileLockingCache::update_and_purge() - current and target size (in MB) " 886 << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
893 CacheFiles::iterator i = contents.begin();
894 while (i != contents.end() && computed_size > d_target_size) {
899 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
900 BESDEBUG(
"cache",
"purge: " << i->name <<
" removed." << endl);
902 if (unlink(i->name.c_str()) != 0)
904 "Unable to purge the file " + i->name +
" from the cache: " + get_errno(), __FILE__,
908 computed_size -= i->size;
913 "BESFileLockingCache::update_and_purge() - current and target size (in MB) " 914 << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
918 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
919 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
921 if (write(d_cache_info_fd, &computed_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
922 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
924 if (BESISDEBUG(
"cache_contents" )) {
926 computed_size = m_collect_cache_dir_info(contents);
927 BESDEBUG(
"cache",
"AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
928 CacheFiles::iterator ti = contents.begin();
929 CacheFiles::iterator te = contents.end();
930 for (; ti != te; ti++) {
931 BESDEBUG(
"cache", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
959 static bool getExclusiveLock(
string file_name,
int &ref_fd)
961 BESDEBUG(
"cache2",
"BESFileLockingCache::getExclusiveLock() - " << file_name <<endl);
964 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
970 BESDEBUG(
"cache2", __func__ <<
"() - FAILED to open file: " << file_name << endl);
975 struct flock *l = lock(F_WRLCK);
976 if (fcntl(fd, F_SETLKW, l) == -1) {
979 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
983 BESDEBUG(
"cache2",
"BESFileLockingCache::getExclusiveLock() - exit: " << file_name <<endl);
1002 BESDEBUG(
"cache",
"BESFileLockingCache::purge_file() - starting the purge" << endl);
1009 if (getExclusiveLock(file, cfile_fd)) {
1011 unsigned long long size = 0;
1013 if (stat(file.c_str(), &buf) == 0) {
1017 BESDEBUG(
"cache",
"BESFileLockingCache::purge_file() - " << file <<
" removed." << endl);
1019 if (unlink(file.c_str()) != 0)
1020 throw BESInternalError(
"Unable to purge the file " + file +
" from the cache: " + get_errno(), __FILE__,
1027 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
1028 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
1030 if (write(d_cache_info_fd, &cache_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
1031 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
1055 return (stat(dir.c_str(), &buf) == 0) && (buf.st_mode & S_IFDIR);
1068 strm << BESIndent::LMarg <<
"BESFileLockingCache::dump - (" << (
void *)
this <<
")" << endl;
1069 BESIndent::Indent();
1070 strm << BESIndent::LMarg <<
"cache dir: " << d_cache_dir << endl;
1071 strm << BESIndent::LMarg <<
"prefix: " << d_prefix << endl;
1072 strm << BESIndent::LMarg <<
"size (bytes): " << d_max_cache_size_in_bytes << endl;
1073 BESIndent::UnIndent();
virtual void unlock_cache()
virtual bool cache_too_big(unsigned long long current_size) const
look at the cache size; is it too large? Look at the cache size and see if it is too big...
exception thrown if inernal error encountered
virtual bool create_and_lock(const string &target, int &fd)
Create a file in the cache and lock it for write access.
void disable()
Disable the cache.
virtual unsigned long long get_cache_size()
Get the cache size. Read the size information from the cache info file and return it...
static string assemblePath(const string &firstPart, const string &secondPart, bool addLeadingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
static bool dir_exists(const string &dir)
Abstract exception class for the BES with basic string message.
virtual void purge_file(const string &file)
Purge a single file from the cache.
const string get_cache_directory()
virtual void lock_cache_write()
virtual void dump(ostream &strm) const
dumps information about this object
virtual string get_cache_file_name(const string &src, bool mangle=true)
void enable()
Enabel the cache.
virtual bool get_read_lock(const string &target, int &fd)
Get a read-only lock on the file if it exists.
const string get_cache_file_prefix()
virtual void update_and_purge(const string &new_file)
Purge files from the cache.
virtual unsigned long long update_cache_info(const string &target)
Update the cache info file to include 'target'.
virtual void lock_cache_read()
virtual void exclusive_to_shared_lock(int fd)
Transfer from an exclusive lock to a shared lock.
virtual void unlock_and_close(const string &target)