cache.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. using namespace std;
  23. using namespace isc::dns;
  24. namespace isc {
  25. namespace datasrc {
  26. /// \brief A \c CacheEntry object contains the data stored with
  27. /// each \c CacheNode: a pointer to the cached RRset (empty in
  28. /// the case of a negative cache entry), and a copy of the
  29. /// query-response flags that were returned when the RRset
  30. /// was originally looked up in the low-level data source.
  31. class CacheEntry {
  32. private:
  33. /// The copy constructor and the assignment operator are intentionally
  34. /// defined as private.
  35. CacheEntry(const CacheEntry& source);
  36. CacheEntry& operator=(const CacheEntry& source);
  37. public:
  38. CacheEntry(RRsetPtr r, uint32_t f) : rrset(r), flags(f) {};
  39. RRsetPtr rrset;
  40. uint32_t flags;
  41. };
  42. typedef boost::shared_ptr<CacheEntry> CacheEntryPtr;
  43. /// \brief A \c CacheNode is a node in the \c HotCache LRU queue. It
  44. /// contains a pointer to a \c CacheEntry, a reference to the \c Question
  45. /// that we are answering, a lifespan during which this entry remains
  46. /// valid, and pointers to the next and previous entries in the list.
  47. class CacheNode {
  48. private:
  49. /// \name Constructors and Assignment Operator
  50. ///
  51. /// Note: The copy constructor and the assignment operator are intentionally
  52. /// defined as private.
  53. //@{
  54. CacheNode(const CacheNode& source);
  55. CacheNode& operator=(const CacheNode& source);
  56. public:
  57. /// \brief Constructor for positive cache entry.
  58. ///
  59. /// \param rrset The \c RRset to cache.
  60. /// \param flags The query response flags returned from the low-level
  61. /// data source when this \c RRset was looked up.
  62. /// \param lifespan How long the cache node is to be considered valid.
  63. CacheNode(const RRsetPtr rrset, uint32_t flags, time_t lifespan);
  64. /// \brief Constructor for negative cache entry.
  65. ///
  66. /// \param name Query name
  67. /// \param rrclass Query class
  68. /// \param rrtype Query type
  69. /// \param flags Query response flags returned from the low-level
  70. /// data source, indicating why this lookup failed (name not found,
  71. /// type not found, etc).
  72. /// \param lifespan How long the cache node is to be considered valid.
  73. CacheNode(const Name& name,
  74. const RRClass& rrclass,
  75. const RRType& rrtype,
  76. uint32_t flags,
  77. time_t lifespan);
  78. //@}
  79. /// \name Getter and Setter Methods
  80. //@{
  81. /// \brief Returns a pointer to the cached RRset (or an empty
  82. /// RRsetPtr for negative cache entries).
  83. /// \return \c RRsetPtr
  84. RRsetPtr getRRset() const { return (entry->rrset); }
  85. /// \brief Returns the query response flags associated with the data.
  86. ///
  87. /// \return \c uint32_t
  88. uint32_t getFlags() const { return (entry->flags); }
  89. /// \brief Is this record still valid?
  90. ///
  91. /// \return True if the expiration time has not yet passed,
  92. /// or false if it has.
  93. bool isValid() const;
  94. //@}
  95. // An iterator referencing this entry in the LRU list. This
  96. // permits unit-time removal using list::erase().
  97. list<CacheNodePtr>::iterator lru_entry_;
  98. /// The \c Question (name/rrclass/rrtype) answered by this cache node
  99. const isc::dns::Question question;
  100. private:
  101. // The cached RRset data
  102. CacheEntryPtr entry;
  103. // When this record should be discarded
  104. time_t expiry;
  105. };
  106. // CacheNode constructor for a positive cache entry
  107. CacheNode::CacheNode(const RRsetPtr rrset, const uint32_t flags,
  108. const time_t lifespan) :
  109. question(Question(rrset->getName(), rrset->getClass(), rrset->getType()))
  110. {
  111. const time_t now = time(NULL);
  112. expiry = now + lifespan;
  113. entry = CacheEntryPtr(new CacheEntry(rrset, flags));
  114. }
  115. // CacheNode constructor for a negative cache entry
  116. CacheNode::CacheNode(const Name& name,
  117. const RRClass& rrclass,
  118. const RRType& rrtype,
  119. const uint32_t flags,
  120. const time_t lifespan) :
  121. question(Question(name, rrclass, rrtype))
  122. {
  123. const time_t now = time(NULL);
  124. expiry = now + lifespan;
  125. entry = CacheEntryPtr(new CacheEntry(RRsetPtr(), flags));
  126. }
  127. // Returns true if the node has not yet expired.
  128. bool
  129. CacheNode::isValid() const {
  130. const time_t now = time(NULL);
  131. return (now < expiry);
  132. }
  133. /// This class abstracts the implementation details for \c HotCache.
  134. ///
  135. /// Each node inserted into the cache is placed at the head of a
  136. /// doubly-linked list. Whenever that node is retrieved from the cache,
  137. /// it is again moved to the head of the list. When the configured
  138. /// number of slots in the cache has been exceeded, the least recently
  139. /// used nodes will be removed from the tail of the list.
  140. ///
  141. /// A pointer to each cache node is also placed in a \c std::map object,
  142. /// keyed by \c isc::dns::Question. This allows retrieval of data in
  143. /// (usually) logarithmic time. (Possible TODO item: replace this with a
  144. /// hash table instead.)
  145. class HotCacheImpl {
  146. public:
  147. HotCacheImpl(int slots, bool enabled);
  148. // The LRU list
  149. list<CacheNodePtr> lru_;
  150. // Flag to indicate whether the cache is enabled
  151. bool enabled_;
  152. // The number of available slots in the LRU list. (If zero,
  153. // then the list is unbounded; otherwise, items are removed
  154. // from the tail of the list whenever it grows past slots_.)
  155. int slots_;
  156. // The number of items currently in the list.
  157. int count_;
  158. // Map from query tuple to cache node pointer, allowing fast retrieval
  159. // of data without a linear search of the LRU list
  160. std::map<Question, CacheNodePtr> map_;
  161. // Move a node to the front of the LRU list.
  162. void promote(CacheNodePtr node);
  163. // Remove a node from the cache.
  164. void remove(ConstCacheNodePtr node);
  165. // Insert a node into the cache (called by both cache() and ncache())
  166. void insert(CacheNodePtr node);
  167. };
  168. // HotCacheImpl constructor
  169. HotCacheImpl::HotCacheImpl(int slots, bool enabled) :
  170. enabled_(enabled), slots_(slots), count_(0)
  171. {}
  172. // Insert a cache node into the cache
  173. inline void
  174. HotCacheImpl::insert(const CacheNodePtr node) {
  175. std::map<Question, CacheNodePtr>::const_iterator iter;
  176. iter = map_.find(node->question);
  177. if (iter != map_.end()) {
  178. CacheNodePtr old = iter->second;
  179. if (old && old->isValid()) {
  180. remove(old);
  181. }
  182. }
  183. lru_.push_front(node);
  184. node->lru_entry_ = lru_.begin();
  185. map_[node->question] = node;
  186. ++count_;
  187. if (slots_ != 0 && count_ > slots_) {
  188. remove(lru_.back());
  189. }
  190. }
  191. // Promote a node to the head of the LRU list
  192. void
  193. HotCacheImpl::promote(CacheNodePtr node) {
  194. if (!node) {
  195. return;
  196. }
  197. if (node->lru_entry_ == lru_.begin()) {
  198. return;
  199. }
  200. lru_.splice(lru_.begin(), lru_, node->lru_entry_); // move node to front
  201. node->lru_entry_ = lru_.begin();
  202. }
  203. // Remove a node from the LRU list and the map
  204. void
  205. HotCacheImpl::remove(ConstCacheNodePtr node) {
  206. lru_.erase(node->lru_entry_);
  207. map_.erase(node->question);
  208. --count_;
  209. }
  210. // HotCache constructor
  211. HotCache::HotCache(const int slots) {
  212. impl_ = new HotCacheImpl(slots, true);
  213. }
  214. // HotCache destructor
  215. HotCache::~HotCache() {
  216. delete impl_;
  217. }
  218. // Add a positive entry to the cache
  219. void
  220. HotCache::addPositive(RRsetPtr rrset, const uint32_t flags,
  221. const time_t lifespan)
  222. {
  223. if (!impl_->enabled_) {
  224. return;
  225. }
  226. impl_->insert(CacheNodePtr(new CacheNode(rrset, flags, lifespan)));
  227. }
  228. // Add a negative entry to the cache
  229. void
  230. HotCache::addNegative(const Name& name, const RRClass &rrclass,
  231. const RRType& rrtype, const uint32_t flags,
  232. const time_t lifespan)
  233. {
  234. if (!impl_->enabled_) {
  235. return;
  236. }
  237. if (rrtype == RRType::ANY() || rrclass == RRClass::ANY()) {
  238. return;
  239. }
  240. impl_->insert(CacheNodePtr(new CacheNode(name, rrclass, rrtype,
  241. flags, lifespan)));
  242. }
  243. // Try to retrieve an entry from the cache, returning true if
  244. // it was found and valid.
  245. bool
  246. HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
  247. RRsetPtr& rrset, uint32_t& flags)
  248. {
  249. if (!impl_->enabled_) {
  250. return (false);
  251. }
  252. std::map<Question, CacheNodePtr>::const_iterator iter;
  253. iter = impl_->map_.find(Question(n, c, t));
  254. if (iter == impl_->map_.end()) {
  255. return (false);
  256. }
  257. CacheNodePtr node = iter->second;
  258. if (node->isValid()) {
  259. impl_->promote(node);
  260. rrset = node->getRRset();
  261. flags = node->getFlags();
  262. return (true);
  263. }
  264. impl_->remove(node);
  265. return (false);
  266. }
  267. // Set the number of slots in the cache.
  268. void
  269. HotCache::setSlots(const int slots) {
  270. impl_->slots_ = slots;
  271. if (!impl_->enabled_) {
  272. return;
  273. }
  274. while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
  275. impl_->remove(impl_->lru_.back());
  276. }
  277. }
  278. // Return the number of slots in the cache
  279. int
  280. HotCache::getSlots() const {
  281. return (impl_->slots_);
  282. }
  283. /// Enable or disable the cache
  284. void
  285. HotCache::setEnabled(const bool e) {
  286. impl_->enabled_ = e;
  287. }
  288. /// Indicate whether the cache is enabled
  289. bool
  290. HotCache::getEnabled() const {
  291. return (impl_->enabled_);
  292. }
  293. // Return the number of entries in the cache
  294. int
  295. HotCache::getCount() const {
  296. return (impl_->count_);
  297. }
  298. }
  299. }