root/trunk/pdns/pdns/packetcache.cc @ 1219

Revision 1219, 6.7 KB (checked in by ahu, 5 years ago)

implement cache purging for 100% matches (disregarding case), plus make updates actually update the cache

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002 - 2008  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17*/
18#include "utility.hh"
19#include "packetcache.hh"
20#include "logger.hh"
21#include "arguments.hh"
22#include "statbag.hh"
23#include <map>
24
25extern StatBag S;
26
27PacketCache::PacketCache()
28{
29  pthread_rwlock_init(&d_mut,0);
30  d_hit=d_miss=0;
31
32  d_ttl=-1;
33  d_recursivettl=-1;
34
35  S.declare("packetcache-hit");
36  S.declare("packetcache-miss");
37  S.declare("packetcache-size");
38
39  statnumhit=S.getPointer("packetcache-hit");
40  statnummiss=S.getPointer("packetcache-miss");
41  statnumentries=S.getPointer("packetcache-size");
42}
43
44int PacketCache::get(DNSPacket *p, DNSPacket *cached)
45{
46  extern StatBag S;
47  if(!((d_hit+d_miss)%5000)) {
48    cleanup();
49  }
50
51  if(d_ttl<0) 
52    getTTLS();
53
54  if(d_doRecursion && p->d.rd) { // wants recursion
55    if(!d_recursivettl) {
56      (*statnummiss)++;
57      d_miss++;
58      return 0;
59    }
60  }
61  else { // does not
62    if(!d_ttl) {
63      (*statnummiss)++;
64      d_miss++;
65      return 0;
66    }
67  }
68   
69  bool packetMeritsRecursion=d_doRecursion && p->d.rd;
70
71  if(ntohs(p->d.qdcount)!=1) // we get confused by packets with more than one question
72    return 0;
73
74  {
75    TryReadLock l(&d_mut); // take a readlock here
76    if(!l.gotIt()) {
77      S.inc("deferred-cache-lookup");
78      return 0;
79    }
80
81    if(!((d_hit+d_miss)%1000)) {
82      *statnumentries=d_map.size(); // needs lock
83    }
84    string value;
85
86    if(getEntry(p->qdomain, p->qtype, PacketCache::PACKETCACHE, value, -1, packetMeritsRecursion)) {
87      //      cerr<<"Packet cache hit!"<<endl;
88      (*statnumhit)++;
89      d_hit++;
90      if(cached->parse(value.c_str(), value.size()) < 0) {
91        return -1;
92      }
93      cached->spoofQuestion(p->qdomain); // for correct case
94      return 1;
95    }
96  }
97  //   cerr<<"Packet cache miss"<<endl;
98  (*statnummiss)++;
99  d_miss++;
100  return 0; // bummer
101}
102
103void PacketCache::getTTLS()
104{
105  d_ttl=::arg().asNum("cache-ttl");
106  d_recursivettl=::arg().asNum("recursive-cache-ttl");
107
108  d_doRecursion=::arg().mustDo("recursor"); 
109}
110
111
112void PacketCache::insert(DNSPacket *q, DNSPacket *r)
113{
114  if(d_ttl < 0)
115    getTTLS();
116 
117  if(ntohs(q->d.qdcount)!=1) {
118    return; // do not try to cache packets with multiple questions
119  }
120
121  bool packetMeritsRecursion=d_doRecursion && q->d.rd;
122
123  insert(q->qdomain, q->qtype, PacketCache::PACKETCACHE, r->getString(), packetMeritsRecursion ? d_recursivettl : d_ttl);  // XXX FIXME forgets meritsRecursion
124}
125
126// universal key appears to be: qname, qtype, kind (packet, query cache), optionally zoneid, meritsRecursion
127void PacketCache::insert(const string &qname, const QType& qtype, CacheEntryType cet, const string& value, unsigned int ttl, int zoneID, bool meritsRecursion)
128{
129  if(!ttl)
130    return;
131 
132  //  cerr<<"Inserting, cet: "<<(int)cet<<endl;
133
134  CacheEntry val;
135  val.ttd=time(0)+ttl;
136  val.qname=qname;
137  val.qtype=qtype.getCode();
138  val.value=value;
139  val.ctype=cet;
140  val.meritsRecursion=meritsRecursion;
141
142  TryWriteLock l(&d_mut);
143  if(l.gotIt()) { 
144    bool success;
145    cmap_t::iterator place;
146    tie(place, success)=d_map.insert(val);
147    //    cerr<<"Insert succeeded: "<<success<<endl;
148    if(!success)
149      d_map.replace(place, val);
150   
151  }
152  else 
153    S.inc("deferred-cache-inserts"); 
154}
155
156/** purges entries from the packetcache. If match ends on a $, it is treated as a suffix */
157int PacketCache::purge(const string &match)
158{
159  WriteLock l(&d_mut);
160  int delcount;
161
162  if(match.empty()) {
163    delcount = d_map.size();
164    d_map.clear();
165    *statnumentries=0;
166    return delcount;
167  }
168
169  /* ok, the suffix delete plan. We want to be able to delete everything that
170     pertains 'www.powerdns.com' but we also want to be able to delete everything
171     in the powerdns.com zone, so: 'powerdns.com' and '*.powerdns.com'.
172
173     However, we do NOT want to delete 'usepowerdns.com!'
174  */
175
176  delcount=d_map.count(tie(match));
177  pair<cmap_t::iterator, cmap_t::iterator> range = d_map.equal_range(tie(match));
178  d_map.erase(range.first, range.second);
179
180  *statnumentries=d_map.size();
181  return delcount;
182}
183
184bool PacketCache::getEntry(const string &qname, const QType& qtype, CacheEntryType cet, string& value, int zoneID, bool meritsRecursion)
185{
186  TryReadLock l(&d_mut); // take a readlock here
187  if(!l.gotIt()) {
188    S.inc( "deferred-cache-lookup");
189    return false;
190  }
191
192  uint16_t qt = qtype.getCode();
193  cmap_t::const_iterator i=d_map.find(tie(qname, qt, cet, zoneID, meritsRecursion));
194  time_t now=time(0);
195  bool ret=(i!=d_map.end() && i->ttd > now);
196  if(ret)
197    value = i->value;
198 
199  //  cerr<<"Cache hit: "<<(int)cet<<", "<<ret<<endl;
200 
201  return ret;
202}
203
204// FIXME: must be converted to boost multi index
205map<char,int> PacketCache::getCounts()
206{
207  ReadLock l(&d_mut);
208
209  map<char,int>ret;
210  return ret;
211}
212
213int PacketCache::size()
214{
215  ReadLock l(&d_mut);
216  return d_map.size();
217}
218
219/** readlock for figuring out which iterators to delete, upgrade to writelock when actually cleaning */
220void PacketCache::cleanup()
221{
222  WriteLock l(&d_mut);
223
224  *statnumentries=d_map.size();
225
226  unsigned int maxCached=::arg().asNum("max-cache-entries");
227  unsigned int toTrim=0;
228 
229  unsigned int cacheSize=*statnumentries;
230
231  if(maxCached && cacheSize > maxCached) {
232    toTrim = cacheSize - maxCached;
233  }
234
235  unsigned int lookAt=0;
236  // two modes - if toTrim is 0, just look through 10000 records and nuke everything that is expired
237  // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
238  if(toTrim)
239    lookAt=5*toTrim;
240  else
241    lookAt=cacheSize/10;
242
243  //  cerr<<"cacheSize: "<<cacheSize<<", lookAt: "<<lookAt<<", toTrim: "<<toTrim<<endl;
244  time_t now=time(0);
245
246  DLOG(L<<"Starting cache clean"<<endl);
247  if(d_map.empty())
248    return; // clean
249
250  typedef cmap_t::nth_index<1>::type sequence_t;
251  sequence_t& sidx=d_map.get<1>();
252  unsigned int erased=0;
253  for(sequence_t::iterator i=sidx.begin(); i != sidx.end();) {
254    if(i->ttd < now) {
255      sidx.erase(i++);
256      erased++;
257    }
258    else
259      ++i;
260
261    if(toTrim && erased > toTrim)
262      break;
263
264  }
265  //  cerr<<"erased: "<<erased<<endl;
266  *statnumentries=d_map.size();
267  DLOG(L<<"Done with cache clean"<<endl);
268}
Note: See TracBrowser for help on using the browser.