]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Transients.cc
Avoid "STORE_DISK_CLIENT == getType()" assertions for ENTRY_ABORTED clients
[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{
9a9954ba
AR
30}
31
32Transients::~Transients()
33{
34 delete map;
35}
36
37void
38Transients::init()
39{
40 const int64_t entryLimit = EntryLimit();
41 if (entryLimit <= 0)
42 return; // no SMP support or a misconfiguration
43
44 Must(!map);
45 map = new TransientsMap(MapLabel);
46 map->cleaner = this;
47}
48
49void
50Transients::getStats(StoreInfoStats &stats) const
51{
52#if TRANSIENT_STATS_SUPPORTED
53 const size_t pageSize = Ipc::Mem::PageSize();
54
55 stats.mem.shared = true;
56 stats.mem.capacity =
57 Ipc::Mem::PageLimit(Ipc::Mem::PageId::cachePage) * pageSize;
58 stats.mem.size =
59 Ipc::Mem::PageLevel(Ipc::Mem::PageId::cachePage) * pageSize;
60 stats.mem.count = currentCount();
61#endif
62}
63
64void
65Transients::stat(StoreEntry &e) const
66{
67 storeAppendPrintf(&e, "\n\nTransient Objects\n");
68
69 storeAppendPrintf(&e, "Maximum Size: %.0f KB\n", maxSize()/1024.0);
70 storeAppendPrintf(&e, "Current Size: %.2f KB %.2f%%\n",
71 currentSize() / 1024.0,
72 Math::doublePercent(currentSize(), maxSize()));
73
74 if (map) {
75 const int limit = map->entryLimit();
76 storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
77 if (limit > 0) {
78 storeAppendPrintf(&e, "Current entries: %" PRId64 " %.2f%%\n",
79 currentCount(), (100.0 * currentCount() / limit));
80 }
81 }
82}
83
84void
85Transients::maintain()
86{
87}
88
89uint64_t
90Transients::minSize() const
91{
92 return 0; // XXX: irrelevant, but Store parent forces us to implement this
93}
94
95uint64_t
96Transients::maxSize() const
97{
98 // Squid currently does not limit the total size of all transient objects
99 return std::numeric_limits<uint64_t>::max();
100}
101
102uint64_t
103Transients::currentSize() const
104{
105 // TODO: we do not get enough information to calculate this
106 // StoreEntry should update associated stores when its size changes
107 return 0;
108}
109
110uint64_t
111Transients::currentCount() const
112{
113 return map ? map->entryCount() : 0;
114}
115
116int64_t
117Transients::maxObjectSize() const
118{
119 // Squid currently does not limit the size of a transient object
120 return std::numeric_limits<uint64_t>::max();
121}
122
123void
124Transients::reference(StoreEntry &)
125{
126}
127
128bool
129Transients::dereference(StoreEntry &, bool)
130{
131 // no need to keep e in the global store_table for us; we have our own map
132 return false;
133}
134
135int
136Transients::callback()
137{
138 return 0;
139}
140
141StoreSearch *
142Transients::search(String const, HttpRequest *)
143{
144 fatal("not implemented");
145 return NULL;
146}
147
148StoreEntry *
149Transients::get(const cache_key *key)
150{
151 if (!map)
152 return NULL;
153
154 sfileno index;
155 if (!map->openForReading(key, index))
156 return NULL;
157
4475555f
AR
158 if (StoreEntry *e = copyFromShm(index))
159 return e; // keep read lock to receive updates from others
160
161 // loading failure
162 map->closeForReading(index);
163 return NULL;
164}
165
166StoreEntry *
167Transients::copyFromShm(const sfileno index)
168{
9a9954ba
AR
169 const TransientsMap::Extras &extras = map->extras(index);
170
171 // create a brand new store entry and initialize it with stored info
172 StoreEntry *e = storeCreateEntry(extras.url, extras.url,
173 extras.reqFlags, extras.reqMethod);
174 // XXX: overwriting storeCreateEntry() because we are expected to return an unlocked entry
175 // TODO: move locking from storeCreateEntry to callers as a mid-term solution
176 e->lock_count = 0;
177
178 assert(e->mem_obj);
179 e->mem_obj->method = extras.reqMethod;
99921d9d 180 e->mem_obj->xitTable.io = MemObject::ioReading;
4475555f 181 e->mem_obj->xitTable.index = index;
9a9954ba
AR
182
183 // XXX: overwriting storeCreateEntry() which calls setPrivateKey() if
184 // neighbors_do_private_keys (which is true in most cases and by default).
185 // This is nothing but waste of CPU cycles. Need a better API to avoid it.
186 e->setPublicKey();
ce49546e 187 assert(e->key);
9a9954ba 188
4475555f
AR
189 // How do we know its SMP- and not just locally-collapsed? A worker gets
190 // locally-collapsed entries from the local store_table, not Transients.
191 // TODO: Can we remove smpCollapsed by not syncing non-transient entries?
192 e->mem_obj->smpCollapsed = true;
193
9a9954ba
AR
194 return e;
195}
196
197void
198Transients::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
199{
200 // XXX: not needed but Store parent forces us to implement this
201 fatal("Transients::get(key,callback,data) should not be called");
202}
203
204void
99921d9d 205Transients::startWriting(StoreEntry *e, const RequestFlags &reqFlags,
9a9954ba
AR
206 const HttpRequestMethod &reqMethod)
207{
208 assert(e);
4475555f
AR
209 assert(e->mem_obj);
210 assert(e->mem_obj->xitTable.index < 0);
9a9954ba
AR
211
212 if (!map) {
213 debugs(20, 5, "No map to add " << *e);
214 return;
215 }
216
217 sfileno index = 0;
218 Ipc::StoreMapAnchor *slot = map->openForWriting(reinterpret_cast<const cache_key *>(e->key), index);
219 if (!slot) {
4475555f 220 debugs(20, 5, "collision registering " << *e);
9a9954ba
AR
221 return;
222 }
223
224 try {
225 if (copyToShm(*e, index, reqFlags, reqMethod)) {
226 slot->set(*e);
99921d9d 227 e->mem_obj->xitTable.io = MemObject::ioWriting;
4475555f
AR
228 e->mem_obj->xitTable.index = index;
229 map->startAppending(index);
230 // keep write lock -- we will be supplying others with updates
9a9954ba
AR
231 return;
232 }
233 // fall through to the error handling code
234 }
235 catch (const std::exception &x) { // TODO: should we catch ... as well?
236 debugs(20, 2, "error keeping entry " << index <<
237 ' ' << *e << ": " << x.what());
238 // fall through to the error handling code
239 }
240
4475555f 241 map->abortWriting(index);
9a9954ba
AR
242}
243
244
245/// copies all relevant local data to shared memory
246bool
247Transients::copyToShm(const StoreEntry &e, const sfileno index,
248 const RequestFlags &reqFlags,
249 const HttpRequestMethod &reqMethod)
250{
251 TransientsMap::Extras &extras = map->extras(index);
252
253 const char *url = e.url();
254 const size_t urlLen = strlen(url);
255 Must(urlLen < sizeof(extras.url)); // we have space to store it all, plus 0
256 strncpy(extras.url, url, sizeof(extras.url));
257 extras.url[urlLen] = '\0';
258
259 extras.reqFlags = reqFlags;
9a9954ba
AR
260
261 Must(reqMethod != Http::METHOD_OTHER);
262 extras.reqMethod = reqMethod.id();
263
264 return true;
265}
266
267void
268Transients::noteFreeMapSlice(const sfileno sliceId)
269{
270 // TODO: we should probably find the entry being deleted and abort it
271}
272
4475555f
AR
273void
274Transients::abandon(const StoreEntry &e)
275{
276 assert(e.mem_obj && map);
277 map->freeEntry(e.mem_obj->xitTable.index); // just marks the locked entry
278 // We do not unlock the entry now because the problem is most likely with
279 // the server resource rather than a specific cache writer, so we want to
280 // prevent other readers from collapsing requests for that resource.
281}
282
283bool
284Transients::abandoned(const StoreEntry &e) const
285{
286 assert(e.mem_obj);
287 return abandonedAt(e.mem_obj->xitTable.index);
288}
289
290/// whether an in-transit entry at the index is now abandoned by its writer
291bool
292Transients::abandonedAt(const sfileno index) const
293{
294 assert(map);
295 return map->readableEntry(index).waitingToBeFreed;
296}
297
99921d9d
AR
298void
299Transients::completeWriting(const StoreEntry &e)
300{
301 if (e.mem_obj && e.mem_obj->xitTable.index >= 0) {
302 assert(e.mem_obj->xitTable.io == MemObject::ioWriting);
303 map->closeForWriting(e.mem_obj->xitTable.index);
304 e.mem_obj->xitTable.index = -1;
305 e.mem_obj->xitTable.io = MemObject::ioDone;
306 }
307}
308
4475555f
AR
309void
310Transients::disconnect(MemObject &mem_obj)
311{
99921d9d
AR
312 if (mem_obj.xitTable.index >= 0) {
313 assert(map);
314 if (mem_obj.xitTable.io == MemObject::ioWriting) {
315 map->abortWriting(mem_obj.xitTable.index);
316 } else {
317 assert(mem_obj.xitTable.io == MemObject::ioReading);
318 map->closeForReading(mem_obj.xitTable.index);
319 }
320 mem_obj.xitTable.index = -1;
321 mem_obj.xitTable.io = MemObject::ioDone;
322 }
4475555f
AR
323}
324
9a9954ba
AR
325/// calculates maximum number of entries we need to store and map
326int64_t
327Transients::EntryLimit()
328{
329 // TODO: we should also check whether any SMP-aware caching is configured
330 if (!UsingSmp() || !Config.onoff.collapsed_forwarding)
331 return 0; // no SMP collapsed forwarding possible or needed
332
333 return 16*1024; // XXX: make configurable
334}
335
336/// initializes shared memory segment used by Transients
337class TransientsRr: public Ipc::Mem::RegisteredRunner
338{
339public:
340 /* RegisteredRunner API */
341 TransientsRr(): mapOwner(NULL) {}
342 virtual void run(const RunnerRegistry &);
343 virtual ~TransientsRr();
344
345protected:
346 virtual void create(const RunnerRegistry &);
347
348private:
349 TransientsMap::Owner *mapOwner;
350};
351
352RunnerRegistrationEntry(rrAfterConfig, TransientsRr);
353
354void TransientsRr::run(const RunnerRegistry &r)
355{
356 assert(Config.memShared.configured());
357 Ipc::Mem::RegisteredRunner::run(r);
358}
359
360void TransientsRr::create(const RunnerRegistry &)
361{
9a9954ba
AR
362 if (!Config.onoff.collapsed_forwarding)
363 return;
364
365 const int64_t entryLimit = Transients::EntryLimit();
9a9954ba
AR
366 if (entryLimit <= 0)
367 return; // no SMP configured or a misconfiguration
368
369 Must(!mapOwner);
370 mapOwner = TransientsMap::Init(MapLabel, entryLimit);
371}
372
373TransientsRr::~TransientsRr()
374{
375 delete mapOwner;
376}