]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/auth-querycache.cc
Merge pull request #9070 from rgacogne/boost-173
[thirdparty/pdns.git] / pdns / auth-querycache.cc
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"
30 extern StatBag S;
31
32 const unsigned int AuthQueryCache::s_mincleaninterval, AuthQueryCache::s_maxcleaninterval;
33
34 AuthQueryCache::AuthQueryCache(size_t mapsCount): d_maps(mapsCount), d_lastclean(time(nullptr))
35 {
36 S.declare("query-cache-hit","Number of hits on the query cache");
37 S.declare("query-cache-miss","Number of misses on the query cache");
38 S.declare("query-cache-size", "Number of entries in the query cache");
39 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
40 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
41
42 d_statnumhit=S.getPointer("query-cache-hit");
43 d_statnummiss=S.getPointer("query-cache-miss");
44 d_statnumentries=S.getPointer("query-cache-size");
45 }
46
47 AuthQueryCache::~AuthQueryCache()
48 {
49 try {
50 vector<WriteLock> locks;
51 for(auto& mc : d_maps) {
52 locks.push_back(WriteLock(mc.d_mut));
53 }
54 locks.clear();
55 }
56 catch(...) {
57 }
58 }
59
60 void AuthQueryCache::MapCombo::reserve(size_t numberOfEntries)
61 {
62 #if BOOST_VERSION >= 105600
63 WriteLock wl(&d_mut);
64 d_map.get<HashTag>().reserve(numberOfEntries);
65 #endif /* BOOST_VERSION >= 105600 */
66 }
67
68 // called from ueberbackend
69 bool AuthQueryCache::getEntry(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>& value, int zoneID)
70 {
71 cleanupIfNeeded();
72
73 time_t now = time(nullptr);
74 uint16_t qt = qtype.getCode();
75 auto& mc = getMap(qname);
76 {
77 TryReadLock rl(&mc.d_mut);
78 if(!rl.gotIt()) {
79 S.inc("deferred-cache-lookup");
80 return false;
81 }
82
83 return getEntryLocked(mc.d_map, qname, qt, value, zoneID, now);
84 }
85 }
86
87 void AuthQueryCache::insert(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>&& value, uint32_t ttl, int zoneID)
88 {
89 cleanupIfNeeded();
90
91 if(!ttl)
92 return;
93
94 time_t now = time(nullptr);
95 CacheEntry val;
96 val.created = now;
97 val.ttd = now + ttl;
98 val.qname = qname;
99 val.qtype = qtype.getCode();
100 val.drs = std::move(value);
101 val.zoneID = zoneID;
102
103 auto& mc = getMap(val.qname);
104
105 {
106 TryWriteLock l(&mc.d_mut);
107 if(!l.gotIt()) {
108 S.inc("deferred-cache-inserts");
109 return;
110 }
111
112 bool inserted;
113 cmap_t::iterator place;
114 tie(place, inserted) = mc.d_map.insert(val);
115
116 if (!inserted) {
117 mc.d_map.replace(place, std::move(val));
118 moveCacheItemToBack<SequencedTag>(mc.d_map, place);
119 }
120 else {
121 if (*d_statnumentries >= d_maxEntries) {
122 /* remove the least recently inserted or replaced entry */
123 auto& sidx = mc.d_map.get<SequencedTag>();
124 sidx.pop_front();
125 }
126 else {
127 (*d_statnumentries)++;
128 }
129 }
130 }
131 }
132
133 bool AuthQueryCache::getEntryLocked(cmap_t& map, const DNSName &qname, uint16_t qtype, vector<DNSZoneRecord>& value, int zoneID, time_t now)
134 {
135 auto& idx = boost::multi_index::get<HashTag>(map);
136 auto iter = idx.find(tie(qname, qtype, zoneID));
137
138 if (iter == idx.end()) {
139 (*d_statnummiss)++;
140 return false;
141 }
142
143 if (iter->ttd < now) {
144 (*d_statnummiss)++;
145 return false;
146 }
147
148 value = iter->drs;
149 (*d_statnumhit)++;
150 return true;
151 }
152
153 map<char,uint64_t> AuthQueryCache::getCounts()
154 {
155 uint64_t queryCacheEntries=0, negQueryCacheEntries=0;
156
157 for(auto& mc : d_maps) {
158 ReadLock l(&mc.d_mut);
159
160 for(cmap_t::const_iterator iter = mc.d_map.begin() ; iter != mc.d_map.end(); ++iter) {
161 if(iter->drs.empty())
162 negQueryCacheEntries++;
163 else
164 queryCacheEntries++;
165 }
166 }
167 map<char,uint64_t> ret;
168
169 ret['!']=negQueryCacheEntries;
170 ret['Q']=queryCacheEntries;
171 return ret;
172 }
173
174 /* clears the entire cache. */
175 uint64_t AuthQueryCache::purge()
176 {
177 d_statnumentries->store(0);
178
179 return purgeLockedCollectionsVector(d_maps);
180 }
181
182 uint64_t AuthQueryCache::purgeExact(const DNSName& qname)
183 {
184 auto& mc = getMap(qname);
185 uint64_t delcount = purgeExactLockedCollection<NameTag>(mc, qname);
186
187 *d_statnumentries -= delcount;
188
189 return delcount;
190 }
191
192 /* purges entries from the querycache. If match ends on a $, it is treated as a suffix */
193 uint64_t AuthQueryCache::purge(const string &match)
194 {
195 uint64_t delcount = 0;
196
197 if(ends_with(match, "$")) {
198 delcount = purgeLockedCollectionsVector<NameTag>(d_maps, match);
199 *d_statnumentries -= delcount;
200 }
201 else {
202 delcount = purgeExact(DNSName(match));
203 }
204
205 return delcount;
206 }
207
208 void AuthQueryCache::cleanup()
209 {
210 uint64_t totErased = pruneLockedCollectionsVector<SequencedTag>(d_maps);
211 *d_statnumentries -= totErased;
212
213 DLOG(g_log<<"Done with cache clean, cacheSize: "<<*d_statnumentries<<", totErased"<<totErased<<endl);
214 }
215
216 /* the logic:
217 after d_nextclean operations, we clean. We also adjust the cleaninterval
218 a bit so we slowly move it to a value where we clean roughly every 30 seconds.
219
220 If d_nextclean has reached its maximum value, we also test if we were called
221 within 30 seconds, and if so, we skip cleaning. This means that under high load,
222 we will not clean more often than every 30 seconds anyhow.
223 */
224
225 void AuthQueryCache::cleanupIfNeeded()
226 {
227 if (d_ops++ == d_nextclean) {
228 time_t now = time(nullptr);
229 int timediff = max((int)(now - d_lastclean), 1);
230
231 DLOG(g_log<<"cleaninterval: "<<d_cleaninterval<<", timediff: "<<timediff<<endl);
232
233 if (d_cleaninterval == s_maxcleaninterval && timediff < 30) {
234 d_cleanskipped = true;
235 d_nextclean += d_cleaninterval;
236
237 DLOG(g_log<<"cleaning skipped, timediff: "<<timediff<<endl);
238
239 return;
240 }
241
242 if(!d_cleanskipped) {
243 d_cleaninterval=(int)(0.6*d_cleaninterval)+(0.4*d_cleaninterval*(30.0/timediff));
244 d_cleaninterval=std::max(d_cleaninterval, s_mincleaninterval);
245 d_cleaninterval=std::min(d_cleaninterval, s_maxcleaninterval);
246
247 DLOG(g_log<<"new cleaninterval: "<<d_cleaninterval<<endl);
248 } else {
249 d_cleanskipped = false;
250 }
251
252 d_nextclean += d_cleaninterval;
253 d_lastclean=now;
254 cleanup();
255 }
256 }