52 static const unsigned long long BYTES_PER_MEG = 1048576ULL;
56 static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
77 d_instance =
new BESCache3(keys, cache_dir_key, prefix_key, size_key);
89 d_instance =
new BESCache3(cache_dir, prefix, size);
101 throw BESInternalError(
"Tried to get the BESCache3 instance, but it hasn't been created yet", __FILE__, __LINE__);
106 static inline string get_errno() {
107 char *s_err = strerror(errno);
111 return "Unknown error.";
117 static inline struct flock *lock(
int type) {
118 static struct flock lock;
120 lock.l_whence = SEEK_SET;
123 lock.l_pid = getpid();
128 inline void BESCache3::m_record_descriptor(
const string &file,
int fd) {
129 BESDEBUG(
"cache",
"BES Cache: recording descriptor: " << file <<
", " << fd << endl);
130 d_locks.insert(std::pair<string, int>(file, fd));
135 inline int BESCache3::m_get_descriptor(
const string &file) {
136 FilesAndLockDescriptors::iterator i = d_locks.find(file);
137 if (i == d_locks.end())
141 BESDEBUG(
"cache",
"BES Cache: getting descriptor: " << file <<
", " << fd << endl);
151 static void unlock(
int fd)
153 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
154 throw BESInternalError(
"An error occurred trying to unlock the file" + get_errno(), __FILE__, __LINE__);
158 throw BESInternalError(
"Could not close the (just) unlocked file.", __FILE__, __LINE__);
173 static bool getSharedLock(
const string &file_name,
int &ref_fd)
175 BESDEBUG(
"cache_internal",
"getSharedLock: " << file_name <<endl);
178 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
184 throw BESInternalError(
"Could not get shared lock for " + file_name +
": " + get_errno(), __FILE__, __LINE__);
188 struct flock *l = lock(F_RDLCK);
189 if (fcntl(fd, F_SETLKW, l) == -1) {
192 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
196 BESDEBUG(
"cache_internal",
"getSharedLock exit: " << file_name <<endl);
216 static bool getExclusiveLock(
string file_name,
int &ref_fd)
218 BESDEBUG(
"cache_internal",
"getExclusiveLock: " << file_name <<endl);
221 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
227 throw BESInternalError(
"Could not get exclusive lock for " + file_name +
": " + get_errno(), __FILE__, __LINE__);
231 struct flock *l = lock(F_WRLCK);
232 if (fcntl(fd, F_SETLKW, l) == -1) {
235 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
239 BESDEBUG(
"cache_internal",
"getExclusiveLock exit: " << file_name <<endl);
258 static bool getExclusiveLockNB(
string file_name,
int &ref_fd)
260 BESDEBUG(
"cache_internal",
"getExclusiveLock_nonblocking: " << file_name <<endl);
263 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
269 throw BESInternalError(
"Could not get a non-blocking exclusive lock for " + file_name +
": " + get_errno(), __FILE__, __LINE__);
273 struct flock *l = lock(F_WRLCK);
274 if (fcntl(fd, F_SETLK, l) == -1) {
277 BESDEBUG(
"cache_internal",
"getExclusiveLock_nonblocking exit (false): " << file_name <<
" by: " << l->l_pid << endl);
284 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
290 BESDEBUG(
"cache_internal",
"getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
310 static bool createLockedFile(
string file_name,
int &ref_fd)
312 BESDEBUG(
"cache_internal",
"createLockedFile: " << file_name <<endl);
315 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
321 throw BESInternalError(
"Could not create locked file (" + file_name +
"): " + get_errno(), __FILE__, __LINE__);
325 struct flock *l = lock(F_WRLCK);
326 if (fcntl(fd, F_SETLKW, l) == -1) {
329 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error: " << get_errno();
333 BESDEBUG(
"cache_internal",
"createLockedFile exit: " << file_name <<endl);
341 void BESCache3::m_check_ctor_params()
343 if (d_cache_dir.empty()) {
344 string err =
"The cache directory was not specified, must be non-empty";
349 int statret = stat(d_cache_dir.c_str(), &buf);
350 if (statret != 0 || !S_ISDIR(buf.st_mode)) {
351 string err =
"The cache directory " + d_cache_dir +
" does not exist";
355 if (d_prefix.empty()) {
356 string err =
"The cache file prefix was not specified, must not be empty";
360 if (d_max_cache_size_in_bytes <= 0) {
361 string err =
"The cache size was not specified, must be greater than zero";
367 if (d_max_cache_size_in_bytes > MAX_CACHE_SIZE_IN_MEGABYTES) {
368 std::ostringstream msg;
369 msg <<
"The specified cache size was larger than the max cache size of: " << MAX_CACHE_SIZE_IN_MEGABYTES;
373 BESDEBUG(
"cache_internal",
"BES Cache: directory " << d_cache_dir
374 <<
", prefix " << d_prefix
375 <<
", max size " << d_max_cache_size_in_bytes << endl );
379 void BESCache3::m_initialize_cache_info()
381 d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
382 if (d_max_cache_size_in_bytes > MAX_CACHE_SIZE_IN_MEGABYTES)
383 *(
BESLog::TheLog()) <<
"Cache size too big in configuration file, set to max limit." << endl ;
385 d_max_cache_size_in_bytes *= BYTES_PER_MEG;
386 d_target_size = d_max_cache_size_in_bytes * 0.8;
388 m_check_ctor_params();
390 d_cache_info = d_cache_dir +
"/bes.cache.info";
394 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
396 unsigned long long size = 0;
397 if (write(d_cache_info_fd, &size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
398 throw BESInternalError(
"Could not write size info to the cache info file in startup!", __FILE__, __LINE__);
404 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
405 throw BESInternalError(
"Could not open the cache info file (" + d_cache_info +
"): " + get_errno(), __FILE__, __LINE__);
409 BESDEBUG(
"cache_internal",
"d_cache_info_fd: " << d_cache_info_fd << endl);
426 BESCache3::BESCache3(
BESKeys *keys,
const string &cache_dir_key,
const string &prefix_key,
const string &size_key) :
427 d_max_cache_size_in_bytes(0)
430 keys->
get_value(cache_dir_key, d_cache_dir, found);
432 throw BESSyntaxUserError(
"The cache directory key " + cache_dir_key +
" was not found in the BES configuration file", __FILE__, __LINE__);
435 keys->
get_value(prefix_key, d_prefix, found);
437 throw BESSyntaxUserError(
"The prefix key " + prefix_key +
" was not found in the BES configuration file", __FILE__, __LINE__);
440 string cache_size_str;
441 keys->
get_value(size_key, cache_size_str, found);
443 throw BESSyntaxUserError(
"The size key " + size_key +
" was not found in the BES configuration file", __FILE__, __LINE__);
445 std::istringstream is(cache_size_str);
446 is >> d_max_cache_size_in_bytes;
448 m_initialize_cache_info();
457 BESCache3::BESCache3(
const string &cache_dir,
const string &prefix,
unsigned long size) :
458 d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size)
460 m_initialize_cache_info();
475 if (target.at(0) ==
'/') {
476 target = src.substr(1, target.length() - 1);
478 string::size_type slash = 0;
479 while ((slash = target.find(
'/')) != string::npos) {
480 target.replace(slash, 1, 1, BESCache3::BES_CACHE_CHAR);
482 string::size_type last_dot = target.rfind(
'.');
483 if (last_dot != string::npos) {
484 target = target.substr(0, last_dot);
487 return d_cache_dir +
"/" + d_prefix + BESCache3::BES_CACHE_CHAR + target;
511 bool status = getSharedLock(target, fd);
513 BESDEBUG(
"cache_internal",
"BES Cache: get_read_lock: " << target <<
"(" << status <<
")" << endl);
516 m_record_descriptor(target, fd);
539 bool status = createLockedFile(target, fd);
541 BESDEBUG(
"cache_internal",
"BES Cache: create_and_lock: " << target <<
"(" << status <<
")" << endl);
544 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) {
575 throw BESInternalError(
"Could not convert an exclusive to a shared lock: " + get_errno(), __FILE__, __LINE__);
589 BESDEBUG(
"cache_internal",
"lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
591 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
592 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__, __LINE__);
601 BESDEBUG(
"cache_internal",
"lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
603 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
604 throw BESInternalError(
"An error occurred trying to lock the cache-control file" + get_errno(), __FILE__, __LINE__);
615 BESDEBUG(
"cache_internal",
"BES Cache: unlock: cache_info (fd: " << d_cache_info_fd <<
")" << endl);
617 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
618 throw BESInternalError(
"An error occurred trying to unlock the cache-control file" + get_errno(), __FILE__, __LINE__);
635 BESDEBUG(
"cache_internal",
"BES Cache: unlock file: " << file_name << endl);
637 int fd = m_get_descriptor(file_name);
640 fd = m_get_descriptor(file_name);
651 BESDEBUG(
"cache_internal",
"BES Cache: unlock fd: " << fd << endl);
655 BESDEBUG(
"cache_internal",
"BES Cache: unlock " << fd <<
" Success" << 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__);
678 unsigned long long current_size;
679 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
680 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
683 int statret = stat(target.c_str(), &buf);
685 current_size += buf.st_size;
687 throw BESInternalError(
"Could not read the size of the new file: " + target +
" : " + get_errno(), __FILE__, __LINE__);
689 BESDEBUG(
"cache_internal",
"BES Cache: cache size updated to: " << current_size << endl);
691 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
692 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
694 if (write(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
695 throw BESInternalError(
"Could not write size info from the cache info file!", __FILE__, __LINE__);
715 return current_size > d_max_cache_size_in_bytes;
731 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
732 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
734 unsigned long long current_size;
735 if (read(d_cache_info_fd, ¤t_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
736 throw BESInternalError(
"Could not get read size info from the cache info file!", __FILE__, __LINE__);
757 unsigned long long BESCache3::m_collect_cache_dir_info(
CacheFiles &contents)
759 DIR *dip = opendir(d_cache_dir.c_str());
761 throw BESInternalError(
"Unable to open cache directory " + d_cache_dir, __FILE__, __LINE__);
764 vector<string> files;
767 while ((dit = readdir(dip)) != NULL) {
768 string dirEntry = dit->d_name;
769 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0) {
770 files.push_back(d_cache_dir +
"/" + dirEntry);
776 unsigned long long current_size = 0;
778 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
779 if (stat(file->c_str(), &buf) == 0) {
780 current_size += buf.st_size;
783 entry.
size = buf.st_size;
784 entry.
time = buf.st_atime;
788 throw BESInternalError(
"Zero-byte file found in cache. " + *file, __FILE__, __LINE__);
790 contents.push_back(entry);
795 contents.sort(entry_op);
813 BESDEBUG(
"cache_purge",
"purge - starting the purge" << endl);
819 unsigned long long computed_size = m_collect_cache_dir_info(contents);
822 BESDEBUG(
"cache_contents", endl <<
"BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
823 CacheFiles::iterator ti = contents.begin();
824 CacheFiles::iterator te = contents.end();
825 for (; ti != te; ti++) {
826 BESDEBUG(
"cache_contents", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
830 BESDEBUG(
"cache_purge",
"purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl );
837 CacheFiles::iterator i = contents.begin();
838 while (i != contents.end() && computed_size > d_target_size) {
843 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
844 BESDEBUG(
"cache_purge",
"purge: " << i->name <<
" removed." << endl );
846 if (unlink(i->name.c_str()) != 0)
847 throw BESInternalError(
"Unable to purge the file " + i->name +
" from the cache: " + get_errno(), __FILE__, __LINE__);
850 computed_size -= i->size;
855 BESDEBUG(
"cache_purge",
"purge: " << i->name <<
" is in use." << endl );
860 BESDEBUG(
"cache_purge",
"purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG <<
", " << d_target_size/BYTES_PER_MEG << endl );
865 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
866 throw BESInternalError(
"Could not rewind to front of cache info file.", __FILE__, __LINE__);
868 if(write(d_cache_info_fd, &computed_size,
sizeof(
unsigned long long)) !=
sizeof(
unsigned long long))
869 throw BESInternalError(
"Could not write size info to the cache info file!", __FILE__, __LINE__);
873 computed_size = m_collect_cache_dir_info(contents);
874 BESDEBUG(
"cache_contents", endl <<
"AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
875 CacheFiles::iterator ti = contents.begin();
876 CacheFiles::iterator te = contents.end();
877 for (; ti != te; ti++) {
878 BESDEBUG(
"cache_contents", (*ti).time <<
": " << (*ti).name <<
": size " << (*ti).size/BYTES_PER_MEG << endl );
899 strm <<
BESIndent::LMarg <<
"BESCache3::dump - (" << (
void *)
this <<
")" << endl;
903 strm <<
BESIndent::LMarg <<
"size (bytes): " << d_max_cache_size_in_bytes << endl;
virtual void exclusive_to_shared_lock(int fd)
Transfer from an exclusive lock to a shared lock.
virtual void dump(ostream &strm) const
dumps information about this object
#define BESISDEBUG(x)
macro used to determine if the specified debug context is set
std::list< cache_entry > CacheFiles
exception thrown if inernal error encountered
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()
Get an exclusive lock on the 'cache info' file.
virtual void unlock_and_close(const string &target)
Unlock the named file.
error thrown if there is a user syntax error in the request or any other user error ...
virtual void lock_cache_read()
Get a shared lock on the 'cache info' file.
mapping of key/value pairs defining different behaviors of an application.
Implementation of a caching mechanism for compressed data.
static ostream & LMarg(ostream &strm)
virtual unsigned long long update_cache_info(const string &target)
Update the cache info file to include 'target'.
virtual bool create_and_lock(const string &target, int &fd)
Create a file in the cache and lock it for write access.
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
virtual bool get_read_lock(const string &target, int &fd)
Get a read-only lock on the file if it exists.
string get_cache_file_name(const string &src)
Build the name of file that will holds the uncompressed data from 'src' in the cache.
virtual void update_and_purge(const string &new_file)
Purge files from the cache.
virtual void unlock_cache()
Unlock the cache info file.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
virtual unsigned long long get_cache_size()
Get the cache size.
static BESCache3 * get_instance()
Get an instance of the BESCache3 object.