]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_digest.cc
Maintenance: bump astyle to 2.04 and quieten report
[thirdparty/squid.git] / src / store_digest.cc
CommitLineData
8638fc66 1/*
bbc27441 2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
8638fc66 7 */
8
bbc27441
AJ
9/* DEBUG: section 71 Store Digest Manager */
10
6168bccd 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
582c2af2
FC
17#include "squid.h"
18#include "Debug.h"
a553a5a3 19#include "event.h"
582c2af2 20#include "globals.h"
8822ebee 21#include "mgr/Registration.h"
35a28a37 22#include "store_digest.h"
d6fd3381 23
b814e8d4
FC
24#if USE_CACHE_DIGESTS
25#include "CacheDigest.h"
528b2c61 26#include "HttpReply.h"
582c2af2 27#include "HttpRequest.h"
308e60be 28#include "internal.h"
528b2c61 29#include "MemObject.h"
aa839030 30#include "PeerDigest.h"
c6f15d40 31#include "refresh.h"
4d5904f7 32#include "SquidConfig.h"
985c86bc 33#include "SquidTime.h"
582c2af2 34#include "Store.h"
c8f4eac4 35#include "StoreSearch.h"
ed6e9fb9 36#include "util.h"
528b2c61 37
074d6a40 38#include <cmath>
582c2af2 39
d6fd3381 40/*
41 * local types
42 */
12784378 43
c8f4eac4 44class StoreDigestState
62e76326 45{
c8f4eac4 46
47public:
12784378 48 StoreDigestCBlock cblock;
b644367b 49 int rebuild_lock; /* bucket number */
aa839030 50 StoreEntry * rewrite_lock; /* points to store entry with the digest */
c8f4eac4 51 StoreSearchPointer theSearch;
12784378 52 int rewrite_offset;
53 int rebuild_count;
b644367b 54 int rewrite_count;
c8f4eac4 55};
12784378 56
26ac0430 57typedef struct {
4b4cd312 58 int del_count; /* #store entries deleted from store_digest */
59 int del_lost_count; /* #store entries not found in store_digest on delete */
60 int add_count; /* #store entries accepted to store_digest */
61 int add_coll_count; /* #accepted entries that collided with existing ones */
62 int rej_count; /* #store entries not accepted to store_digest */
63 int rej_coll_count; /* #not accepted entries that collided with existing ones */
2fadd50d 64} StoreDigestStats;
6168bccd 65
462f66d2 66/* local vars */
12784378 67static StoreDigestState sd_state;
6168bccd 68static StoreDigestStats sd_stats;
12784378 69
70/* local prototypes */
6168bccd 71static void storeDigestRebuildStart(void *datanotused);
d6fd3381 72static void storeDigestRebuildResume(void);
73static void storeDigestRebuildFinish(void);
12784378 74static void storeDigestRebuildStep(void *datanotused);
d6fd3381 75static void storeDigestRewriteStart(void *);
76static void storeDigestRewriteResume(void);
b644367b 77static void storeDigestRewriteFinish(StoreEntry * e);
d6f51e3c 78static EVH storeDigestSwapOutStep;
b644367b 79static void storeDigestCBlockSwapOut(StoreEntry * e);
d6fd3381 80static int storeDigestCalcCap(void);
81static int storeDigestResize(void);
82static void storeDigestAdd(const StoreEntry *);
12784378 83
d6fd3381 84#endif /* USE_CACHE_DIGESTS */
85
5f5e883f
FC
86static void
87storeDigestRegisterWithCacheManager(void)
88{
8822ebee 89 Mgr::RegisterAction("store_digest", "Store Digest", storeDigestReport, 0, 1);
5f5e883f
FC
90}
91
d6fd3381 92/*
93 * PUBLIC FUNCTIONS
94 */
12784378 95
8638fc66 96void
d6fd3381 97storeDigestInit(void)
8638fc66 98{
d120ed12
FC
99 storeDigestRegisterWithCacheManager();
100
6cfa8966 101#if USE_CACHE_DIGESTS
304b267e 102 const int cap = storeDigestCalcCap();
7e3ce7b9 103
104 if (!Config.onoff.digest_generation) {
62e76326 105 store_digest = NULL;
bf8fe701 106 debugs(71, 3, "Local cache digest generation disabled");
62e76326 107 return;
7e3ce7b9 108 }
62e76326 109
7e3ce7b9 110 store_digest = cacheDigestCreate(cap, Config.digest.bits_per_entry);
e0236918 111 debugs(71, DBG_IMPORTANT, "Local cache digest enabled; rebuild/rewrite every " <<
bf8fe701 112 (int) Config.digest.rebuild_period << "/" <<
113 (int) Config.digest.rewrite_period << " sec");
114
12784378 115 memset(&sd_state, 0, sizeof(sd_state));
d6fd3381 116#else
117 store_digest = NULL;
bf8fe701 118 debugs(71, 3, "Local cache digest is 'off'");
d6fd3381 119#endif
8638fc66 120}
121
6168bccd 122/* called when store_rebuild completes */
8638fc66 123void
d6fd3381 124storeDigestNoteStoreReady(void)
12784378 125{
d6fd3381 126#if USE_CACHE_DIGESTS
62e76326 127
7e3ce7b9 128 if (Config.onoff.digest_generation) {
62e76326 129 storeDigestRebuildStart(NULL);
130 storeDigestRewriteStart(NULL);
7e3ce7b9 131 }
62e76326 132
d6fd3381 133#endif
134}
135
35a28a37 136//TODO: this seems to be dead code. Is it needed?
d6fd3381 137void
138storeDigestDel(const StoreEntry * entry)
139{
140#if USE_CACHE_DIGESTS
62e76326 141
7e3ce7b9 142 if (!Config.onoff.digest_generation) {
62e76326 143 return;
7e3ce7b9 144 }
62e76326 145
d6fd3381 146 assert(entry && store_digest);
bf8fe701 147 debugs(71, 6, "storeDigestDel: checking entry, key: " << entry->getMD5Text());
62e76326 148
d46a87a8 149 if (!EBIT_TEST(entry->flags, KEY_PRIVATE)) {
62e76326 150 if (!cacheDigestTest(store_digest, (const cache_key *)entry->key)) {
5db6bf73 151 ++sd_stats.del_lost_count;
bf8fe701 152 debugs(71, 6, "storeDigestDel: lost entry, key: " << entry->getMD5Text() << " url: " << entry->url() );
62e76326 153 } else {
5db6bf73 154 ++sd_stats.del_count;
62e76326 155 cacheDigestDel(store_digest, (const cache_key *)entry->key);
bf8fe701 156 debugs(71, 6, "storeDigestDel: deled entry, key: " << entry->getMD5Text());
62e76326 157 }
d6fd3381 158 }
35a28a37 159#endif //USE_CACHE_DIGESTS
12784378 160}
161
12784378 162void
d6fd3381 163storeDigestReport(StoreEntry * e)
164{
165#if USE_CACHE_DIGESTS
62e76326 166
7e3ce7b9 167 if (!Config.onoff.digest_generation) {
62e76326 168 return;
7e3ce7b9 169 }
62e76326 170
d6fd3381 171 if (store_digest) {
62e76326 172 cacheDigestReport(store_digest, "store", e);
173 storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n",
174 sd_stats.add_count,
175 sd_stats.rej_count,
176 xpercent(sd_stats.rej_count, sd_stats.rej_count + sd_stats.add_count),
177 sd_stats.del_count);
178 storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n",
179 xpercent(sd_stats.add_coll_count, sd_stats.add_count),
180 xpercent(sd_stats.rej_coll_count, sd_stats.rej_count));
d6fd3381 181 } else {
62e76326 182 storeAppendPrintf(e, "store digest: disabled.\n");
d6fd3381 183 }
62e76326 184
35a28a37 185#endif //USE_CACHE_DIGESTS
d6fd3381 186}
187
188/*
189 * LOCAL FUNCTIONS
190 */
191
192#if USE_CACHE_DIGESTS
193
c68e9c6b 194/* should we digest this entry? used by storeDigestAdd() */
195static int
196storeDigestAddable(const StoreEntry * e)
197{
198 /* add some stats! XXX */
199
bf8fe701 200 debugs(71, 6, "storeDigestAddable: checking entry, key: " << e->getMD5Text());
c68e9c6b 201
3900307b 202 /* check various entry flags (mimics StoreEntry::checkCachable XXX) */
62e76326 203
c68e9c6b 204 if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
bf8fe701 205 debugs(71, 6, "storeDigestAddable: NO: private key");
62e76326 206 return 0;
c68e9c6b 207 }
62e76326 208
c68e9c6b 209 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
bf8fe701 210 debugs(71, 6, "storeDigestAddable: NO: negative cached");
62e76326 211 return 0;
c68e9c6b 212 }
62e76326 213
c68e9c6b 214 if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
bf8fe701 215 debugs(71, 6, "storeDigestAddable: NO: release requested");
62e76326 216 return 0;
c68e9c6b 217 }
62e76326 218
c68e9c6b 219 if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
bf8fe701 220 debugs(71, 6, "storeDigestAddable: NO: wrong content-length");
62e76326 221 return 0;
c68e9c6b 222 }
62e76326 223
c68e9c6b 224 /* do not digest huge objects */
47f6e231 225 if (e->swap_file_sz > (uint64_t )Config.Store.maxObjectSize) {
bf8fe701 226 debugs(71, 6, "storeDigestAddable: NO: too big");
62e76326 227 return 0;
c68e9c6b 228 }
62e76326 229
c68e9c6b 230 /* still here? check staleness */
231 /* Note: We should use the time of the next rebuild, not (cur_time+period) */
7e3ce7b9 232 if (refreshCheckDigest(e, Config.digest.rebuild_period)) {
4a7a3d56 233 debugs(71, 6, "storeDigestAdd: entry expires within " << Config.digest.rebuild_period << " secs, ignoring");
62e76326 234 return 0;
c68e9c6b 235 }
62e76326 236
17a80fc2 237 /*
238 * idea: how about also skipping very fresh (thus, potentially
239 * unstable) entries? Should be configurable through
240 * cd_refresh_pattern, of course.
241 */
242 /*
243 * idea: skip objects that are going to be purged before the next
244 * update.
245 */
c68e9c6b 246 return 1;
247}
248
d6fd3381 249static void
6168bccd 250storeDigestAdd(const StoreEntry * entry)
251{
6168bccd 252 assert(entry && store_digest);
c68e9c6b 253
254 if (storeDigestAddable(entry)) {
5db6bf73 255 ++sd_stats.add_count;
62e76326 256
257 if (cacheDigestTest(store_digest, (const cache_key *)entry->key))
5db6bf73 258 ++sd_stats.add_coll_count;
62e76326 259
260 cacheDigestAdd(store_digest, (const cache_key *)entry->key);
261
bf8fe701 262 debugs(71, 6, "storeDigestAdd: added entry, key: " << entry->getMD5Text());
6168bccd 263 } else {
5db6bf73 264 ++sd_stats.rej_count;
62e76326 265
266 if (cacheDigestTest(store_digest, (const cache_key *)entry->key))
5db6bf73 267 ++sd_stats.rej_coll_count;
6168bccd 268 }
12784378 269}
270
12784378 271/* rebuilds digest from scratch */
272static void
6168bccd 273storeDigestRebuildStart(void *datanotused)
8638fc66 274{
275 assert(store_digest);
12784378 276 /* prevent overlapping if rebuild schedule is too tight */
62e76326 277
12784378 278 if (sd_state.rebuild_lock) {
e0236918 279 debugs(71, DBG_IMPORTANT, "storeDigestRebuildStart: overlap detected, consider increasing rebuild period");
62e76326 280 return;
12784378 281 }
62e76326 282
12784378 283 sd_state.rebuild_lock = 1;
bf8fe701 284 debugs(71, 2, "storeDigestRebuildStart: rebuild #" << sd_state.rebuild_count + 1);
62e76326 285
6168bccd 286 if (sd_state.rewrite_lock) {
bf8fe701 287 debugs(71, 2, "storeDigestRebuildStart: waiting for Rewrite to finish.");
62e76326 288 return;
6168bccd 289 }
62e76326 290
6168bccd 291 storeDigestRebuildResume();
292}
293
294/* called be Rewrite to push Rebuild forward */
295static void
d6fd3381 296storeDigestRebuildResume(void)
6168bccd 297{
298 assert(sd_state.rebuild_lock);
299 assert(!sd_state.rewrite_lock);
c8f4eac4 300 sd_state.theSearch = Store::Root().search(NULL, NULL);
304b267e 301 /* resize or clear */
62e76326 302
304b267e 303 if (!storeDigestResize())
62e76326 304 cacheDigestClear(store_digest); /* not clean()! */
305
6168bccd 306 memset(&sd_stats, 0, sizeof(sd_stats));
62e76326 307
c43f5247 308 eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1);
12784378 309}
310
311/* finishes swap out sequence for the digest; schedules next rebuild */
312static void
d6fd3381 313storeDigestRebuildFinish(void)
12784378 314{
315 assert(sd_state.rebuild_lock);
316 sd_state.rebuild_lock = 0;
5db6bf73 317 ++sd_state.rebuild_count;
bf8fe701 318 debugs(71, 2, "storeDigestRebuildFinish: done.");
7e3ce7b9 319 eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, NULL, (double)
62e76326 320 Config.digest.rebuild_period, 1);
6168bccd 321 /* resume pending Rewrite if any */
62e76326 322
12784378 323 if (sd_state.rewrite_lock)
62e76326 324 storeDigestRewriteResume();
12784378 325}
326
327/* recalculate a few hash buckets per invocation; schedules next step */
328static void
329storeDigestRebuildStep(void *datanotused)
330{
c8f4eac4 331 /* TODO: call Store::Root().size() to determine this.. */
332 int count = Config.Store.objectsPerBucket * (int) ceil((double) store_hash_buckets *
333 (double) Config.digest.rebuild_chunk_percentage / 100.0);
12784378 334 assert(sd_state.rebuild_lock);
62e76326 335
bf8fe701 336 debugs(71, 3, "storeDigestRebuildStep: buckets: " << store_hash_buckets << " entries to check: " << count);
62e76326 337
c8f4eac4 338 while (count-- && !sd_state.theSearch->isDone() && sd_state.theSearch->next())
339 storeDigestAdd(sd_state.theSearch->currentItem());
62e76326 340
12784378 341 /* are we done ? */
c8f4eac4 342 if (sd_state.theSearch->isDone())
62e76326 343 storeDigestRebuildFinish();
12784378 344 else
62e76326 345 eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1);
12784378 346}
347
12784378 348/* starts swap out sequence for the digest */
349static void
6168bccd 350storeDigestRewriteStart(void *datanotused)
12784378 351{
f206b652 352 RequestFlags flags;
462f66d2 353 char *url;
6168bccd 354 StoreEntry *e;
12784378 355
356 assert(store_digest);
357 /* prevent overlapping if rewrite schedule is too tight */
62e76326 358
12784378 359 if (sd_state.rewrite_lock) {
e0236918 360 debugs(71, DBG_IMPORTANT, "storeDigestRewrite: overlap detected, consider increasing rewrite period");
62e76326 361 return;
12784378 362 }
62e76326 363
bf8fe701 364 debugs(71, 2, "storeDigestRewrite: start rewrite #" << sd_state.rewrite_count + 1);
12784378 365 /* make new store entry */
e13ee7ad 366 url = internalLocalUri("/squid-internal-periodic/", StoreDigestFileName);
e857372a 367 flags.cachable = true;
c2a7cefd 368 e = storeCreateEntry(url, url, flags, Http::METHOD_GET);
28c60158 369 assert(e);
aa839030 370 sd_state.rewrite_lock = e;
bf8fe701 371 debugs(71, 3, "storeDigestRewrite: url: " << url << " key: " << e->getMD5Text());
c21ad0f5 372 HttpRequest *req = HttpRequest::CreateFromUrl(url);
b248c2a3
AJ
373 e->mem_obj->request = req;
374 HTTPMSGLOCK(e->mem_obj->request);
6168bccd 375 /* wait for rebuild (if any) to finish */
62e76326 376
6168bccd 377 if (sd_state.rebuild_lock) {
bf8fe701 378 debugs(71, 2, "storeDigestRewriteStart: waiting for rebuild to finish.");
62e76326 379 return;
6168bccd 380 }
62e76326 381
6168bccd 382 storeDigestRewriteResume();
383}
384
385static void
d6fd3381 386storeDigestRewriteResume(void)
6168bccd 387{
28c60158 388 StoreEntry *e;
6168bccd 389
390 assert(sd_state.rewrite_lock);
391 assert(!sd_state.rebuild_lock);
aa839030 392 e = sd_state.rewrite_lock;
12784378 393 sd_state.rewrite_offset = 0;
d46a87a8 394 EBIT_SET(e->flags, ENTRY_SPECIAL);
462f66d2 395 /* setting public key will purge old digest entry if any */
d88e3c49 396 e->setPublicKey();
462f66d2 397 /* fake reply */
06a5ae20 398 HttpReply *rep = new HttpReply;
955394ce 399 rep->setHeaders(Http::scOkay, "Cache Digest OK",
11992b6f
AJ
400 "application/cache-digest", (store_digest->mask_size + sizeof(sd_state.cblock)),
401 squid_curtime, (squid_curtime + Config.digest.rewrite_period) );
26ac0430 402 debugs(71, 3, "storeDigestRewrite: entry expires on " << rep->expires <<
bf8fe701 403 " (" << std::showpos << (int) (rep->expires - squid_curtime) << ")");
3900307b 404 e->buffer();
db237875 405 e->replaceHttpReply(rep);
12784378 406 storeDigestCBlockSwapOut(e);
3900307b 407 e->flush();
aa839030 408 eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, sd_state.rewrite_lock, 0.0, 1, false);
12784378 409}
410
411/* finishes swap out sequence for the digest; schedules next rewrite */
412static void
b644367b 413storeDigestRewriteFinish(StoreEntry * e)
12784378 414{
aa839030 415 assert(e == sd_state.rewrite_lock);
528b2c61 416 e->complete();
3900307b 417 e->timestampsSet();
26ac0430 418 debugs(71, 2, "storeDigestRewriteFinish: digest expires at " << e->expires <<
bf8fe701 419 " (" << std::showpos << (int) (e->expires - squid_curtime) << ")");
6168bccd 420 /* is this the write order? @?@ */
528b2c61 421 e->mem_obj->unlinkRequest();
acc5dc4c 422 e->unlock("storeDigestRewriteFinish");
feefc1d9 423 sd_state.rewrite_lock = NULL;
5db6bf73 424 ++sd_state.rewrite_count;
7e3ce7b9 425 eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, NULL, (double)
62e76326 426 Config.digest.rewrite_period, 1);
6168bccd 427 /* resume pending Rebuild if any */
62e76326 428
6168bccd 429 if (sd_state.rebuild_lock)
62e76326 430 storeDigestRebuildResume();
12784378 431}
432
433/* swaps out one digest "chunk" per invocation; schedules next swap out */
434static void
52040193 435storeDigestSwapOutStep(void *data)
12784378 436{
aa839030 437 StoreEntry *e = static_cast<StoreEntry *>(data);
7e3ce7b9 438 int chunk_size = Config.digest.swapout_chunk_size;
aa839030 439 assert(e == sd_state.rewrite_lock);
12784378 440 assert(e);
12784378 441 /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */
62e76326 442
57d55dfa 443 if (sd_state.rewrite_offset + chunk_size > store_digest->mask_size)
62e76326 444 chunk_size = store_digest->mask_size - sd_state.rewrite_offset;
445
3900307b 446 e->append(store_digest->mask + sd_state.rewrite_offset, chunk_size);
62e76326 447
e4049756 448 debugs(71, 3, "storeDigestSwapOutStep: size: " << store_digest->mask_size <<
449 " offset: " << sd_state.rewrite_offset << " chunk: " <<
450 chunk_size << " bytes");
62e76326 451
12784378 452 sd_state.rewrite_offset += chunk_size;
62e76326 453
12784378 454 /* are we done ? */
57d55dfa 455 if (sd_state.rewrite_offset >= store_digest->mask_size)
62e76326 456 storeDigestRewriteFinish(e);
12784378 457 else
aa839030 458 eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, data, 0.0, 1, false);
12784378 459}
460
461static void
b644367b 462storeDigestCBlockSwapOut(StoreEntry * e)
12784378 463{
12784378 464 memset(&sd_state.cblock, 0, sizeof(sd_state.cblock));
462f66d2 465 sd_state.cblock.ver.current = htons(CacheDigestVer.current);
466 sd_state.cblock.ver.required = htons(CacheDigestVer.required);
467 sd_state.cblock.capacity = htonl(store_digest->capacity);
468 sd_state.cblock.count = htonl(store_digest->count);
469 sd_state.cblock.del_count = htonl(store_digest->del_count);
470 sd_state.cblock.mask_size = htonl(store_digest->mask_size);
7e3ce7b9 471 sd_state.cblock.bits_per_entry = (unsigned char)
62e76326 472 Config.digest.bits_per_entry;
6168bccd 473 sd_state.cblock.hash_func_count = (unsigned char) CacheDigestHashFuncCount;
3900307b 474 e->append((char *) &sd_state.cblock, sizeof(sd_state.cblock));
8638fc66 475}
476
304b267e 477/* calculates digest capacity */
478static int
d6fd3381 479storeDigestCalcCap(void)
304b267e 480{
481 /*
482 * To-Do: Bloom proved that the optimal filter utilization is 50% (half of
26ac0430 483 * the bits are off). However, we do not have a formula to calculate the
304b267e 484 * number of _entries_ we want to pre-allocate for.
485 */
58d5c5dd
DK
486 const int hi_cap = Store::Root().maxSize() / Config.Store.avgObjectSize;
487 const int lo_cap = 1 + Store::Root().currentSize() / Config.Store.avgObjectSize;
528b2c61 488 const int e_count = StoreEntry::inUseCount();
26ac0430 489 int cap = e_count ? e_count :hi_cap;
bf8fe701 490 debugs(71, 2, "storeDigestCalcCap: have: " << e_count << ", want " << cap <<
491 " entries; limits: [" << lo_cap << ", " << hi_cap << "]");
62e76326 492
304b267e 493 if (cap < lo_cap)
62e76326 494 cap = lo_cap;
495
6168bccd 496 /* do not enforce hi_cap limit, average-based estimation may be wrong
497 *if (cap > hi_cap)
26ac0430 498 * cap = hi_cap;
6168bccd 499 */
500 return cap;
304b267e 501}
502
503/* returns true if we actually resized the digest */
504static int
d6fd3381 505storeDigestResize(void)
304b267e 506{
507 const int cap = storeDigestCalcCap();
508 int diff;
509 assert(store_digest);
510 diff = abs(cap - store_digest->capacity);
26ac0430
AJ
511 debugs(71, 2, "storeDigestResize: " <<
512 store_digest->capacity << " -> " << cap << "; change: " <<
bf8fe701 513 diff << " (" << xpercentInt(diff, store_digest->capacity) << "%)" );
304b267e 514 /* avoid minor adjustments */
62e76326 515
4b4cd312 516 if (diff <= store_digest->capacity / 10) {
bf8fe701 517 debugs(71, 2, "storeDigestResize: small change, will not resize.");
62e76326 518 return 0;
304b267e 519 } else {
bf8fe701 520 debugs(71, 2, "storeDigestResize: big change, resizing.");
62e76326 521 cacheDigestChangeCap(store_digest, cap);
522 return 1;
304b267e 523 }
524}
525
d6fd3381 526#endif /* USE_CACHE_DIGESTS */