cache.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. .arg(node->getNodeName());
  199. remove(old);
  200. }
  201. }
  202. lru_.push_front(node);
  203. node->lru_entry_ = lru_.begin();
  204. map_[node->question] = node;
  205. ++count_;
  206. if (slots_ != 0 && count_ > slots_) {
  207. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FULL);
  208. remove(lru_.back());
  209. }
  210. }
  211. // Promote a node to the head of the LRU list
  212. void
  213. HotCacheImpl::promote(CacheNodePtr node) {
  214. if (!node) {
  215. return;
  216. }
  217. if (node->lru_entry_ == lru_.begin()) {
  218. return;
  219. }
  220. lru_.splice(lru_.begin(), lru_, node->lru_entry_); // move node to front
  221. node->lru_entry_ = lru_.begin();
  222. }
  223. // Remove a node from the LRU list and the map
  224. void
  225. HotCacheImpl::remove(ConstCacheNodePtr node) {
  226. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_REMOVE).
  227. arg(node->getNodeName());
  228. lru_.erase(node->lru_entry_);
  229. map_.erase(node->question);
  230. --count_;
  231. }
  232. // HotCache constructor
  233. HotCache::HotCache(const int slots) {
  234. impl_ = new HotCacheImpl(slots, true);
  235. }
  236. // HotCache destructor
  237. HotCache::~HotCache() {
  238. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_DESTROY);
  239. delete impl_;
  240. }
  241. // Add a positive entry to the cache
  242. void
  243. HotCache::addPositive(RRsetPtr rrset, const uint32_t flags,
  244. const time_t lifespan)
  245. {
  246. if (!impl_->enabled_) {
  247. return;
  248. }
  249. impl_->insert(CacheNodePtr(new CacheNode(rrset, flags, lifespan)));
  250. }
  251. // Add a negative entry to the cache
  252. void
  253. HotCache::addNegative(const Name& name, const RRClass &rrclass,
  254. const RRType& rrtype, const uint32_t flags,
  255. const time_t lifespan)
  256. {
  257. if (!impl_->enabled_) {
  258. return;
  259. }
  260. if (rrtype == RRType::ANY() || rrclass == RRClass::ANY()) {
  261. return;
  262. }
  263. impl_->insert(CacheNodePtr(new CacheNode(name, rrclass, rrtype,
  264. flags, lifespan)));
  265. }
  266. // Try to retrieve an entry from the cache, returning true if
  267. // it was found and valid.
  268. bool
  269. HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
  270. RRsetPtr& rrset, uint32_t& flags)
  271. {
  272. if (!impl_->enabled_) {
  273. return (false);
  274. }
  275. std::map<Question, CacheNodePtr>::const_iterator iter;
  276. iter = impl_->map_.find(Question(n, c, t));
  277. if (iter == impl_->map_.end()) {
  278. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_NOT_FOUND).arg(n);
  279. return (false);
  280. }
  281. CacheNodePtr node = iter->second;
  282. if (node->isValid()) {
  283. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FOUND).arg(n);
  284. impl_->promote(node);
  285. rrset = node->getRRset();
  286. flags = node->getFlags();
  287. return (true);
  288. }
  289. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_EXPIRED).arg(n);
  290. impl_->remove(node);
  291. return (false);
  292. }
  293. // Set the number of slots in the cache.
  294. void
  295. HotCache::setSlots(const int slots) {
  296. impl_->slots_ = slots;
  297. if (!impl_->enabled_) {
  298. return;
  299. }
  300. logger.info(DATASRC_CACHE_SLOTS).arg(slots).arg(max(0, impl_->count_ -
  301. slots));
  302. while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
  303. impl_->remove(impl_->lru_.back());
  304. }
  305. }
  306. // Return the number of slots in the cache
  307. int
  308. HotCache::getSlots() const {
  309. return (impl_->slots_);
  310. }
  311. /// Enable or disable the cache
  312. void
  313. HotCache::setEnabled(const bool e) {
  314. impl_->enabled_ = e;
  315. if (e) {
  316. logger.info(DATASRC_CACHE_ENABLE);
  317. } else {
  318. logger.info(DATASRC_CACHE_DISABLE);
  319. }
  320. }
  321. /// Indicate whether the cache is enabled
  322. bool
  323. HotCache::getEnabled() const {
  324. return (impl_->enabled_);
  325. }
  326. // Return the number of entries in the cache
  327. int
  328. HotCache::getCount() const {
  329. return (impl_->count_);
  330. }
  331. }
  332. }