]>
Commit | Line | Data |
---|---|---|
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 | 50 | typedef 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 | 60 | typedef 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 | 74 | static const int StoreDigestBitsPerEntry = 5; |
6168bccd | 75 | /* how often we want to rebuild the digest, in seconds */ |
ef4317c8 | 76 | static const time_t StoreDigestRebuildPeriod = 60 * 60; |
6168bccd | 77 | /* how often we want to rewrite the digest after rebuild, in seconds */ |
78 | static const int StoreDigestRewritePeriod = 60 * 60; | |
12784378 | 79 | /* how many bytes to swap out at a time */ |
80 | static const int StoreDigestSwapOutChunkSize = SM_PAGE_SIZE; | |
81 | /* portion (0,1] of a hash table to be rescanned at a time */ | |
82 | static const double StoreDigestRebuildChunkPercent = 0.10; | |
12784378 | 83 | |
462f66d2 | 84 | /* local vars */ |
12784378 | 85 | static StoreDigestState sd_state; |
6168bccd | 86 | static StoreDigestStats sd_stats; |
12784378 | 87 | |
88 | /* local prototypes */ | |
6168bccd | 89 | static void storeDigestRebuildStart(void *datanotused); |
d6fd3381 | 90 | static void storeDigestRebuildResume(void); |
91 | static void storeDigestRebuildFinish(void); | |
12784378 | 92 | static void storeDigestRebuildStep(void *datanotused); |
d6fd3381 | 93 | static void storeDigestRewriteStart(void *); |
94 | static void storeDigestRewriteResume(void); | |
b644367b | 95 | static void storeDigestRewriteFinish(StoreEntry * e); |
d6f51e3c | 96 | static EVH storeDigestSwapOutStep; |
b644367b | 97 | static void storeDigestCBlockSwapOut(StoreEntry * e); |
d6fd3381 | 98 | static int storeDigestCalcCap(void); |
99 | static int storeDigestResize(void); | |
100 | static void storeDigestAdd(const StoreEntry *); | |
12784378 | 101 | |
d6fd3381 | 102 | #endif /* USE_CACHE_DIGESTS */ |
103 | ||
104 | /* | |
105 | * PUBLIC FUNCTIONS | |
106 | */ | |
12784378 | 107 | |
8638fc66 | 108 | void |
d6fd3381 | 109 | storeDigestInit(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 | 126 | void |
d6fd3381 | 127 | storeDigestNoteStoreReady(void) |
12784378 | 128 | { |
d6fd3381 | 129 | #if USE_CACHE_DIGESTS |
6168bccd | 130 | storeDigestRebuildStart(NULL); |
131 | storeDigestRewriteStart(NULL); | |
d6fd3381 | 132 | #endif |
133 | } | |
134 | ||
135 | void | |
136 | storeDigestDel(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 | 157 | void |
d6fd3381 | 158 | storeDigestReport(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() */ |
184 | static int | |
185 | storeDigestAddable(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 | 241 | static void |
6168bccd | 242 | storeDigestAdd(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 */ |
261 | static void | |
6168bccd | 262 | storeDigestRebuildStart(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 */ | |
280 | static void | |
d6fd3381 | 281 | storeDigestRebuildResume(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 */ | |
294 | static void | |
d6fd3381 | 295 | storeDigestRebuildFinish(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 */ | |
308 | static void | |
309 | storeDigestRebuildStep(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 */ | |
333 | static void | |
6168bccd | 334 | storeDigestRewriteStart(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 | ||
364 | static void | |
d6fd3381 | 365 | storeDigestRewriteResume(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 */ | |
390 | static void | |
b644367b | 391 | storeDigestRewriteFinish(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 */ | |
416 | static void | |
52040193 | 417 | storeDigestSwapOutStep(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 | ||
437 | static void | |
b644367b | 438 | storeDigestCBlockSwapOut(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 */ |
453 | static int | |
d6fd3381 | 454 | storeDigestCalcCap(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 */ | |
477 | static int | |
d6fd3381 | 478 | storeDigestResize(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 */ |