]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Transients.cc
Support discovery of collapsable entries in kids via the new Transients map.
[thirdparty/squid.git] / src / Transients.cc
CommitLineData
9a9954ba
AR
1/*
2 * DEBUG: section 20 Storage Manager
3 *
4 */
5
6#include "squid.h"
7#include "base/RunnersRegistry.h"
8#include "HttpReply.h"
9#include "ipc/mem/Page.h"
10#include "ipc/mem/Pages.h"
11#include "MemObject.h"
12#include "Transients.h"
13#include "mime_header.h"
14#include "SquidConfig.h"
15#include "SquidMath.h"
16#include "StoreStats.h"
17#include "tools.h"
18
19#if HAVE_LIMITS_H
20#include <limits>
21#endif
22
23
24/// shared memory segment path to use for Transients maps
25static const char *MapLabel = "transients_map";
26
27
28Transients::Transients(): map(NULL)
29{
30debugs(0,0, "Transients::ctor");
31}
32
33Transients::~Transients()
34{
35 delete map;
36}
37
38void
39Transients::init()
40{
41 const int64_t entryLimit = EntryLimit();
42 if (entryLimit <= 0)
43 return; // no SMP support or a misconfiguration
44
45 Must(!map);
46 map = new TransientsMap(MapLabel);
47 map->cleaner = this;
48}
49
50void
51Transients::getStats(StoreInfoStats &stats) const
52{
53#if TRANSIENT_STATS_SUPPORTED
54 const size_t pageSize = Ipc::Mem::PageSize();
55
56 stats.mem.shared = true;
57 stats.mem.capacity =
58 Ipc::Mem::PageLimit(Ipc::Mem::PageId::cachePage) * pageSize;
59 stats.mem.size =
60 Ipc::Mem::PageLevel(Ipc::Mem::PageId::cachePage) * pageSize;
61 stats.mem.count = currentCount();
62#endif
63}
64
65void
66Transients::stat(StoreEntry &e) const
67{
68 storeAppendPrintf(&e, "\n\nTransient Objects\n");
69
70 storeAppendPrintf(&e, "Maximum Size: %.0f KB\n", maxSize()/1024.0);
71 storeAppendPrintf(&e, "Current Size: %.2f KB %.2f%%\n",
72 currentSize() / 1024.0,
73 Math::doublePercent(currentSize(), maxSize()));
74
75 if (map) {
76 const int limit = map->entryLimit();
77 storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
78 if (limit > 0) {
79 storeAppendPrintf(&e, "Current entries: %" PRId64 " %.2f%%\n",
80 currentCount(), (100.0 * currentCount() / limit));
81 }
82 }
83}
84
85void
86Transients::maintain()
87{
88}
89
90uint64_t
91Transients::minSize() const
92{
93 return 0; // XXX: irrelevant, but Store parent forces us to implement this
94}
95
96uint64_t
97Transients::maxSize() const
98{
99 // Squid currently does not limit the total size of all transient objects
100 return std::numeric_limits<uint64_t>::max();
101}
102
103uint64_t
104Transients::currentSize() const
105{
106 // TODO: we do not get enough information to calculate this
107 // StoreEntry should update associated stores when its size changes
108 return 0;
109}
110
111uint64_t
112Transients::currentCount() const
113{
114 return map ? map->entryCount() : 0;
115}
116
117int64_t
118Transients::maxObjectSize() const
119{
120 // Squid currently does not limit the size of a transient object
121 return std::numeric_limits<uint64_t>::max();
122}
123
124void
125Transients::reference(StoreEntry &)
126{
127}
128
129bool
130Transients::dereference(StoreEntry &, bool)
131{
132 // no need to keep e in the global store_table for us; we have our own map
133 return false;
134}
135
136int
137Transients::callback()
138{
139 return 0;
140}
141
142StoreSearch *
143Transients::search(String const, HttpRequest *)
144{
145 fatal("not implemented");
146 return NULL;
147}
148
149StoreEntry *
150Transients::get(const cache_key *key)
151{
152 if (!map)
153 return NULL;
154
155 sfileno index;
156 if (!map->openForReading(key, index))
157 return NULL;
158
159 const TransientsMap::Extras &extras = map->extras(index);
160
161 // create a brand new store entry and initialize it with stored info
162 StoreEntry *e = storeCreateEntry(extras.url, extras.url,
163 extras.reqFlags, extras.reqMethod);
164 // XXX: overwriting storeCreateEntry() because we are expected to return an unlocked entry
165 // TODO: move locking from storeCreateEntry to callers as a mid-term solution
166 e->lock_count = 0;
167
168 assert(e->mem_obj);
169 e->mem_obj->method = extras.reqMethod;
170
171 // we copied everything we could to local memory; no more need to lock
172 map->closeForReading(index);
173
174 // XXX: overwriting storeCreateEntry() which calls setPrivateKey() if
175 // neighbors_do_private_keys (which is true in most cases and by default).
176 // This is nothing but waste of CPU cycles. Need a better API to avoid it.
177 e->setPublicKey();
178
179 assert(e->next); // e->hashInsert(key) is done in setPublicKey()
180 return e;
181}
182
183void
184Transients::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
185{
186 // XXX: not needed but Store parent forces us to implement this
187 fatal("Transients::get(key,callback,data) should not be called");
188}
189
190void
191Transients::put(StoreEntry *e, const RequestFlags &reqFlags,
192 const HttpRequestMethod &reqMethod)
193{
194 assert(e);
195
196 if (!map) {
197 debugs(20, 5, "No map to add " << *e);
198 return;
199 }
200
201 sfileno index = 0;
202 Ipc::StoreMapAnchor *slot = map->openForWriting(reinterpret_cast<const cache_key *>(e->key), index);
203 if (!slot) {
204 debugs(20, 5, "No room in map to index " << *e);
205 return;
206 }
207
208 try {
209 if (copyToShm(*e, index, reqFlags, reqMethod)) {
210 slot->set(*e);
211 map->closeForWriting(index, false);
212 return;
213 }
214 // fall through to the error handling code
215 }
216 catch (const std::exception &x) { // TODO: should we catch ... as well?
217 debugs(20, 2, "error keeping entry " << index <<
218 ' ' << *e << ": " << x.what());
219 // fall through to the error handling code
220 }
221
222 map->abortIo(index);
223}
224
225
226/// copies all relevant local data to shared memory
227bool
228Transients::copyToShm(const StoreEntry &e, const sfileno index,
229 const RequestFlags &reqFlags,
230 const HttpRequestMethod &reqMethod)
231{
232 TransientsMap::Extras &extras = map->extras(index);
233
234 const char *url = e.url();
235 const size_t urlLen = strlen(url);
236 Must(urlLen < sizeof(extras.url)); // we have space to store it all, plus 0
237 strncpy(extras.url, url, sizeof(extras.url));
238 extras.url[urlLen] = '\0';
239
240 extras.reqFlags = reqFlags;
241
242
243 Must(reqMethod != Http::METHOD_OTHER);
244 extras.reqMethod = reqMethod.id();
245
246 return true;
247}
248
249void
250Transients::noteFreeMapSlice(const sfileno sliceId)
251{
252 // TODO: we should probably find the entry being deleted and abort it
253}
254
255/// calculates maximum number of entries we need to store and map
256int64_t
257Transients::EntryLimit()
258{
259 // TODO: we should also check whether any SMP-aware caching is configured
260 if (!UsingSmp() || !Config.onoff.collapsed_forwarding)
261 return 0; // no SMP collapsed forwarding possible or needed
262
263 return 16*1024; // XXX: make configurable
264}
265
266/// initializes shared memory segment used by Transients
267class TransientsRr: public Ipc::Mem::RegisteredRunner
268{
269public:
270 /* RegisteredRunner API */
271 TransientsRr(): mapOwner(NULL) {}
272 virtual void run(const RunnerRegistry &);
273 virtual ~TransientsRr();
274
275protected:
276 virtual void create(const RunnerRegistry &);
277
278private:
279 TransientsMap::Owner *mapOwner;
280};
281
282RunnerRegistrationEntry(rrAfterConfig, TransientsRr);
283
284void TransientsRr::run(const RunnerRegistry &r)
285{
286 assert(Config.memShared.configured());
287 Ipc::Mem::RegisteredRunner::run(r);
288}
289
290void TransientsRr::create(const RunnerRegistry &)
291{
292debugs(0,0, "TransientsRr::create1: " << Config.onoff.collapsed_forwarding);
293 if (!Config.onoff.collapsed_forwarding)
294 return;
295
296 const int64_t entryLimit = Transients::EntryLimit();
297debugs(0,0, "TransientsRr::create2: " << entryLimit);
298 if (entryLimit <= 0)
299 return; // no SMP configured or a misconfiguration
300
301 Must(!mapOwner);
302 mapOwner = TransientsMap::Init(MapLabel, entryLimit);
303}
304
305TransientsRr::~TransientsRr()
306{
307 delete mapOwner;
308}