]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Add a NetmaskTree-based cache index for ECS entries
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 19 Jun 2017 10:51:39 +0000 (12:51 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 27 Jun 2017 12:37:59 +0000 (14:37 +0200)
The main idea is not to have to go through all the netmask-specific
entries for a given (qname/qtype), but to have to know quickly which
netmask-specific entry is the best match.
To do that we add an index containing a NetmaskTree for each
(qname,qtype), and we then know quickly which entry to get from the
"regular" cache.

Initial benchmarking results:
 - inserting non-netmask-specific entries has the same performance ;
 - inserting netmask-specific entries is 40% slower because of the additional insertion ;
 - looking for a (qname/qtype) that has no netmask-specific entries remains the same ;
 - looking for (qname/qtype) with 65k netmask-specific entries but only matching the non-netmask one is around 2000 times faster ;
 - looking for (qname/qtype) with 65k netmask-specific entries and matching one is also around 2000 times faster ;
 - pruning the cache is a lot slower (from 11 millions/s to 1.8 millions/s)

Remaining issues:
 - ANY queries do not use the index ;
 - we have to do two lookups
 - removal is slower, but might still be good enough
 - NetmaskTree.erase() does not compact the tree.

Ideas that didn't seem to work out:
 - Storing a pointer of some kind in the NetmaskTree to save a lookup:
   caused issues with our generic cache management functions (moving
   entries to the front or to the back requires an iterator)
 - Keeping the NMT index in the empty Netmak entry (the non-netmask
   specific one) save the additional lookup when we have no ECS
   entries, but made cache management very awkward because we needed
   to keep the non-netmask specific entry around as a place holder
   for the ECS index even if it held no data.

pdns/cachecleaner.hh
pdns/dbdnsseckeeper.cc
pdns/dnsseckeeper.hh
pdns/recpacketcache.cc
pdns/recpacketcache.hh
pdns/recursor_cache.cc
pdns/recursor_cache.hh
pdns/recursordist/negcache.cc
pdns/recursordist/negcache.hh
pdns/syncres.cc

index 0e746fa5e0913ce7280384edc035e52729858db0..955757876e1b20920e54ceced740857ce35aaca1 100644 (file)
 
 #include "lock.hh"
 
-// this function can clean any cache that has a getTTD() method on its entries, and a 'sequence' index as its second index
+// this function can clean any cache that has a getTTD() method on its entries, a preRemoval() method and a 'sequence' index as its second index
 // the ritual is that the oldest entries are in *front* of the sequence collection, so on a hit, move an item to the end
 // on a miss, move it to the beginning
-template <typename T> void pruneCollection(T& collection, unsigned int maxCached, unsigned int scanFraction=1000)
+template <typename C, typename T> void pruneCollection(C& container, T& collection, unsigned int maxCached, unsigned int scanFraction=1000)
 {
   time_t now=time(0);
   unsigned int toTrim=0;
@@ -54,7 +54,8 @@ template <typename T> void pruneCollection(T& collection, unsigned int maxCached
 
   typename sequence_t::iterator iter=sidx.begin(), eiter;
   for(; iter != sidx.end() && tried < lookAt ; ++tried) {
-    if(iter->getTTD() < now) { 
+    if(iter->getTTD() < now) {
+      container.preRemoval(*iter);
       sidx.erase(iter++);
       erased++;
     }
@@ -76,8 +77,16 @@ template <typename T> void pruneCollection(T& collection, unsigned int maxCached
     // cout<<"Still have "<<toTrim - erased<<" entries left to erase to meet target\n"; 
 
   eiter=iter=sidx.begin();
-  std::advance(eiter, toTrim); 
-  sidx.erase(iter, eiter);      // just lob it off from the beginning
+  std::advance(eiter, toTrim);
+  // just lob it off from the beginning
+  for (auto i = iter; ; ) {
+    if (i == eiter) {
+      break;
+    }
+
+    container.preRemoval(*i);
+    sidx.erase(i++);
+  }
 }
 
 // note: this expects iterator from first index, and sequence MUST be second index!
index c121f125ba89db0d7413eacf490193174dd6a6ac..0c70d560840d01bfac43a1f4cac00b22a2ae3b39 100644 (file)
@@ -566,11 +566,11 @@ void DNSSECKeeper::cleanup()
   if(now.tv_sec - s_last_prune > (time_t)(30)) {
     {
         WriteLock l(&s_metacachelock);
-        pruneCollection(s_metacache, ::arg().asNum("max-cache-entries"));
+        pruneCollection(*this, s_metacache, ::arg().asNum("max-cache-entries"));
     }
     {
         WriteLock l(&s_keycachelock);
-        pruneCollection(s_keycache, ::arg().asNum("max-cache-entries"));
+        pruneCollection(*this, s_keycache, ::arg().asNum("max-cache-entries"));
     }
     s_last_prune=time(0);
   }
index 7f111ecbf74f20a808eba16d523a8c38098440c4..3473a4e461ee39483346241b46f4f0e465478e8c 100644 (file)
@@ -268,6 +268,14 @@ private:
   static pthread_rwlock_t s_keycachelock;
   static AtomicCounter s_ops;
   static time_t s_last_prune;
+
+public:
+  void preRemoval(const KeyCacheEntry&)
+  {
+  }
+  void preRemoval(const METACacheEntry&)
+  {
+  }
 };
 
 class DNSPacket;
index 6186caad14c1ecd3e5ef4dcbe9c156efbc63e560..9a1104d0687578e0c0e22d28383b47cc1062e595 100644 (file)
@@ -199,7 +199,7 @@ uint64_t RecursorPacketCache::bytes()
 
 void RecursorPacketCache::doPruneTo(unsigned int maxCached)
 {
-  pruneCollection(d_packetCache, maxCached);
+  pruneCollection(*this, d_packetCache, maxCached);
 }
 
 uint64_t RecursorPacketCache::doDump(int fd)
index 51f4d642aab0a2761cf66a4e9bc69c6d522e7f24..bfa16961f216e259e484cf72c74edad3aee03da8 100644 (file)
@@ -103,6 +103,11 @@ private:
   packetCache_t d_packetCache;
 
   bool checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, RecProtoBufMessage* protobufMessage);
+
+public:
+  void preRemoval(const Entry& entry)
+  {
+  }
 };
 
 #endif
index 4105055633c8e6bb8a723a7bf19eec131f30557b..d64067d428992fc0c53da6c8e2d14f659b44f160 100644 (file)
@@ -33,11 +33,113 @@ unsigned int MemRecursorCache::bytes()
   return ret;
 }
 
+int32_t MemRecursorCache::handleHit(cache_t::iterator entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
+{
+  int32_t ttd = entry->d_ttd;
+
+  if(variable && !entry->d_netmask.empty()) {
+    *variable = true;
+  }
+
+  // cerr<<"Looking at "<<entry->d_records.size()<<" records for this name"<<endl;
+  if (res) {
+    for(const auto& k : entry->d_records) {
+      DNSRecord dr;
+      dr.d_name = qname;
+      dr.d_type = entry->d_qtype;
+      dr.d_class = QClass::IN;
+      dr.d_content = k;
+      dr.d_ttl = static_cast<uint32_t>(entry->d_ttd);
+      dr.d_place = DNSResourceRecord::ANSWER;
+      res->push_back(dr);
+    }
+  }
+
+  if(signatures) { // if you do an ANY lookup you are hosed XXXX
+    *signatures = entry->d_signatures;
+  }
+
+  if(authorityRecs) {
+    *authorityRecs = entry->d_authorityRecs;
+  }
+
+  if (state) {
+    *state = entry->d_state;
+  }
+
+  if (wasAuth) {
+    *wasAuth = entry->d_auth;
+  }
+
+  moveCacheItemToBack(d_cache, entry);
+
+  return ttd;
+}
+
+MemRecursorCache::cache_t::const_iterator MemRecursorCache::getEntryUsingIndex(time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who)
+{
+  auto indexKey = tie(qname, qtype);
+  auto index = d_index.find(indexKey);
+  if (index != d_index.end() && !index->isEmpty()) {
+    /* we have netmask-specific entries, let's see if we match one */
+    while (true) {
+      const Netmask best = index->lookupBestMatch(who);
+      if (best.empty()) {
+        /* we have nothing more specific for you */
+        break;
+      }
+
+      auto key = boost::make_tuple(qname, qtype, best);
+      auto entry = d_cache.find(key);
+      if (entry == d_cache.end()) {
+        /* index is not up-to-date */
+        index->removeNetmask(best);
+        if (index->isEmpty()) {
+          d_index.erase(index);
+          break;
+        }
+        continue;
+      }
+
+      if (entry->d_ttd > now) {
+        if (!requireAuth || entry->d_auth) {
+          return entry;
+        }
+      }
+      else {
+        /* this netmask-specific entry has expired */
+        moveCacheItemToFront(d_cache, entry);
+        index->removeNetmask(best);
+        if (index->isEmpty()) {
+          d_index.erase(index);
+          break;
+        }
+      }
+    }
+  }
+
+  /* we have nothing specific, let's see if we have a generic one */
+  auto key = boost::make_tuple(qname, qtype, Netmask());
+  auto entry = d_cache.find(key);
+  if (entry != d_cache.end()) {
+    if (entry->d_ttd > now) {
+      if (!requireAuth || entry->d_auth) {
+        return entry;
+      }
+    }
+    else {
+      moveCacheItemToFront(d_cache, entry);
+    }
+  }
+
+  /* nothing for you, sorry */
+  return d_cache.end();
+}
+
 // returns -1 for no hits
 std::pair<MemRecursorCache::cache_t::const_iterator, MemRecursorCache::cache_t::const_iterator> MemRecursorCache::getEntries(const DNSName &qname, const QType& qt)
 {
   //  cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
-
   if(!d_cachecachevalid || d_cachedqname!= qname) {
     //    cerr<<"had cache cache miss"<<endl;
     d_cachedqname=qname;
@@ -49,13 +151,13 @@ std::pair<MemRecursorCache::cache_t::const_iterator, MemRecursorCache::cache_t::
   return d_cachecache;
 }
 
-bool MemRecursorCache::entryMatches(cache_t::const_iterator& entry, const QType& qt, bool requireAuth, const ComboAddress& who)
+bool MemRecursorCache::entryMatches(cache_t::const_iterator& entry, uint16_t qt, bool requireAuth, const ComboAddress& who)
 {
   if (requireAuth && !entry->d_auth)
     return false;
 
-  return ((entry->d_qtype == qt.getCode() || qt.getCode()==QType::ANY ||
-           (qt.getCode()==QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
+  return ((entry->d_qtype == qt || qt == QType::ANY ||
+           (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
           && (entry->d_netmask.empty() || entry->d_netmask.match(who)));
 }
 
@@ -64,11 +166,42 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt,
 {
   time_t ttd=0;
   //  cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
+  if(res) {
+    res->clear();
+  }
 
-  auto entries = getEntries(qname, qt);
+  const uint16_t qtype = qt.getCode();
+  /* If we don't have any netmask-specific entries at all, let's just skip this
+     to be able to use the nice d_cachecache hack. */
+  if (qtype != QType::ANY && !d_index.empty()) {
+    if (qtype == QType::ADDR) {
+      int32_t ret = -1;
 
-  if(res)
-    res->clear();
+      auto entryA = getEntryUsingIndex(now, qname, QType::A, requireAuth, who);
+      if (entryA != d_cache.end()) {
+        ret = handleHit(entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+      }
+      auto entryAAAA = getEntryUsingIndex(now, qname, QType::AAAA, requireAuth, who);
+      if (entryAAAA != d_cache.end()) {
+        int32_t ttdAAAA = handleHit(entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+        if (ret > 0) {
+          ret = std::min(ret, ttdAAAA);
+        } else {
+          ret = ttdAAAA;
+        }
+      }
+      return ret > 0 ? static_cast<int32_t>(ret-now) : ret;
+    }
+    else {
+      auto entry = getEntryUsingIndex(now, qname, qtype, requireAuth, who);
+      if (entry != d_cache.end()) {
+        return static_cast<int32_t>(handleHit(entry, qname, who, res, signatures, authorityRecs, variable, state, wasAuth) - now);
+      }
+      return -1;
+    }
+  }
+
+  auto entries = getEntries(qname, qt);
 
   if(entries.first!=entries.second) {
     for(cache_t::const_iterator i=entries.first; i != entries.second; ++i) {
@@ -78,44 +211,10 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt,
         continue;
       }
 
-      if (!entryMatches(i, qt, requireAuth, who))
+      if (!entryMatches(i, qtype, requireAuth, who))
         continue;
 
-      if(variable && !i->d_netmask.empty()) {
-        *variable=true;
-      }
-
-      ttd = i->d_ttd;
-
-      //        cerr<<"Looking at "<<i->d_records.size()<<" records for this name"<<endl;
-      for(auto k=i->d_records.begin(); k != i->d_records.end(); ++k) {
-        if(res) {
-          DNSRecord dr;
-          dr.d_name = qname;
-          dr.d_type = i->d_qtype;
-          dr.d_class = QClass::IN;
-          dr.d_content = *k;
-          dr.d_ttl = static_cast<uint32_t>(i->d_ttd);
-          dr.d_place = DNSResourceRecord::ANSWER;
-          res->push_back(dr);
-        }
-      }
-
-      if(signatures)  // if you do an ANY lookup you are hosed XXXX
-        *signatures=i->d_signatures;
-
-      if(authorityRecs)  // if you do an ANY lookup you are hosed here too XXXX
-        *authorityRecs=i->d_authorityRecs;
-
-      moveCacheItemToBack(d_cache, i);
-
-      if(state) {
-        *state = i->d_state;
-      }
-
-      if(wasAuth) {
-        *wasAuth = i->d_auth;
-      }
+      ttd = handleHit(i, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
 
       if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
         break;
@@ -155,16 +254,26 @@ bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const vector<DNSRe
   return true;
 }
 
-void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
+void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
 {
-  d_cachecachevalid=false;
-  cache_t::iterator stored;
+  d_cachecachevalid = false;
+
+  auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
   bool isNew = false;
-  auto key=boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
-  stored=d_cache.find(key);
-  if(stored == d_cache.end()) {
-    stored=d_cache.insert(CacheEntry(key,CacheEntry::records_t(), auth)).first;
+  cache_t::iterator stored = d_cache.find(key);
+  if (stored == d_cache.end()) {
+    stored = d_cache.insert(CacheEntry(key, CacheEntry::records_t(), auth)).first;
     isNew = true;
+
+    /* don't bother building an index if we don't have any netmask-specific entries */
+    if (ednsmask && !ednsmask->empty()) {
+      auto indexKey = boost::make_tuple(qname, qt.getCode());
+      auto index = d_index.find(indexKey);
+      if (index == d_index.end()) {
+        index = d_index.insert(IndexEntry(qname, qt.getCode())).first;
+      }
+      index->addMask(*ednsmask);
+    }
   }
 
   time_t maxTTD=std::numeric_limits<time_t>::max();
@@ -204,13 +313,12 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt
   //else cerr<<"\tNot nuking"<<endl;
 
 
-  for(auto i=content.cbegin(); i != content.cend(); ++i) {
-
+  for(const auto i : content) {
     /* Yes, we have altered the d_ttl value by adding time(nullptr) to it
        prior to calling this function, so the TTL actually holds a TTD. */
-    ce.d_ttd=min(maxTTD, static_cast<time_t>(i->d_ttl));   // XXX this does weird things if TTLs differ in the set
-    //    cerr<<"To store: "<<i->d_content->getZoneRepresentation()<<" with ttl/ttd "<<i->d_ttl<<", capped at: "<<maxTTD<<endl;
-    ce.d_records.push_back(i->d_content);
+    ce.d_ttd=min(maxTTD, static_cast<time_t>(i.d_ttl));   // XXX this does weird things if TTLs differ in the set
+    //    cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
+    ce.d_records.push_back(i.d_content);
     // there was code here that did things with TTL and auth. Unsure if it was good. XXX
   }
 
@@ -227,14 +335,22 @@ int MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qtype)
   pair<cache_t::iterator, cache_t::iterator> range;
 
   if(!sub) {
-    if(qtype==0xffff)
-      range=d_cache.equal_range(tie(name));
-    else
+    pair<index_t::iterator, index_t::iterator> indexRange;
+    if(qtype==0xffff) {
+      range = d_cache.equal_range(tie(name));
+      indexRange = d_index.equal_range(tie(name));
+    }
+    else {
       range=d_cache.equal_range(tie(name, qtype));
+      indexRange = d_index.equal_range(tie(name, qtype));
+    }
     for(cache_t::const_iterator i=range.first; i != range.second; ) {
       count++;
       d_cache.erase(i++);
     }
+    for(auto i = indexRange.first; i != indexRange.second; ) {
+      d_index.erase(i++);
+    }
   }
   else {
     for(auto iter = d_cache.lower_bound(tie(name)); iter != d_cache.end(); ) {
@@ -247,6 +363,16 @@ int MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qtype)
       else 
        iter++;
     }
+    for(auto iter = d_index.lower_bound(tie(name)); iter != d_index.end(); ) {
+      if(!iter->d_qname.isPartOf(name))
+       break;
+      if(iter->d_qtype == qtype || qtype == 0xffff) {
+       d_index.erase(iter++);
+      }
+      else {
+       iter++;
+      }
+    }
   }
   return count;
 }
@@ -279,22 +405,31 @@ bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtyp
   return false;
 }
 
-bool MemRecursorCache::updateValidationStatus(const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState)
+bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState)
 {
   bool updated = false;
+  uint16_t qtype = qt.getCode();
+  if (qtype != QType::ANY && qtype != QType::ADDR && !d_index.empty()) {
+    auto entry = getEntryUsingIndex(now, qname, qtype, requireAuth, who);
+    if (entry == d_cache.end()) {
+      return false;
+    }
+
+    entry->d_state = newState;
+    return true;
+  }
+
   auto entries = getEntries(qname, qt);
 
   for(auto i = entries.first; i != entries.second; ++i) {
-
-    if (!entryMatches(i, qt, requireAuth, who))
+    if (!entryMatches(i, qtype, requireAuth, who))
       continue;
 
     i->d_state = newState;
     updated = true;
 
-    if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
+    if(qtype != QType::ANY && qtype != QType::ADDR) // normally if we have a hit, we are done
       break;
-
   }
 
   return updated;
@@ -311,14 +446,14 @@ uint64_t MemRecursorCache::doDump(int fd)
 
   uint64_t count=0;
   time_t now=time(0);
-  for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) {
-    for(auto j=i->d_records.cbegin(); j != i->d_records.cend(); ++j) {
+  for(const auto i : sidx) {
+    for(const auto j : i.d_records) {
       count++;
       try {
-        fprintf(fp, "%s %" PRId64 " IN %s %s ; %s\n", i->d_qname.toString().c_str(), static_cast<int64_t>(i->d_ttd - now), DNSRecordContent::NumberToType(i->d_qtype).c_str(), (*j)->getZoneRepresentation().c_str(), i->d_netmask.empty() ? "" : i->d_netmask.toString().c_str());
+        fprintf(fp, "%s %" PRId64 " IN %s %s ; %s\n", i.d_qname.toString().c_str(), static_cast<int64_t>(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str());
       }
       catch(...) {
-        fprintf(fp, "; error printing '%s'\n", i->d_qname.empty() ? "EMPTY" : i->d_qname.toString().c_str());
+        fprintf(fp, "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
       }
     }
   }
@@ -331,5 +466,5 @@ void MemRecursorCache::doPrune(void)
   d_cachecachevalid=false;
 
   unsigned int maxCached=::arg().asNum("max-cache-entries") / g_numThreads;
-  pruneCollection(d_cache, maxCached);
+  pruneCollection(*this, d_cache, maxCached);
 }
index 0f3fbc91bb5527d27102660db576ee6bad2517b0..1fb43745403d7554847fc10ae423e18f527e7595 100644 (file)
@@ -33,6 +33,7 @@
 #undef L
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/hashed_index.hpp>
 #include <boost/tuple/tuple_comparison.hpp>
 #include <boost/multi_index/key_extractors.hpp>
 #include <boost/multi_index/sequenced_index.hpp>
@@ -63,7 +64,7 @@ public:
 
   int doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff);
   bool doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL);
-  bool updateValidationStatus(const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState);
+  bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState);
 
   uint64_t cacheHits, cacheMisses;
 
@@ -72,24 +73,63 @@ private:
   struct CacheEntry
   {
     CacheEntry(const boost::tuple<DNSName, uint16_t, Netmask>& key, const vector<shared_ptr<DNSRecordContent>>& records, bool auth) : 
-      d_qname(key.get<0>()), d_qtype(key.get<1>()), d_auth(auth), d_ttd(0), d_records(records), d_netmask(key.get<2>())
+      d_records(records), d_qname(key.get<0>()), d_netmask(key.get<2>()), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
     {}
 
     typedef vector<std::shared_ptr<DNSRecordContent>> records_t;
-    vector<std::shared_ptr<RRSIGRecordContent>> d_signatures;
     time_t getTTD() const
     {
       return d_ttd;
     }
 
-    DNSName d_qname; 
-    uint16_t d_qtype;
-    bool d_auth;
-    time_t d_ttd;
     records_t d_records;
+    vector<std::shared_ptr<RRSIGRecordContent>> d_signatures;
     std::vector<std::shared_ptr<DNSRecord>> d_authorityRecs;
+    DNSName d_qname;
     Netmask d_netmask;
     mutable vState d_state;
+    time_t d_ttd;
+    uint16_t d_qtype;
+    bool d_auth;
+  };
+
+  class IndexEntry
+  {
+  public:
+    IndexEntry(const DNSName& qname, uint16_t qtype): d_qname(qname), d_qtype(qtype)
+    {
+    }
+
+    Netmask lookupBestMatch(const ComboAddress& addr) const
+    {
+      Netmask result = Netmask();
+
+      const auto best = d_nmt.lookup(addr);
+      if (best != nullptr) {
+        result = best->first;
+      }
+
+      return result;
+    }
+
+    void addMask(const Netmask& nm) const
+    {
+      d_nmt.insert(nm).second = true;
+    }
+
+    void removeNetmask(const Netmask& nm) const
+    {
+      d_nmt.erase(nm);
+    }
+
+    bool isEmpty() const
+    {
+      return d_nmt.empty();
+    }
+
+    mutable NetmaskTree<bool> d_nmt;
+    DNSName d_qname;
+    uint16_t d_qtype;
   };
 
   typedef multi_index_container<
@@ -107,15 +147,46 @@ private:
                sequenced<>
                >
   > cache_t;
+  typedef multi_index_container<
+    IndexEntry,
+    indexed_by <
+      ordered_unique <
+        composite_key<
+          IndexEntry,
+          member<IndexEntry,DNSName,&IndexEntry::d_qname>,
+          member<IndexEntry,uint16_t,&IndexEntry::d_qtype>
+        >
+      >
+    >
+  > index_t;
 
   cache_t d_cache;
+  index_t d_index;
   pair<cache_t::iterator, cache_t::iterator> d_cachecache;
   DNSName d_cachedqname;
   bool d_cachecachevalid;
 
   bool attemptToRefreshNSTTL(const QType& qt, const vector<DNSRecord>& content, const CacheEntry& stored);
-  bool entryMatches(cache_t::const_iterator& entry, const QType& qt, bool requireAuth, const ComboAddress& who);
+  bool entryMatches(cache_t::const_iterator& entry, uint16_t qt, bool requireAuth, const ComboAddress& who);
   std::pair<cache_t::const_iterator, cache_t::const_iterator> getEntries(const DNSName &qname, const QType& qt);
+  cache_t::const_iterator getEntryUsingIndex(time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who);
+  int32_t handleHit(cache_t::iterator entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth);
 
+public:
+  void preRemoval(const CacheEntry& entry)
+  {
+    if (entry.d_netmask.empty()) {
+      return;
+    }
+
+    auto key = tie(entry.d_qname, entry.d_qtype);
+    auto indexEntry = d_index.find(key);
+    if (indexEntry != d_index.end()) {
+      indexEntry->removeNetmask(entry.d_netmask);
+      if (indexEntry->isEmpty()) {
+        d_index.erase(indexEntry);
+      }
+    }
+  }
 };
 #endif
index c719eae85a9b3c379a7258eb8178e209302a58a9..49f4e2448eb65256ece0dddcc4662f9baea04471 100644 (file)
@@ -155,7 +155,7 @@ void NegCache::clear() {
  * \param maxEntries The maximum number of entries that may exist in the cache.
  */
 void NegCache::prune(unsigned int maxEntries) {
-  pruneCollection(d_negcache, maxEntries, 200);
+  pruneCollection(*this, d_negcache, maxEntries, 200);
 }
 
 /*!
index e82883b8011ce86c55ac0bc668cf7b44fb302f2b..e25fe5da98ae0902a9416119951418879750e131 100644 (file)
@@ -71,6 +71,10 @@ class NegCache : public boost::noncopyable {
       return d_negcache.size();
     };
 
+    void preRemoval(const NegCacheEntry& entry)
+    {
+    }
+
   private:
     typedef boost::multi_index_container <
       NegCacheEntry,
index 95123efddc2138e63c33046fa481c74dcbe2fb10..e6065c33dbe651cfd1cea4297d65ad276d8620ef 100644 (file)
@@ -832,7 +832,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
             state = SyncRes::validateRecordsWithSigs(depth, qname, QType(QType::CNAME), qname, cset, signatures);
             if (state != Indeterminate) {
               LOG(prefix<<qname<<": got Indeterminate state from the CNAME cache, new validation result is "<<vStates[state]<<endl);
-              t_RC->updateValidationStatus(qname, QType(QType::CNAME), d_requestor, d_requireAuthData, state);
+              t_RC->updateValidationStatus(d_now.tv_sec, qname, QType(QType::CNAME), d_requestor, d_requireAuthData, state);
             }
           }
         }
@@ -990,7 +990,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector<DNSR
 
         if (cachedState != Indeterminate) {
           LOG(prefix<<qname<<": got Indeterminate state from the cache, validation result is "<<vStates[cachedState]<<endl);
-          t_RC->updateValidationStatus(sqname, sqt, d_requestor, d_requireAuthData, cachedState);
+          t_RC->updateValidationStatus(d_now.tv_sec, sqname, sqt, d_requestor, d_requireAuthData, cachedState);
         }
       }
     }