store_digest.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 71 Store Digest Manager */
10 
11 /*
12  * TODO: We probably do not track all the cases when
13  * storeDigestNoteStoreReady() must be called; this may prevent
14  * storeDigestRebuild/write schedule to be activated
15  */
16 
17 #include "squid.h"
18 #include "debug/Stream.h"
19 #include "event.h"
20 #include "globals.h"
21 #include "mgr/Registration.h"
22 #include "store_digest.h"
23 
24 #if USE_CACHE_DIGESTS
25 #include "CacheDigest.h"
26 #include "HttpReply.h"
27 #include "HttpRequest.h"
28 #include "internal.h"
29 #include "MemObject.h"
30 #include "PeerDigest.h"
31 #include "refresh.h"
32 #include "SquidConfig.h"
33 #include "Store.h"
34 #include "StoreSearch.h"
35 #include "util.h"
36 
37 #include <cmath>
38 
39 /*
40  * local types
41  */
42 
44 {
45 public:
47  int rebuild_lock = 0;
48  StoreEntry * rewrite_lock = nullptr;
49  StoreEntry * publicEntry = nullptr;
51  int rewrite_offset = 0;
52  int rebuild_count = 0;
53  int rewrite_count = 0;
54 };
55 
57 {
58 public:
59  int del_count = 0; /* #store entries deleted from store_digest */
60  int del_lost_count = 0; /* #store entries not found in store_digest on delete */
61  int add_count = 0; /* #store entries accepted to store_digest */
62  int add_coll_count = 0; /* #accepted entries that collided with existing ones */
63  int rej_count = 0; /* #store entries not accepted to store_digest */
64  int rej_coll_count = 0; /* #not accepted entries that collided with existing ones */
65 };
66 
67 /* local vars */
70 
71 /* local prototypes */
72 static void storeDigestRebuildStart(void *datanotused);
73 static void storeDigestRebuildResume(void);
74 static void storeDigestRebuildFinish(void);
75 static void storeDigestRebuildStep(void *datanotused);
76 static void storeDigestRewriteStart(void *);
77 static void storeDigestRewriteResume(void);
78 static void storeDigestRewriteFinish(StoreEntry * e);
80 static void storeDigestCBlockSwapOut(StoreEntry * e);
81 static void storeDigestAdd(const StoreEntry *);
82 
84 static uint64_t
86 {
87  /*
88  * To-Do: Bloom proved that the optimal filter utilization is 50% (half of
89  * the bits are off). However, we do not have a formula to calculate the
90  * number of _entries_ we want to pre-allocate for.
91  */
92  const uint64_t hi_cap = Store::Root().maxSize() / Config.Store.avgObjectSize;
93  const uint64_t lo_cap = 1 + Store::Root().currentSize() / Config.Store.avgObjectSize;
94  const uint64_t e_count = StoreEntry::inUseCount();
95  uint64_t cap = e_count ? e_count : hi_cap;
96  debugs(71, 2, "have: " << e_count << ", want " << cap <<
97  " entries; limits: [" << lo_cap << ", " << hi_cap << "]");
98 
99  if (cap < lo_cap)
100  cap = lo_cap;
101 
102  /* do not enforce hi_cap limit, average-based estimation may be wrong
103  *if (cap > hi_cap)
104  * cap = hi_cap;
105  */
106 
107  // Bug 4534: we still have to set an upper-limit at some reasonable value though.
108  // this matches cacheDigestCalcMaskSize doing (cap*bpe)+7 < INT_MAX
109  const uint64_t absolute_max = (INT_MAX -8) / Config.digest.bits_per_entry;
110  if (cap > absolute_max) {
111  static time_t last_loud = 0;
112  if (last_loud < squid_curtime - 86400) {
113  debugs(71, DBG_IMPORTANT, "WARNING: Cache Digest cannot store " << cap << " entries. Limiting to " << absolute_max);
114  last_loud = squid_curtime;
115  } else {
116  debugs(71, 3, "WARNING: Cache Digest cannot store " << cap << " entries. Limiting to " << absolute_max);
117  }
118  cap = absolute_max;
119  }
120 
121  return cap;
122 }
123 #endif /* USE_CACHE_DIGESTS */
124 
125 void
127 {
128  Mgr::RegisterAction("store_digest", "Store Digest", storeDigestReport, 0, 1);
129 
130 #if USE_CACHE_DIGESTS
132  store_digest = nullptr;
133  debugs(71, 3, "Local cache digest generation disabled");
134  return;
135  }
136 
137  const uint64_t cap = storeDigestCalcCap();
139  debugs(71, DBG_IMPORTANT, "Local cache digest enabled; rebuild/rewrite every " <<
140  (int) Config.digest.rebuild_period << "/" <<
141  (int) Config.digest.rewrite_period << " sec");
142 
144 #else
145  store_digest = nullptr;
146  debugs(71, 3, "Local cache digest is 'off'");
147 #endif
148 }
149 
150 /* called when store_rebuild completes */
151 void
153 {
154 #if USE_CACHE_DIGESTS
155 
157  storeDigestRebuildStart(nullptr);
158  storeDigestRewriteStart(nullptr);
159  }
160 
161 #endif
162 }
163 
164 //TODO: this seems to be dead code. Is it needed?
165 void
167 {
168 #if USE_CACHE_DIGESTS
169 
171  return;
172  }
173 
174  assert(entry && store_digest);
175  debugs(71, 6, "storeDigestDel: checking entry, key: " << entry->getMD5Text());
176 
177  if (!EBIT_TEST(entry->flags, KEY_PRIVATE)) {
178  if (!store_digest->contains(static_cast<const cache_key *>(entry->key))) {
180  debugs(71, 6, "storeDigestDel: lost entry, key: " << entry->getMD5Text() << " url: " << entry->url() );
181  } else {
183  store_digest->remove(static_cast<const cache_key *>(entry->key));
184  debugs(71, 6, "storeDigestDel: deled entry, key: " << entry->getMD5Text());
185  }
186  }
187 #else
188  (void)entry;
189 #endif //USE_CACHE_DIGESTS
190 }
191 
192 void
194 {
195 #if USE_CACHE_DIGESTS
196 
198  return;
199  }
200 
201  if (store_digest) {
202  static const SBuf label("store");
203  cacheDigestReport(store_digest, label, e);
204  storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n",
209  storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n",
212  } else {
213  storeAppendPrintf(e, "store digest: disabled.\n");
214  }
215 #else
216  (void)e;
217 #endif //USE_CACHE_DIGESTS
218 }
219 
220 /*
221  * LOCAL FUNCTIONS
222  */
223 
224 #if USE_CACHE_DIGESTS
225 
226 /* should we digest this entry? used by storeDigestAdd() */
227 static int
229 {
230  /* add some stats! XXX */
231 
232  debugs(71, 6, "storeDigestAddable: checking entry, key: " << e->getMD5Text());
233 
234  /* check various entry flags (mimics StoreEntry::checkCachable XXX) */
235 
236  if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
237  debugs(71, 6, "storeDigestAddable: NO: private key");
238  return 0;
239  }
240 
241  if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
242  debugs(71, 6, "storeDigestAddable: NO: negative cached");
243  return 0;
244  }
245 
246  if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
247  debugs(71, 6, "storeDigestAddable: NO: release requested");
248  return 0;
249  }
250 
251  if (EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
252  debugs(71, 6, "storeDigestAddable: NO: wrong content-length");
253  return 0;
254  }
255 
256  /* do not digest huge objects */
257  if (e->swap_file_sz > (uint64_t )Config.Store.maxObjectSize) {
258  debugs(71, 6, "storeDigestAddable: NO: too big");
259  return 0;
260  }
261 
262  /* still here? check staleness */
263  // Include hittingRequiresCollapsing() entries: They are fresh _now_, but we
264  // check future freshness. They should not have enough info to judge future
265  // freshness since we are still waiting for their response headers, but
266  // admins might configure Squid to consider such entries fresh anyway.
267  /* Note: We should use the time of the next rebuild, not (cur_time+period) */
269  debugs(71, 6, "storeDigestAdd: entry expires within " << Config.digest.rebuild_period << " secs, ignoring");
270  return 0;
271  }
272 
273  /*
274  * idea: how about also skipping very fresh (thus, potentially
275  * unstable) entries? Should be configurable through
276  * cd_refresh_pattern, of course.
277  */
278  /*
279  * idea: skip objects that are going to be purged before the next
280  * update.
281  */
282  return 1;
283 }
284 
285 static void
287 {
288  assert(entry && store_digest);
289 
290  if (storeDigestAddable(entry)) {
292 
293  if (store_digest->contains(static_cast<const cache_key *>(entry->key)))
295 
296  store_digest->add(static_cast<const cache_key *>(entry->key));
297 
298  debugs(71, 6, "storeDigestAdd: added entry, key: " << entry->getMD5Text());
299  } else {
301 
302  if (store_digest->contains(static_cast<const cache_key *>(entry->key)))
304  }
305 }
306 
307 /* rebuilds digest from scratch */
308 static void
310 {
312  /* prevent overlapping if rebuild schedule is too tight */
313 
314  if (sd_state.rebuild_lock) {
315  debugs(71, DBG_IMPORTANT, "storeDigestRebuildStart: overlap detected, consider increasing rebuild period");
316  return;
317  }
318 
320  debugs(71, 2, "storeDigestRebuildStart: rebuild #" << sd_state.rebuild_count + 1);
321 
322  if (sd_state.rewrite_lock) {
323  debugs(71, 2, "storeDigestRebuildStart: waiting for Rewrite to finish.");
324  return;
325  }
326 
328 }
329 
331 static bool
333 {
334  const uint64_t cap = storeDigestCalcCap();
336  uint64_t diff;
337  if (cap > store_digest->capacity)
338  diff = cap - store_digest->capacity;
339  else
340  diff = store_digest->capacity - cap;
341  debugs(71, 2, store_digest->capacity << " -> " << cap << "; change: " <<
342  diff << " (" << xpercentInt(diff, store_digest->capacity) << "%)" );
343  /* avoid minor adjustments */
344 
345  if (diff <= store_digest->capacity / 10) {
346  debugs(71, 2, "small change, will not resize.");
347  return false;
348  } else {
349  debugs(71, 2, "big change, resizing.");
351  }
352  return true;
353 }
354 
355 /* called be Rewrite to push Rebuild forward */
356 static void
358 {
362  /* resize or clear */
363 
364  if (!storeDigestResize())
365  store_digest->clear(); /* not clean()! */
366 
368 
369  eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, nullptr, 0.0, 1);
370 }
371 
372 /* finishes swap out sequence for the digest; schedules next rebuild */
373 static void
375 {
379  debugs(71, 2, "storeDigestRebuildFinish: done.");
380  eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, nullptr, (double)
382  /* resume pending Rewrite if any */
383 
386 }
387 
388 /* recalculate a few hash buckets per invocation; schedules next step */
389 static void
391 {
392  /* TODO: call Store::Root().size() to determine this.. */
393  int count = Config.Store.objectsPerBucket * (int) ceil((double) store_hash_buckets *
396 
397  debugs(71, 3, "storeDigestRebuildStep: buckets: " << store_hash_buckets << " entries to check: " << count);
398 
399  while (count-- && !sd_state.theSearch->isDone() && sd_state.theSearch->next())
401 
402  /* are we done ? */
403  if (sd_state.theSearch->isDone())
405  else
406  eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, nullptr, 0.0, 1);
407 }
408 
409 /* starts swap out sequence for the digest */
410 static void
412 {
414  /* prevent overlapping if rewrite schedule is too tight */
415 
416  if (sd_state.rewrite_lock) {
417  debugs(71, DBG_IMPORTANT, "storeDigestRewrite: overlap detected, consider increasing rewrite period");
418  return;
419  }
420 
421  debugs(71, 2, "storeDigestRewrite: start rewrite #" << sd_state.rewrite_count + 1);
422 
423  const char *url = internalLocalUri("/squid-internal-periodic/", SBuf(StoreDigestFileName));
424  const auto mx = MasterXaction::MakePortless<XactionInitiator::initCacheDigest>();
425  auto req = HttpRequest::FromUrlXXX(url, mx);
426 
427  RequestFlags flags;
428  flags.cachable.support(); // prevent RELEASE_REQUEST in storeCreateEntry()
429 
430  StoreEntry *e = storeCreateEntry(url, url, flags, Http::METHOD_GET);
431  assert(e);
433  debugs(71, 3, "storeDigestRewrite: url: " << url << " key: " << e->getMD5Text());
434  e->mem_obj->request = req;
435 
436  /* wait for rebuild (if any) to finish */
437  if (sd_state.rebuild_lock) {
438  debugs(71, 2, "storeDigestRewriteStart: waiting for rebuild to finish.");
439  return;
440  }
441 
443 }
444 
445 static void
447 {
448  StoreEntry *e;
449 
455  /* setting public key will mark the old digest entry for removal once unlocked */
456  e->setPublicKey();
457  if (const auto oldEntry = sd_state.publicEntry) {
458  oldEntry->release(true);
459  sd_state.publicEntry = nullptr;
460  oldEntry->unlock("storeDigestRewriteResume");
461  }
462  assert(e->locked());
463  sd_state.publicEntry = e;
464  /* fake reply */
465  HttpReply *rep = new HttpReply;
466  rep->setHeaders(Http::scOkay, "Cache Digest OK",
467  "application/cache-digest", (store_digest->mask_size + sizeof(sd_state.cblock)),
469  debugs(71, 3, "storeDigestRewrite: entry expires on " << rep->expires <<
470  " (" << std::showpos << (int) (rep->expires - squid_curtime) << ")");
471  e->buffer();
472  e->replaceHttpReply(rep);
474  e->flush();
475  eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, sd_state.rewrite_lock, 0.0, 1, false);
476 }
477 
478 /* finishes swap out sequence for the digest; schedules next rewrite */
479 static void
481 {
483  e->complete();
484  e->timestampsSet();
485  debugs(71, 2, "storeDigestRewriteFinish: digest expires at " << e->expires <<
486  " (" << std::showpos << (int) (e->expires - squid_curtime) << ")");
487  /* is this the write order? @?@ */
488  e->mem_obj->unlinkRequest();
489  sd_state.rewrite_lock = nullptr;
491  eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, nullptr, (double)
493  /* resume pending Rebuild if any */
494 
497 }
498 
499 /* swaps out one digest "chunk" per invocation; schedules next swap out */
500 static void
502 {
503  StoreEntry *e = static_cast<StoreEntry *>(data);
504  int chunk_size = Config.digest.swapout_chunk_size;
506  assert(e);
507  /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */
508 
509  if (static_cast<uint32_t>(sd_state.rewrite_offset + chunk_size) > store_digest->mask_size)
511 
512  e->append(store_digest->mask + sd_state.rewrite_offset, chunk_size);
513 
514  debugs(71, 3, "storeDigestSwapOutStep: size: " << store_digest->mask_size <<
515  " offset: " << sd_state.rewrite_offset << " chunk: " <<
516  chunk_size << " bytes");
517 
518  sd_state.rewrite_offset += chunk_size;
519 
520  /* are we done ? */
521  if (static_cast<uint32_t>(sd_state.rewrite_offset) >= store_digest->mask_size)
523  else
524  eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, data, 0.0, 1, false);
525 }
526 
527 static void
529 {
530  memset(&sd_state.cblock, 0, sizeof(sd_state.cblock));
539  e->append((char *) &sd_state.cblock, sizeof(sd_state.cblock));
540 }
541 
542 #endif /* USE_CACHE_DIGESTS */
543 
unsigned char bits_per_entry
Definition: PeerDigest.h:34
static void storeDigestRebuildStep(void *datanotused)
unsigned char cache_key
Store key.
Definition: forward.h:29
SupportOrVeto cachable
whether the response may be stored in the cache
Definition: RequestFlags.h:35
#define EBIT_SET(flag, bit)
Definition: defines.h:65
uint64_t capacity
Definition: CacheDigest.h:57
MemObject * mem_obj
Definition: Store.h:220
time_t rewrite_period
Definition: SquidConfig.h:479
static int storeDigestAddable(const StoreEntry *e)
void clear()
reset the digest mask and counters
Definition: CacheDigest.cc:79
const char * url() const
Definition: store.cc:1566
void append(char const *, int) override
Appends a c-string to existing packed data.
Definition: store.cc:803
time_t rebuild_period
Definition: SquidConfig.h:478
StoreDigestCBlock cblock
Definition: store_digest.cc:46
@ ENTRY_BAD_LENGTH
Definition: enums.h:109
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
@ KEY_PRIVATE
Definition: enums.h:97
StoreSearchPointer theSearch
Definition: store_digest.cc:50
Definition: SBuf.h:93
StoreEntry * publicEntry
points to the previous store entry with the digest
Definition: store_digest.cc:49
int objectsPerBucket
Definition: SquidConfig.h:264
bool setPublicKey(const KeyScope keyScope=ksDefault)
Definition: store.cc:575
const Version CacheDigestVer
Definition: peer_digest.cc:51
struct SquidConfig::@97 onoff
@ ENTRY_NEGCACHED
Definition: enums.h:107
void updateCapacity(uint64_t newCapacity)
changes mask size to fit newCapacity, resets bits to 0
Definition: CacheDigest.cc:86
uint16_t flags
Definition: Store.h:231
static StoreDigestState sd_state
Definition: store_digest.cc:68
time_t expires
Definition: Store.h:225
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1705
void remove(const cache_key *key)
Definition: CacheDigest.cc:140
const char * StoreDigestFileName
time_t expires
Definition: HttpReply.h:44
void cacheDigestReport(CacheDigest *cd, const SBuf &label, StoreEntry *e)
Definition: CacheDigest.cc:245
static void storeDigestRewriteFinish(StoreEntry *e)
short int required
Definition: PeerDigest.h:21
int bits_per_entry
Definition: SquidConfig.h:477
void storeDigestDel(const StoreEntry *entry)
@ RELEASE_REQUEST
prohibits making the key public
Definition: enums.h:93
double xpercent(double part, double whole)
Definition: util.cc:40
#define INT_MAX
Definition: types.h:70
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
int unlock(const char *context)
Definition: store.cc:469
static void storeDigestRewriteResume(void)
struct SquidConfig::@95 Store
static size_t inUseCount()
Definition: store.cc:199
#define assert(EX)
Definition: assert.h:17
void storeDigestNoteStoreReady(void)
virtual void next(void(callback)(void *cbdata), void *cbdata)=0
void EVH void double
Definition: stub_event.cc:16
char * mask
Definition: CacheDigest.h:58
int xpercentInt(double part, double whole)
Definition: util.cc:46
void buffer() override
Definition: store.cc:1601
virtual bool isDone() const =0
virtual StoreEntry * currentItem()=0
uint64_t del_count
Definition: CacheDigest.h:56
void flush() override
Definition: store.cc:1612
StoreSearch * search()
Definition: Controller.cc:205
static EVH storeDigestSwapOutStep
Definition: store_digest.cc:79
time_t squid_curtime
Definition: stub_libtime.cc:20
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:759
static bool storeDigestResize()
void storeDigestReport(StoreEntry *e)
void EVH(void *)
Definition: event.h:18
static void storeDigestRebuildResume(void)
uint64_t currentSize() const override
current size
Definition: Controller.cc:167
int rebuild_lock
bucket number
Definition: store_digest.cc:47
uint64_t maxSize() const override
Definition: Controller.cc:153
void complete()
Definition: store.cc:1031
void add(const cache_key *key)
Definition: CacheDigest.cc:107
short int current
Definition: PeerDigest.h:20
bool timestampsSet()
Definition: store.cc:1387
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
Definition: Registration.cc:54
static uint64_t storeDigestCalcCap()
calculates digest capacity
Definition: store_digest.cc:85
void unlinkRequest()
Definition: MemObject.h:56
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:528
uint64_t swap_file_sz
Definition: Store.h:229
bool contains(const cache_key *key) const
Definition: CacheDigest.cc:93
@ ENTRY_SPECIAL
Definition: enums.h:79
int refreshCheckDigest(const StoreEntry *entry, time_t delta)
Definition: refresh.cc:617
#define DBG_IMPORTANT
Definition: Stream.h:38
CacheDigest * store_digest
int digest_generation
Definition: SquidConfig.h:309
int64_t maxObjectSize
Definition: SquidConfig.h:266
int64_t avgObjectSize
Definition: SquidConfig.h:265
unsigned char hash_func_count
Definition: PeerDigest.h:35
int rebuild_chunk_percentage
Definition: SquidConfig.h:481
int store_hash_buckets
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
static void storeDigestRebuildFinish(void)
int locked() const
Definition: Store.h:145
uint32_t mask_size
Definition: CacheDigest.h:59
@ scOkay
Definition: StatusCode.h:27
@ METHOD_GET
Definition: MethodType.h:25
struct SquidConfig::@103 digest
static void storeDigestCBlockSwapOut(StoreEntry *e)
uint64_t count
Definition: CacheDigest.h:55
char * internalLocalUri(const char *dir, const SBuf &name)
Definition: internal.cc:139
size_t swapout_chunk_size
Definition: SquidConfig.h:480
static StoreDigestStats sd_stats
Definition: store_digest.cc:69
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
HttpRequestPointer request
Definition: MemObject.h:205
int CacheDigestHashFuncCount
const char * getMD5Text() const
Definition: store.cc:207
static void storeDigestRewriteStart(void *)
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
void storeDigestInit(void)
class SquidConfig Config
Definition: SquidConfig.cc:12
int unsigned int
Definition: stub_fd.cc:19
static void storeDigestRebuildStart(void *datanotused)
static void storeDigestAdd(const StoreEntry *)
StoreEntry * rewrite_lock
points to store entry with the digest
Definition: store_digest.cc:48
Controller & Root()
safely access controller singleton
Definition: Controller.cc:926

 

Introduction

Documentation

Support

Miscellaneous