cache.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <stdint.h>
  15. #include <map>
  16. #include <dns/question.h>
  17. #include <dns/rrclass.h>
  18. #include <dns/rrset.h>
  19. #include <dns/rrtype.h>
  20. #include <list>
  21. #include <datasrc/cache.h>
  22. #include <datasrc/logger.h>
  23. using namespace std;
  24. using namespace isc::dns;
  25. namespace isc {
  26. namespace datasrc {
  27. /// \brief A \c CacheEntry object contains the data stored with
  28. /// each \c CacheNode: a pointer to the cached RRset (empty in
  29. /// the case of a negative cache entry), and a copy of the
  30. /// query-response flags that were returned when the RRset
  31. /// was originally looked up in the low-level data source.
  32. class CacheEntry {
  33. private:
  34. /// The copy constructor and the assignment operator are intentionally
  35. /// defined as private.
  36. CacheEntry(const CacheEntry& source);
  37. CacheEntry& operator=(const CacheEntry& source);
  38. public:
  39. CacheEntry(RRsetPtr r, uint32_t f) : rrset(r), flags(f) {};
  40. RRsetPtr rrset;
  41. uint32_t flags;
  42. };
  43. typedef boost::shared_ptr<CacheEntry> CacheEntryPtr;
  44. /// \brief A \c CacheNode is a node in the \c HotCache LRU queue. It
  45. /// contains a pointer to a \c CacheEntry, a reference to the \c Question
  46. /// that we are answering, a lifespan during which this entry remains
  47. /// valid, and pointers to the next and previous entries in the list.
  48. class CacheNode {
  49. private:
  50. /// \name Constructors and Assignment Operator
  51. ///
  52. /// Note: The copy constructor and the assignment operator are intentionally
  53. /// defined as private.
  54. //@{
  55. CacheNode(const CacheNode& source);
  56. CacheNode& operator=(const CacheNode& source);
  57. public:
  58. /// \brief Constructor for positive cache entry.
  59. ///
  60. /// \param rrset The \c RRset to cache.
  61. /// \param flags The query response flags returned from the low-level
  62. /// data source when this \c RRset was looked up.
  63. /// \param lifespan How long the cache node is to be considered valid.
  64. CacheNode(const RRsetPtr rrset, uint32_t flags, time_t lifespan);
  65. /// \brief Constructor for negative cache entry.
  66. ///
  67. /// \param name Query name
  68. /// \param rrclass Query class
  69. /// \param rrtype Query type
  70. /// \param flags Query response flags returned from the low-level
  71. /// data source, indicating why this lookup failed (name not found,
  72. /// type not found, etc).
  73. /// \param lifespan How long the cache node is to be considered valid.
  74. CacheNode(const Name& name,
  75. const RRClass& rrclass,
  76. const RRType& rrtype,
  77. uint32_t flags,
  78. time_t lifespan);
  79. //@}
  80. /// \name Getter and Setter Methods
  81. //@{
  82. /// \brief Returns a pointer to the cached RRset (or an empty
  83. /// RRsetPtr for negative cache entries).
  84. /// \return \c RRsetPtr
  85. RRsetPtr getRRset() const { return (entry->rrset); }
  86. /// \brief Returns name associated with cached node
  87. ///
  88. /// This is the name associated with the RRset if it is a positive
  89. /// entry, and the associated question name if the RRSet is NULL
  90. /// and this is a negative entry (together with an indication that
  91. /// this is a negative entry).
  92. string getNodeName() const {
  93. if (getRRset()) {
  94. return (getRRset()->getName().toText());
  95. }
  96. return (std::string("negative entry for ") + question.toText());
  97. }
  98. /// \brief Returns the query response flags associated with the data.
  99. ///
  100. /// \return \c uint32_t
  101. uint32_t getFlags() const { return (entry->flags); }
  102. /// \brief Is this record still valid?
  103. ///
  104. /// \return True if the expiration time has not yet passed,
  105. /// or false if it has.
  106. bool isValid() const;
  107. //@}
  108. // An iterator referencing this entry in the LRU list. This
  109. // permits unit-time removal using list::erase().
  110. list<CacheNodePtr>::iterator lru_entry_;
  111. /// The \c Question (name/rrclass/rrtype) answered by this cache node
  112. const isc::dns::Question question;
  113. private:
  114. // The cached RRset data
  115. CacheEntryPtr entry;
  116. // When this record should be discarded
  117. time_t expiry;
  118. };
  119. // CacheNode constructor for a positive cache entry
  120. CacheNode::CacheNode(const RRsetPtr rrset, const uint32_t flags,
  121. const time_t lifespan) :
  122. question(Question(rrset->getName(), rrset->getClass(), rrset->getType()))
  123. {
  124. const time_t now = time(NULL);
  125. expiry = now + lifespan;
  126. entry = CacheEntryPtr(new CacheEntry(rrset, flags));
  127. }
  128. // CacheNode constructor for a negative cache entry
  129. CacheNode::CacheNode(const Name& name,
  130. const RRClass& rrclass,
  131. const RRType& rrtype,
  132. const uint32_t flags,
  133. const time_t lifespan) :
  134. question(Question(name, rrclass, rrtype))
  135. {
  136. const time_t now = time(NULL);
  137. expiry = now + lifespan;
  138. entry = CacheEntryPtr(new CacheEntry(RRsetPtr(), flags));
  139. }
  140. // Returns true if the node has not yet expired.
  141. bool
  142. CacheNode::isValid() const {
  143. const time_t now = time(NULL);
  144. return (now < expiry);
  145. }
  146. /// This class abstracts the implementation details for \c HotCache.
  147. ///
  148. /// Each node inserted into the cache is placed at the head of a
  149. /// doubly-linked list. Whenever that node is retrieved from the cache,
  150. /// it is again moved to the head of the list. When the configured
  151. /// number of slots in the cache has been exceeded, the least recently
  152. /// used nodes will be removed from the tail of the list.
  153. ///
  154. /// A pointer to each cache node is also placed in a \c std::map object,
  155. /// keyed by \c isc::dns::Question. This allows retrieval of data in
  156. /// (usually) logarithmic time. (Possible TODO item: replace this with a
  157. /// hash table instead.)
  158. class HotCacheImpl {
  159. public:
  160. HotCacheImpl(int slots, bool enabled);
  161. // The LRU list
  162. list<CacheNodePtr> lru_;
  163. // Flag to indicate whether the cache is enabled
  164. bool enabled_;
  165. // The number of available slots in the LRU list. (If zero,
  166. // then the list is unbounded; otherwise, items are removed
  167. // from the tail of the list whenever it grows past slots_.)
  168. int slots_;
  169. // The number of items currently in the list.
  170. int count_;
  171. // Map from query tuple to cache node pointer, allowing fast retrieval
  172. // of data without a linear search of the LRU list
  173. std::map<Question, CacheNodePtr> map_;
  174. // Move a node to the front of the LRU list.
  175. void promote(CacheNodePtr node);
  176. // Remove a node from the cache.
  177. void remove(ConstCacheNodePtr node);
  178. // Insert a node into the cache (called by both cache() and ncache())
  179. void insert(CacheNodePtr node);
  180. };
  181. // HotCacheImpl constructor
  182. HotCacheImpl::HotCacheImpl(int slots, bool enabled) :
  183. enabled_(enabled), slots_(slots), count_(0)
  184. {
  185. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_CREATE);
  186. }
  187. // Insert a cache node into the cache
  188. inline void
  189. HotCacheImpl::insert(const CacheNodePtr node) {
  190. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_INSERT).
  191. arg(node->getNodeName());
  192. std::map<Question, CacheNodePtr>::const_iterator iter;
  193. iter = map_.find(node->question);
  194. if (iter != map_.end()) {
  195. CacheNodePtr old = iter->second;
  196. if (old && old->isValid()) {
  197. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND);
  198. remove(old);
  199. }
  200. }
  201. lru_.push_front(node);
  202. node->lru_entry_ = lru_.begin();
  203. map_[node->question] = node;
  204. ++count_;
  205. if (slots_ != 0 && count_ > slots_) {
  206. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FULL);
  207. remove(lru_.back());
  208. }
  209. }
  210. // Promote a node to the head of the LRU list
  211. void
  212. HotCacheImpl::promote(CacheNodePtr node) {
  213. if (!node) {
  214. return;
  215. }
  216. if (node->lru_entry_ == lru_.begin()) {
  217. return;
  218. }
  219. lru_.splice(lru_.begin(), lru_, node->lru_entry_); // move node to front
  220. node->lru_entry_ = lru_.begin();
  221. }
  222. // Remove a node from the LRU list and the map
  223. void
  224. HotCacheImpl::remove(ConstCacheNodePtr node) {
  225. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_REMOVE).
  226. arg(node->getNodeName());
  227. lru_.erase(node->lru_entry_);
  228. map_.erase(node->question);
  229. --count_;
  230. }
  231. // HotCache constructor
  232. HotCache::HotCache(const int slots) {
  233. impl_ = new HotCacheImpl(slots, true);
  234. }
  235. // HotCache destructor
  236. HotCache::~HotCache() {
  237. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_DESTROY);
  238. delete impl_;
  239. }
  240. // Add a positive entry to the cache
  241. void
  242. HotCache::addPositive(RRsetPtr rrset, const uint32_t flags,
  243. const time_t lifespan)
  244. {
  245. if (!impl_->enabled_) {
  246. return;
  247. }
  248. impl_->insert(CacheNodePtr(new CacheNode(rrset, flags, lifespan)));
  249. }
  250. // Add a negative entry to the cache
  251. void
  252. HotCache::addNegative(const Name& name, const RRClass &rrclass,
  253. const RRType& rrtype, const uint32_t flags,
  254. const time_t lifespan)
  255. {
  256. if (!impl_->enabled_) {
  257. return;
  258. }
  259. if (rrtype == RRType::ANY() || rrclass == RRClass::ANY()) {
  260. return;
  261. }
  262. impl_->insert(CacheNodePtr(new CacheNode(name, rrclass, rrtype,
  263. flags, lifespan)));
  264. }
  265. // Try to retrieve an entry from the cache, returning true if
  266. // it was found and valid.
  267. bool
  268. HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
  269. RRsetPtr& rrset, uint32_t& flags)
  270. {
  271. if (!impl_->enabled_) {
  272. return (false);
  273. }
  274. std::map<Question, CacheNodePtr>::const_iterator iter;
  275. iter = impl_->map_.find(Question(n, c, t));
  276. if (iter == impl_->map_.end()) {
  277. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_NOT_FOUND).arg(n);
  278. return (false);
  279. }
  280. CacheNodePtr node = iter->second;
  281. if (node->isValid()) {
  282. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FOUND).arg(n);
  283. impl_->promote(node);
  284. rrset = node->getRRset();
  285. flags = node->getFlags();
  286. return (true);
  287. }
  288. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_EXPIRED).arg(n);
  289. impl_->remove(node);
  290. return (false);
  291. }
  292. // Set the number of slots in the cache.
  293. void
  294. HotCache::setSlots(const int slots) {
  295. impl_->slots_ = slots;
  296. if (!impl_->enabled_) {
  297. return;
  298. }
  299. logger.info(DATASRC_CACHE_SLOTS).arg(slots).arg(max(0, impl_->count_ -
  300. slots));
  301. while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
  302. impl_->remove(impl_->lru_.back());
  303. }
  304. }
  305. // Return the number of slots in the cache
  306. int
  307. HotCache::getSlots() const {
  308. return (impl_->slots_);
  309. }
  310. /// Enable or disable the cache
  311. void
  312. HotCache::setEnabled(const bool e) {
  313. impl_->enabled_ = e;
  314. if (e) {
  315. logger.info(DATASRC_CACHE_ENABLE);
  316. } else {
  317. logger.info(DATASRC_CACHE_DISABLE);
  318. }
  319. }
  320. /// Indicate whether the cache is enabled
  321. bool
  322. HotCache::getEnabled() const {
  323. return (impl_->enabled_);
  324. }
  325. // Return the number of entries in the cache
  326. int
  327. HotCache::getCount() const {
  328. return (impl_->count_);
  329. }
  330. }
  331. }