]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
- better synchronization between Rebuild and Rewrite
authorrousskov <>
Wed, 22 Apr 1998 22:22:58 +0000 (22:22 +0000)
committerrousskov <>
Wed, 22 Apr 1998 22:22:58 +0000 (22:22 +0000)
  now each process can wait for other to complete
- added storeDigestAdd/Del() to check object freshness
- better, but still not perfect, pre-calculation of
  cache digest size
- stats for additions, deletions, collisions, etc.
- polish

src/store_digest.cc

index b89297b266daa2c2a0fd034ef8f2a5a2159b585b..a41e3ff01b73848da3163ab10dd10db33a5571c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: store_digest.cc,v 1.8 1998/04/22 05:54:28 rousskov Exp $
+ * $Id: store_digest.cc,v 1.9 1998/04/22 16:22:58 rousskov Exp $
  *
  * DEBUG: section 71    Store Digest Manager
  * AUTHOR: Alex Rousskov
  *  
  */
 
+
+/*
+ * TODO: We probably do not track all the cases when
+ *       storeDigestNoteStoreReady() must be called; this may prevent
+ *       storeDigestRebuild/write schedule to be activated
+ */
+
 #include "squid.h"
 
 /* local types */
@@ -36,36 +43,47 @@ typedef struct {
     StoreDigestCBlock cblock;
     int rebuild_lock;          /* bucket number */
     StoreEntry *rewrite_lock;  /* store entry with the digest */
-    const char *other_lock;    /* used buy external modules to pause rebuilds and rewrites */
     int rebuild_offset;
     int rewrite_offset;
     int rebuild_count;
     int rewrite_count;
 } StoreDigestState;
 
+typedef struct {
+    int del_count;         /* #store entries deleted from store_digest */
+    int del_lost_count;    /* #store entries not found in store_digest on delete */
+    int add_count;         /* #store entries accepted to store_digest */
+    int add_coll_count;    /* #accepted entries that collided with existing ones */
+    int rej_count;         /* #store entries not accepted to store_digest */
+    int rej_coll_count;    /* #not accepted entries that collided with existing ones */
+} StoreDigestStats;
+
 /*
  * local constants (many of these are good candidates for SquidConfig
  */
 
-/* how often we want to rebuild the digest, seconds */
+/* #bits per entry in store digest */
+static const int StoreDigestBitsPerEntry = 6;
+/* how often we want to rebuild the digest, in seconds */
 static const time_t StoreDigestRebuildPeriod = 60 * 60;
-/* how often we want to rewrite the digest, seconds */
-static const time_t StoreDigestRewritePeriod = 60 * 60;
+/* how often we want to rewrite the digest after rebuild, in seconds */
+static const int StoreDigestRewritePeriod = 60 * 60;
 /* how many bytes to swap out at a time */
 static const int StoreDigestSwapOutChunkSize = SM_PAGE_SIZE;
 /* portion (0,1] of a hash table to be rescanned at a time */
 static const double StoreDigestRebuildChunkPercent = 0.10;
-/* Fudge Factor for sizing the digest */
-static const double StoreDigestFudgeFactor = 1.5;
 
 /* local vars */
 static StoreDigestState sd_state;
+static StoreDigestStats sd_stats;
 
 /* local prototypes */
-static void storeDigestRebuild(void *datanotused);
+static void storeDigestRebuildStart(void *datanotused);
+static void storeDigestRebuildResume();
 static void storeDigestRebuildFinish();
 static void storeDigestRebuildStep(void *datanotused);
-static void storeDigestRewrite();
+static void storeDigestRewriteStart();
+static void storeDigestRewriteResume();
 static void storeDigestRewriteFinish(StoreEntry * e);
 static void storeDigestSwapOutStep(StoreEntry * e);
 static void storeDigestCBlockSwapOut(StoreEntry * e);
@@ -78,9 +96,8 @@ storeDigestInit()
 {
 #if SQUID_MAINTAIN_CACHE_DIGEST
     const int cap = storeDigestCalcCap();
-    store_digest = cacheDigestCreate(cap);
-    debug(71, 1) ("Using %d byte cache digest (%d entries); rebuild/rewrite every %d/%d sec\n",
-       store_digest->mask_size, store_digest->capacity,
+    store_digest = cacheDigestCreate(cap, StoreDigestBitsPerEntry);
+    debug(71, 1) ("Local cache digest enabled; rebuild/rewrite every %d/%d sec\n",
        StoreDigestRebuildPeriod, StoreDigestRewritePeriod);
 #else
     store_digest = NULL;
@@ -91,57 +108,99 @@ storeDigestInit()
        storeDigestReport, 0);
 }
 
+/* called when store_rebuild completes */
 void
-storeDigestScheduleRebuild()
+storeDigestNoteStoreReady()
 {
-    eventAdd("storeDigestRebuild", storeDigestRebuild, NULL, StoreDigestRebuildPeriod);
+    storeDigestRebuildStart(NULL);
+    storeDigestRewriteStart(NULL);
 }
 
-/* externally initiated rewrite (inits store entry and pauses) */
 void
-storeDigestRewriteStart() {
-    eventAdd("storeDigestRewrite", storeDigestRewrite, NULL, 0);
-}
-
-#if OLD_CODE
-/* externally initiated rewrite (inits store entry and pauses) */
-void
-storeDigestRewriteStart(const char *initiator) {
-    assert(initiator);
-    assert(!sd_state.other_lock);
-    sd_state.other_lock = initiator;
-    storeDigestRewrite(NULL);
+storeDigestAdd(const StoreEntry * entry)
+{
+    int good_entry = 0;
+    assert(entry && store_digest);
+    debug(71,6) ("storeDigestAdd: checking entry, key: %s\n",
+       storeKeyText(entry->key));
+    /* only public entries are digested */
+    if (!EBIT_TEST(entry->flag, KEY_PRIVATE)) {
+       const time_t expires = refreshWhen(entry);
+       debug(71,6) ("storeDigestAdd: entry expires in %d secs\n",
+               (int)(expires - squid_curtime));
+       /* if expires too soon, ignore */
+       /* Note: We should use the time of the next rebuild, not cur_time @?@ */
+       if (expires <= squid_curtime + StoreDigestRebuildPeriod) {
+           debug(71,6) ("storeDigestAdd: entry expires too early, ignoring\n");
+       } else {
+           good_entry = 1;
+       }
+    }
+    if (good_entry) {
+       sd_stats.add_count++;
+       if (cacheDigestTest(store_digest, entry->key))
+           sd_stats.add_coll_count++;
+       cacheDigestAdd(store_digest, entry->key);
+       debug(71,6) ("storeDigestAdd: added entry, key: %s\n",
+           storeKeyText(entry->key));
+    } else {
+       sd_stats.rej_count++;
+       if (cacheDigestTest(store_digest, entry->key))
+           sd_stats.rej_coll_count++;
+    }
 }
 
-/* continue externally initiated rewrite */
 void
-storeDigestRewriteContinue(const char *initiator)
+storeDigestDel(const StoreEntry * entry)
 {
-    assert(initiator);
-    assert(!strcmp(sd_state.other_lock, initiator));
-    assert(sd_state.rewrite_lock);
-    sd_state.other_lock = NULL;
-    storeDigestSwapOutStep(sd_state.rewrite_lock);
+    assert(entry && store_digest);
+    debug(71,6) ("storeDigestDel: checking entry, key: %s\n",
+       storeKeyText(entry->key));
+    if (!EBIT_TEST(entry->flag, KEY_PRIVATE)) {
+       if (!cacheDigestTest(store_digest, entry->key)) {
+           sd_stats.del_lost_count++;
+           debug(71,6) ("storeDigestDel: lost entry, key: %s url: %s\n",
+               storeKeyText(entry->key), storeUrl(entry));
+       } else {
+           sd_stats.del_count++;
+           cacheDigestDel(store_digest, entry->key);
+           debug(71,6) ("storeDigestDel: deled entry, key: %s\n",
+               storeKeyText(entry->key));
+       }
+    }
 }
-#endif /* OLD_CODE */
 
 /* rebuilds digest from scratch */
 static void
-storeDigestRebuild(void *datanotused)
+storeDigestRebuildStart(void *datanotused)
 {
     assert(store_digest);
     /* prevent overlapping if rebuild schedule is too tight */
     if (sd_state.rebuild_lock) {
-       debug(71, 1) ("storeDigestRebuild: overlap detected, consider increasing rebuild period\n");
+       debug(71, 1) ("storeDigestRebuildStart: overlap detected, consider increasing rebuild period\n");
        return;
     }
     sd_state.rebuild_lock = 1;
+    debug(71, 2) ("storeDigestRebuildStart: rebuild #%d\n", sd_state.rebuild_count + 1);
+    if (sd_state.rewrite_lock) {
+       debug(71, 2) ("storeDigestRebuildStart: waiting for Rewrite to finish.\n");
+       return;
+    }
+    storeDigestRebuildResume();
+}
+
+/* called be Rewrite to push Rebuild forward */
+static void
+storeDigestRebuildResume()
+{
+    assert(sd_state.rebuild_lock);
+    assert(!sd_state.rewrite_lock);
     sd_state.rebuild_offset = 0;
     /* resize or clear */
     if (!storeDigestResize())
        cacheDigestClear(store_digest); /* not clean()! */
-    debug(71, 2) ("storeDigestRebuild: start rebuild #%d\n", sd_state.rebuild_count + 1);
-    storeDigestRebuildStep(NULL);
+    memset(&sd_stats, 0, sizeof(sd_stats));
+    eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0);
 }
 
 /* finishes swap out sequence for the digest; schedules next rebuild */
@@ -152,10 +211,10 @@ storeDigestRebuildFinish()
     sd_state.rebuild_lock = 0;
     sd_state.rebuild_count++;
     debug(71, 2) ("storeDigestRebuildFinish: done.\n");
-    storeDigestScheduleRebuild();
-    /* resume pending write if any */
+    eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, NULL, StoreDigestRebuildPeriod);
+    /* resume pending Rewrite if any */
     if (sd_state.rewrite_lock)
-       storeDigestSwapOutStep(sd_state.rewrite_lock);
+       storeDigestRewriteResume();
 }
 
 /* recalculate a few hash buckets per invocation; schedules next step */
@@ -171,9 +230,7 @@ storeDigestRebuildStep(void *datanotused)
     while (bcount--) {
        hash_link *link_ptr = hash_get_bucket(store_table, sd_state.rebuild_offset);
        for (; link_ptr; link_ptr = link_ptr->next) {
-           StoreEntry *e = (StoreEntry *) link_ptr;
-           if (!EBIT_TEST(e->flag, KEY_PRIVATE))
-               cacheDigestAdd(store_digest, e->key);
+           storeDigestAdd((StoreEntry *) link_ptr);
        }
        sd_state.rebuild_offset++;
     }
@@ -187,11 +244,11 @@ storeDigestRebuildStep(void *datanotused)
 
 /* starts swap out sequence for the digest */
 static void
-storeDigestRewrite(void *datanotused)
+storeDigestRewriteStart(void *datanotused)
 {
     int flags;
-    StoreEntry *e;
     char *url;
+    StoreEntry *e;
 
     assert(store_digest);
     /* prevent overlapping if rewrite schedule is too tight */
@@ -204,31 +261,40 @@ storeDigestRewrite(void *datanotused)
     url = urlInternal("", StoreDigestUrlPath);
     flags = 0;
     EBIT_SET(flags, REQ_CACHABLE);
-    e = storeCreateEntry(url, url, flags, METHOD_GET);
-    assert(e);
-    sd_state.rewrite_lock = e;
+    sd_state.rewrite_lock = e = storeCreateEntry(url, url, flags, METHOD_GET);
+    assert(sd_state.rewrite_lock);
+    debug(71, 3) ("storeDigestRewrite: url: %s key: %s\n", url, storeKeyText(e->key));
+    e->mem_obj->request = requestLink(urlParse(METHOD_GET, url));
+    /* wait for rebuild (if any) to finish */
+    if (sd_state.rebuild_lock) {
+       debug(71, 2) ("storeDigestRewriteStart: waiting for rebuild to finish.\n");
+       return;
+    }
+    storeDigestRewriteResume();
+}
+
+static void
+storeDigestRewriteResume()
+{
+    StoreEntry *e = sd_state.rewrite_lock;
+
+    assert(sd_state.rewrite_lock);
+    assert(!sd_state.rebuild_lock);
     sd_state.rewrite_offset = 0;
     EBIT_SET(e->flag, ENTRY_SPECIAL);
     /* setting public key will purge old digest entry if any */
     storeSetPublicKey(e);
-    debug(71, 3) ("storeDigestRewrite: url: %s key: %s\n", url, storeKeyText(e->key));
-    /* we never unlink it! @?@ @?@ */
-    e->mem_obj->request = requestLink(urlParse(METHOD_GET, url));
     /* fake reply */
     httpReplyReset(e->mem_obj->reply);
     httpReplySetHeaders(e->mem_obj->reply, 1.0, 200, "Cache Digest OK",
        "application/cache-digest", store_digest->mask_size + sizeof(sd_state.cblock),
        squid_curtime, squid_curtime + StoreDigestRewritePeriod);
-    debug(71, 3) ("storeDigestRewrite: reply.expires = %s\n", mkrfc1123(e->mem_obj->reply->expires));
+    debug(71, 3) ("storeDigestRewrite: entry expires on %s\n", mkrfc1123(e->mem_obj->reply->expires));
     storeBuffer(e);
     httpReplySwapOut(e->mem_obj->reply, e);
     storeDigestCBlockSwapOut(e);
     storeBufferFlush(e);
-    if (sd_state.other_lock) {
-       debug(71, 2) ("storeDigestRewrite: waiting for %s to finish.\n", sd_state.other_lock);
-       return;
-    }
-    storeDigestSwapOutStep(e);
+    eventAdd("storeDigestSwapOutStep", (EVH *) storeDigestSwapOutStep, sd_state.rewrite_lock, 0);
 }
 
 /* finishes swap out sequence for the digest; schedules next rewrite */
@@ -240,10 +306,16 @@ storeDigestRewriteFinish(StoreEntry * e)
     storeTimestampsSet(e);
     debug(71, 2) ("storeDigestRewriteFinish: digest expires on %s (%d)\n",
        mkrfc1123(e->expires), e->expires);
+    /* is this the write order? @?@ */
+    requestUnlink(e->mem_obj->request);
+    e->mem_obj->request = NULL;
     storeUnlockObject(e);
     sd_state.rewrite_lock = e = NULL;
     sd_state.rewrite_count++;
-    eventAdd("storeDigestRewrite", storeDigestRewrite, NULL, StoreDigestRewritePeriod);
+    eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, NULL, StoreDigestRewritePeriod);
+    /* resume pending Rebuild if any */
+    if (sd_state.rebuild_lock)
+       storeDigestRebuildResume();
 }
 
 /* swaps out one digest "chunk" per invocation; schedules next swap out */
@@ -252,13 +324,7 @@ storeDigestSwapOutStep(StoreEntry * e)
 {
     int chunk_size = StoreDigestSwapOutChunkSize;
     assert(e);
-    assert(!sd_state.other_lock);
     assert(e == sd_state.rewrite_lock);
-    /* wait for rebuild (if any) to finish @?@ */
-    if (sd_state.rebuild_lock) {
-       debug(71, 2) ("storeDigestRewrite: waiting for rebuild to finish.\n");
-       return;
-    }
     /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */
     if (sd_state.rewrite_offset + chunk_size > store_digest->mask_size)
        chunk_size = store_digest->mask_size - sd_state.rewrite_offset;
@@ -283,6 +349,8 @@ storeDigestCBlockSwapOut(StoreEntry * e)
     sd_state.cblock.count = htonl(store_digest->count);
     sd_state.cblock.del_count = htonl(store_digest->del_count);
     sd_state.cblock.mask_size = htonl(store_digest->mask_size);
+    sd_state.cblock.bits_per_entry = (unsigned char) StoreDigestBitsPerEntry;
+    sd_state.cblock.hash_func_count = (unsigned char) CacheDigestHashFuncCount;
     storeAppend(e, (char*) &sd_state.cblock, sizeof(sd_state.cblock));
 }
 
@@ -295,15 +363,19 @@ storeDigestCalcCap()
      * the bits are off). However, we do not have a formula to calculate the 
      * number of _entries_ we want to pre-allocate for.
      */
-    const int hi_cap = store_table->size;
-    const int lo_cap = hi_cap/5;
-    int cap = (int) (1.5 * memInUse(MEM_STOREENTRY));
+    const int hi_cap = Config.Swap.maxSize / Config.Store.avgObjectSize;
+    const int lo_cap = 1 + store_swap_size / Config.Store.avgObjectSize;
+    const int e_count = memInUse(MEM_STOREENTRY);
+    int cap = e_count ? e_count : hi_cap;
+    debug(71, 2) ("storeDigestCalcCap: have: %d, want %d entries; limits: [%d, %d]\n",
+       e_count, cap, lo_cap, hi_cap);
     if (cap < lo_cap)
        cap = lo_cap;
-    if (cap > hi_cap)
-       cap = hi_cap;
-    cap = 1 + (int) (StoreDigestFudgeFactor * cap);
-    return hi_cap;
+    /* do not enforce hi_cap limit, average-based estimation may be wrong
+     *if (cap > hi_cap)
+     * cap = hi_cap; 
+     */
+    return cap;
 }
 
 /* returns true if we actually resized the digest */
@@ -319,10 +391,10 @@ storeDigestResize()
        xpercentInt(diff, store_digest->capacity));
     /* avoid minor adjustments */
     if (diff <= store_digest->capacity/10) {
-       debug(71, 1) ("storeDigestResize: small change, will not resize.\n");
-       return 0; /* at most 10% change */
+       debug(71, 2) ("storeDigestResize: small change, will not resize.\n");
+       return 0;
     } else {
-       debug(71, 1) ("storeDigestResize: big change, resizing.\n");
+       debug(71, 2) ("storeDigestResize: big change, resizing.\n");
        cacheDigestChangeCap(store_digest, cap);
        return 1;
     }
@@ -333,6 +405,14 @@ storeDigestReport(StoreEntry * e)
 {
     if (store_digest) {
        cacheDigestReport(store_digest, "store", e);
+       storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n",
+           sd_stats.add_count, 
+           sd_stats.rej_count,
+               xpercent(sd_stats.rej_count, sd_stats.rej_count + sd_stats.add_count),
+           sd_stats.del_count);
+       storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n",
+           xpercent(sd_stats.add_coll_count, sd_stats.add_count), 
+           xpercent(sd_stats.rej_coll_count, sd_stats.rej_count));
     } else {
        storeAppendPrintf(e, "store digest: disabled.\n");
     }