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;
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);
119 d_locks.insert(std::pair<string, int>(file, fd));
122 inline int BESFileLockingCache::m_get_descriptor(
const string &file)
124 BESDEBUG(
"cache",
"BESFileLockingCache::m_get_descriptor(): d_locks size: " << d_locks.size() << endl);
125 FilesAndLockDescriptors::iterator i = d_locks.find(file);
126 if (i == d_locks.end())
return -1;
130 "BESFileLockingCache::m_get_descriptor(): Found file descriptor [" << fd <<
"] for file: " << file << endl);
135 string lockStatus(
const int fd)
137 struct flock isLocked, lock_query;
139 isLocked.l_type = F_WRLCK;
140 isLocked.l_start = 0;
141 isLocked.l_whence = SEEK_SET;
143 lock_query = isLocked;
145 int ret = fcntl(fd, F_GETLK, &lock_query);
150 ss <<
"ERROR! fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret <<
" errno[" << errno <<
"]: " 151 << strerror(errno) << endl;
154 ss <<
"SUCCESS. fnctl(" << fd <<
",F_GETLK, &lock) returned: " << ret << endl;
157 ss <<
"lock_info.l_len: " << lock_query.l_len << endl;
158 ss <<
"lock_info.l_pid: " << lock_query.l_pid << endl;
159 ss <<
"lock_info.l_start: " << lock_query.l_start << endl;
162 switch (lock_query.l_type) {
175 ss <<
"lock_info.l_type: " << type << endl;
176 ss <<
"lock_info.l_whence: " << lock_query.l_whence << endl;
186 static void unlock(
int fd)
188 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
189 throw BESInternalError(
"An error occurred trying to unlock the file: " + get_errno(), __FILE__, __LINE__);
192 BESDEBUG(
"cache",
"BESFileLockingCache::unlock() - lock status: " << lockStatus(fd) << endl);
194 if (close(fd) == -1)
throw BESInternalError(
"Could not close the (just) unlocked file.", __FILE__, __LINE__);
196 BESDEBUG(
"cache",
"BESFileLockingCache::unlock() - File Closed. fd: " << fd << endl);
211 static bool getSharedLock(
const string &file_name,
int &ref_fd)
213 BESDEBUG(
"cache",
"getSharedLock(): Acquiring cache read lock for " << file_name <<endl);
216 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
226 struct flock *l = lock(F_RDLCK);
227 if (fcntl(fd, F_SETLKW, l) == -1) {
230 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
234 BESDEBUG(
"cache",
"getSharedLock(): SUCCESS Read Lock Acquired For " << file_name <<endl);
255 BESDEBUG(
"cache",
"BESFileLockingCache::getExclusiveLock() - " << file_name <<endl);
258 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
265 "BESFileLockingCache::getExclusiveLock() - FAILED to open file. name: " << file_name <<
" name_length: " << file_name.length( )<<endl);
270 struct flock *l = lock(F_WRLCK);
271 if (fcntl(fd, F_SETLKW, l) == -1) {
274 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
278 BESDEBUG(
"cache",
"BESFileLockingCache::getExclusiveLock() - exit: " << file_name <<endl);
296 static bool getExclusiveLockNB(
string file_name,
int &ref_fd)
298 BESDEBUG(
"cache",
"getExclusiveLock_nonblocking: " << file_name <<endl);
301 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
311 struct flock *l = lock(F_WRLCK);
312 if (fcntl(fd, F_SETLK, l) == -1) {
316 "getExclusiveLock_nonblocking exit (false): " << file_name <<
" by: " << l->l_pid << endl);
323 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
329 BESDEBUG(
"cache",
"getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
349 static bool createLockedFile(
string file_name,
int &ref_fd)
351 BESDEBUG(
"cache",
"createLockedFile() - filename: " << file_name <<endl);
354 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
364 struct flock *l = lock(F_WRLCK);
365 if (fcntl(fd, F_SETLKW, l) == -1) {
368 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
372 BESDEBUG(
"cache",
"createLockedFile exit: " << file_name <<endl);
380 void BESFileLockingCache::m_check_ctor_params()
393 if (d_cache_dir.empty()) {
394 string err =
"BESFileLockingCache::m_check_ctor_params() - The cache directory was not specified";
398 int status = mkdir(d_cache_dir.c_str(), 0775);
401 if (status == -1 && errno != EEXIST) {
402 string err =
"The cache directory " + d_cache_dir +
" could not be created: " + strerror(errno);
403 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
406 if (d_prefix.empty()) {
407 string err =
"The cache file prefix was not specified, must not be empty";
408 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
412 string err =
"The cache size was not specified, must be greater than zero";
413 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
417 "BESFileLockingCache::m_check_ctor_params() - directory " << d_cache_dir <<
", prefix " << d_prefix <<
", max size " <<
d_max_cache_size_in_bytes << endl);
421 void BESFileLockingCache::m_initialize_cache_info()
423 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - BEGIN" << endl);
431 "BESFileLockingCache::m_initialize_cache_info() - d_max_cache_size_in_bytes: " <<
d_max_cache_size_in_bytes <<
" d_target_size: "<<d_target_size<< endl);
433 m_check_ctor_params();
437 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info: " << d_cache_info << endl);
441 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
443 unsigned long long size = 0;
444 if (write(d_cache_info_fd, &size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
445 throw BESInternalError(
"Could not write size info to the cache info file `" + d_cache_info +
"`", __FILE__,
452 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
457 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info_fd: " << d_cache_info_fd << endl);
458 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - END" << endl);
461 const string chars_excluded_from_filenames =
"<>=,/()\\\"\':? []()$";
481 BESDEBUG(
"cache", __FUNCTION__ <<
" - src: '" << src <<
"' mangle: "<< mangle << endl);
483 string target = getCacheFilePrefix() + src;
493 if (target.at(0) ==
'/') {
494 target = src.substr(1, target.length() - 1);
497 string::size_type pos = target.find_first_of(chars_excluded_from_filenames);
498 while (pos != string::npos) {
499 target.replace(pos, 1,
"#", 1);
500 pos = target.find_first_of(chars_excluded_from_filenames);
504 if (target.length() > 254) {
506 msg <<
"Cache filename is longer than 254 characters (name length: ";
507 msg << target.length() <<
", name: " << target;
513 BESDEBUG(
"cache", __FUNCTION__ <<
" - target: '" << target <<
"'" << endl);
539 bool status = getSharedLock(target, fd);
542 "BESFileLockingCache::get_read_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
544 if (status) m_record_descriptor(target, fd);
567 bool status = createLockedFile(target, fd);
570 "BESFileLockingCache::create_and_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
572 if (status) m_record_descriptor(target, fd);
595 lock.l_type = F_RDLCK;
596 lock.l_whence = SEEK_SET;
599 lock.l_pid = getpid();
601 if (fcntl(fd, F_SETLKW, &lock) == -1) {
605 BESDEBUG(
"cache",
"BESFileLockingCache::exclusive_to_shared_lock() - lock status: " << lockStatus(fd) << endl);
618 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_write() - d_cache_info_fd: " << d_cache_info_fd << endl);
620 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
621 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
625 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_write() - lock status: " << lockStatus(d_cache_info_fd) << endl);
633 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_read() - d_cache_info_fd: " << d_cache_info_fd << endl);
635 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
636 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__,
640 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_read() - lock status: " << lockStatus(d_cache_info_fd) << endl);
650 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_cache() - d_cache_info_fd: " << d_cache_info_fd << endl);
652 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
653 throw BESInternalError(
"An error occurred trying to unlock the cache-control file" + get_errno(), __FILE__,
657 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_cache() - lock status: " << lockStatus(d_cache_info_fd) << endl);
677 BESDEBUG(
"cache2",
"BESFileLockingCache::unlock_and_close() - BEGIN file: " << file_name << endl);
679 int fd = m_get_descriptor(file_name);
682 fd = m_get_descriptor(file_name);
685 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_and_close() - lock status: " << lockStatus(d_cache_info_fd) << endl);
686 BESDEBUG(
"cache2",
"BESFileLockingCache::unlock_and_close() - END"<< endl);
701 unsigned long long current_size;
705 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
706 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
709 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
710 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
713 int statret = stat(target.c_str(), &buf);
715 current_size += buf.st_size;
717 throw BESInternalError(
"Could not read the size of the new file: " + target +
" : " + get_errno(), __FILE__,
720 BESDEBUG(
"cache",
"BESFileLockingCache::update_cache_info() - cache size updated to: " << current_size << endl);
722 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
723 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
725 if (write(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
726 throw BESInternalError(
"Could not write size info from the cache info file!", __FILE__, __LINE__);
756 unsigned long long current_size;
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 (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
764 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
778 return e1.time < e2.time;
782 unsigned long long BESFileLockingCache::m_collect_cache_dir_info(CacheFiles &contents)
784 DIR *dip = opendir(d_cache_dir.c_str());
785 if (!dip)
throw BESInternalError(
"Unable to open cache directory " + d_cache_dir, __FILE__, __LINE__);
788 vector<string> files;
791 while ((dit = readdir(dip)) != NULL) {
792 string dirEntry = dit->d_name;
793 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0 && dirEntry != d_cache_info) {
794 files.push_back(d_cache_dir +
"/" + dirEntry);
800 unsigned long long current_size = 0;
802 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
803 if (stat(file->c_str(), &buf) == 0) {
804 current_size += buf.st_size;
807 entry.size = buf.st_size;
808 entry.time = buf.st_atime;
812 throw BESInternalError(
"Zero-byte file found in cache. " + *file, __FILE__, __LINE__);
814 contents.push_back(entry);
819 contents.sort(entry_op);
837 BESDEBUG(
"cache",
"purge - starting the purge" << endl);
843 unsigned long long computed_size = m_collect_cache_dir_info(contents);
845 if (BESISDEBUG(
"cache_contents" )) {
846 BESDEBUG(
"cache",
"BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
847 CacheFiles::iterator ti = contents.begin();
848 CacheFiles::iterator te = contents.end();
849 for (; ti != te; ti++) {
850 BESDEBUG(
"cache", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
855 "BESFileLockingCache::update_and_purge() - current and target size (in MB) " << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
862 CacheFiles::iterator i = contents.begin();
863 while (i != contents.end() && computed_size > d_target_size) {
868 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
869 BESDEBUG(
"cache",
"purge: " << i->name <<
" removed." << endl);
871 if (unlink(i->name.c_str()) != 0)
873 "Unable to purge the file " + i->name +
" from the cache: " + get_errno(), __FILE__,
877 computed_size -= i->size;
882 "BESFileLockingCache::update_and_purge() - current and target size (in MB) " << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl);
886 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
887 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
889 if (write(d_cache_info_fd, &computed_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
890 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
892 if (BESISDEBUG(
"cache_contents" )) {
894 computed_size = m_collect_cache_dir_info(contents);
895 BESDEBUG(
"cache",
"AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
896 CacheFiles::iterator ti = contents.begin();
897 CacheFiles::iterator te = contents.end();
898 for (; ti != te; ti++) {
899 BESDEBUG(
"cache", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
924 BESDEBUG(
"cache",
"BESFileLockingCache::purge_file() - starting the purge" << endl);
933 unsigned long long size = 0;
935 if (stat(file.c_str(), &buf) == 0) {
939 BESDEBUG(
"cache",
"BESFileLockingCache::purge_file() - " << file <<
" removed." << endl);
941 if (unlink(file.c_str()) != 0)
942 throw BESInternalError(
"Unable to purge the file " + file +
" from the cache: " + get_errno(), __FILE__,
949 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
950 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
952 if (write(d_cache_info_fd, &cache_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
953 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
964 const string BESFileLockingCache::getCacheFilePrefix()
969 const string BESFileLockingCache::getCacheDirectory()
984 return (stat(dir.c_str(), &buf) == 0) && (buf.st_mode & S_IFDIR);
996 strm << BESIndent::LMarg <<
"BESFileLockingCache::dump - (" << (
void *)
this <<
")" << endl;
998 strm << BESIndent::LMarg <<
"cache dir: " << d_cache_dir << endl;
999 strm << BESIndent::LMarg <<
"prefix: " << d_prefix << endl;
1001 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. If the file does not exist, make it, open it for read-write access and get an exclusive lock on it. The locking operation blocks, although that should never happen.
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.
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)
virtual bool get_read_lock(const string &target, int &fd)
Get a read-only lock on the file if it exists.
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'.
unsigned long long d_max_cache_size_in_bytes
How many bytes can the cache hold before we have to purge.
virtual void lock_cache_read()
virtual void exclusive_to_shared_lock(int fd)
Transfer from an exclusive lock to a shared lock. If the file has an exclusive write lock on it...
virtual bool getExclusiveLock(string file_name, int &ref_fd)
virtual void unlock_and_close(const string &target)