From: Bert Hubert Date: Tue, 28 Jul 2009 20:33:20 +0000 (+0000) Subject: clean up recursor cache and make it thread safe, plus change the core logic X-Git-Tag: rec-3.2~148 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=342648791c111ddbfe667f3091e7a8862e7faab7;p=pdns clean up recursor cache and make it thread safe, plus change the core logic git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1374 d19b8d6e-7fed-0310-83ef-9ca221ded41b --- diff --git a/pdns/recursor_cache.cc b/pdns/recursor_cache.cc index 0c92f127c..931f0ac99 100644 --- a/pdns/recursor_cache.cc +++ b/pdns/recursor_cache.cc @@ -5,35 +5,15 @@ #include "dnsrecords.hh" #include "arguments.hh" #include "syncres.hh" +#include "lock.hh" +#include "recursor_cache.hh" using namespace std; using namespace boost; #include "config.h" -#ifdef GCC_SKIP_LOCKING -#include -// This code is ugly but does speedup the recursor tremendously on multi-processor systems, and even has a large effect (20, 30%) on uniprocessor -namespace __gnu_cxx -{ - _Atomic_word - __attribute__ ((__unused__)) - __exchange_and_add(volatile _Atomic_word* __mem, int __val) - { - register _Atomic_word __result=*__mem; - *__mem+=__val; - return __result; - } - - void - __attribute__ ((__unused__)) - __atomic_add(volatile _Atomic_word* __mem, int __val) - { - *__mem+=__val; - } -} -#endif - +pthread_rwlock_t MemRecursorCache::s_rwlock; DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const string& serial, uint32_t ttd) { @@ -82,6 +62,7 @@ DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const strin return rr; } +// returns the RDATA for rr - might be compressed! string DNSRR2String(const DNSResourceRecord& rr) { uint16_t type=rr.qtype.getCode(); @@ -125,6 +106,8 @@ unsigned int MemRecursorCache::bytes() int MemRecursorCache::getDirect(time_t now, const char* qname, const QType& qt, uint32_t ttd[10], char* data[10], uint16_t len[10]) { + ReadLock rl(&s_rwlock); + if(!d_cachecachevalid || Utility::strcasecmp(d_cachedqname.c_str(), qname)) { // cerr<<"had cache cache miss for '"<* res) { + WriteLock wl(&s_rwlock); unsigned int ttd=0; // cerr<<"looking up "<< qname+"|"+qt.getName()<<"\n"; @@ -214,7 +198,7 @@ int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set< sequence_t::iterator si=d_cache.project<1>(i); for(vector::const_iterator k=i->d_records.begin(); k != i->d_records.end(); ++k) { - if(k->d_ttd < 1000000000 || k->d_ttd > (uint32_t) now) { + if(k->d_ttd < 1000000000 || k->d_ttd > (uint32_t) now) { // FIXME what does the 100000000 number mean? ttd=k->d_ttd; if(res) { DNSResourceRecord rr=String2DNSRR(qname, QType(i->d_qtype), k->d_string, ttd); @@ -237,9 +221,12 @@ int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set< } return -1; } + + bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const set& content, const CacheEntry& stored) { + // WriteLock wl(&s_rwlock); (holds the lock already) if(!stored.d_auth) { // cerr<<"feel free to scribble non-auth data!"<& content, bool auth) { + WriteLock wl(&s_rwlock); d_cachecachevalid=false; tuple key=make_tuple(qname, qt.getCode()); cache_t::iterator stored=d_cache.find(key); - // cerr<<"storing "<< qname+"|"+qt.getName()<<" -> '"<content<<"'\n"; - bool isNew=false; if(stored == d_cache.end()) { stored=d_cache.insert(CacheEntry(key,vector(), auth)).first; isNew=true; } - pair::iterator, vector::iterator> range; StoredRecord dr; CacheEntry ce=*stored; - if(qt.getCode()==QType::SOA || qt.getCode()==QType::CNAME) // you can only have one (1) each of these - ce.d_records.clear(); + // cerr<<"storing "<< qname+"|"+qt.getName()<<" -> '"<content<<"', isnew="<d_ttd > now) break; if(j != ce.d_records.end()) { // we still have valid data, ignore unauth data + // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n"; return; } else { ce.d_auth = false; // new data won't be auth } } - +#if 0 + if(auth && !attemptToRefreshNSTTL(qt, content, ce) ) { + cerr<<"\tGot auth data, and it was not refresh attempt of an NS record, nuking storage"<::const_iterator i=content.begin(); i != content.end(); ++i) { dr.d_ttd=i->ttl; dr.d_string=DNSRR2String(*i); @@ -318,17 +310,32 @@ void MemRecursorCache::replace(time_t now, const string &qname, const QType& qt, ce.d_records.push_back(dr); else { range=equal_range(ce.d_records.begin(), ce.d_records.end(), dr); - + + if(range.first != range.second && (range.first != ce.d_records.begin() || range.second != ce.d_records.end())) { + // cerr<<"\t\tIncomplete match! Must nuke"<::iterator j=range.first ; j!=range.second; ++j) { /* see http://mailman.powerdns.com/pipermail/pdns-users/2006-May/003413.html */ - if(j->d_ttd > (unsigned int) now && i->ttl > j->d_ttd && qt.getCode()==QType::NS && auth) // don't allow auth servers to *raise* TTL of an NS record + if(j->d_ttd > (unsigned int) now && i->ttl > j->d_ttd && qt.getCode()==QType::NS && auth) { // don't allow auth servers to *raise* TTL of an NS recor + // cerr<<"\t\tNot doing so, trying to raise TTL NS\n"; continue; - if(i->ttl > j->d_ttd || (auth && d_followRFC2181) ) // authoritative packets can override the TTL to be lower + } + if(i->ttl > j->d_ttd || (auth && d_followRFC2181) ) { // authoritative packets can override the TTL to be lower + // cerr<<"\t\tUpdating the ttl, diff="<d_ttd - i->ttl<d_ttd=i->ttl; + } + else { + // cerr<<"\t\tNOT updating the ttl, old= " <d_ttd - now <<", new: "<ttl - now < range; @@ -364,6 +373,7 @@ int MemRecursorCache::doWipeCache(const string& name, uint16_t qtype) bool MemRecursorCache::doAgeCache(time_t now, const string& name, uint16_t qtype, int32_t newTTL) { + WriteLock wl(&s_rwlock); cache_t::iterator iter = d_cache.find(tie(name, qtype)); if(iter == d_cache.end()) return false; @@ -392,6 +402,7 @@ bool MemRecursorCache::doAgeCache(time_t now, const string& name, uint16_t qtype void MemRecursorCache::doDumpAndClose(int fd) { + WriteLock wl(&s_rwlock); FILE* fp=fdopen(fd, "w"); if(!fp) { close(fd); @@ -423,6 +434,7 @@ void MemRecursorCache::doSlash(int perc) void MemRecursorCache::doPrune(void) { + WriteLock wl(&s_rwlock); uint32_t now=(uint32_t)time(0); d_cachecachevalid=false; diff --git a/pdns/recursor_cache.hh b/pdns/recursor_cache.hh index f78daf288..1de0f5226 100644 --- a/pdns/recursor_cache.hh +++ b/pdns/recursor_cache.hh @@ -6,6 +6,7 @@ #include "qtype.hh" #include "misc.hh" #include + #include #undef L #include @@ -28,7 +29,10 @@ class MemRecursorCache : public boost::noncopyable // : public RecursorCache { public: MemRecursorCache() : d_followRFC2181(false), d_cachecachevalid(false) - {} + { + pthread_rwlock_init(&s_rwlock, 0); + // cerr<<"ce: "<* res); @@ -62,32 +66,14 @@ private: }; - struct predicate - { - predicate(uint32_t limit) : d_limit(limit) - { - } - - bool operator()(const StoredRecord& sr) const - { - return sr.d_ttd <= d_limit; - } - uint32_t d_limit; - }; - - // typedef __gnu_cxx::hash_map > cache_t; struct CacheEntry { - string d_qname; - uint16_t d_qtype; - bool d_auth; - CacheEntry(const tuple& key, const vector& records, bool auth) : d_qname(key.get<0>()), d_qtype(key.get<1>()), d_auth(auth), d_records(records) {} typedef vector records_t; - records_t d_records; + uint32_t getTTD() const { if(d_records.size()==1) @@ -98,6 +84,11 @@ private: earliest=min(earliest, i->d_ttd); return earliest; } + + string d_qname; + uint16_t d_qtype; + bool d_auth; + records_t d_records; }; typedef multi_index_container< @@ -121,6 +112,8 @@ private: bool d_cachecachevalid; bool attemptToRefreshNSTTL(const QType& qt, const set& content, const CacheEntry& stored); + static pthread_rwlock_t s_rwlock; + }; string DNSRR2String(const DNSResourceRecord& rr); DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const string& serial, uint32_t ttd);