store_digest.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:16k
源码类别:

代理服务器

开发平台:

Unix_Linux

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