Squid Web Cache master
Loading...
Searching...
No Matches
Transients.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 20 Storage Manager */
10
11#include "squid.h"
13#include "CollapsedForwarding.h"
14#include "HttpReply.h"
15#include "ipc/mem/Page.h"
16#include "ipc/mem/Pages.h"
17#include "MemObject.h"
18#include "mime_header.h"
19#include "SquidConfig.h"
20#include "SquidMath.h"
21#include "StoreStats.h"
22#include "tools.h"
23#include "Transients.h"
24
25#include <limits>
26
28static const auto &
30{
31 static const auto label = new SBuf("transients_map");
32 return *label;
33}
34
35Transients::Transients(): map(nullptr), locals(nullptr)
36{
37}
38
40{
41 delete map;
42 delete locals;
43}
44
45void
47{
48 assert(Enabled());
49 const int64_t entryLimit = EntryLimit();
50 assert(entryLimit > 0);
51
52 Must(!map);
53 map = new TransientsMap(MapLabel());
54 map->cleaner = this;
55 map->disableHitValidation(); // Transients lacks slices to validate
56
57 locals = new Locals(entryLimit, nullptr);
58}
59
60void
62{
63#if TRANSIENT_STATS_SUPPORTED
64 const size_t pageSize = Ipc::Mem::PageSize();
65
66 stats.mem.shared = true;
67 stats.mem.capacity =
69 stats.mem.size =
71 stats.mem.count = currentCount();
72#else
73 (void)stats;
74#endif
75}
76
77void
79{
80 storeAppendPrintf(&e, "\n\nTransient Objects\n");
81
82 storeAppendPrintf(&e, "Maximum Size: %.0f KB\n", maxSize()/1024.0);
83 storeAppendPrintf(&e, "Current Size: %.2f KB %.2f%%\n",
84 currentSize() / 1024.0,
86
87 if (map) {
88 const int limit = map->entryLimit();
89 storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
90 if (limit > 0) {
91 storeAppendPrintf(&e, "Current entries: %" PRId64 " %.2f%%\n",
92 currentCount(), (100.0 * currentCount() / limit));
93 }
94 }
95}
96
97void
99{
100 // no lazy garbage collection needed
101}
102
103uint64_t
105{
106 return 0; // XXX: irrelevant, but Store parent forces us to implement this
107}
108
109uint64_t
111{
112 // Squid currently does not limit the total size of all transient objects
113 return std::numeric_limits<uint64_t>::max();
114}
115
116uint64_t
118{
119 // TODO: we do not get enough information to calculate this
120 // StoreEntry should update associated stores when its size changes
121 return 0;
122}
123
124uint64_t
126{
127 return map ? map->entryCount() : 0;
128}
129
130int64_t
132{
133 // Squid currently does not limit the size of a transient object
134 return std::numeric_limits<uint64_t>::max();
135}
136
137void
139{
140 // no replacement policy (but the cache(s) storing the entry may have one)
141}
142
143bool
145{
146 // no need to keep e in the global store_table for us; we have our own map
147 return false;
148}
149
152{
153 if (!map)
154 return nullptr;
155
156 sfileno index;
157 const Ipc::StoreMapAnchor *anchor = map->openForReading(key, index);
158 if (!anchor)
159 return nullptr;
160
161 // If we already have a local entry, the store_table should have found it.
162 // Since it did not, the local entry key must have changed from public to
163 // private. We still need to keep the private entry around for syncing as
164 // its clients depend on it, but we should not allow new clients to join.
165 if (StoreEntry *oldE = locals->at(index)) {
166 debugs(20, 3, "not joining private " << *oldE);
167 assert(EBIT_TEST(oldE->flags, KEY_PRIVATE));
169 return nullptr;
170 }
171
172 StoreEntry *e = new StoreEntry();
173 e->createMemObject();
175
176 // keep read lock to receive updates from others
177 return e;
178}
179
182{
183 if (!map)
184 return nullptr;
185
186 if (StoreEntry *oldE = locals->at(index)) {
187 debugs(20, 5, "found " << *oldE << " at " << index << " in " << MapLabel());
188 assert(oldE->mem_obj && oldE->mem_obj->xitTable.index == index);
189 return oldE;
190 }
191
192 debugs(20, 3, "no entry at " << index << " in " << MapLabel());
193 return nullptr;
194}
195
196void
198{
199 if (!e->hasTransients()) {
200 addEntry(e, key, direction);
201 assert(e->hasTransients());
202 }
203
204 const auto index = e->mem_obj->xitTable.index;
205 if (const auto old = locals->at(index)) {
206 assert(old == e);
207 } else {
208 // We do not lock e because we do not want to prevent its destruction;
209 // e is tied to us via mem_obj so we will know when it is destructed.
210 locals->at(index) = e;
211 }
212}
213
215void
217{
218 assert(e);
219 assert(e->mem_obj);
220 assert(!e->hasTransients());
221
222 Must(map); // configured to track transients
223
224 if (direction == Store::ioWriting)
225 return addWriterEntry(*e, key);
226
227 assert(direction == Store::ioReading);
228 addReaderEntry(*e, key);
229}
230
232void
234{
235 sfileno index = 0;
236 const auto anchor = map->openForWriting(key, index);
237 if (!anchor)
238 throw TextException("writer collision", Here());
239
240 // set ASAP in hope to unlock the slot if something throws
241 // and to provide index to such methods as hasWriter()
243
244 anchor->setKey(key);
245 // allow reading and receive remote DELETE events, but do not switch to
246 // the reading lock because transientReaders() callers want true readers
247 map->startAppending(index);
248}
249
252void
254{
255 sfileno index = 0;
256 const auto anchor = map->openOrCreateForReading(key, index);
257 if (!anchor)
258 throw TextException("reader collision", Here());
259
261 // keep the entry locked (for reading) to receive remote DELETE events
262}
263
264bool
266{
267 if (!e.hasTransients())
268 return false;
270}
271
272void
274{
275 // TODO: we should probably find the entry being deleted and abort it
276}
277
278void
280{
281 assert(map);
282 assert(entry.hasTransients());
283 const auto idx = entry.mem_obj->xitTable.index;
284 const auto &anchor = isWriter(entry) ?
285 map->writeableEntry(idx) : map->readableEntry(idx);
286 entryStatus.hasWriter = anchor.writing();
287 entryStatus.waitingToBeFreed = anchor.waitingToBeFreed;
288}
289
290void
300
301int
303{
304 if (e.hasTransients()) {
305 assert(map);
307 }
308 return 0;
309}
310
311void
313{
314 debugs(20, 5, e);
315 if (e.hasTransients()) {
316 const auto index = e.mem_obj->xitTable.index;
317 if (map->freeEntry(index)) {
318 // Delay syncCollapsed(index) which may end `e` wait for updates.
319 // Calling it directly/here creates complex reentrant call chains.
321 }
322 } // else nothing to do because e must be private
323}
324
325void
327{
328 if (!map)
329 return;
330
331 const sfileno index = map->fileNoByKey(key);
332 if (map->freeEntry(index))
334}
335
336void
338{
339 debugs(20, 5, entry);
340 if (entry.hasTransients()) {
341 auto &xitTable = entry.mem_obj->xitTable;
342 assert(map);
343 if (isWriter(entry)) {
344 // completeWriting() was not called, so there could be an active
345 // Store writer out there, but we should not abortWriting() here
346 // because another writer may have succeeded, making readers happy.
347 // If none succeeded, the readers will notice the lack of writers.
348 map->closeForWriting(xitTable.index);
350 } else {
351 assert(isReader(entry));
352 map->closeForReadingAndFreeIdle(xitTable.index);
353 }
354 locals->at(xitTable.index) = nullptr;
355 xitTable.close();
356 }
357}
358
360int64_t
366
367bool
369{
370 assert(map);
371 return map->markedForDeletion(key);
372}
373
374bool
376{
377 return e.mem_obj && e.mem_obj->xitTable.io == Store::ioReading;
378}
379
380bool
382{
383 return e.mem_obj && e.mem_obj->xitTable.io == Store::ioWriting;
384}
385
388{
389public:
390 /* RegisteredRunner API */
391 void useConfig() override;
392 ~TransientsRr() override;
393
394protected:
395 void create() override;
396
397private:
399};
400
402
403void
409
410void
412{
413 const int64_t entryLimit = Transients::EntryLimit();
414 if (entryLimit <= 0)
415 return; // no SMP configured or a misconfiguration
416
417 Must(!mapOwner);
418 mapOwner = TransientsMap::Init(MapLabel(), entryLimit);
419}
420
422{
423 delete mapOwner;
424}
425
#define Here()
source code location of the caller
Definition Here.h:15
static const auto MapLabel
shared memory segment path to use for MemStore maps
Definition MemStore.cc:29
#define DefineRunnerRegistrator(ClassName)
class SquidConfig Config
#define Must(condition)
static const auto & MapLabel()
shared memory segment path to use for Transients map
Definition Transients.cc:29
Ipc::StoreMap TransientsMap
Definition Transients.h:20
#define assert(EX)
Definition assert.h:17
static void Broadcast(const StoreEntry &e, const bool includingThisWorker=false)
notify other workers about changes in entry state (e.g., new data)
void useConfig() override
Definition Segment.cc:375
std::atomic< uint32_t > readers
number of reading users
ReadWriteLock lock
protects slot data below
Definition StoreMap.h:80
aggregates anchor and slice owners for Init() caller convenience
Definition StoreMap.h:233
Anchor * openForWriting(const cache_key *const key, sfileno &fileno)
Definition StoreMap.cc:141
bool markedForDeletion(const cache_key *const)
Definition StoreMap.cc:355
Anchor & writeableEntry(const AnchorId anchorId)
writeable anchor for the entry created by openForWriting()
Definition StoreMap.cc:238
const Anchor & readableEntry(const AnchorId anchorId) const
readable anchor for the entry created by openForReading()
Definition StoreMap.cc:245
int entryCount() const
number of writeable and readable entries
Definition StoreMap.cc:739
static Owner * Init(const SBuf &path, const int slotLimit)
initialize shared memory
Definition StoreMap.cc:43
void closeForWriting(const sfileno fileno)
successfully finish creating or updating the entry at fileno pos
Definition StoreMap.cc:201
const Anchor * openOrCreateForReading(const cache_key *, sfileno &)
openForReading() but creates a new entry if there is no old one
Definition StoreMap.cc:104
StoreMapCleaner * cleaner
notified before a readable entry is freed
Definition StoreMap.h:361
const Anchor * openForReading(const cache_key *const key, sfileno &fileno)
opens entry (identified by key) for reading, increments read level
Definition StoreMap.cc:440
bool freeEntry(const sfileno)
Definition StoreMap.cc:313
const Anchor * peekAtWriter(const sfileno fileno) const
Definition StoreMap.cc:297
void closeForReadingAndFreeIdle(const sfileno fileno)
same as closeForReading() but also frees the entry if it is unlocked
Definition StoreMap.cc:506
void startAppending(const sfileno fileno)
restrict opened for writing entry to appending operations; allow reads
Definition StoreMap.cc:192
sfileno fileNoByKey(const cache_key *const key) const
computes map entry anchor position for a given entry key
Definition StoreMap.cc:912
const Anchor & peekAtEntry(const sfileno fileno) const
Definition StoreMap.cc:307
void switchWritingToReading(const sfileno fileno)
stop writing (or updating) the locked entry and start reading it
Definition StoreMap.cc:212
void disableHitValidation()
Definition StoreMap.h:346
int entryLimit() const
maximum entryCount() possible
Definition StoreMap.cc:733
int32_t index
entry position inside the in-transit table
Definition MemObject.h:189
void open(const int32_t anIndex, const Store::IoStatus anIo)
associate our StoreEntry with a Transients entry at the given index
Definition MemObject.h:176
Store::IoStatus io
current I/O state
Definition MemObject.h:190
XitTable xitTable
current [shared] memory caching state for the entry
Definition MemObject.h:192
Definition SBuf.h:94
int64_t shared_transient_entries_limit
YesNoNone memShared
whether the memory cache is shared among workers
Definition SquidConfig.h:89
MemObject * mem_obj
Definition Store.h:220
void createMemObject()
Definition store.cc:1575
bool hasTransients() const
whether there is a corresponding locked transients table entry
Definition Store.h:210
bool shared
whether memory cache is shared among workers
Definition StoreStats.h:42
double capacity
the size limit
Definition StoreStats.h:22
double count
number of cached objects
Definition StoreStats.h:21
double size
bytes currently in use
Definition StoreStats.h:20
High-level store statistics used by mgr:info action. Used inside PODs!
Definition StoreStats.h:14
Mem mem
all cache_dirs stats
Definition StoreStats.h:48
static bool SmpAware()
whether there are any SMP-aware storages
an std::runtime_error with thrower location info
initializes shared memory segment used by Transients
void useConfig() override
TransientsMap::Owner * mapOwner
~TransientsRr() override
void create() override
called when the runner should create a new memory segment
shared entry metadata, used for synchronization
Definition Transients.h:32
bool waitingToBeFreed
whether the entry was marked for deletion
Definition Transients.h:35
bool hasWriter
whether some worker is storing the entry
Definition Transients.h:34
int readers(const StoreEntry &e) const
number of entry readers some time ago
StoreEntry * get(const cache_key *) override
bool hasWriter(const StoreEntry &)
whether we or somebody else is in the "writing to Transients" I/O state
~Transients() override
Definition Transients.cc:39
void monitorIo(StoreEntry *, const cache_key *, const Store::IoStatus)
void addReaderEntry(StoreEntry &, const cache_key *)
Locals * locals
Definition Transients.h:108
void noteFreeMapSlice(const Ipc::StoreMapSliceId sliceId) override
adjust slice-linked state before a locked Readable slice is erased
TransientsMap * map
shared packed info indexed by Store keys, for creating new StoreEntries
Definition Transients.h:103
void addWriterEntry(StoreEntry &, const cache_key *)
addEntry() helper used for cache entry creators/writers
bool isWriter(const StoreEntry &) const
whether the entry is in "writing to Transients" I/O state
void disconnect(StoreEntry &)
the caller is done writing or reading the given entry
StoreEntry * findCollapsed(const sfileno xitIndex)
return a local, previously collapsed entry
void getStats(StoreInfoStats &stats) const override
collect statistics
Definition Transients.cc:61
int64_t maxObjectSize() const override
the maximum size of a storable object; -1 if unlimited
void stat(StoreEntry &e) const override
Definition Transients.cc:78
void addEntry(StoreEntry *, const cache_key *, const Store::IoStatus)
creates a new Transients entry
bool markedForDeletion(const cache_key *) const
void evictCached(StoreEntry &) override
void status(const StoreEntry &e, EntryStatus &entryStatus) const
copies current shared entry metadata into entryStatus
uint64_t minSize() const override
the minimum size the store will shrink to via normal housekeeping
void reference(StoreEntry &e) override
somebody needs this entry (many cache replacement policies need to know)
void init() override
Definition Transients.cc:46
uint64_t maxSize() const override
bool dereference(StoreEntry &e) override
void maintain() override
perform regular periodic maintenance; TODO: move to UFSSwapDir::Maintain
Definition Transients.cc:98
static int64_t EntryLimit()
calculates maximum number of entries we need to store and map
bool isReader(const StoreEntry &) const
whether the entry is in "reading from Transients" I/O state
uint64_t currentSize() const override
current size
void completeWriting(const StoreEntry &e)
called when the in-transit entry has been successfully cached
uint64_t currentCount() const override
the total number of objects stored right now
std::vector< StoreEntry * > Locals
Definition Transients.h:105
void evictIfFound(const cache_key *) override
static bool Enabled()
Can we create and initialize Transients?
Definition Transients.h:91
bool configured() const
Definition YesNoNone.h:67
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define EBIT_TEST(flag, bit)
Definition defines.h:67
@ KEY_PRIVATE
Definition enums.h:97
size_t PageLevel()
approximate total number of shared memory pages used now
Definition Pages.cc:80
size_t PageSize()
returns page size in bytes; all pages are assumed to be the same size
Definition Pages.cc:28
size_t PageLimit()
the total number of shared memory pages that can be in use at any time
Definition Pages.cc:55
int32_t StoreMapSliceId
Definition StoreMap.h:24
double doublePercent(const double, const double)
Definition SquidMath.cc:25
IoStatus
cache "I/O" direction and status
Definition forward.h:40
@ ioReading
Definition forward.h:40
@ ioWriting
Definition forward.h:40
unsigned char cache_key
Store key.
Definition forward.h:29
signed_int32_t sfileno
Definition forward.h:22
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
bool UsingSmp()
Whether there should be more than one worker process running.
Definition tools.cc:697
#define PRId64
Definition types.h:104