using namespace std;
using namespace boost;
+#include <bits/atomicity.h>
+
+#ifdef GCC_SKIP_LOGGING
+// 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;
+ __asm__ __volatile__ ("xadd{l} {%0,%1|%1,%0}"
+ : "=r" (__result), "=m" (*__mem)
+ : "0" (__val), "m" (*__mem));
+ }
+
+ void
+ __attribute__ ((__unused__))
+ __atomic_add(volatile _Atomic_word* __mem, int __val)
+ {
+ __asm__ __volatile__ ("add{l} {%1,%0|%0,%1}"
+ : "=m" (*__mem) : "ir" (__val), "m" (*__mem));
+ }
+}
+#endif
+
DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const string& serial, uint32_t ttd)
{
shared_ptr<DNSRecordContent> regen=DNSRecordContent::unserialize(qname,qt.getCode(), serial);
rr.content=regen->getZoneRepresentation();
rr.qtype=regen->d_qtype;
// cerr<<"Returning: '"<<rr.qname<<"' "<<rr.qtype.getName()<<" "<<rr.ttl<<" '"<<rr.content<<"'\n";
+ rr.content.reserve(0);
+ rr.qname.reserve(0);
return rr;
}
unsigned int MemRecursorCache::size()
{
- return d_cache.size();
+ unsigned int ret=0;
+ for(cache_t::const_iterator i=d_cache.begin(); i!=d_cache.end(); ++i) {
+ ret+=i->second.size();
+ }
+ return ret;
+}
+
+unsigned int MemRecursorCache::bytes()
+{
+ unsigned int ret=0;
+
+ for(cache_t::const_iterator i=d_cache.begin(); i!=d_cache.end(); ++i) {
+ ret+=i->first.length();
+ for(vector<StoredRecord>::const_iterator j=i->second.begin(); j!= i->second.end(); ++j)
+ ret+=j->size();
+ }
+ return ret;
}
+
int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set<DNSResourceRecord>* res)
{
unsigned int ttd=0;
- cache_t::const_iterator j=d_cache.find(toLowerCanonic(qname)+"|"+qt.getName());
+ uint16_t code=qt.getCode();
+ string key(toLowerCanonic(qname)); key.append(1,'|'); key.append((char*)&code, ((char*)&code)+2);
+ cache_t::const_iterator j=d_cache.find(key);
// cerr<<"looking up "<< toLowerCanonic(qname)+"|"+qt.getName() << endl;
if(res)
res->clear();
- if(j!=d_cache.end() && j->first==toLowerCanonic(qname)+"|"+qt.getName() && j->second.begin()->d_ttd>(unsigned int)now) {
+ if(j!=d_cache.end() && j->second.begin()->d_ttd>(unsigned int)now) {
if(res) {
- for(set<StoredRecord>::const_iterator k=j->second.begin(); k != j->second.end(); ++k) {
+ for(vector<StoredRecord>::const_iterator k=j->second.begin(); k != j->second.end(); ++k) {
DNSResourceRecord rr=String2DNSRR(qname, qt, k->d_string, ttd=k->d_ttd);
// cerr<<"Returning '"<<rr.content<<"'\n";
res->insert(rr);
return -1;
}
-
+
/* the code below is rather tricky - it basically replaces the stuff cached for qname by content, but it is special
cased for when inserting identical records with only differing ttls, in which case the entry is not
touched, but only given a new ttd */
void MemRecursorCache::replace(const string &qname, const QType& qt, const set<DNSResourceRecord>& content)
{
- set<StoredRecord>& stored=d_cache[toLowerCanonic(qname)+"|"+qt.getName()];
+ int code=qt.getCode();
+ string key(toLowerCanonic(qname)); key.append(1,'|'); key.append((char*)&code, ((char*)&code)+2);
+ cache_t::iterator stored=d_cache.find(key);
+ bool isNew=false;
+ if(stored == d_cache.end()) {
+ stored=d_cache.insert(make_pair(key,vector<StoredRecord>())).first;
+ isNew=true;
+ }
- set<StoredRecord>::iterator k;
- typedef vector<set<StoredRecord>::iterator> touched_t;
- touched_t touched;
+ pair<vector<StoredRecord>::iterator, vector<StoredRecord>::iterator> range;
- // walk through new content, encode it as new
StoredRecord dr;
-
for(set<DNSResourceRecord>::const_iterator i=content.begin(); i != content.end(); ++i) {
dr.d_ttd=i->ttl;
dr.d_string=DNSRR2String(*i);
- k=stored.find(dr);
- if(k!=stored.end()) { // was it there already?
- // cerr<<"updating record '"<<qname<<"' -> '"<<i->content<<"'\n";
- k->d_ttd=i->ttl; // update ttl
- touched.push_back(k); // note that this record is here to stay
- }
+
+ if(isNew)
+ stored->second.push_back(dr);
else {
- // cerr<<"inserting record '"<<qname<<"' -> '"<<i->content<<"'\n";
- touched.push_back(stored.insert(dr).first); // same thing
- }
- }
- if(touched.size() != stored.size()) {
- for(k=stored.begin(); k!=stored.end(); ) { // walk over the stored set of records
- touched_t::const_iterator j;
- for(j=touched.begin(); j!=touched.end() && *j != k ; ++j); // walk over touched iterators
- if(j==touched.end()) { // this record was not there
- // DNSResourceRecord rr=String2DNSRR(qname, qt, k->d_string, 0);
- // cerr<<"removing from record '"<<qname<<"' '"<<rr.content<<"'\n";
- // k->d_string.prune();
- stored.erase(k++); // cleanup
+ range=equal_range(stored->second.begin(), stored->second.end(), dr);
+
+ if(range.first != range.second) {
+ for(vector<StoredRecord>::iterator j=range.first ; j!=range.second; ++j)
+ j->d_ttd=i->ttl;
+ }
+ else {
+ stored->second.push_back(dr);
+ sort(stored->second.begin(), stored->second.end());
}
- else
- ++k;
}
}
+ if(isNew) {
+ sort(stored->second.begin(), stored->second.end());
+ }
+ if(stored->second.capacity() != stored->second.size())
+ vector<StoredRecord>(stored->second).swap(stored->second);
}
+
void MemRecursorCache::doPrune(void)
{
- unsigned int names=0, records=0;
+ unsigned int names=0;
time_t now=time(0);
for(cache_t::iterator j=d_cache.begin();j!=d_cache.end();){
- for(set<StoredRecord>::iterator k=j->second.begin();k!=j->second.end();)
- if((unsigned int)k->d_ttd < (unsigned int) now) {
- // k->d_string.prune();
- j->second.erase(k++);
- records++;
- }
- else
- ++k;
-
+ predicate p(now);
+ j->second.erase(remove_if(j->second.begin(), j->second.end(), p), j->second.end());
+
if(j->second.empty()) { // everything is gone
d_cache.erase(j++);
names++;
++j;
}
}
+ // cache_t(d_cache).swap(d_cache);
}
#ifndef RECURSOR_CACHE_HH
#define RECURSOR_CACHE_HH
-#include <map>
+#include <ext/hash_map>
#include <string>
#include <set>
#include "dns.hh"
#include <iostream>
#include <boost/utility.hpp>
-template<int N=14>
-struct optString
-{
- optString()
- {
- d_len=0;
- *buf=0;
- }
-
- optString(const optString& rhs) : d_len(rhs.d_len)
- {
- memcpy(buf, rhs.buf, N);
- }
-
- optString(const string& str)
+namespace __gnu_cxx {
+ template<>
+ struct hash<string>
{
- if(str.size() < N-1) {
- memcpy(buf, str.c_str(), str.size()+1);
- d_len = str.size() + 1;
- }
- else {
- new(buf) string(str);
- d_len = 0;
+ size_t
+ operator()(const string& __s) const
+ {
+ return __stl_hash_string(__s.c_str());
}
- }
-
- operator string() const
- {
-
- if(d_len) {
- return string(buf, buf + d_len - 1);
- }
- else {
- return *((string*)buf);
- }
- }
-
- void prune() const
- {
- // cerr<<"did a prune!"<<endl;
- if(!d_len)
- ((string*)buf)->~string();
- }
-
- bool operator<(const optString& os) const
- {
- return (string)*this < (string) os;
- }
-
- char buf[N];
- uint8_t d_len;
-} __attribute__((packed));
-
+ };
+}
class MemRecursorCache : public boost::noncopyable // : public RecursorCache
{
public:
unsigned int size();
+ unsigned int bytes();
int get(time_t, const string &qname, const QType& qt, set<DNSResourceRecord>* res);
void replace(const string &qname, const QType& qt, const set<DNSResourceRecord>& content);
void doPrune(void);
struct StoredRecord
{
mutable uint32_t d_ttd;
- // optString<> d_string;
+ //optString<> d_string;
string d_string;
bool operator<(const StoredRecord& rhs) const
{
return d_string < rhs.d_string;
- // return make_pair(d_ttd, d_string) < make_pair(rhs.d_ttd, rhs.d_string);
+ }
+
+ unsigned int size() const
+ {
+ return 4+d_string.size();
}
};
- typedef map<string, set<StoredRecord> > cache_t;
+
+ struct predicate
+ {
+ predicate(time_t limit) : d_limit(limit)
+ {
+ }
+
+ bool operator()(const StoredRecord& sr) const
+ {
+ return sr.d_ttd < d_limit;
+ }
+ time_t d_limit;
+ };
+
+ typedef __gnu_cxx::hash_map<string, vector<StoredRecord> > cache_t;
+
private:
cache_t d_cache;
};