From: rousskov <> Date: Sat, 4 Apr 1998 05:05:09 +0000 (+0000) Subject: - added code to create/rebuild/rewrite store_digest X-Git-Tag: SQUID_3_0_PRE1~3640 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=127843786e59be79fba4a581c5c16d95869949e3;p=thirdparty%2Fsquid.git - added code to create/rebuild/rewrite store_digest - to disable local digest support, set squid.h::SQUID_MAINTAIN_CACHE_DIGEST to 0 - added a hack to support simple IMS requests for ENTRY_SPECIAL entries --- diff --git a/src/CacheDigest.cc b/src/CacheDigest.cc index af963beee7..5b3532abc7 100644 --- a/src/CacheDigest.cc +++ b/src/CacheDigest.cc @@ -1,6 +1,6 @@ /* - * $Id: CacheDigest.cc,v 1.7 1998/04/02 17:11:20 rousskov Exp $ + * $Id: CacheDigest.cc,v 1.8 1998/04/03 22:05:09 rousskov Exp $ * * DEBUG: section 70 Cache Digest * AUTHOR: Alex Rousskov @@ -31,14 +31,23 @@ #include "squid.h" +/* local types */ +typedef struct { + int bit_count; /* total number of bits */ + int bit_on_count; /* #bits turned on */ + int bseq_len_sum; /* sum of all bit seq length */ + int bseq_count; /* number of bit seqs */ +} CacheDigestStats; + +/* local functions */ +static void cacheDigestHashKey(int bit_count, const char *key); + +/* configuration params */ static const int BitsPerEntry = 4; /* static array used by cacheDigestHashKey for optimization purposes */ static u_num32 hashed_keys[4]; -/* local functions */ -static void cacheDigestHashKey(int bit_count, const char *key); - CacheDigest * cacheDigestCreate(int capacity) @@ -71,6 +80,14 @@ cacheDigestClone(const CacheDigest * cd) return clone; } +void +cacheDigestClear(CacheDigest * cd) +{ + assert(cd); + cd->count = cd->del_count = 0; + memset(cd->mask, 0, cd->mask_size); +} + /* returns true if the key belongs to the digest */ int cacheDigestTest(const CacheDigest * cd, const cache_key * key) @@ -109,32 +126,44 @@ cacheDigestDel(CacheDigest * cd, const cache_key * key) } /* returns mask utilization parameters */ -double -cacheDigestUtil(const CacheDigest * cd, int *bit_cnt_p, int *on_cnt_p) +static void +cacheDigestStats(const CacheDigest * cd, CacheDigestStats *stats) { const int bit_count = cd->capacity * BitsPerEntry; - int pos = bit_count; int on_count = 0; + int pos = bit_count; + int seq_len_sum = 0; + int seq_count = 0; + int cur_seq_len = 0; + int cur_seq_type = 1; + assert(stats); + memset(stats, 0, sizeof(*stats)); while (pos-- > 0) { - if (CBIT_TEST(cd->mask, pos)) + const int is_on = CBIT_TEST(cd->mask, pos); + if (is_on) on_count++; + if (is_on != cur_seq_type || !pos) { + seq_len_sum += cur_seq_len; + seq_count++; + cur_seq_type = !cur_seq_type; + cur_seq_len = 0; + } + cur_seq_len++; } - if (bit_cnt_p) - *bit_cnt_p = bit_count; - if (on_cnt_p) - *on_cnt_p = on_count; - return xpercent(on_count, bit_count); + stats->bit_count = bit_count; + stats->bit_on_count = on_count; + stats->bseq_len_sum = seq_len_sum; + stats->bseq_count = seq_count; } void cacheDigestReport(CacheDigest *cd, const char *label, StoreEntry * e) { - int bit_count, on_count; + CacheDigestStats stats; assert(cd && e); - cacheDigestUtil(cd, &bit_count, &on_count); - storeAppendPrintf(e, "%s digest: size: %d bytes.\n", - label ? label : "", - bit_count/8 + cacheDigestStats(cd, &stats); + storeAppendPrintf(e, "%s digest: size: %d bytes\n", + label ? label : "", stats.bit_count/8 ); storeAppendPrintf(e, "\t entries: count: %d capacity: %d util: %d%%\n", cd->count, @@ -145,8 +174,12 @@ cacheDigestReport(CacheDigest *cd, const char *label, StoreEntry * e) cd->del_count ); storeAppendPrintf(e, "\t bits: on: %d capacity: %d util: %d%%\n", - on_count, bit_count, - xpercentInt(on_count, bit_count) + stats.bit_on_count, stats.bit_count, + xpercentInt(stats.bit_on_count, stats.bit_count) + ); + storeAppendPrintf(e, "\t bit-seq: count: %d avg.len: %.2f\n", + stats.bseq_count, + xdiv(stats.bseq_len_sum, stats.bseq_count) ); } diff --git a/src/client_side.cc b/src/client_side.cc index 8590639cef..27244d3bc4 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.252 1998/04/02 07:37:16 wessels Exp $ + * $Id: client_side.cc,v 1.253 1998/04/03 22:05:10 rousskov Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -1257,6 +1257,11 @@ clientProcessRequest2(clientHttpRequest * http) /* this object isn't in the cache */ return LOG_TCP_MISS; } else if (EBIT_TEST(e->flag, ENTRY_SPECIAL)) { + /* ideally, special entries should be processed later, + * so we can use std processing routines for IMS and such */ + if (EBIT_TEST(r->flags, REQ_IMS) && e->lastmod <= r->ims) + return LOG_TCP_IMS_HIT; + else if (e->mem_status == IN_MEMORY) return LOG_TCP_MEM_HIT; else @@ -1355,6 +1360,7 @@ clientProcessRequest(clientHttpRequest * http) case LOG_TCP_HIT: case LOG_TCP_NEGATIVE_HIT: case LOG_TCP_MEM_HIT: + case LOG_TCP_IMS_HIT: entry->refcount++; /* HIT CASE */ if (entry->store_status == STORE_ABORTED) debug(33, 0) ("clientProcessRequest: entry->swap_status == STORE_ABORTED\n"); @@ -1370,6 +1376,7 @@ clientProcessRequest(clientHttpRequest * http) break; } /* ok, it is a miss or a "dirty" hit (will contact other servers) */ + /* note: bug: LOG_TCP_IMS_MISS may result in a "pure" hit @?@ */ /* are we allowed to contact other servers? */ if (EBIT_TEST(r->flags, REQ_CC_ONLY_IF_CACHED)) { /* future interface: if (r->cache_control && EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED)) { */ @@ -1418,6 +1425,8 @@ clientProcessMiss(clientHttpRequest * http) * or IMS request. */ if (http->entry) { + if (EBIT_TEST(http->entry->flag, ENTRY_SPECIAL)) + debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url); storeUnregister(http->entry, http); storeUnlockObject(http->entry); http->entry = NULL; diff --git a/src/main.cc b/src/main.cc index 99ff6c4451..955b3e0c6b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,6 @@ /* - * $Id: main.cc,v 1.242 1998/03/31 05:34:48 wessels Exp $ + * $Id: main.cc,v 1.243 1998/04/03 22:05:12 rousskov Exp $ * * DEBUG: section 1 Startup and Main Loop * AUTHOR: Harvest Derived @@ -497,6 +497,7 @@ mainInitialize(void) pconnInit(); eventInit(); } + serverConnectionsOpen(); if (theOutIcpConnection >= 0 && (!Config2.Accel.on || Config.onoff.accel_with_proxy)) neighbors_open(theOutIcpConnection); @@ -524,6 +525,8 @@ mainInitialize(void) eventAdd("start_announce", start_announce, NULL, 3600); eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10); eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 15); + if (store_digest) + storeDigestScheduleRebuild(); } configured_once = 1; } diff --git a/src/protos.h b/src/protos.h index 61634364bb..710fd12ab7 100644 --- a/src/protos.h +++ b/src/protos.h @@ -698,7 +698,9 @@ extern EVH storeDirClean; /* store_digest.c */ extern void storeDigestInit(); -extern void storeDigestRebuild(); +extern void storeDigestScheduleRebuild(); +extern void storeDigestRewriteStart(const char *initiator); +extern void storeDigestRewriteContinue(const char *initiator); extern void storeDigestReport(); @@ -743,12 +745,10 @@ void storeSwapTLVFree(tlv * n); * store_rebuild.c */ extern void storeDoRebuildFromSwapFiles(void *data); -extern void storeCleanup(void *datanotused); extern void storeValidate(StoreEntry *, STVLDCB *, void *, void *); extern void storeValidateComplete(void *data, int retcode, int errcode); extern void storeRebuildStart(void); - /* * store_swapin.c */ @@ -902,6 +902,7 @@ extern int ipcCreate(int type, extern CacheDigest *cacheDigestCreate(int capacity); extern void cacheDigestDestroy(CacheDigest * cd); extern CacheDigest *cacheDigestClone(const CacheDigest * cd); +extern void cacheDigestClear(CacheDigest * cd); extern int cacheDigestTest(const CacheDigest * cd, const cache_key * key); extern void cacheDigestAdd(CacheDigest * cd, const cache_key * key); extern void cacheDigestDel(CacheDigest * cd, const cache_key * key); diff --git a/src/squid.h b/src/squid.h index 7f96a5c035..829e6423e9 100644 --- a/src/squid.h +++ b/src/squid.h @@ -1,6 +1,6 @@ /* - * $Id: squid.h,v 1.166 1998/04/02 17:11:25 rousskov Exp $ + * $Id: squid.h,v 1.167 1998/04/03 22:05:14 rousskov Exp $ * * AUTHOR: Duane Wessels * @@ -360,6 +360,6 @@ extern struct snmp_mib_tree *Mib; * maintain a digest of cache contents and send the digest to neighbors upon * request; if disabled we still can request digests from other caches */ -#define SQUID_MAINTAIN_CACHE_DIGEST 1 +#define SQUID_MAINTAIN_CACHE_DIGEST 0 #endif /* SQUID_H */ diff --git a/src/store_digest.cc b/src/store_digest.cc index 68ab82ff7c..01e5ce48b9 100644 --- a/src/store_digest.cc +++ b/src/store_digest.cc @@ -1,5 +1,5 @@ /* - * $Id: store_digest.cc,v 1.1 1998/04/02 17:11:27 rousskov Exp $ + * $Id: store_digest.cc,v 1.2 1998/04/03 22:05:14 rousskov Exp $ * * DEBUG: section 71 Store Digest Manager * AUTHOR: Alex Rousskov @@ -30,6 +30,52 @@ #include "squid.h" +/* local types */ + +/* digest control block */ +typedef struct { + char reserved[128]; +} StoreDigestCBlock; + +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; + +/* + * local constants (many of these are good candidates for SquidConfig + */ + +/* fake url suffix */ +static const char *StoreDigestUrl = "cache_digest"; +/* how often we want to rebuild the digest, 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 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; +/* local vars */ + +static StoreDigestState sd_state; + +/* local prototypes */ +static void storeDigestRebuild(void *datanotused); +static void storeDigestRebuildFinish(); +static void storeDigestRebuildStep(void *datanotused); +static void storeDigestRewrite(); +static void storeDigestRewriteFinish(StoreEntry *e); +static void storeDigestSwapOutStep(StoreEntry *e); +static void storeDigestCBlockSwapOut(StoreEntry *e); + + void storeDigestInit() { @@ -39,21 +85,199 @@ storeDigestInit() * number of _entries_ we want to pre-allocate for. * Use 1.5*max#entries because 2*max#entries gives about 40% utilization. */ - const int cap = (int)(1.5 * Config.Swap.maxSize / Config.Store.avgObjectSize); #if SQUID_MAINTAIN_CACHE_DIGEST + const int cap = (int)(1.5 * Config.Swap.maxSize / Config.Store.avgObjectSize); store_digest = cacheDigestCreate(cap); + debug(71, 1) ("Using %d byte cache digest; rebuild/rewrite every %d/%d sec\n", + store_digest->mask_size, StoreDigestRebuildPeriod, StoreDigestRewritePeriod); #else store_digest = NULL; + debug(71, 1) ("Local cache digest is 'off'\n"); #endif + memset(&sd_state, 0, sizeof(sd_state)); cachemgrRegister("store_digest", "Store Digest", storeDigestReport, 0); } -/* rebuilds digest from scratch */ +/* you probably want to call this before storeDigestRewriteContinue() */ void -storeDigestRebuild() +storeDigestScheduleRebuild() +{ + eventAdd("storeDigestRebuild", storeDigestRebuild, NULL, StoreDigestRebuildPeriod); +} + +/* 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); +} + +/* continue externally initiated rewrite */ +void +storeDigestRewriteContinue(const char *initiator) { + assert(initiator); + assert(!strcmp(sd_state.other_lock, initiator)); + assert(sd_state.rewrite_lock); + sd_state.other_lock = NULL; + storeDigestSwapOutStep(sd_state.rewrite_lock); +} + +/* rebuilds digest from scratch */ +static void +storeDigestRebuild(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"); + return; + } + sd_state.rebuild_lock = 1; + sd_state.rebuild_offset = 0; + /* not clean()! */ + cacheDigestClear(store_digest); + debug(71, 2) ("storeDigestRebuild: start rebuild #%d\n", sd_state.rebuild_count+1); + storeDigestRebuildStep(NULL); +} + +/* finishes swap out sequence for the digest; schedules next rebuild */ +static void +storeDigestRebuildFinish() +{ + assert(sd_state.rebuild_lock); + sd_state.rebuild_lock = 0; + sd_state.rebuild_count++; + debug(71, 2) ("storeDigestRebuildFinish: done.\n"); + storeDigestScheduleRebuild(); + /* resume pending write if any */ + if (sd_state.rewrite_lock) + storeDigestSwapOutStep(sd_state.rewrite_lock); +} + +/* recalculate a few hash buckets per invocation; schedules next step */ +static void +storeDigestRebuildStep(void *datanotused) +{ + int bcount = (int)ceil(store_hash_buckets*StoreDigestRebuildChunkPercent); + assert(sd_state.rebuild_lock); + if (sd_state.rebuild_offset + bcount > store_hash_buckets) + bcount = store_hash_buckets - sd_state.rebuild_offset; + debug(71, 3) ("storeDigestRebuildStep: buckets: %d offset: %d chunk: %d buckets\n", + store_hash_buckets, sd_state.rebuild_offset, bcount); + 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); + } + sd_state.rebuild_offset++; + } + /* are we done ? */ + if (sd_state.rebuild_offset >= store_hash_buckets) + storeDigestRebuildFinish(); + else + eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0); +} + + +/* starts swap out sequence for the digest */ +static void +storeDigestRewrite(void *datanotused) +{ + int flags; + StoreEntry *e; + char url[MAX_URL]; + + assert(store_digest); + /* prevent overlapping if rewrite schedule is too tight */ + if (sd_state.rewrite_lock) { + debug(71, 1) ("storeDigestRewrite: overlap detected, consider increasing rewrite period\n"); + return; + } + debug(71, 2) ("storeDigestRewrite: start rewrite #%d\n", sd_state.rewrite_count+1); + /* make new store entry */ + snprintf(url, sizeof(url), "http://%s:%d/squid-internal/%s", + getMyHostname(), Config.Port.http->i, StoreDigestUrl); + flags = 0; + EBIT_SET(flags, REQ_CACHABLE); + e = storeCreateEntry(url, url, flags, METHOD_GET); + assert(e); + sd_state.rewrite_lock = e; + sd_state.rewrite_offset = 0; + EBIT_SET(e->flag, ENTRY_SPECIAL); + /* this will purge old digest entry if any */ + storeSetPublicKey(e); + e->mem_obj->request = requestLink(urlParse(METHOD_GET, url)); + 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); + 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); +} + +/* finishes swap out sequence for the digest; schedules next rewrite */ +static void +storeDigestRewriteFinish(StoreEntry *e) +{ + assert(e); + assert(e == sd_state.rewrite_lock); + storeComplete(e); + storeTimestampsSet(e); + storeUnlockObject(e); + sd_state.rewrite_lock = NULL; + sd_state.rewrite_count++; + debug(71, 2) ("storeDigestRewriteFinish: done.\n"); + eventAdd("storeDigestRewrite", storeDigestRewrite, NULL, StoreDigestRewritePeriod); +} + +/* swaps out one digest "chunk" per invocation; schedules next swap out */ +static void +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; + storeAppend(e, store_digest->mask + sd_state.rewrite_offset, chunk_size); + debug(71, 3) ("storeDigestSwapOutStep: size: %d offset: %d chunk: %d bytes\n", + store_digest->mask_size, sd_state.rewrite_offset, chunk_size); + sd_state.rewrite_offset += chunk_size; + /* are we done ? */ + if (sd_state.rewrite_offset >= store_digest->mask_size) + storeDigestRewriteFinish(e); + else + eventAdd("storeDigestSwapOutStep", (EVH*) storeDigestSwapOutStep, e, 0); +} + +static void +storeDigestCBlockSwapOut(StoreEntry *e) +{ + /* + * when we actually start using control block, do not forget to convert to + * network byte order if needed + */ + memset(&sd_state.cblock, 0, sizeof(sd_state.cblock)); + storeAppend(e, (char*) &sd_state.cblock, sizeof(sd_state.cblock)); } void diff --git a/src/store_rebuild.cc b/src/store_rebuild.cc index 695555d399..ceaf5cb1a0 100644 --- a/src/store_rebuild.cc +++ b/src/store_rebuild.cc @@ -1,5 +1,5 @@ /* - * $Id: store_rebuild.cc,v 1.27 1998/04/02 17:11:27 rousskov Exp $ + * $Id: store_rebuild.cc,v 1.28 1998/04/03 22:05:15 rousskov Exp $ * * DEBUG: section 20 Store Rebuild Routines * AUTHOR: Duane Wessels @@ -564,7 +564,7 @@ storeAddDiskRestore(const cache_key * key, return e; } -void +static void storeCleanup(void *datanotused) { static int bucketnum = -1; @@ -579,6 +579,8 @@ storeCleanup(void *datanotused) store_rebuilding = 0; if (opt_store_doublecheck) assert(store_errors == 0); + if (store_digest) + storeDigestRewriteContinue("store-rebuild"); return; } link_ptr = hash_get_bucket(store_table, bucketnum); @@ -740,6 +742,8 @@ storeRebuildStart(void) int i; memset(&RebuildState, '\0', sizeof(RebuildState)); RebuildState.start = squid_curtime; + if (store_digest) + storeDigestRewriteStart("store-rebuild"); for (i = 0; i < Config.cacheSwap.n_configured; i++) { d = xcalloc(1, sizeof(rebuild_dir)); d->dirn = i;