49 #include "BESInternalError.h" 55 #include "BESFileLockingCache.h" 60 static const unsigned long long BYTES_PER_MEG = 1048576ULL;
64 static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
79 BESFileLockingCache::BESFileLockingCache(
const string &cache_dir,
const string &prefix,
unsigned long long size) :
80 d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size), d_target_size(0), d_cache_info(
""), d_cache_info_fd(-1)
82 m_initialize_cache_info();
85 void BESFileLockingCache::initialize(
const string &cache_dir,
const string &prefix,
unsigned long long size)
87 d_cache_dir = cache_dir;
91 m_initialize_cache_info();
94 static inline string get_errno() {
95 char *s_err = strerror(errno);
99 return "Unknown error.";
103 static inline struct flock *lock(
int type) {
104 static struct flock lock;
106 lock.l_whence = SEEK_SET;
109 lock.l_pid = getpid();
114 inline void BESFileLockingCache::m_record_descriptor(
const string &file,
int fd) {
115 BESDEBUG(
"cache",
"BESFileLockingCache::m_record_descriptor() - Recording descriptor: " << file <<
", " << fd << endl);
116 d_locks.insert(std::pair<string, int>(file, fd));
119 inline int BESFileLockingCache::m_get_descriptor(
const string &file) {
120 BESDEBUG(
"cache",
"BESFileLockingCache::m_get_descriptor(): d_locks size: " << d_locks.size() << endl);
121 FilesAndLockDescriptors::iterator i = d_locks.find(file);
122 if (i == d_locks.end())
126 BESDEBUG(
"cache",
"BESFileLockingCache::m_get_descriptor(): Found file descriptor [" << fd <<
"] for file: " << file << endl);
136 static void unlock(
int fd)
138 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
139 throw BESInternalError(
"An error occurred trying to unlock the file: " + get_errno(), __FILE__, __LINE__);
143 throw BESInternalError(
"Could not close the (just) unlocked file.", __FILE__, __LINE__);
158 static bool getSharedLock(
const string &file_name,
int &ref_fd)
160 BESDEBUG(
"cache",
"getSharedLock(): Acquiring cache read lock for " << file_name <<endl);
163 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
173 struct flock *l = lock(F_RDLCK);
174 if (fcntl(fd, F_SETLKW, l) == -1) {
177 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
181 BESDEBUG(
"cache",
"getSharedLock(): SUCCESS Read Lock Acquired For " << file_name <<endl);
202 BESDEBUG(
"cache",
"BESFileLockingCache::getExclusiveLock() - " << file_name <<endl);
205 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
211 BESDEBUG(
"cache",
"BESFileLockingCache::getExclusiveLock() - FAILED to open file. name: " << file_name <<
" name_length: " << file_name.length( )<<endl);
216 struct flock *l = lock(F_WRLCK);
217 if (fcntl(fd, F_SETLKW, l) == -1) {
220 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
224 BESDEBUG(
"cache",
"BESFileLockingCache::getExclusiveLock() - exit: " << file_name <<endl);
242 static bool getExclusiveLockNB(
string file_name,
int &ref_fd)
244 BESDEBUG(
"cache",
"getExclusiveLock_nonblocking: " << file_name <<endl);
247 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
257 struct flock *l = lock(F_WRLCK);
258 if (fcntl(fd, F_SETLK, l) == -1) {
261 BESDEBUG(
"cache",
"getExclusiveLock_nonblocking exit (false): " << file_name <<
" by: " << l->l_pid << endl);
268 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
274 BESDEBUG(
"cache",
"getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
294 static bool createLockedFile(
string file_name,
int &ref_fd)
296 BESDEBUG(
"cache",
"createLockedFile() - filename: " << file_name <<endl);
299 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
309 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(
"cache",
"createLockedFile exit: " << file_name <<endl);
325 void BESFileLockingCache::m_check_ctor_params()
330 if (d_cache_dir.empty()) {
331 string err =
"BESFileLockingCache::m_check_ctor_params() - The cache directory was not specified";
345 int statret = stat(d_cache_dir.c_str(), &buf);
346 if (statret != 0 || !S_ISDIR(buf.st_mode)) {
348 int status = mkdir(d_cache_dir.c_str(), 0775);
350 string err =
"BESFileLockingCache::m_check_ctor_params() - The cache directory " + d_cache_dir +
" does not exist or could not be created.";
358 int status = mkdir(d_cache_dir.c_str(), 0775);
361 if (status == -1 && errno != EEXIST) {
362 string err =
"The cache directory " + d_cache_dir +
" could not be created: " + strerror(errno);
363 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
366 if (d_prefix.empty()) {
367 string err =
"The cache file prefix was not specified, must not be empty";
368 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
372 string err =
"The cache size was not specified, must be greater than zero";
373 throw BESError(err, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
376 BESDEBUG(
"cache",
"BESFileLockingCache::m_check_ctor_params() - directory " << d_cache_dir <<
", prefix " << d_prefix
381 void BESFileLockingCache::m_initialize_cache_info()
383 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - BEGIN" << endl);
390 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - d_max_cache_size_in_bytes: " <<
d_max_cache_size_in_bytes 391 <<
" d_target_size: "<<d_target_size<< endl);
394 m_check_ctor_params();
398 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info: " << d_cache_info << endl);
402 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
404 unsigned long long size = 0;
405 if (write(d_cache_info_fd, &size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
406 throw BESInternalError(
"Could not write size info to the cache info file `"+d_cache_info+
"`", __FILE__, __LINE__);
412 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
419 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - d_cache_info_fd: " << d_cache_info_fd << endl);
420 BESDEBUG(
"cache",
"BESFileLockingCache::m_initialize_cache_info() - END" << endl);
437 const string chars_excluded_from_filenames =
"<>=,/()\"\':? []()$";
438 string BESFileLockingCache::get_cache_file_name(
const string &src,
bool mangle)
442 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - src: '" << src <<
"' mangle: "<< mangle << endl);
447 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - target: '" << target <<
"'" << endl);
450 if (target.at(0) ==
'/') {
451 target = src.substr(1, target.length() - 1);
454 string::size_type pos = target.find_first_of(chars_excluded_from_filenames);
455 while (pos != string::npos) {
456 target.replace(pos, 1,
"#", 1);
457 pos = target.find_first_of(chars_excluded_from_filenames);
462 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - target: '" << target <<
"'" << endl);
464 if(target.length()>254){
466 msg <<
"[ERROR} OOPS! Cache filename is longer than 254 characters. The file system is going to balk at that. name.length: ";
467 msg << target.length() <<
" characters. name.value: " << target;
468 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - " << msg.str() << endl);
475 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - d_cache_dir: '" << d_cache_dir <<
"'" << endl);
476 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - d_prefix: '" << d_prefix <<
"'" << endl);
477 BESDEBUG(
"cache",
"BESFileLockingCache::get_cache_file_name() - target: '" << target <<
"'" << endl);
503 bool status = getSharedLock(target, fd);
505 BESDEBUG(
"cache2",
"BESFileLockingCache::get_read_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
508 m_record_descriptor(target, fd);
531 bool status = createLockedFile(target, fd);
533 BESDEBUG(
"cache2",
"BESFileLockingCache::create_and_lock() - " << target <<
" (status: " << status <<
", fd: " << fd <<
")" << endl);
536 m_record_descriptor(target, fd);
560 lock.l_type = F_RDLCK;
561 lock.l_whence = SEEK_SET;
564 lock.l_pid = getpid();
566 if (fcntl(fd, F_SETLKW, &lock) == -1) {
581 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_write() - d_cache_info_fd: " << d_cache_info_fd << endl);
583 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
584 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__, __LINE__);
593 BESDEBUG(
"cache",
"BESFileLockingCache::lock_cache_read() - d_cache_info_fd: " << d_cache_info_fd << endl);
595 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
596 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__, __LINE__);
607 BESDEBUG(
"cache",
"BESFileLockingCache::unlock_cache() - d_cache_info_fd: " << d_cache_info_fd << endl);
609 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
610 throw BESInternalError(
"An error occurred trying to unlock the cache-control file" + get_errno(), __FILE__, __LINE__);
631 BESDEBUG(
"cache2",
"BESFileLockingCache::unlock_and_close() - BEGIN file: " << file_name << endl);
633 int fd = m_get_descriptor(file_name);
636 fd = m_get_descriptor(file_name);
638 BESDEBUG(
"cache2",
"BESFileLockingCache::unlock_and_close() - END"<< endl);
654 unsigned long long current_size;
658 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
659 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
662 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
663 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
666 int statret = stat(target.c_str(), &buf);
668 current_size += buf.st_size;
670 throw BESInternalError(
"Could not read the size of the new file: " + target +
" : " + get_errno(), __FILE__, __LINE__);
672 BESDEBUG(
"cache",
"BESFileLockingCache::update_cache_info() - cache size updated to: " << current_size << endl);
674 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
675 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
677 if(write(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
678 throw BESInternalError(
"Could not write size info from the cache info file!", __FILE__, __LINE__);
708 unsigned long long current_size;
712 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
713 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
715 if(read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
716 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
731 return e1.time < e2.time;
735 unsigned long long BESFileLockingCache::m_collect_cache_dir_info(CacheFiles &contents)
737 DIR *dip = opendir(d_cache_dir.c_str());
739 throw BESInternalError(
"Unable to open cache directory " + d_cache_dir, __FILE__, __LINE__);
742 vector<string> files;
745 while ((dit = readdir(dip)) != NULL) {
746 string dirEntry = dit->d_name;
747 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0 && dirEntry!=d_cache_info) {
748 files.push_back(d_cache_dir +
"/" + dirEntry);
754 unsigned long long current_size = 0;
756 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
757 if (stat(file->c_str(), &buf) == 0) {
758 current_size += buf.st_size;
761 entry.size = buf.st_size;
762 entry.time = buf.st_atime;
766 throw BESInternalError(
"Zero-byte file found in cache. " + *file, __FILE__, __LINE__);
768 contents.push_back(entry);
773 contents.sort(entry_op);
791 BESDEBUG(
"cache",
"purge - starting the purge" << endl);
797 unsigned long long computed_size = m_collect_cache_dir_info(contents);
799 if (BESISDEBUG(
"cache_contents" )) {
800 BESDEBUG(
"cache",
"BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
801 CacheFiles::iterator ti = contents.begin();
802 CacheFiles::iterator te = contents.end();
803 for (; ti != te; ti++) {
804 BESDEBUG(
"cache", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
808 BESDEBUG(
"cache",
"BESFileLockingCache::update_and_purge() - current and target size (in MB) " << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl );
815 CacheFiles::iterator i = contents.begin();
816 while (i != contents.end() && computed_size > d_target_size) {
821 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
822 BESDEBUG(
"cache",
"purge: " << i->name <<
" removed." << endl );
824 if (unlink(i->name.c_str()) != 0)
825 throw BESInternalError(
"Unable to purge the file " + i->name +
" from the cache: " + get_errno(), __FILE__, __LINE__);
828 computed_size -= i->size;
832 BESDEBUG(
"cache",
"BESFileLockingCache::update_and_purge() - current and target size (in MB) " << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl );
837 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
838 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
840 if(write(d_cache_info_fd, &computed_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
841 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
843 if (BESISDEBUG(
"cache_contents" )) {
845 computed_size = m_collect_cache_dir_info(contents);
846 BESDEBUG(
"cache",
"AFTER 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 );
875 BESDEBUG(
"cache",
"BESFileLockingCache::purge_file() - starting the purge" << endl);
884 unsigned long long size = 0;
886 if (stat(file.c_str(), &buf) == 0) {
890 BESDEBUG(
"cache",
"BESFileLockingCache::purge_file() - " << file <<
" removed." << endl );
892 if (unlink(file.c_str()) != 0)
893 throw BESInternalError(
"Unable to purge the file " + file +
" from the cache: " + get_errno(), __FILE__, __LINE__);
899 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
900 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
902 if (write(d_cache_info_fd, &cache_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
903 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
915 const string BESFileLockingCache::getCacheFilePrefix(){
919 const string BESFileLockingCache::getCacheDirectory(){
933 return (stat(dir.c_str(), &buf) == 0) && (buf.st_mode & S_IFDIR);
945 strm << BESIndent::LMarg <<
"BESFileLockingCache::dump - (" << (
void *)
this <<
")" << endl;
947 strm << BESIndent::LMarg <<
"cache dir: " << d_cache_dir << endl;
948 strm << BESIndent::LMarg <<
"prefix: " << d_prefix << endl;
950 BESIndent::UnIndent();
virtual void unlock_cache()
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)
virtual void dump(ostream &strm) const
dumps information about this object
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 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...
virtual void lock_cache_write()
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)