]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_digest.cc
AC_CHECK_TYPE(mtyp_t) needs sys/msg.h
[thirdparty/squid.git] / src / store_digest.cc
CommitLineData
8638fc66 1/*
17a80fc2 2 * $Id: store_digest.cc,v 1.35 1999/09/07 22:15:11 wessels Exp $
8638fc66 3 *
4 * DEBUG: section 71 Store Digest Manager
5 * AUTHOR: Alex Rousskov
6 *
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 8 * ----------------------------------------------------------
8638fc66 9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
e25c139f 12 * National Laboratory for Applied Network Research and funded by the
13 * National Science Foundation. Squid is Copyrighted (C) 1998 by
14 * Duane Wessels and the University of California San Diego. Please
15 * see the COPYRIGHT file for full details. Squid incorporates
16 * software developed and/or copyrighted by other sources. Please see
17 * the CREDITS file for full details.
8638fc66 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
8638fc66 33 */
34
6168bccd 35
36/*
37 * TODO: We probably do not track all the cases when
38 * storeDigestNoteStoreReady() must be called; this may prevent
39 * storeDigestRebuild/write schedule to be activated
40 */
41
8638fc66 42#include "squid.h"
43
d6fd3381 44#if USE_CACHE_DIGESTS
45
46/*
47 * local types
48 */
12784378 49
12784378 50typedef struct {
51 StoreDigestCBlock cblock;
b644367b 52 int rebuild_lock; /* bucket number */
53 StoreEntry *rewrite_lock; /* store entry with the digest */
12784378 54 int rebuild_offset;
55 int rewrite_offset;
56 int rebuild_count;
b644367b 57 int rewrite_count;
12784378 58} StoreDigestState;
59
6168bccd 60typedef struct {
4b4cd312 61 int del_count; /* #store entries deleted from store_digest */
62 int del_lost_count; /* #store entries not found in store_digest on delete */
63 int add_count; /* #store entries accepted to store_digest */
64 int add_coll_count; /* #accepted entries that collided with existing ones */
65 int rej_count; /* #store entries not accepted to store_digest */
66 int rej_coll_count; /* #not accepted entries that collided with existing ones */
6168bccd 67} StoreDigestStats;
68
12784378 69/*
70 * local constants (many of these are good candidates for SquidConfig
71 */
72
6168bccd 73/* #bits per entry in store digest */
a8c98096 74static const int StoreDigestBitsPerEntry = 5;
6168bccd 75/* how often we want to rebuild the digest, in seconds */
ef4317c8 76static const time_t StoreDigestRebuildPeriod = 60 * 60;
6168bccd 77/* how often we want to rewrite the digest after rebuild, in seconds */
78static const int StoreDigestRewritePeriod = 60 * 60;
12784378 79/* how many bytes to swap out at a time */
80static const int StoreDigestSwapOutChunkSize = SM_PAGE_SIZE;
81/* portion (0,1] of a hash table to be rescanned at a time */
82static const double StoreDigestRebuildChunkPercent = 0.10;
12784378 83
462f66d2 84/* local vars */
12784378 85static StoreDigestState sd_state;
6168bccd 86static StoreDigestStats sd_stats;
12784378 87
88/* local prototypes */
6168bccd 89static void storeDigestRebuildStart(void *datanotused);
d6fd3381 90static void storeDigestRebuildResume(void);
91static void storeDigestRebuildFinish(void);
12784378 92static void storeDigestRebuildStep(void *datanotused);
d6fd3381 93static void storeDigestRewriteStart(void *);
94static void storeDigestRewriteResume(void);
b644367b 95static void storeDigestRewriteFinish(StoreEntry * e);
d6f51e3c 96static EVH storeDigestSwapOutStep;
b644367b 97static void storeDigestCBlockSwapOut(StoreEntry * e);
d6fd3381 98static int storeDigestCalcCap(void);
99static int storeDigestResize(void);
100static void storeDigestAdd(const StoreEntry *);
12784378 101
d6fd3381 102#endif /* USE_CACHE_DIGESTS */
103
104/*
105 * PUBLIC FUNCTIONS
106 */
12784378 107
8638fc66 108void
d6fd3381 109storeDigestInit(void)
8638fc66 110{
6cfa8966 111#if USE_CACHE_DIGESTS
304b267e 112 const int cap = storeDigestCalcCap();
6168bccd 113 store_digest = cacheDigestCreate(cap, StoreDigestBitsPerEntry);
114 debug(71, 1) ("Local cache digest enabled; rebuild/rewrite every %d/%d sec\n",
304b267e 115 StoreDigestRebuildPeriod, StoreDigestRewritePeriod);
12784378 116 memset(&sd_state, 0, sizeof(sd_state));
8638fc66 117 cachemgrRegister("store_digest", "Store Digest",
1da3b90b 118 storeDigestReport, 0, 1);
d6fd3381 119#else
120 store_digest = NULL;
121 debug(71, 3) ("Local cache digest is 'off'\n");
122#endif
8638fc66 123}
124
6168bccd 125/* called when store_rebuild completes */
8638fc66 126void
d6fd3381 127storeDigestNoteStoreReady(void)
12784378 128{
d6fd3381 129#if USE_CACHE_DIGESTS
6168bccd 130 storeDigestRebuildStart(NULL);
131 storeDigestRewriteStart(NULL);
d6fd3381 132#endif
133}
134
135void
136storeDigestDel(const StoreEntry * entry)
137{
138#if USE_CACHE_DIGESTS
139 assert(entry && store_digest);
140 debug(71, 6) ("storeDigestDel: checking entry, key: %s\n",
141 storeKeyText(entry->key));
d46a87a8 142 if (!EBIT_TEST(entry->flags, KEY_PRIVATE)) {
d6fd3381 143 if (!cacheDigestTest(store_digest, entry->key)) {
144 sd_stats.del_lost_count++;
145 debug(71, 6) ("storeDigestDel: lost entry, key: %s url: %s\n",
146 storeKeyText(entry->key), storeUrl(entry));
147 } else {
148 sd_stats.del_count++;
149 cacheDigestDel(store_digest, entry->key);
150 debug(71, 6) ("storeDigestDel: deled entry, key: %s\n",
151 storeKeyText(entry->key));
152 }
153 }
154#endif
12784378 155}
156
12784378 157void
d6fd3381 158storeDigestReport(StoreEntry * e)
159{
160#if USE_CACHE_DIGESTS
161 if (store_digest) {
162 cacheDigestReport(store_digest, "store", e);
163 storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n",
164 sd_stats.add_count,
165 sd_stats.rej_count,
166 xpercent(sd_stats.rej_count, sd_stats.rej_count + sd_stats.add_count),
167 sd_stats.del_count);
168 storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n",
169 xpercent(sd_stats.add_coll_count, sd_stats.add_count),
170 xpercent(sd_stats.rej_coll_count, sd_stats.rej_count));
171 } else {
172 storeAppendPrintf(e, "store digest: disabled.\n");
173 }
174#endif
175}
176
177/*
178 * LOCAL FUNCTIONS
179 */
180
181#if USE_CACHE_DIGESTS
182
c68e9c6b 183/* should we digest this entry? used by storeDigestAdd() */
184static int
185storeDigestAddable(const StoreEntry * e)
186{
187 /* add some stats! XXX */
188
189 debug(71, 6) ("storeDigestAddable: checking entry, key: %s\n",
190 storeKeyText(e->key));
191
192 /* check various entry flags (mimics storeCheckCachable XXX) */
193 if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) {
194 debug(71, 6) ("storeDigestAddable: NO: not cachable\n");
195 return 0;
196 }
197 if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
198 debug(71, 6) ("storeDigestAddable: NO: private key\n");
199 return 0;
200 }
201 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
202 debug(71, 6) ("storeDigestAddable: NO: negative cached\n");
203 return 0;
204 }
205 if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
206 debug(71, 6) ("storeDigestAddable: NO: release requested\n");
207 return 0;
208 }
209 if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
210 debug(71, 6) ("storeDigestAddable: NO: wrong content-length\n");
211 return 0;
212 }
213 /* do not digest huge objects */
214 if (e->swap_file_sz > Config.Store.maxObjectSize) {
215 debug(71, 6) ("storeDigestAddable: NO: too big\n");
216 return 0;
217 }
218 /* still here? check staleness */
219 /* Note: We should use the time of the next rebuild, not (cur_time+period) */
220 if (refreshCheckDigest(e, StoreDigestRebuildPeriod)) {
221 debug(71, 6) ("storeDigestAdd: entry expires within %d secs, ignoring\n",
222 StoreDigestRebuildPeriod);
223 return 0;
224 }
17a80fc2 225 /*
226 * idea: how about also skipping very fresh (thus, potentially
227 * unstable) entries? Should be configurable through
228 * cd_refresh_pattern, of course.
229 */
230 /*
231 * idea: skip objects that are going to be purged before the next
232 * update.
233 */
234#if !HEAP_REPLACEMENT
235 if ((squid_curtime + StoreDigestRebuildPeriod) - e->lastref > storeExpiredReferenceAge())
236 return 0;
237#endif
c68e9c6b 238 return 1;
239}
240
d6fd3381 241static void
6168bccd 242storeDigestAdd(const StoreEntry * entry)
243{
6168bccd 244 assert(entry && store_digest);
c68e9c6b 245
246 if (storeDigestAddable(entry)) {
6168bccd 247 sd_stats.add_count++;
248 if (cacheDigestTest(store_digest, entry->key))
249 sd_stats.add_coll_count++;
250 cacheDigestAdd(store_digest, entry->key);
4b4cd312 251 debug(71, 6) ("storeDigestAdd: added entry, key: %s\n",
6168bccd 252 storeKeyText(entry->key));
253 } else {
254 sd_stats.rej_count++;
255 if (cacheDigestTest(store_digest, entry->key))
256 sd_stats.rej_coll_count++;
257 }
12784378 258}
259
12784378 260/* rebuilds digest from scratch */
261static void
6168bccd 262storeDigestRebuildStart(void *datanotused)
8638fc66 263{
264 assert(store_digest);
12784378 265 /* prevent overlapping if rebuild schedule is too tight */
266 if (sd_state.rebuild_lock) {
6168bccd 267 debug(71, 1) ("storeDigestRebuildStart: overlap detected, consider increasing rebuild period\n");
12784378 268 return;
269 }
270 sd_state.rebuild_lock = 1;
6168bccd 271 debug(71, 2) ("storeDigestRebuildStart: rebuild #%d\n", sd_state.rebuild_count + 1);
272 if (sd_state.rewrite_lock) {
273 debug(71, 2) ("storeDigestRebuildStart: waiting for Rewrite to finish.\n");
274 return;
275 }
276 storeDigestRebuildResume();
277}
278
279/* called be Rewrite to push Rebuild forward */
280static void
d6fd3381 281storeDigestRebuildResume(void)
6168bccd 282{
283 assert(sd_state.rebuild_lock);
284 assert(!sd_state.rewrite_lock);
12784378 285 sd_state.rebuild_offset = 0;
304b267e 286 /* resize or clear */
287 if (!storeDigestResize())
4b4cd312 288 cacheDigestClear(store_digest); /* not clean()! */
6168bccd 289 memset(&sd_stats, 0, sizeof(sd_stats));
c43f5247 290 eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1);
12784378 291}
292
293/* finishes swap out sequence for the digest; schedules next rebuild */
294static void
d6fd3381 295storeDigestRebuildFinish(void)
12784378 296{
297 assert(sd_state.rebuild_lock);
298 sd_state.rebuild_lock = 0;
299 sd_state.rebuild_count++;
300 debug(71, 2) ("storeDigestRebuildFinish: done.\n");
52040193 301 eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, NULL, (double) StoreDigestRebuildPeriod, 1);
6168bccd 302 /* resume pending Rewrite if any */
12784378 303 if (sd_state.rewrite_lock)
6168bccd 304 storeDigestRewriteResume();
12784378 305}
306
307/* recalculate a few hash buckets per invocation; schedules next step */
308static void
309storeDigestRebuildStep(void *datanotused)
310{
b644367b 311 int bcount = (int) ceil(store_hash_buckets * StoreDigestRebuildChunkPercent);
12784378 312 assert(sd_state.rebuild_lock);
313 if (sd_state.rebuild_offset + bcount > store_hash_buckets)
314 bcount = store_hash_buckets - sd_state.rebuild_offset;
315 debug(71, 3) ("storeDigestRebuildStep: buckets: %d offset: %d chunk: %d buckets\n",
316 store_hash_buckets, sd_state.rebuild_offset, bcount);
317 while (bcount--) {
318 hash_link *link_ptr = hash_get_bucket(store_table, sd_state.rebuild_offset);
319 for (; link_ptr; link_ptr = link_ptr->next) {
6168bccd 320 storeDigestAdd((StoreEntry *) link_ptr);
12784378 321 }
322 sd_state.rebuild_offset++;
323 }
324 /* are we done ? */
325 if (sd_state.rebuild_offset >= store_hash_buckets)
326 storeDigestRebuildFinish();
327 else
c43f5247 328 eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1);
12784378 329}
330
331
332/* starts swap out sequence for the digest */
333static void
6168bccd 334storeDigestRewriteStart(void *datanotused)
12784378 335{
92695e5e 336 request_flags flags;
462f66d2 337 char *url;
6168bccd 338 StoreEntry *e;
12784378 339
340 assert(store_digest);
341 /* prevent overlapping if rewrite schedule is too tight */
342 if (sd_state.rewrite_lock) {
343 debug(71, 1) ("storeDigestRewrite: overlap detected, consider increasing rewrite period\n");
344 return;
345 }
b644367b 346 debug(71, 2) ("storeDigestRewrite: start rewrite #%d\n", sd_state.rewrite_count + 1);
12784378 347 /* make new store entry */
e13ee7ad 348 url = internalLocalUri("/squid-internal-periodic/", StoreDigestFileName);
92695e5e 349 flags = null_request_flags;
350 flags.cachable = 1;
6168bccd 351 sd_state.rewrite_lock = e = storeCreateEntry(url, url, flags, METHOD_GET);
352 assert(sd_state.rewrite_lock);
db1cd23c 353 cbdataAdd(sd_state.rewrite_lock, NULL, 0);
6168bccd 354 debug(71, 3) ("storeDigestRewrite: url: %s key: %s\n", url, storeKeyText(e->key));
355 e->mem_obj->request = requestLink(urlParse(METHOD_GET, url));
356 /* wait for rebuild (if any) to finish */
357 if (sd_state.rebuild_lock) {
358 debug(71, 2) ("storeDigestRewriteStart: waiting for rebuild to finish.\n");
359 return;
360 }
361 storeDigestRewriteResume();
362}
363
364static void
d6fd3381 365storeDigestRewriteResume(void)
6168bccd 366{
367 StoreEntry *e = sd_state.rewrite_lock;
368
369 assert(sd_state.rewrite_lock);
370 assert(!sd_state.rebuild_lock);
12784378 371 sd_state.rewrite_offset = 0;
d46a87a8 372 EBIT_SET(e->flags, ENTRY_SPECIAL);
462f66d2 373 /* setting public key will purge old digest entry if any */
12784378 374 storeSetPublicKey(e);
462f66d2 375 /* fake reply */
12784378 376 httpReplyReset(e->mem_obj->reply);
377 httpReplySetHeaders(e->mem_obj->reply, 1.0, 200, "Cache Digest OK",
b644367b 378 "application/cache-digest", store_digest->mask_size + sizeof(sd_state.cblock),
12784378 379 squid_curtime, squid_curtime + StoreDigestRewritePeriod);
8a6218c6 380 debug(71, 3) ("storeDigestRewrite: entry expires on %d (%+d)\n",
381 e->mem_obj->reply->expires, e->mem_obj->reply->expires - squid_curtime);
12784378 382 storeBuffer(e);
383 httpReplySwapOut(e->mem_obj->reply, e);
384 storeDigestCBlockSwapOut(e);
385 storeBufferFlush(e);
c43f5247 386 eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, sd_state.rewrite_lock, 0.0, 1);
12784378 387}
388
389/* finishes swap out sequence for the digest; schedules next rewrite */
390static void
b644367b 391storeDigestRewriteFinish(StoreEntry * e)
12784378 392{
12784378 393 assert(e == sd_state.rewrite_lock);
394 storeComplete(e);
395 storeTimestampsSet(e);
e13ee7ad 396 debug(71, 2) ("storeDigestRewriteFinish: digest expires at %d (%+d)\n",
8a6218c6 397 e->expires, e->expires - squid_curtime);
6168bccd 398 /* is this the write order? @?@ */
399 requestUnlink(e->mem_obj->request);
400 e->mem_obj->request = NULL;
12784378 401 storeUnlockObject(e);
52040193 402 /*
403 * note, it won't really get free()'d here because we used
404 * MEM_DONTFREE in the call to cbdataAdd().
405 */
d90c79ee 406 cbdataFree(sd_state.rewrite_lock);
462f66d2 407 sd_state.rewrite_lock = e = NULL;
12784378 408 sd_state.rewrite_count++;
52040193 409 eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, NULL, (double) StoreDigestRewritePeriod, 1);
6168bccd 410 /* resume pending Rebuild if any */
411 if (sd_state.rebuild_lock)
412 storeDigestRebuildResume();
12784378 413}
414
415/* swaps out one digest "chunk" per invocation; schedules next swap out */
416static void
52040193 417storeDigestSwapOutStep(void *data)
12784378 418{
52040193 419 StoreEntry *e = data;
12784378 420 int chunk_size = StoreDigestSwapOutChunkSize;
421 assert(e);
12784378 422 assert(e == sd_state.rewrite_lock);
12784378 423 /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */
424 if (sd_state.rewrite_offset + chunk_size > store_digest->mask_size)
425 chunk_size = store_digest->mask_size - sd_state.rewrite_offset;
426 storeAppend(e, store_digest->mask + sd_state.rewrite_offset, chunk_size);
427 debug(71, 3) ("storeDigestSwapOutStep: size: %d offset: %d chunk: %d bytes\n",
428 store_digest->mask_size, sd_state.rewrite_offset, chunk_size);
429 sd_state.rewrite_offset += chunk_size;
430 /* are we done ? */
431 if (sd_state.rewrite_offset >= store_digest->mask_size)
432 storeDigestRewriteFinish(e);
433 else
c43f5247 434 eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, e, 0.0, 1);
12784378 435}
436
437static void
b644367b 438storeDigestCBlockSwapOut(StoreEntry * e)
12784378 439{
12784378 440 memset(&sd_state.cblock, 0, sizeof(sd_state.cblock));
462f66d2 441 sd_state.cblock.ver.current = htons(CacheDigestVer.current);
442 sd_state.cblock.ver.required = htons(CacheDigestVer.required);
443 sd_state.cblock.capacity = htonl(store_digest->capacity);
444 sd_state.cblock.count = htonl(store_digest->count);
445 sd_state.cblock.del_count = htonl(store_digest->del_count);
446 sd_state.cblock.mask_size = htonl(store_digest->mask_size);
6168bccd 447 sd_state.cblock.bits_per_entry = (unsigned char) StoreDigestBitsPerEntry;
448 sd_state.cblock.hash_func_count = (unsigned char) CacheDigestHashFuncCount;
4b4cd312 449 storeAppend(e, (char *) &sd_state.cblock, sizeof(sd_state.cblock));
8638fc66 450}
451
304b267e 452/* calculates digest capacity */
453static int
d6fd3381 454storeDigestCalcCap(void)
304b267e 455{
456 /*
457 * To-Do: Bloom proved that the optimal filter utilization is 50% (half of
458 * the bits are off). However, we do not have a formula to calculate the
459 * number of _entries_ we want to pre-allocate for.
460 */
6168bccd 461 const int hi_cap = Config.Swap.maxSize / Config.Store.avgObjectSize;
462 const int lo_cap = 1 + store_swap_size / Config.Store.avgObjectSize;
463 const int e_count = memInUse(MEM_STOREENTRY);
464 int cap = e_count ? e_count : hi_cap;
465 debug(71, 2) ("storeDigestCalcCap: have: %d, want %d entries; limits: [%d, %d]\n",
466 e_count, cap, lo_cap, hi_cap);
304b267e 467 if (cap < lo_cap)
468 cap = lo_cap;
6168bccd 469 /* do not enforce hi_cap limit, average-based estimation may be wrong
470 *if (cap > hi_cap)
4b4cd312 471 * cap = hi_cap;
6168bccd 472 */
473 return cap;
304b267e 474}
475
476/* returns true if we actually resized the digest */
477static int
d6fd3381 478storeDigestResize(void)
304b267e 479{
480 const int cap = storeDigestCalcCap();
481 int diff;
482 assert(store_digest);
483 diff = abs(cap - store_digest->capacity);
484 debug(71, 2) ("storeDigestResize: %d -> %d; change: %d (%d%%)\n",
485 store_digest->capacity, cap, diff,
486 xpercentInt(diff, store_digest->capacity));
487 /* avoid minor adjustments */
4b4cd312 488 if (diff <= store_digest->capacity / 10) {
6168bccd 489 debug(71, 2) ("storeDigestResize: small change, will not resize.\n");
490 return 0;
304b267e 491 } else {
6168bccd 492 debug(71, 2) ("storeDigestResize: big change, resizing.\n");
304b267e 493 cacheDigestChangeCap(store_digest, cap);
494 return 1;
495 }
496}
497
d6fd3381 498#endif /* USE_CACHE_DIGESTS */