]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/auth-querycache.cc
Merge remote-tracking branch 'origin/master' into rec-edsn-unaligned-test
[thirdparty/pdns.git] / pdns / auth-querycache.cc
CommitLineData
bf269e28
RG
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include "auth-querycache.hh"
27#include "logger.hh"
28#include "statbag.hh"
29#include "cachecleaner.hh"
30extern StatBag S;
31
32const unsigned int AuthQueryCache::s_mincleaninterval, AuthQueryCache::s_maxcleaninterval;
33
bf269e28
RG
34AuthQueryCache::AuthQueryCache(size_t mapsCount): d_lastclean(time(nullptr))
35{
36 d_maps.resize(mapsCount);
37 for(auto& mc : d_maps) {
38 pthread_rwlock_init(&mc.d_mut, 0);
39 }
40
41 S.declare("query-cache-hit","Number of hits on the query cache");
42 S.declare("query-cache-miss","Number of misses on the query cache");
43 S.declare("query-cache-size", "Number of entries in the query cache");
44 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
45 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
46
47 d_statnumhit=S.getPointer("query-cache-hit");
48 d_statnummiss=S.getPointer("query-cache-miss");
49 d_statnumentries=S.getPointer("query-cache-size");
50}
51
52AuthQueryCache::~AuthQueryCache()
53{
54 try {
55 vector<WriteLock*> locks;
56 for(auto& mc : d_maps) {
57 locks.push_back(new WriteLock(&mc.d_mut));
58 }
59 for(auto wl : locks) {
60 delete wl;
61 }
62 }
63 catch(...) {
64 }
65}
66
67// called from ueberbackend
68bool AuthQueryCache::getEntry(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>& value, int zoneID)
69{
70 cleanupIfNeeded();
71
72 time_t now = time(nullptr);
73 uint16_t qt = qtype.getCode();
74 auto& mc = getMap(qname);
75 {
76 TryReadLock rl(&mc.d_mut);
77 if(!rl.gotIt()) {
78 S.inc("deferred-cache-lookup");
79 return false;
80 }
81
82 return getEntryLocked(mc.d_map, qname, qt, value, zoneID, now);
83 }
84}
85
86void AuthQueryCache::insert(const DNSName &qname, const QType& qtype, const vector<DNSZoneRecord>& value, uint32_t ttl, int zoneID)
87{
88 cleanupIfNeeded();
89
90 if(!ttl)
91 return;
92
93 time_t now = time(nullptr);
94 CacheEntry val;
95 val.created = now;
96 val.ttd = now + ttl;
97 val.qname = qname;
98 val.qtype = qtype.getCode();
99 val.drs = value;
100 val.zoneID = zoneID;
101
102 auto& mc = getMap(val.qname);
103
104 {
105 TryWriteLock l(&mc.d_mut);
106 if(!l.gotIt()) {
107 S.inc("deferred-cache-inserts");
108 return;
109 }
110
111 bool inserted;
112 cmap_t::iterator place;
113 tie(place, inserted) = mc.d_map.insert(val);
114
115 if (!inserted) {
116 mc.d_map.replace(place, val);
117 }
118 else {
119 (*d_statnumentries)++;
120 }
121 }
122}
123
124bool AuthQueryCache::getEntryLocked(cmap_t& map, const DNSName &qname, uint16_t qtype, vector<DNSZoneRecord>& value, int zoneID, time_t now)
125{
126 auto& idx = boost::multi_index::get<HashTag>(map);
127 auto iter = idx.find(tie(qname, qtype, zoneID));
128
54d1705f
KM
129 if (iter == idx.end()) {
130 (*d_statnummiss)++;
bf269e28 131 return false;
54d1705f 132 }
bf269e28 133
54d1705f
KM
134 if (iter->ttd < now) {
135 (*d_statnummiss)++;
bf269e28 136 return false;
54d1705f 137 }
bf269e28
RG
138
139 value = iter->drs;
54d1705f 140 (*d_statnumhit)++;
bf269e28
RG
141 return true;
142}
143
144map<char,uint64_t> AuthQueryCache::getCounts()
145{
146 uint64_t queryCacheEntries=0, negQueryCacheEntries=0;
147
148 for(auto& mc : d_maps) {
149 ReadLock l(&mc.d_mut);
150
151 for(cmap_t::const_iterator iter = mc.d_map.begin() ; iter != mc.d_map.end(); ++iter) {
152 if(iter->drs.empty())
153 negQueryCacheEntries++;
154 else
155 queryCacheEntries++;
156 }
157 }
158 map<char,uint64_t> ret;
159
160 ret['!']=negQueryCacheEntries;
161 ret['Q']=queryCacheEntries;
162 return ret;
163}
164
165/* clears the entire cache. */
166uint64_t AuthQueryCache::purge()
167{
168 d_statnumentries->store(0);
169
170 return purgeLockedCollectionsVector(d_maps);
171}
172
173uint64_t AuthQueryCache::purgeExact(const DNSName& qname)
174{
175 auto& mc = getMap(qname);
176 uint64_t delcount = purgeExactLockedCollection(mc, qname);
177
178 *d_statnumentries -= delcount;
179
180 return delcount;
181}
182
183/* purges entries from the querycache. If match ends on a $, it is treated as a suffix */
184uint64_t AuthQueryCache::purge(const string &match)
185{
186 uint64_t delcount = 0;
187
188 if(ends_with(match, "$")) {
189 delcount = purgeLockedCollectionsVector(d_maps, match);
190 *d_statnumentries -= delcount;
191 }
192 else {
193 delcount = purgeExact(DNSName(match));
194 }
195
196 return delcount;
197}
198
199void AuthQueryCache::cleanup()
200{
201 uint64_t maxCached = d_maxEntries;
202 uint64_t cacheSize = *d_statnumentries;
203 uint64_t totErased = 0;
204
205 totErased = pruneLockedCollectionsVector(d_maps, maxCached, cacheSize);
206
207 *d_statnumentries -= totErased;
e6a9dde5 208 DLOG(g_log<<"Done with cache clean, cacheSize: "<<*d_statnumentries<<", totErased"<<totErased<<endl);
bf269e28
RG
209}
210
211/* the logic:
212 after d_nextclean operations, we clean. We also adjust the cleaninterval
213 a bit so we slowly move it to a value where we clean roughly every 30 seconds.
214
215 If d_nextclean has reached its maximum value, we also test if we were called
216 within 30 seconds, and if so, we skip cleaning. This means that under high load,
217 we will not clean more often than every 30 seconds anyhow.
218*/
219
220void AuthQueryCache::cleanupIfNeeded()
221{
222 if (d_ops++ == d_nextclean) {
223 time_t now = time(nullptr);
224 int timediff = max((int)(now - d_lastclean), 1);
225
e6a9dde5 226 DLOG(g_log<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
bf269e28
RG
227
228 if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
229 d_cleanskipped = true;
230 d_nextclean += d_cleaninterval;
231
e6a9dde5 232 DLOG(g_log<<"cleaning skipped, timediff: "<<timediff<<endl);
bf269e28
RG
233
234 return;
235 }
236
237 if(!d_cleanskipped) {
238 d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
239 d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
240 d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
241
e6a9dde5 242 DLOG(g_log<<"new cleaninterval: "<<d_cleaninterval<<endl);
bf269e28
RG
243 } else {
244 d_cleanskipped = false;
245 }
246
247 d_nextclean += d_cleaninterval;
248 d_lastclean=now;
249 cleanup();
250 }
251}