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