]>
Commit | Line | Data |
---|---|---|
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" | |
30 | extern StatBag S; | |
31 | ||
32 | const unsigned int AuthQueryCache::s_mincleaninterval, AuthQueryCache::s_maxcleaninterval; | |
33 | ||
9c0ad051 | 34 | AuthQueryCache::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 |
47 | void 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 |
55 | bool 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 | 73 | void 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 | 118 | bool 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 | ||
138 | map<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. */ | |
160 | uint64_t AuthQueryCache::purge() | |
161 | { | |
162 | d_statnumentries->store(0); | |
163 | ||
164 | return purgeLockedCollectionsVector(d_maps); | |
165 | } | |
166 | ||
167 | uint64_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 */ | |
178 | uint64_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 | ||
193 | void 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 | ||
210 | void 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 | } |