libdap Updated for version 3.21.0
libdap4 is an implementation of OPeNDAP's DAP protocol.
DAPCache3.cc
1// DAPCache3.cc
2
3// This file was originally part of bes, A C++ back-end server
4// implementation framework for the OPeNDAP Data Access Protocol.
5// Copied to libdap. This is used to cache responses built from
6// functional CE expressions.
7
8// Copyright (c) 2012 OPeNDAP, Inc
9// Author: James Gallagher <jgallagher@opendap.org>
10// Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
11//
12// This library is free software; you can redistribute it and/or
13// modify it under the terms of the GNU Lesser General Public
14// License as published by the Free Software Foundation; either
15// version 2.1 of the License, or (at your option) any later version.
16//
17// This library is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20// Lesser General Public License for more details.
21//
22// You should have received a copy of the GNU Lesser General Public
23// License along with this library; if not, write to the Free Software
24// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25//
26// You can contact University Corporation for Atmospheric Research at
27// 3080 Center Green Drive, Boulder, CO 80301
28
29#include "config.h"
30
31#include <sys/file.h>
32#include <sys/stat.h>
33#include <unistd.h>
34#include <dirent.h>
35#include <fcntl.h>
36
37#ifdef HAVE_STDLIB_H
38#include <stdlib.h>
39#endif
40
41#include <string>
42#include <sstream>
43#include <vector>
44#include <cstring>
45#include <cerrno>
46
47#include "DAPCache3.h"
48
49//#define DODS_DEBUG
50
51#include "InternalErr.h"
52#include "DapIndent.h"
53#include "debug.h"
54
55#if 0
56#include "BESSyntaxUserError.h"
57#include "BESInternalError.h"
58
59#include "TheBESKeys.h"
60#include "BESDebug.h"
61#include "BESLog.h"
62#endif
63using namespace std;
64using namespace libdap;
65
66namespace libdap {
67
68// conversion factor
69static const unsigned long long BYTES_PER_MEG = 1048576ULL;
70
71// Max cache size in megs, so we can check the user input and warn.
72// 2^64 / 2^20 == 2^44
73static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
74
75DAPCache3 *DAPCache3::d_instance = 0;
76
77
91DAPCache3::DAPCache3(const string &cache_dir, const string &prefix, unsigned long long size) :
92 d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size)
93{
94 m_initialize_cache_info();
95}
96
97void DAPCache3::delete_instance() {
98 DBG(cerr << "DAPCache3::delete_instance() - Deleting singleton DAPCache3 instance." << endl);
99 delete d_instance;
100 d_instance = 0;
101}
102
103#if 0
104// The BESCache3 code is a singleton that assumes it's running in the absence of threads but that
105// the cache is shared by several processes, each of which have their own instance of BESCache3.
117BESCache3 *
118BESCache3::get_instance(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key)
119{
120 if (d_instance == 0)
121 d_instance = new BESCache3(keys, cache_dir_key, prefix_key, size_key);
122
123 return d_instance;
124}
125#endif
137DAPCache3 *
138DAPCache3::get_instance(const string &cache_dir, const string &prefix, unsigned long long size)
139{
140 if (d_instance == 0){
141 d_instance = new DAPCache3(cache_dir, prefix, size);
142#if HAVE_ATEXIT
143 atexit(delete_instance);
144#endif
145 }
146 return d_instance;
147}
148
152DAPCache3 *
154{
155 if (d_instance == 0)
156 throw InternalErr(__FILE__, __LINE__, "Tried to get the DAPCache3 instance, but it hasn't been created yet");
157
158 return d_instance;
159}
160
161static inline string get_errno() {
162 char *s_err = strerror(errno);
163 if (s_err)
164 return s_err;
165 else
166 return "Unknown error.";
167}
168
169// Build a lock of a certain type.
170static inline struct flock *lock(int type) {
171 static struct flock lock;
172 lock.l_type = type;
173 lock.l_whence = SEEK_SET;
174 lock.l_start = 0;
175 lock.l_len = 0;
176 lock.l_pid = getpid();
177
178 return &lock;
179}
180
181inline void DAPCache3::m_record_descriptor(const string &file, int fd) {
182 DBG(cerr << "DAP Cache: recording descriptor: " << file << ", " << fd << endl);
183 d_locks.insert(std::pair<string, int>(file, fd));
184}
185
186inline int DAPCache3::m_get_descriptor(const string &file) {
187 FilesAndLockDescriptors::iterator i = d_locks.find(file);
188 int fd = i->second;
189 DBG(cerr << "DAP Cache: getting descriptor: " << file << ", " << fd << endl);
190 d_locks.erase(i);
191 return fd;
192}
193
199static void unlock(int fd)
200{
201 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
202 throw InternalErr(__FILE__, __LINE__, "An error occurred trying to unlock the file" + get_errno());
203 }
204
205 if (close(fd) == -1)
206 throw InternalErr(__FILE__, __LINE__, "Could not close the (just) unlocked file.");
207}
208
221static bool getSharedLock(const string &file_name, int &ref_fd)
222{
223 DBG(cerr << "getSharedLock: " << file_name <<endl);
224
225 int fd;
226 if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
227 switch (errno) {
228 case ENOENT:
229 return false;
230
231 default:
232 throw InternalErr(__FILE__, __LINE__, get_errno());
233 }
234 }
235
236 struct flock *l = lock(F_RDLCK);
237 if (fcntl(fd, F_SETLKW, l) == -1) {
238 close(fd);
239 ostringstream oss;
240 oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
241 throw InternalErr(__FILE__, __LINE__, oss.str());
242 }
243
244 DBG(cerr << "getSharedLock exit: " << file_name <<endl);
245
246 // Success
247 ref_fd = fd;
248 return true;
249}
250
263static bool getExclusiveLock(string file_name, int &ref_fd)
264{
265 DBG(cerr << "getExclusiveLock: " << file_name <<endl);
266
267 int fd;
268 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
269 switch (errno) {
270 case ENOENT:
271 return false;
272
273 default:
274 throw InternalErr(__FILE__, __LINE__, get_errno());
275 }
276 }
277
278 struct flock *l = lock(F_WRLCK);
279 if (fcntl(fd, F_SETLKW, l) == -1) {
280 close(fd);
281 ostringstream oss;
282 oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
283 throw InternalErr(__FILE__, __LINE__, oss.str());
284 }
285
286 DBG(cerr << "getExclusiveLock exit: " << file_name <<endl);
287
288 // Success
289 ref_fd = fd;
290 return true;
291}
292
304static bool getExclusiveLockNB(string file_name, int &ref_fd)
305{
306 DBG(cerr << "getExclusiveLock_nonblocking: " << file_name <<endl);
307
308 int fd;
309 if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
310 switch (errno) {
311 case ENOENT:
312 return false;
313
314 default:
315 throw InternalErr(__FILE__, __LINE__, get_errno());
316 }
317 }
318
319 struct flock *l = lock(F_WRLCK);
320 if (fcntl(fd, F_SETLK, l) == -1) {
321 switch (errno) {
322 case EAGAIN:
323 DBG(cerr << "getExclusiveLock_nonblocking exit (false): " << file_name << " by: " << l->l_pid << endl);
324 close(fd);
325 return false;
326
327 default: {
328 close(fd);
329 ostringstream oss;
330 oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
331 throw InternalErr(__FILE__, __LINE__, oss.str());
332 }
333 }
334 }
335
336 DBG(cerr << "getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
337
338 // Success
339 ref_fd = fd;
340 return true;
341}
342
356static bool createLockedFile(string file_name, int &ref_fd)
357{
358 DBG(cerr << "createLockedFile: " << file_name <<endl);
359
360 int fd;
361 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
362 switch (errno) {
363 case EEXIST:
364 return false;
365
366 default:
367 throw InternalErr(__FILE__, __LINE__, get_errno());
368 }
369 }
370
371 struct flock *l = lock(F_WRLCK);
372 if (fcntl(fd, F_SETLKW, l) == -1) {
373 close(fd);
374 ostringstream oss;
375 oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
376 throw InternalErr(__FILE__, __LINE__, oss.str());
377 }
378
379 DBG(cerr << "createLockedFile exit: " << file_name <<endl);
380
381 // Success
382 ref_fd = fd;
383 return true;
384}
385
387void DAPCache3::m_check_ctor_params()
388{
389 if (d_cache_dir.empty()) {
390 string err = "The cache directory was not specified, must be non-empty";
391 throw InternalErr(__FILE__, __LINE__, err);
392 }
393
394 // TODO New feature: Makes the directory f it does not exist
395 struct stat buf;
396 int statret = stat(d_cache_dir.c_str(), &buf);
397 if (statret != 0 || !S_ISDIR(buf.st_mode)) {
398 // Try to make the directory
399 int status = mkdir(d_cache_dir.c_str(), 0775);
400 if (status != 0) {
401 string err = "The cache directory " + d_cache_dir + " does not exist or could not be created.";
402 throw InternalErr(__FILE__, __LINE__, err);
403 }
404 }
405
406 if (d_prefix.empty()) {
407 string err = "The cache file prefix was not specified, must not be empty";
408 throw InternalErr(__FILE__, __LINE__, err);
409 }
410
411 if (d_max_cache_size_in_bytes <= 0) {
412 string err = "The cache size was not specified, must be greater than zero";
413 throw InternalErr(__FILE__, __LINE__, err);
414 }
415#if 0
416 // redundant check
417
418 // If the user specifies a cache that is too large,
419 // it is a user exception and we should tell them.
420 if (d_max_cache_size_in_bytes > MAX_CACHE_SIZE_IN_MEGABYTES) {
421 std::ostringstream msg;
422 msg << "The specified cache size was larger than the max cache size of: " << MAX_CACHE_SIZE_IN_MEGABYTES
423 << " (was " << d_max_cache_size_in_bytes << ").";
424 throw InternalErr(__FILE__, __LINE__, msg.str());
425 }
426#endif
427 DBG(cerr << "DAP Cache: directory " << d_cache_dir << ", prefix " << d_prefix
428 << ", max size " << d_max_cache_size_in_bytes << endl );
429}
430
432void DAPCache3::m_initialize_cache_info()
433{
434 // The value set in configuration files, etc., is the size in megabytes. The private
435 // variable holds the size in bytes (converted below).
436 d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
437 d_max_cache_size_in_bytes *= BYTES_PER_MEG;
438 d_target_size = d_max_cache_size_in_bytes * 0.8;
439
440 m_check_ctor_params(); // Throws InternalErr on error.
441
442 d_cache_info = d_cache_dir + "/dap.cache.info";
443
444 // See if we can create it. If so, that means it doesn't exist. So make it and
445 // set the cache initial size to zero.
446 if (createLockedFile(d_cache_info, d_cache_info_fd)) {
447 // initialize the cache size to zero
448 unsigned long long size = 0;
449 if (write(d_cache_info_fd, &size, sizeof(unsigned long long)) != sizeof(unsigned long long))
450 throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file in startup!");
451
452 // This leaves the d_cache_info_fd file descriptor open
453 unlock_cache();
454 }
455 else {
456 if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
457 throw InternalErr(__FILE__, __LINE__, get_errno());
458 }
459 }
460
461 DBG(cerr << "d_cache_info_fd: " << d_cache_info_fd << endl);
462}
463
464#if 0
479BESCache3::BESCache3(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key) :
480 d_max_cache_size_in_bytes(0)
481{
482 bool found = false;
483 keys->get_value(cache_dir_key, d_cache_dir, found);
484 if (!found)
485 throw BESSyntaxUserError("The cache directory key " + cache_dir_key + " was not found in the BES configuration file", __FILE__, __LINE__);
486
487 found = false;
488 keys->get_value(prefix_key, d_prefix, found);
489 if (!found)
490 throw BESSyntaxUserError("The prefix key " + prefix_key + " was not found in the BES configuration file", __FILE__, __LINE__);
491
492 found = false;
493 string cache_size_str;
494 keys->get_value(size_key, cache_size_str, found);
495 if (!found)
496 throw BESSyntaxUserError("The size key " + size_key + " was not found in the BES configuration file", __FILE__, __LINE__);
497
498 std::istringstream is(cache_size_str);
499 is >> d_max_cache_size_in_bytes;
500
501 m_initialize_cache_info();
502}
503#endif
504
505
521string DAPCache3::get_cache_file_name(const string &src, bool mangle)
522{
523 string target = src;
524
525 if (mangle) {
526 if (target.at(0) == '/') {
527 target = src.substr(1, target.length() - 1);
528 }
529 string::size_type slash = 0;
530 while ((slash = target.find('/')) != string::npos) {
531 target.replace(slash, 1, 1, DAPCache3::DAP_CACHE_CHAR);
532 }
533 string::size_type last_dot = target.rfind('.');
534 if (last_dot != string::npos) {
535 target = target.substr(0, last_dot);
536 }
537 }
538 DBG(cerr << " d_cache_dir: '" << d_cache_dir << "'" << endl);
539 DBG(cerr << " d_prefix: '" << d_prefix << "'" << endl);
540 DBG(cerr << " target: '" << target << "'" << endl);
541
542 return d_cache_dir + "/" + d_prefix + DAPCache3::DAP_CACHE_CHAR + target;
543}
544
562bool DAPCache3::get_read_lock(const string &target, int &fd)
563{
565
566 bool status = getSharedLock(target, fd);
567
568 DBG(cerr << "DAP Cache: read_lock: " << target << "(" << status << ")" << endl);
569
570 if (status)
571 m_record_descriptor(target, fd);
572
573 unlock_cache();
574
575 return status;
576}
577
590bool DAPCache3::create_and_lock(const string &target, int &fd)
591{
593
594 bool status = createLockedFile(target, fd);
595
596 DBG(cerr << "DAP Cache: create_and_lock: " << target << "(" << status << ")" << endl);
597
598 if (status)
599 m_record_descriptor(target, fd);
600
601 unlock_cache();
602
603 return status;
604
605}
606
621{
622 struct flock lock;
623 lock.l_type = F_RDLCK;
624 lock.l_whence = SEEK_SET;
625 lock.l_start = 0;
626 lock.l_len = 0;
627 lock.l_pid = getpid();
628
629 if (fcntl(fd, F_SETLKW, &lock) == -1) {
630 throw InternalErr(__FILE__, __LINE__, get_errno());
631 }
632}
633
643{
644 DBG(cerr << "lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
645
646 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
647 throw InternalErr(__FILE__, __LINE__, "An error occurred trying to lock the cache-control file" + get_errno());
648 }
649}
650
655{
656 DBG(cerr << "lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
657
658 if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
659 throw InternalErr(__FILE__, __LINE__, "An error occurred trying to lock the cache-control file" + get_errno());
660 }
661}
662
669{
670 DBG(cerr << "DAP Cache: unlock: cache_info (fd: " << d_cache_info_fd << ")" << endl);
671
672 if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
673 throw InternalErr(__FILE__, __LINE__, "An error occurred trying to unlock the cache-control file" + get_errno());
674 }
675}
676
688void DAPCache3::unlock_and_close(const string &file_name)
689{
690 DBG(cerr << "DAP Cache: unlock file: " << file_name << endl);
691
692 unlock(m_get_descriptor(file_name));
693}
694
700void DAPCache3::unlock_and_close(int fd)
701{
702 DBG(cerr << "DAP Cache: unlock fd: " << fd << endl);
703
704 unlock(fd);
705
706 DBG(cerr << "DAP Cache: unlock " << fd << " Success" << endl);
707}
708
719unsigned long long DAPCache3::update_cache_info(const string &target)
720{
721 try {
723
724 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
725 throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
726
727 // read the size from the cache info file
728 unsigned long long current_size;
729 if (read(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
730 throw InternalErr(__FILE__, __LINE__, "Could not get read size info from the cache info file!");
731
732 struct stat buf;
733 int statret = stat(target.c_str(), &buf);
734 if (statret == 0)
735 current_size += buf.st_size;
736 else
737 throw InternalErr(__FILE__, __LINE__, "Could not read the size of the new file: " + target + " : " + get_errno());
738
739 DBG(cerr << "DAP Cache: cache size updated to: " << current_size << endl);
740
741 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
742 throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
743
744 if(write(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
745 throw InternalErr(__FILE__, __LINE__, "Could not write size info from the cache info file!");
746
747 unlock_cache();
748 return current_size;
749 }
750 catch (...) {
751 unlock_cache();
752 throw;
753 }
754}
755
760bool DAPCache3::cache_too_big(unsigned long long current_size) const
761{
762 return current_size > d_max_cache_size_in_bytes;
763}
764
772unsigned long long DAPCache3::get_cache_size()
773{
774 try {
776
777 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
778 throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
779 // read the size from the cache info file
780 unsigned long long current_size;
781 if(read(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
782 throw InternalErr(__FILE__, __LINE__, "Could not get read size info from the cache info file!");
783
784 unlock_cache();
785 return current_size;
786 }
787 catch(...) {
788 unlock_cache();
789 throw;
790 }
791}
792
793
794static bool entry_op(cache_entry &e1, cache_entry &e2)
795{
796 return e1.time < e2.time;
797}
798
800unsigned long long DAPCache3::m_collect_cache_dir_info(CacheFiles &contents)
801{
802 DIR *dip = opendir(d_cache_dir.c_str());
803 if (!dip)
804 throw InternalErr(__FILE__, __LINE__, "Unable to open cache directory " + d_cache_dir);
805
806 struct dirent *dit;
807 vector<string> files;
808 // go through the cache directory and collect all of the files that
809 // start with the matching prefix
810 while ((dit = readdir(dip)) != NULL) {
811 string dirEntry = dit->d_name;
812 if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0) {
813 files.push_back(d_cache_dir + "/" + dirEntry);
814 }
815 }
816
817 closedir(dip);
818
819 unsigned long long current_size = 0;
820 struct stat buf;
821 for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
822 if (stat(file->c_str(), &buf) == 0) {
823 current_size += buf.st_size;
824 cache_entry entry;
825 entry.name = *file;
826 entry.size = buf.st_size;
827 entry.time = buf.st_atime;
828 // Sanity check; Removed after initial testing since some files might be zero bytes
829#if 0
830 if (entry.size == 0)
831 throw InternalErr(__FILE__, __LINE__, "Zero-byte file found in cache. " + *file);
832#endif
833 contents.push_back(entry);
834 }
835 }
836
837 // Sort so smaller (older) times are first.
838 contents.sort(entry_op);
839
840 return current_size;
841}
842
854void DAPCache3::update_and_purge(const string &new_file)
855{
856 DBG(cerr << "purge - starting the purge" << endl);
857
858 try {
860
861 CacheFiles contents;
862 unsigned long long computed_size = m_collect_cache_dir_info(contents);
863#if 0
864 if (BESISDEBUG( "cache_contents" )) {
865 DBG(endl << "BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
866 CacheFiles::iterator ti = contents.begin();
867 CacheFiles::iterator te = contents.end();
868 for (; ti != te; ti++) {
869 DBG((*ti).time << ": " << (*ti).name << ": size " << (*ti).size/BYTES_PER_MEG << endl );
870 }
871 }
872#endif
873 DBG(cerr << "purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG << ", " << d_target_size/BYTES_PER_MEG << endl );
874
875 // This deletes files and updates computed_size
876 if (cache_too_big(computed_size)) {
877
878 // d_target_size is 80% of the maximum cache size.
879 // Grab the first which is the oldest in terms of access time.
880 CacheFiles::iterator i = contents.begin();
881 while (i != contents.end() && computed_size > d_target_size) {
882 // Grab an exclusive lock but do not block - if another process has the file locked
883 // just move on to the next file. Also test to see if the current file is the file
884 // this process just added to the cache - don't purge that!
885 int cfile_fd;
886 if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
887 DBG(cerr << "purge: " << i->name << " removed." << endl );
888
889 if (unlink(i->name.c_str()) != 0)
890 throw InternalErr(__FILE__, __LINE__, "Unable to purge the file " + i->name + " from the cache: " + get_errno());
891
892 unlock(cfile_fd);
893 computed_size -= i->size;
894 }
895#if 0
896 else {
897 // This information is useful when debugging... Might comment out for production
898 DBG(cerr << "purge: " << i->name << " is in use." << endl );
899 }
900#endif
901 ++i;
902
903 DBG(cerr << "purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG << ", " << d_target_size/BYTES_PER_MEG << endl );
904 }
905
906 }
907
908 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
909 throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
910
911 if(write(d_cache_info_fd, &computed_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
912 throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file!");
913#if 0
914 if (BESISDEBUG( "cache_contents" )) {
915 contents.clear();
916 computed_size = m_collect_cache_dir_info(contents);
917 DBG(endl << "AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
918 CacheFiles::iterator ti = contents.begin();
919 CacheFiles::iterator te = contents.end();
920 for (; ti != te; ti++) {
921 DBG((*ti).time << ": " << (*ti).name << ": size " << (*ti).size/BYTES_PER_MEG << endl );
922 }
923 }
924#endif
925 unlock_cache();
926 }
927 catch(...) {
928 unlock_cache();
929 throw;
930 }
931}
932
944void DAPCache3::purge_file(const string &file)
945{
946 DBG(cerr << "purge_file - starting the purge" << endl);
947
948 try {
950
951 // Grab an exclusive lock on the file
952 int cfile_fd;
953 if (getExclusiveLock(file, cfile_fd)) {
954 // Get the file's size
955 unsigned long long size = 0;
956 struct stat buf;
957 if (stat(file.c_str(), &buf) == 0) {
958 size = buf.st_size;
959 }
960
961 DBG(cerr << "purge_file: " << file << " removed." << endl );
962
963 if (unlink(file.c_str()) != 0)
964 throw InternalErr(__FILE__, __LINE__,
965 "Unable to purge the file " + file + " from the cache: " + get_errno());
966
967 unlock(cfile_fd);
968
969 unsigned long long cache_size = get_cache_size() - size;
970
971 if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
972 throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
973
974 if (write(d_cache_info_fd, &cache_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
975 throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file!");
976 }
977
978 unlock_cache();
979 }
980 catch (...) {
981 unlock_cache();
982 throw;
983 }
984}
985
993void DAPCache3::dump(ostream &strm) const
994{
995 strm << DapIndent::LMarg << "DAPCache3::dump - (" << (void *) this << ")" << endl;
996 DapIndent::Indent();
997 strm << DapIndent::LMarg << "cache dir: " << d_cache_dir << endl;
998 strm << DapIndent::LMarg << "prefix: " << d_prefix << endl;
999 strm << DapIndent::LMarg << "size (bytes): " << d_max_cache_size_in_bytes << endl;
1000 DapIndent::UnIndent();
1001}
1002
1003} // namespace libdap
Implementation of a caching mechanism for compressed data. This cache uses simple advisory locking fo...
Definition DAPCache3.h:82
virtual unsigned long long get_cache_size()
Get the cache size. Read the size information from the cache info file and return it....
Definition DAPCache3.cc:772
string get_cache_file_name(const string &src, bool mangle=true)
Definition DAPCache3.cc:521
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.
Definition DAPCache3.cc:760
virtual void purge_file(const string &file)
Purge a single file from the cache.
Definition DAPCache3.cc:944
virtual bool get_read_lock(const string &target, int &fd)
Get a read-only lock on the file if it exists.
Definition DAPCache3.cc:562
virtual void unlock_cache()
Definition DAPCache3.cc:668
virtual void dump(ostream &strm) const
dumps information about this object
Definition DAPCache3.cc:993
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,...
Definition DAPCache3.cc:620
virtual void lock_cache_read()
Definition DAPCache3.cc:654
virtual unsigned long long update_cache_info(const string &target)
Update the cache info file to include 'target'.
Definition DAPCache3.cc:719
static DAPCache3 * get_instance()
Definition DAPCache3.cc:153
virtual void update_and_purge(const string &new_file)
Purge files from the cache.
Definition DAPCache3.cc:854
virtual void lock_cache_write()
Definition DAPCache3.cc:642
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,...
Definition DAPCache3.cc:590
A class for software fault reporting.
Definition InternalErr.h:65
top level DAP object to house generic methods
Definition AISConnect.cc:30
Definition DAPCache3.h:51