]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Use multi-index for all time-based tables except nsspeeds
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 20 Nov 2019 09:59:17 +0000 (10:59 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 20 Nov 2019 09:59:17 +0000 (10:59 +0100)
Consistently move to float for nsspeeds, plus some minor cleanup.

pdns/pdns_recursor.cc
pdns/recursordist/test-syncres_cc2.cc
pdns/syncres.cc
pdns/syncres.hh

index 76a99b86f9db2d0959215fdb5a7350e746cdacfe..3fc361433a3cee5c45dbba57af96900208a4a6bf 100644 (file)
@@ -2882,7 +2882,8 @@ static void doStats(void)
 
 static void houseKeeping(void *)
 {
-  static thread_local time_t last_rootupdate, last_prune, last_secpoll, last_trustAnchorUpdate{0};
+  static thread_local time_t last_rootupdate, last_secpoll, last_trustAnchorUpdate{0};
+  static thread_local timeval last_prune;
   static thread_local int cleanCounter=0;
   static thread_local bool s_running;  // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
   auto luaconfsLocal = g_luaconfs.getLocal();
@@ -2898,24 +2899,27 @@ static void houseKeeping(void *)
     }
     s_running=true;
 
-    struct timeval now;
-    Utility::gettimeofday(&now, 0);
-
-    if(now.tv_sec - last_prune > (time_t)(5 + t_id)) {
+    struct timeval now, past;
+    Utility::gettimeofday(&now, nullptr);
+    past = now;
+    past.tv_sec -= 5;
+    if (last_prune < past) {
       t_RC->doPrune(g_maxCacheEntries / g_numThreads); // this function is local to a thread, so fine anyhow
       t_packetCache->doPruneTo(g_maxPacketCacheEntries / g_numWorkerThreads);
 
       SyncRes::pruneNegCache(g_maxCacheEntries / (g_numWorkerThreads * 10));
 
+      time_t limit;
       if(!((cleanCounter++)%40)) {  // this is a full scan!
-       time_t limit=now.tv_sec-300;
+       limit=now.tv_sec-300;
         SyncRes::pruneNSSpeeds(limit);
-        limit = now.tv_sec - SyncRes::s_serverdownthrottletime * 10;
-        SyncRes::pruneFailedServers(limit);
-        limit = now.tv_sec - 2*3600;
-        SyncRes::pruneEDNSStatuses(limit);
       }
-      last_prune=time(0);
+      limit = now.tv_sec - SyncRes::s_serverdownthrottletime * 10;
+      SyncRes::pruneFailedServers(limit);
+      limit = now.tv_sec - 2*3600;
+      SyncRes::pruneEDNSStatuses(limit);
+      SyncRes::pruneThrottledServers();
+      Utility::gettimeofday(&last_prune, nullptr);
     }
 
     if(now.tv_sec - last_rootupdate > 7200) {
index 4387ba68faf748ef41d0b8c3879fb4202ed45aaf..8ac306e6ac99ac0bc689571adce6ba37cade6203 100644 (file)
@@ -1136,12 +1136,12 @@ BOOST_AUTO_TEST_CASE(test_ns_speed) {
 
   /* make pdns-public-ns2.powerdns.com. the fastest NS, with its IPv6 address faster than the IPV4 one,
      then pdns-public-ns1.powerdns.com. on IPv4 */
-  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("192.0.2.1:53"), 100, &now);
-  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("[2001:DB8::1]:53"), 10000, &now);
-  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("192.0.2.2:53"), 10, &now);
-  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("[2001:DB8::2]:53"), 1, &now);
-  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("192.0.2.3:53"), 10000, &now);
-  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("[2001:DB8::3]:53"), 10000, &now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("192.0.2.1:53"), 100, now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("[2001:DB8::1]:53"), 10000, now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("192.0.2.2:53"), 10, now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("[2001:DB8::2]:53"), 1, now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("192.0.2.3:53"), 10000, now);
+  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("[2001:DB8::3]:53"), 10000, now);
 
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
index 501d453f33dbd3380ce854f764a689ebf93d27f3..31529d2783761d2b9b894240707f8c0e407f7359 100644 (file)
@@ -423,7 +423,7 @@ uint64_t SyncRes::doEDNSDump(int fd)
   for(const auto& eds : t_sstorage.ednsstatus) {
     count++;
     char tmp[26];
-    fprintf(fp.get(), "%s\t%d\t%s", eds.first.toString().c_str(), (int)eds.second.mode, ctime_r(&eds.second.modeSetAt, tmp));
+    fprintf(fp.get(), "%s\t%d\t%s", eds.address.toString().c_str(), (int)eds.mode, ctime_r(&eds.modeSetAt, tmp));
   }
   return count;
 }
@@ -462,12 +462,12 @@ uint64_t SyncRes::doDumpThrottleMap(int fd)
   uint64_t count=0;
 
   const auto& throttleMap = t_sstorage.throttle.getThrottleMap();
-  for(const auto& i : throttleMap)
+  for(const auto i : throttleMap)
   {
     count++;
     char tmp[26];
     // remote IP, dns name, qtype, count, ttd
-    fprintf(fp.get(), "%s\t%s\t%d\t%u\t%s", i.first.get<0>().toString().c_str(), i.first.get<1>().toLogString().c_str(), i.first.get<2>(), i.second.count, ctime_r(&i.second.ttd, tmp));
+    fprintf(fp.get(), "%s\t%s\t%d\t%u\t%s", i.thing.get<0>().toString().c_str(), i.thing.get<1>().toLogString().c_str(), i.thing.get<2>(), i.count, ctime_r(&i.ttd, tmp));
   }
 
   return count;
@@ -486,9 +486,9 @@ uint64_t SyncRes::doDumpFailedServers(int fd)
   {
     count++;
     char tmp[26];
-    ctime_r(&i.second.last, tmp);
-    fprintf(fp.get(), "%s\t%lld\t%s", i.first.toString().c_str(),
-            static_cast<long long>(i.second.value), tmp);
+    ctime_r(&i.last, tmp);
+    fprintf(fp.get(), "%s\t%lld\t%s", i.address.toString().c_str(),
+            static_cast<long long>(i.value), tmp);
   }
 
   return count;
@@ -537,15 +537,15 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
      If '3', send bare queries
   */
 
-  SyncRes::EDNSStatus* ednsstatus = &t_sstorage.ednsstatus[ip]; // does this include port? YES
-
+  auto ednsstatus = t_sstorage.ednsstatus.insert(ip).first; // does this include port? YES
+  auto &ind = t_sstorage.ednsstatus.get<ComboAddress>();
   if (ednsstatus->modeSetAt && ednsstatus->modeSetAt + 3600 < d_now.tv_sec) {
-    *ednsstatus = SyncRes::EDNSStatus();
+    t_sstorage.ednsstatus.reset(ind, ednsstatus);
     //    cerr<<"Resetting EDNS Status for "<<ip.toString()<<endl);
   }
 
-  SyncRes::EDNSStatus::EDNSMode *mode = &ednsstatus->mode;
-  SyncRes::EDNSStatus::EDNSMode oldmode = *mode;
+  const SyncRes::EDNSStatus::EDNSMode *mode = &ednsstatus->mode;
+  const SyncRes::EDNSStatus::EDNSMode oldmode = *mode;
   int EDNSLevel = 0;
   auto luaconfsLocal = g_luaconfs.getLocal();
   ResolveContext ctx;
@@ -578,7 +578,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
       ret=asyncresolve(ip, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
     }
     // ednsstatus might be cleared, so do a new lookup
-    ednsstatus = &t_sstorage.ednsstatus[ip]; // does this include port? YES
+    ednsstatus = t_sstorage.ednsstatus.insert(ip).first;
     mode = &ednsstatus->mode;
     if(ret < 0) {
       return ret; // transport error, nothing to learn here
@@ -590,23 +590,23 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
     else if (*mode == EDNSStatus::UNKNOWN || *mode == EDNSStatus::EDNSOK || *mode == EDNSStatus::EDNSIGNORANT ) {
       if(res->d_validpacket && !res->d_haveEDNS && res->d_rcode == RCode::FormErr)  {
        //      cerr<<"Downgrading to NOEDNS because of "<<RCode::to_s(res->d_rcode)<<" for query to "<<ip.toString()<<" for '"<<domain<<"'"<<endl;
-        *mode = EDNSStatus::NOEDNS;
+        t_sstorage.ednsstatus.setMode(ind, ednsstatus, EDNSStatus::NOEDNS);
         continue;
       }
       else if(!res->d_haveEDNS) {
         if (*mode != EDNSStatus::EDNSIGNORANT) {
-          *mode = EDNSStatus::EDNSIGNORANT;
+          t_sstorage.ednsstatus.setMode(ind, ednsstatus, EDNSStatus::EDNSIGNORANT);
          //      cerr<<"We find that "<<ip.toString()<<" is an EDNS-ignorer for '"<<domain<<"', moving to mode 2"<<endl;
        }
       }
       else {
-       *mode = EDNSStatus::EDNSOK;
+        t_sstorage.ednsstatus.setMode(ind, ednsstatus, EDNSStatus::EDNSOK);
        //      cerr<<"We find that "<<ip.toString()<<" is EDNS OK!"<<endl;
       }
       
     }
     if (oldmode != *mode || !ednsstatus->modeSetAt)
-      ednsstatus->modeSetAt=d_now.tv_sec;
+      t_sstorage.ednsstatus.setTS(ind, ednsstatus, d_now.tv_sec);
     //    cerr<<"Result: ret="<<ret<<", EDNS-level: "<<EDNSLevel<<", haveEDNS: "<<res->d_haveEDNS<<", new mode: "<<mode<<endl;  
     return ret;
   }
@@ -878,12 +878,12 @@ static bool ipv6First(const ComboAddress& a, const ComboAddress& b)
 
 struct speedOrderCA
 {
-  speedOrderCA(std::map<ComboAddress,double>& speeds): d_speeds(speeds) {}
+  speedOrderCA(std::map<ComboAddress,float>& speeds): d_speeds(speeds) {}
   bool operator()(const ComboAddress& a, const ComboAddress& b) const
   {
     return d_speeds[a] < d_speeds[b];
   }
-  std::map<ComboAddress, double>& d_speeds;
+  std::map<ComboAddress, float>& d_speeds;
 };
 
 /** This function explicitly goes out for A or AAAA addresses
@@ -949,10 +949,10 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
      for this nameserver that are no longer in the set, even if there
      is only one or none at all in the current set.
   */
-  map<ComboAddress, double> speeds;
+  map<ComboAddress, float> speeds;
   auto& collection = t_sstorage.nsSpeeds[qname].d_collection;
   for(const auto& val: ret) {
-    speeds[val] = collection[val].get(&d_now);
+    speeds[val] = collection[val].get(d_now);
   }
 
   t_sstorage.nsSpeeds[qname].purge(speeds);
@@ -1606,18 +1606,18 @@ bool SyncRes::moreSpecificThan(const DNSName& a, const DNSName &b) const
 
 struct speedOrder
 {
-  bool operator()(const std::pair<DNSName, double> &a, const std::pair<DNSName, double> &b) const
+  bool operator()(const std::pair<DNSName, float> &a, const std::pair<DNSName, float> &b) const
   {
     return a.second < b.second;
   }
 };
 
-inline std::vector<std::pair<DNSName, double>> SyncRes::shuffleInSpeedOrder(NsSet &tnameservers, const string &prefix)
+inline std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(NsSet &tnameservers, const string &prefix)
 {
-  std::vector<std::pair<DNSName, double>> rnameservers;
+  std::vector<std::pair<DNSName, float>> rnameservers;
   rnameservers.reserve(tnameservers.size());
   for(const auto& tns: tnameservers) {
-    double speed = t_sstorage.nsSpeeds[tns.first].get(&d_now);
+    float speed = t_sstorage.nsSpeeds[tns.first].get(d_now);
     rnameservers.push_back({tns.first, speed});
     if(tns.first.empty()) // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
       return rnameservers;
@@ -1646,12 +1646,12 @@ inline std::vector<std::pair<DNSName, double>> SyncRes::shuffleInSpeedOrder(NsSe
 inline vector<ComboAddress> SyncRes::shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd)
 {
   vector<ComboAddress> nameservers = rnameservers;
-  map<ComboAddress, double> speeds;
+  map<ComboAddress, float> speeds;
 
   for(const auto& val: nameservers) {
-    double speed;
+    float speed;
     DNSName nsName = DNSName(val.toStringWithPort());
-    speed=t_sstorage.nsSpeeds[nsName].get(&d_now);
+    speed=t_sstorage.nsSpeeds[nsName].get(d_now);
     speeds[val]=speed;
   }
   random_shuffle(nameservers.begin(),nameservers.end());
@@ -1799,7 +1799,7 @@ bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAd
   return false;
 }
 
-vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, double>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, double>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
+vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
 {
   vector<ComboAddress> result;
 
@@ -3176,7 +3176,7 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
     if(resolveret != -2 && !chained && !dontThrottle) {
       // don't account for resource limits, they are our own fault
       // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
-      t_sstorage.nsSpeeds[nsName.empty()? DNSName(remoteIP.toStringWithPort()) : nsName].submit(remoteIP, 1000000, &d_now); // 1 sec
+      t_sstorage.nsSpeeds[nsName.empty()? DNSName(remoteIP.toStringWithPort()) : nsName].submit(remoteIP, 1000000, d_now); // 1 sec
 
       // code below makes sure we don't filter COM or the root
       if (s_serverdownmaxfails > 0 && (auth != g_rootdnsname) && t_sstorage.fails.incr(remoteIP, d_now) >= s_serverdownmaxfails) {
@@ -3496,7 +3496,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
           */
           //        cout<<"msec: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
 
-          t_sstorage.nsSpeeds[tns->first.empty()? DNSName(remoteIP->toStringWithPort()) : tns->first].submit(*remoteIP, lwr.d_usec, &d_now);
+          t_sstorage.nsSpeeds[tns->first.empty()? DNSName(remoteIP->toStringWithPort()) : tns->first].submit(*remoteIP, lwr.d_usec, d_now);
 
           /* we have received an answer, are we done ? */
           bool done = processAnswer(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state);
index d71f848c41a13d6f7657ebfce79b6aca80d9ba88..0804490671021a3e1db25d95659082da35c66c3c 100644 (file)
@@ -78,60 +78,53 @@ typedef std::unordered_map<
 template<class Thing> class Throttle : public boost::noncopyable
 {
 public:
-  Throttle() : d_limit(3), d_ttl(60), d_last_clean(time(nullptr))
-  {
-  }
 
-  struct entry
+  struct entry_t
   {
+    Thing thing;
     time_t ttd;
-    unsigned int count;
+    mutable unsigned int count;
   };
-  typedef map<Thing,entry> cont_t;
+  typedef multi_index_container<entry_t,
+                                indexed_by<
+                                  ordered_unique<tag<Thing>, member<entry_t, Thing, &entry_t::thing>>,
+                                  ordered_non_unique<tag<time_t>, member<entry_t, time_t, &entry_t::ttd>>
+                                  >> cont_t;
 
-  bool shouldThrottle(time_t now, const Thingt)
+  bool shouldThrottle(time_t now, const Thing &t)
   {
-    if(now > d_last_clean + 300 ) {
-
-      d_last_clean=now;
-      for(typename cont_t::iterator i=d_cont.begin();i!=d_cont.end();) {
-        if( i->second.ttd < now) {
-          d_cont.erase(i++);
-        }
-        else
-          ++i;
-      }
-    }
-
-    typename cont_t::iterator i=d_cont.find(t);
-    if(i==d_cont.end())
+    auto i = d_cont.find(t);
+    if (i == d_cont.end()) {
       return false;
-    if(now > i->second.ttd || i->second.count == 0) {
+    }
+    if (now > i->ttd || i->count == 0) {
       d_cont.erase(i);
       return false;
     }
-    i->second.count--;
+    i->count--;
 
     return true; // still listed, still blocked
   }
-  void throttle(time_t now, const Thing& t, time_t ttl=0, unsigned int tries=0)
-  {
-    typename cont_t::iterator i=d_cont.find(t);
-    entry e={ now+(ttl ? ttl : d_ttl), tries ? tries : d_limit};
 
-    if(i==d_cont.end()) {
-      d_cont[t]=e;
+  void throttle(time_t now, const Thing &t, time_t ttl, unsigned int count)
+  {
+    auto i = d_cont.find(t);
+    time_t ttd = now + ttl;
+    if (i == d_cont.end()) {
+      entry_t e = { t, ttd, count };
+      d_cont.insert(e);
+    } else if (i->ttd > ttd || i->count < count) { // ????
+      auto &ind = d_cont.template get<Thing>();
+      ind.modify(i, [ttd,count](entry_t &e) { e.ttd = ttd; e.count = count; });
     }
-    else if(i->second.ttd > e.ttd || (i->second.count) < e.count)
-      d_cont[t]=e;
   }
 
-  unsigned int size() const
+  size_t size() const
   {
-    return (unsigned int)d_cont.size();
+    return d_cont.size();
   }
 
-  const cont_tgetThrottleMap() const
+  const cont_t &getThrottleMap() const
   {
     return d_cont;
   }
@@ -141,10 +134,13 @@ public:
     d_cont.clear();
   }
 
+  void prune() {
+    time_t now = time(NULL);
+    auto &ind = d_cont.template get<time_t>();
+    ind.erase(ind.begin(), ind.upper_bound(now));
+  }
+
 private:
-  unsigned int d_limit;
-  time_t d_ttl;
-  time_t d_last_clean;
   cont_t d_cont;
 };
 
@@ -166,10 +162,8 @@ public:
   DecayingEwma(const DecayingEwma& orig) = delete;
   DecayingEwma & operator=(const DecayingEwma& orig) = delete;
   
-  void submit(int val, const struct timeval* tv)
+  void submit(int val, const struct timeval& now)
   {
-    struct timeval now=*tv;
-
     if(d_needinit) {
       d_last=now;
       d_lastget=now;
@@ -185,9 +179,8 @@ public:
     }
   }
 
-  float get(const struct timeval* tv)
+  float get(const struct timeval &now)
   {
-    struct timeval now=*tv;
     float diff=makeFloat(d_lastget-now);
     d_lastget=now;
     float factor=expf(diff/60.0f); // is 1.0 or less
@@ -211,55 +204,51 @@ private:
   bool d_needinit;
 };
 
-template<class Thing> class Counters : public boost::noncopyable
+class fails_t : public boost::noncopyable
 {
 public:
   typedef unsigned long counter_t;
   struct value_t {
-    counter_t value;
-    time_t last;
+    value_t(const ComboAddress &a) : address(a) {}
+    ComboAddress address;
+    mutable counter_t value{0};
+    time_t last{0};
   };
-  typedef std::map<Thing, value_t> cont_t;
+
+  typedef multi_index_container<value_t,
+                                indexed_by<
+                                  ordered_unique<tag<ComboAddress>, member<value_t, ComboAddress, &value_t::address>>,
+                                  ordered_non_unique<tag<time_t>, member<value_t, time_t, &value_t::last>>
+                                  >> cont_t;
 
   cont_t getMap() const {
     return d_cont;
   }
-  counter_t value(const Thing& t) const
+  counter_t value(const ComboAddress& t) const
   {
-    typename cont_t::const_iterator i = d_cont.find(t);
+    auto i = d_cont.find(t);
 
     if (i == d_cont.end()) {
       return 0;
     }
-    return i->second.value;
+    return i->value;
   }
 
-  counter_t incr(const Thing& t, const struct timeval & now)
+  counter_t incr(const ComboAddress& t, const struct timeval & now)
   {
-    typename cont_t::iterator i = d_cont.find(t);
+    auto i = d_cont.insert(t).first;
 
-    if (i == d_cont.end()) {
-      auto &r = d_cont[t];
-      r.value = 1;
-      r.last = now.tv_sec;
-      return 1;
-    }
-    else {
-      if (i->second.value < std::numeric_limits<counter_t>::max()) {
-        i->second.value++;
-      }
-      i->second.last = now.tv_sec;
-      return i->second.value;
+    if (i->value < std::numeric_limits<counter_t>::max()) {
+      i->value++;
     }
+    auto &ind = d_cont.get<ComboAddress>();
+    ind.modify(i, [t = now.tv_sec](value_t &val) { val.last = t;});
+    return i->value;
   }
 
-  void clear(const Thing& t)
+  void clear(const ComboAddress& a)
   {
-    typename cont_t::iterator i = d_cont.find(t);
-
-    if (i != d_cont.end()) {
-      d_cont.erase(i);
-    }
+    d_cont.erase(a);
   }
 
   void clear()
@@ -273,32 +262,20 @@ public:
   }
 
   void prune(time_t cutoff) {
-    for (auto it = d_cont.begin(); it != d_cont.end(); ) {
-      if (it->second.last <= cutoff) {
-        it = d_cont.erase(it);
-      } else {
-        ++it;
-      }
-    }
+    auto &ind = d_cont.get<time_t>();
+    ind.erase(ind.begin(), ind.upper_bound(cutoff));
   }
 
 private:
   cont_t d_cont;
 };
 
-
 class SyncRes : public boost::noncopyable
 {
 public:
   enum LogMode { LogNone, Log, Store};
   typedef std::function<int(const ComboAddress& ip, const DNSName& qdomain, int qtype, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult *lwr, bool* chained)> asyncresolve_t;
 
-  struct EDNSStatus
-  {
-    time_t modeSetAt{0};
-    enum EDNSMode { UNKNOWN=0, EDNSOK=1, EDNSIGNORANT=2, NOEDNS=3 } mode{UNKNOWN};
-  };
-
   enum class HardenNXD { No, DNSSEC, Yes };
   
   //! This represents a number of decaying Ewmas, used to store performance per nameserver-name.
@@ -306,17 +283,17 @@ public:
       d_best is filled out with the best address for this collection */
   struct DecayingEwmaCollection
   {
-    void submit(const ComboAddress& remote, int usecs, const struct timeval* now)
+    void submit(const ComboAddress& remote, int usecs, const struct timeval& now)
     {
       d_collection[remote].submit(usecs, now);
     }
 
-    double get(const struct timeval* now)
+    float get(const struct timeval& now)
     {
       if(d_collection.empty())
         return 0;
-      double ret=std::numeric_limits<double>::max();
-      double tmp;
+      float ret=std::numeric_limits<float>::max();
+      float tmp;
       for (auto& entry : d_collection) {
         if((tmp = entry.second.get(now)) < ret) {
           ret=tmp;
@@ -335,7 +312,7 @@ public:
       return true;
     }
 
-    void purge(const std::map<ComboAddress, double>& keep)
+    void purge(const std::map<ComboAddress, float>& keep)
     {
       for (auto iter = d_collection.begin(); iter != d_collection.end(); ) {
         if (keep.find(iter->first) != keep.end()) {
@@ -348,12 +325,11 @@ public:
     }
 
     typedef std::map<ComboAddress, DecayingEwma> collection_t;
-    collection_t d_collection;
     ComboAddress d_best;
+    collection_t d_collection;
   };
 
   typedef std::unordered_map<DNSName, DecayingEwmaCollection> nsspeeds_t;
-  typedef map<ComboAddress, EDNSStatus> ednsstatus_t;
 
   vState getDSRecords(const DNSName& zone, dsmap_t& ds, bool onlyTA, unsigned int depth, bool bogusOnNXD=true, bool* foundCut=nullptr);
 
@@ -402,7 +378,35 @@ public:
 
   typedef std::unordered_map<DNSName, AuthDomain> domainmap_t;
   typedef Throttle<boost::tuple<ComboAddress,DNSName,uint16_t> > throttle_t;
-  typedef Counters<ComboAddress> fails_t;
+
+  struct EDNSStatus {
+    EDNSStatus(const ComboAddress &arg) : address(arg) {}
+    ComboAddress address;
+    time_t modeSetAt{0};
+    mutable enum EDNSMode { UNKNOWN=0, EDNSOK=1, EDNSIGNORANT=2, NOEDNS=3 } mode{UNKNOWN};
+  };
+
+  struct ednsstatus_t : public multi_index_container<EDNSStatus,
+                                                     indexed_by<
+                                                       ordered_unique<tag<ComboAddress>, member<EDNSStatus, ComboAddress, &EDNSStatus::address>>,
+                                                       ordered_non_unique<tag<time_t>, member<EDNSStatus, time_t, &EDNSStatus::modeSetAt>>
+                                  >> {
+    void reset(index<ComboAddress>::type &ind, iterator it) {
+      ind.modify(it, [](EDNSStatus &s) { s.mode = EDNSStatus::EDNSMode::UNKNOWN; s.modeSetAt = 0; }); 
+    }
+    void setMode(index<ComboAddress>::type &ind, iterator it, EDNSStatus::EDNSMode mode) {
+      it->mode = mode;
+    }
+    void setTS(index<ComboAddress>::type &ind, iterator it, time_t ts) {
+      ind.modify(it, [ts](EDNSStatus &s) { s.modeSetAt = ts; });
+    }
+
+    void prune(time_t cutoff) {
+      auto &ind = get<time_t>();
+      ind.erase(ind.begin(), ind.upper_bound(cutoff));
+    }
+
+  };
 
   struct ThreadLocalStorage {
     NegCache negcache;
@@ -489,7 +493,7 @@ public:
   {
     return t_sstorage.nsSpeeds.size();
   }
-  static void submitNSSpeed(const DNSName& server, const ComboAddress& ca, uint32_t usec, const struct timeval* now)
+  static void submitNSSpeed(const DNSName& server, const ComboAddress& ca, uint32_t usec, const struct timeval& now)
   {
     t_sstorage.nsSpeeds[server].submit(ca, usec, now);
   }
@@ -503,7 +507,7 @@ public:
     if (it == t_sstorage.ednsstatus.end())
       return EDNSStatus::UNKNOWN;
 
-    return it->second.mode;
+    return it->mode;
   }
   static uint64_t getEDNSStatusesSize()
   {
@@ -515,18 +519,16 @@ public:
   }
   static void pruneEDNSStatuses(time_t cutoff)
   {
-    for (auto it = t_sstorage.ednsstatus.begin(); it != t_sstorage.ednsstatus.end(); ) {
-      if (it->second.modeSetAt <= cutoff) {
-        it = t_sstorage.ednsstatus.erase(it);
-      } else {
-        ++it;
-      }
-    }
+    t_sstorage.ednsstatus.prune(cutoff);
   }
   static uint64_t getThrottledServersSize()
   {
     return t_sstorage.throttle.size();
   }
+  static void pruneThrottledServers()
+  {
+    t_sstorage.throttle.prune();
+  }
   static void clearThrottle()
   {
     t_sstorage.throttle.clear();
@@ -840,7 +842,7 @@ private:
   void getBestNSFromCache(const DNSName &qname, const QType &qtype, vector<DNSRecord>&bestns, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>& beenthere);
   DNSName getBestNSNamesFromCache(const DNSName &qname, const QType &qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>&beenthere);
 
-  inline vector<std::pair<DNSName, double>> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
+  inline vector<std::pair<DNSName, float>> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
   inline vector<ComboAddress> shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd);
   bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
   vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
@@ -849,7 +851,7 @@ private:
   bool nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress&);
   bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
 
-  vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, double>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, double>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
+  vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
 
   void sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, bool rdQuery);
   RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery);