]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Improve statistic reporting for shared Rock caches.
authorDmitry Kurochkin <dmitry.kurochkin@measurement-factory.com>
Mon, 18 Apr 2011 10:28:07 +0000 (14:28 +0400)
committerDmitry Kurochkin <dmitry.kurochkin@measurement-factory.com>
Mon, 18 Apr 2011 10:28:07 +0000 (14:28 +0400)
Rock cache is shared between multiple processes.  We need to make sure
cache related stats are not counted twice by different processes.  The
patch makes Rock store statistics to be reported by the disker process
only.

Some global variables for cache related stats are replaced with Store
class methods.  This is needed because it may be difficult or
impossible to correctly update these variables for shared caches.

The patch also fixes cache manager output for some requests like
mgr:storedir: Before the change stats from disker processes were not
surrounded with "by kidN".

19 files changed:
src/MemStore.cc
src/MemStore.h
src/Store.h
src/StoreHashIndex.h
src/SwapDir.cc
src/SwapDir.h
src/fs/coss/store_dir_coss.cc
src/fs/rock/RockSwapDir.cc
src/fs/rock/RockSwapDir.h
src/fs/ufs/ufscommon.cc
src/globals.h
src/mgr/FunAction.cc
src/mgr/InfoAction.cc
src/snmp_agent.cc
src/stat.cc
src/store.cc
src/store_digest.cc
src/store_dir.cc
src/store_rebuild.cc

index d90364f7fdd5de53978fef67c4a7748767cfd861..517a495143db7bd9ec7895b040c8902b0533e561 100644 (file)
@@ -29,7 +29,7 @@ MemStore::Init()
     delete map; // we just wanted to initialize shared memory segments
 }
 
-MemStore::MemStore(): map(NULL)
+MemStore::MemStore(): map(NULL), cur_size(0)
 {
 }
 
@@ -59,9 +59,8 @@ MemStore::stat(StoreEntry &e) const
         const int limit = map->entryLimit();
         storeAppendPrintf(&e, "Maximum entries: %9d\n", limit);
         if (limit > 0) {
-            const int entryCount = map->entryCount();
-            storeAppendPrintf(&e, "Current entries: %9d %.2f%%\n",
-                entryCount, (100.0 * entryCount / limit));
+            storeAppendPrintf(&e, "Current entries: %"PRId64" %.2f%%\n",
+                currentCount(), (100.0 * currentCount() / limit));
 
             if (limit < 100) { // XXX: otherwise too expensive to count
                 Ipc::ReadWriteLockStats stats;
@@ -89,6 +88,18 @@ MemStore::maxSize() const
     return 0; // XXX: make configurable
 }
 
+uint64_t
+MemStore::currentSize() const
+{
+    return cur_size >> 10;
+}
+
+uint64_t
+MemStore::currentCount() const
+{
+    return map ? map->entryCount() : 0;
+}
+
 void
 MemStore::updateSize(int64_t eSize, int sign)
 {
@@ -300,6 +311,7 @@ MemStore::copyToShm(StoreEntry &e, MemStoreMap::Extras &extras)
     debugs(20, 7, HERE << "mem-cached all " << eSize << " bytes of " << e <<
            " in " << page);
 
+    cur_size += eSize;
     // remember storage location and size
     extras.page = page;
     extras.storedSize = copied;
@@ -310,6 +322,7 @@ void
 MemStore::cleanReadable(const sfileno fileno)
 {
     Ipc::Mem::PutPage(map->extras(fileno).page);
+    cur_size -= map->extras(fileno).storedSize;
 }
 
 /// calculates maximum number of entries we need to store and map
index 7709b7019d200d623342a2205b39e48ceb8bbfdf..b628e25898a9a85cd02d897b6ec792737c2bcc36 100644 (file)
@@ -25,6 +25,8 @@ public:
     virtual void init();
     virtual uint64_t maxSize() const;
     virtual uint64_t minSize() const;
+    virtual uint64_t currentSize() const;
+    virtual uint64_t currentCount() const;
     virtual void stat(StoreEntry &) const;
     virtual StoreSearch *search(String const url, HttpRequest *);
     virtual void reference(StoreEntry &);
@@ -49,6 +51,7 @@ protected:
 
 private:
     MemStoreMap *map; ///< index of mem-cached entries
+    uint64_t cur_size; ///< currently used space in the storage area
 };
 
 // Why use Store as a base? MemStore and SwapDir are both "caches".
index 2b2cc54d1619210931546a740a45c8132db468af..d12d1ffccfd0b2c3a6d5e2c6a45c16caac4bada4 100644 (file)
@@ -300,6 +300,12 @@ public:
     /** The minimum size the store will shrink to via normal housekeeping */
     virtual uint64_t minSize() const = 0;
 
+    /** current store size in kiloBytes */
+    virtual uint64_t currentSize() const = 0; // TODO: return size in bytes
+
+    /** the total number of objects stored */
+    virtual uint64_t currentCount() const = 0;
+
     /**
      * Output stats to the provided store entry.
      \todo make these calls asynchronous
index 655d846eec29feddab541ca7282a66bfa4b2251b..8ed0c009919d849a22499addd331305daba865d9 100644 (file)
@@ -67,6 +67,10 @@ public:
 
     virtual uint64_t minSize() const;
 
+    virtual uint64_t currentSize() const;
+
+    virtual uint64_t currentCount() const;
+
     virtual void stat(StoreEntry&) const;
 
     virtual void reference(StoreEntry&);
index 353e452c0941c3dd822dbe5c159f6736d9386bc5..9460f7a6a138647eb6d44321b6f6f0790c7ba04b 100644 (file)
@@ -39,7 +39,7 @@
 #include "ConfigOption.h"
 
 SwapDir::SwapDir(char const *aType): theType(aType),
-        cur_size (0), max_size(0),
+        cur_size (0), max_size(0), n_disk_objects(0),
         path(NULL), index(-1), min_objsize(0), max_objsize (-1),
         repl(NULL), removals(0), scanned(0),
         cleanLog(NULL)
@@ -71,6 +71,9 @@ SwapDir::unlink(StoreEntry &) {}
 void
 SwapDir::stat(StoreEntry &output) const
 {
+    if (!doReportStat())
+        return;
+
     storeAppendPrintf(&output, "Store Directory #%d (%s): %s\n", index, type(),
                       path);
     storeAppendPrintf(&output, "FS Block Size %d Bytes\n",
index 2c11895724693ec249b6d30413474dab2521c58c..df0648f8a5c8c18cedad32fe5e785d779843d6ae 100644 (file)
@@ -69,6 +69,10 @@ public:
 
     virtual uint64_t minSize() const;
 
+    virtual uint64_t currentSize() const;
+
+    virtual uint64_t currentCount() const;
+
     virtual void stat(StoreEntry &) const;
 
     virtual void sync();       /* Sync the store prior to shutdown */
@@ -124,6 +128,8 @@ public:
 
     virtual bool needsDiskStrand() const; ///< needs a dedicated kid process
     virtual bool active() const; ///< may be used in this strand
+    /// whether stat should be reported by this SwapDir
+    virtual bool doReportStat() const { return active(); }
 
     /* official Store interface functions */
     virtual void diskFull();
@@ -135,6 +141,11 @@ public:
     virtual uint64_t maxSize() const { return max_size;}
 
     virtual uint64_t minSize() const;
+
+    virtual uint64_t currentSize() const { return cur_size; }
+
+    virtual uint64_t currentCount() const { return n_disk_objects; }
+
     virtual void stat (StoreEntry &anEntry) const;
     virtual StoreSearch *search(String const url, HttpRequest *) = 0;
 
@@ -159,8 +170,10 @@ private:
     char const *theType;
 
 public:
-    uint64_t cur_size;        ///< currently used space in the storage area
-    uint64_t max_size;        ///< maximum allocatable size of the storage area
+    // TODO: store cur_size and max_size in bytes
+    uint64_t cur_size;        ///< currently used space in the storage area in kiloBytes
+    uint64_t max_size;        ///< maximum allocatable size of the storage area in kiloBytes
+    uint64_t n_disk_objects;  ///< total number of objects stored
     char *path;
     int index;                 /* This entry's index into the swapDirs array */
     int64_t min_objsize;
index 6f033ba7596981bfb14eb70d56f179d22e29bd79..cd5b99e10e9bcfc4e649b5144b1b4f6e8543008f 100644 (file)
@@ -427,9 +427,8 @@ storeCossRebuildFromSwapLog(void *data)
                 /*
                  * Make sure we don't unlink the file, it might be
                  * in use by a subsequent entry.  Also note that
-                 * we don't have to subtract from store_swap_size
-                 * because adding to store_swap_size happens in
-                 * the cleanup procedure.
+                 * we don't have to subtract from cur_size because
+                 * adding to cur_size happens in the cleanup procedure.
                  */
                 e->expireNow();
                 e->releaseRequest();
@@ -481,7 +480,6 @@ storeCossRebuildFromSwapLog(void *data)
             continue;
         }
 
-        /* update store_swap_size */
         rb->counts.objcount++;
 
         e = storeCossAddDiskRestore(rb->sd, s.key,
index 49cef04cb4c2f8c7580cae589da2e8ca2e49988e..57ed5ab684c119a6a3f8ec860b2f0538be5e5d3f 100644 (file)
@@ -95,6 +95,26 @@ void Rock::SwapDir::disconnect(StoreEntry &e)
     e.swap_status = SWAPOUT_NONE;
 }
 
+uint64_t
+Rock::SwapDir::currentSize() const
+{
+    return (HeaderSize + max_objsize * currentCount()) >> 10;
+}
+
+uint64_t
+Rock::SwapDir::currentCount() const
+{
+    return map ? map->entryCount() : 0;
+}
+
+/// In SMP mode only the disker process reports stats to avoid
+/// counting the same stats by multiple processes.
+bool
+Rock::SwapDir::doReportStat() const
+{
+    return ::SwapDir::doReportStat() && (!UsingSmp() || IamDiskProcess());
+}
+
 // TODO: encapsulate as a tool; identical to CossSwapDir::create()
 void
 Rock::SwapDir::create()
@@ -284,9 +304,6 @@ Rock::SwapDir::validateOptions()
         debugs(47, 0, "WARNING: Rock store config wastes space.");
        }
     */
-
-    // XXX: misplaced, map is not yet created
-    //cur_size = (HeaderSize + max_objsize * map->entryCount()) >> 10;
 }
 
 void
@@ -463,8 +480,6 @@ Rock::SwapDir::ioCompletedNotification()
     if (!map)
         map = new DirMap(path);
 
-    cur_size = (HeaderSize + max_objsize * map->entryCount()) >> 10;
-
     // TODO: lower debugging level
     debugs(47,1, "Rock cache_dir[" << index << "] limits: " << 
         std::setw(12) << maximumSize() << " disk bytes and " <<
@@ -516,8 +531,6 @@ Rock::SwapDir::writeCompleted(int errflag, size_t rlen, RefCount< ::WriteRequest
         map->free(sio.swap_filen); // will mark as unusable, just in case
     }
 
-    // TODO: always compute cur_size based on map, do not store it
-    cur_size = (HeaderSize + max_objsize * map->entryCount()) >> 10;
     assert(sio.diskOffset + sio.offset_ <= diskOffsetLimit()); // post-factum
 
     sio.finishedWriting(errflag);
@@ -532,15 +545,7 @@ Rock::SwapDir::full() const
 void
 Rock::SwapDir::updateSize(int64_t size, int sign)
 {
-    // it is not clear what store_swap_size really is; TODO: move low-level
-       // size maintenance to individual store dir types
-    cur_size = (HeaderSize + max_objsize * map->entryCount()) >> 10;
-    store_swap_size = cur_size;
-
-    if (sign > 0)
-        ++n_disk_objects;
-    else if (sign < 0)
-        --n_disk_objects;
+    // stats are not stored but computed when needed
 }
 
 // storeSwapOutFileClosed calls this nethod on DISK_NO_SPACE_LEFT,
@@ -571,8 +576,8 @@ Rock::SwapDir::maintain()
     if (StoreController::store_dirs_rebuilding)
         return;
 
-    debugs(47,3, HERE << "cache_dir[" << index << "] state: " << 
-        map->full() << ' ' << currentSize() << " < " << diskOffsetLimit());
+    debugs(47,3, HERE << "cache_dir[" << index << "] state: " << map->full() <<
+           ' ' << (currentSize() << 10) << " < " << diskOffsetLimit());
 
     // Hopefully, we find a removable entry much sooner (TODO: use time?)
     const int maxProbed = 10000;
@@ -647,8 +652,8 @@ Rock::SwapDir::statfs(StoreEntry &e) const
 {
     storeAppendPrintf(&e, "\n");
     storeAppendPrintf(&e, "Maximum Size: %"PRIu64" KB\n", max_size);
-    storeAppendPrintf(&e, "Current Size: %"PRIu64" KB %.2f%%\n", cur_size,
-                      100.0 * cur_size / max_size);
+    storeAppendPrintf(&e, "Current Size: %"PRIu64" KB %.2f%%\n",
+                      currentSize(), 100.0 * currentSize() / max_size);
 
     if (map) {
         const int limit = map->entryLimit();
index 00ff9d2c4dda80f911ae47f76620a464d2be5842..b0c58bf269d9d31a2d23a59647c0cd1f05ac2dce 100644 (file)
@@ -26,6 +26,9 @@ public:
     virtual StoreSearch *search(String const url, HttpRequest *);
     virtual StoreEntry *get(const cache_key *key);
     virtual void disconnect(StoreEntry &e);
+    virtual uint64_t currentSize() const;
+    virtual uint64_t currentCount() const;
+    virtual bool doReportStat() const;
 
 protected:
     /* protected ::SwapDir API */
@@ -62,7 +65,6 @@ protected:
     void ignoreReferences(StoreEntry &e); ///< delete from repl policy scope
 
     // TODO: change cur_size and max_size type to stop this madness
-    int64_t currentSize() const { return static_cast<int64_t>(cur_size) << 10;}
     int64_t maximumSize() const { return static_cast<int64_t>(max_size) << 10;}
     int64_t diskOffset(int filen) const;
     int64_t diskOffsetLimit() const;
index d1a18cac30dbbff45e524a6b7902bb45dd3e85d3..ab5e5a23b9d95aaa0675a794fd3e739572aab9bc 100644 (file)
@@ -508,9 +508,8 @@ RebuildState::rebuildFromSwapLog()
                 /*
                  * Make sure we don't unlink the file, it might be
                  * in use by a subsequent entry.  Also note that
-                 * we don't have to subtract from store_swap_size
-                 * because adding to store_swap_size happens in
-                 * the cleanup procedure.
+                 * we don't have to subtract from cur_size because
+                 * adding to cur_size happens in the cleanup procedure.
                  */
                 currentEntry()->expireNow();
                 currentEntry()->releaseRequest();
@@ -647,7 +646,6 @@ RebuildState::rebuildFromSwapLog()
             (void) 0;
         }
 
-        /* update store_swap_size */
         counts.objcount++;
 
         currentEntry(sd->addDiskRestore(swapData.key,
index d2195f7a2b08f9c7a2f3c727c136833bacc03b98..a9ec17454e335bec3be6eedc04c5e51f1628b409 100644 (file)
@@ -108,7 +108,6 @@ extern "C" {
     extern char *snmp_agentinfo;
 #endif
 
-    extern int n_disk_objects; /* 0 */
     extern iostats IOStats;
 
     extern struct acl_deny_info_list *DenyInfoList;    /* NULL */
@@ -117,7 +116,6 @@ extern "C" {
     extern int starting_up;    /* 1 */
     extern int shutting_down;  /* 0 */
     extern int reconfiguring;  /* 0 */
-    extern unsigned long store_swap_size;      /* 0 */
     extern time_t hit_only_mode_until; /* 0 */
     extern StatCounters statCounter;
     extern double request_failure_ratio;       /* 0.0 */
index 6fd0f97c1a4140c747f49aff832b08f886de0501..aa9e212a768d11c946bad92659b17917959e9d81 100644 (file)
@@ -43,9 +43,9 @@ Mgr::FunAction::dump(StoreEntry* entry)
 {
     debugs(16, 5, HERE);
     Must(entry != NULL);
-    if (UsingSmp() && IamWorkerProcess())
+    if (UsingSmp())
         storeAppendPrintf(entry, "by kid%d {\n", KidIdentifier);
     handler(entry);
-    if (atomic() && UsingSmp() && IamWorkerProcess())
+    if (atomic() && UsingSmp())
         storeAppendPrintf(entry, "} by kid%d\n\n", KidIdentifier);
 }
index eb5760ba0b86236bc867a0d2a74444a2e15c35ac..905a330e1cc337b8dd1c5256313d0a2ee52b08fe 100644 (file)
@@ -175,10 +175,10 @@ Mgr::InfoAction::dump(StoreEntry* entry)
     Must(entry != NULL);
 
 #if XMALLOC_STATISTICS
-    if (UsingSmp() && IamWorkerProcess())
+    if (UsingSmp())
         storeAppendPrintf(entry, "by kid%d {\n", KidIdentifier);
     DumpMallocStatistics(entry);
-    if (UsingSmp() && IamWorkerProcess())
+    if (UsingSmp())
         storeAppendPrintf(entry, "} by kid%d\n\n", KidIdentifier);
 #endif
     if (IamPrimaryProcess())
index 17b0d463cb02d06c7137bcd4f915e9e8818d16e3..9948c45b70cafe036796f2147018affa9dd3976e 100644 (file)
@@ -67,7 +67,7 @@ snmp_sysFn(variable_list * Var, snint * ErrP)
 
     case SYSSTOR:
         Answer = snmp_var_new_integer(Var->name, Var->name_length,
-                                      store_swap_size,
+                                      Store::Root().currentSize(),
                                       ASN_INTEGER);
         break;
 
@@ -531,7 +531,7 @@ snmp_prfProtoFn(variable_list * Var, snint * ErrP)
 
         case PERF_PROTOSTAT_AGGR_CURSWAP:
             Answer = snmp_var_new_integer(Var->name, Var->name_length,
-                                          (snint) store_swap_size,
+                                          (snint) Store::Root().currentSize(),
                                           SMI_GAUGE32);
             break;
 
index a1413ee2c6e2ecec234fb001bb9c4613470d9755..6b0a88c3a798b49159dd74ee94b30f8134446894 100644 (file)
@@ -546,14 +546,16 @@ GetInfo(Mgr::InfoActionData& stats)
     stats.request_hit_disk_ratio5 = statRequestHitDiskRatio(5);
     stats.request_hit_disk_ratio60 = statRequestHitDiskRatio(60);
 
-    stats.store_swap_size = store_swap_size;
+    stats.store_swap_size = Store::Root().currentSize();
     stats.store_swap_max_size = Store::Root().maxSize();
 
     stats.store_mem_size = mem_node::StoreMemSize();
     stats.store_pages_max = store_pages_max;
     stats.store_mem_used = mem_node::InUseCount();
 
-    stats.objects_size = n_disk_objects ? (double) store_swap_size / n_disk_objects : 0.0;
+    stats.n_disk_objects = Store::Root().currentCount();
+    stats.objects_size = stats.n_disk_objects > 0 ?
+        (double)Store::Root().currentSize() / stats.n_disk_objects : 0.0;
 
     stats.unlink_requests = statCounter.unlink.requests;
 
@@ -666,7 +668,6 @@ GetInfo(Mgr::InfoActionData& stats)
     stats.store_entries = StoreEntry::inUseCount();
     stats.store_mem_entries = MemObject::inUseCount();
     stats.hot_obj_count = hot_obj_count;
-    stats.n_disk_objects = n_disk_objects;
 }
 
 void
index 6c0ca9dfb589cb2c2c9070ac4b49b0bc245df593..7d7adbcfe03c0d0ee87fe50ca9a324291836e84d 100644 (file)
@@ -1233,9 +1233,10 @@ StoreController::maintain()
 
     /* this should be emitted by the oversize dir, not globally */
 
-    if (store_swap_size > Store::Root().maxSize()) {
+    if (Store::Root().currentSize() > Store::Root().maxSize()) {
         if (squid_curtime - last_warn_time > 10) {
-            debugs(20, 0, "WARNING: Disk space over limit: " << store_swap_size << " KB > "
+            debugs(20, 0, "WARNING: Disk space over limit: "
+                   << Store::Root().currentSize() << " KB > "
                    << Store::Root().maxSize() << " KB");
             last_warn_time = squid_curtime;
         }
index 4d419d5e5c06758e25422c9870876b4cc7c48513..14649ab5f1c858fc2bc83cc459f1bfa99733ab85 100644 (file)
@@ -507,7 +507,7 @@ storeDigestCalcCap(void)
      * number of _entries_ we want to pre-allocate for.
      */
     const int hi_cap = Store::Root().maxSize() / Config.Store.avgObjectSize;
-    const int lo_cap = 1 + store_swap_size / Config.Store.avgObjectSize;
+    const int lo_cap = 1 + Store::Root().currentSize() / Config.Store.avgObjectSize;
     const int e_count = StoreEntry::inUseCount();
     int cap = e_count ? e_count :hi_cap;
     debugs(71, 2, "storeDigestCalcCap: have: " << e_count << ", want " << cap <<
index 976d6caf47e555ef9ae6968771725c44ddcf3016..4d64c018e5a8035b935eea7d438aa3580ecad2e3 100644 (file)
@@ -334,7 +334,6 @@ SwapDir::updateSize(int64_t size, int sign)
     int64_t blks = (size + fs.blksize - 1) / fs.blksize;
     int64_t k = ((blks * fs.blksize) >> 10) * sign;
     cur_size += k;
-    store_swap_size += k;
 
     if (sign > 0)
         n_disk_objects++;
@@ -351,10 +350,10 @@ StoreController::stat(StoreEntry &output) const
     storeAppendPrintf(&output, "Maximum Swap Size      : %"PRIu64" KB\n",
                       maxSize());
     storeAppendPrintf(&output, "Current Store Swap Size: %8lu KB\n",
-                      store_swap_size);
+                      currentSize());
     storeAppendPrintf(&output, "Current Capacity       : %"PRId64"%% used, %"PRId64"%% free\n",
-                      Math::int64Percent(store_swap_size, maxSize()),
-                      Math::int64Percent((maxSize() - store_swap_size), maxSize()));
+                      Math::int64Percent(currentSize(), maxSize()),
+                      Math::int64Percent((maxSize() - currentSize()), maxSize()));
 
     if (memStore)
         memStore->stat(output);
@@ -378,6 +377,18 @@ StoreController::minSize() const
     return swapDir->minSize();
 }
 
+uint64_t
+StoreController::currentSize() const
+{
+    return swapDir->currentSize();
+}
+
+uint64_t
+StoreController::currentCount() const
+{
+    return swapDir->currentCount();
+}
+
 void
 SwapDir::diskFull()
 {
@@ -900,8 +911,10 @@ StoreHashIndex::maxSize() const
 {
     uint64_t result = 0;
 
-    for (int i = 0; i < Config.cacheSwap.n_configured; i++)
-        result += store(i)->maxSize();
+    for (int i = 0; i < Config.cacheSwap.n_configured; i++) {
+        if (dir(i).doReportStat())
+            result += store(i)->maxSize();
+    }
 
     return result;
 }
@@ -911,8 +924,36 @@ StoreHashIndex::minSize() const
 {
     uint64_t result = 0;
 
-    for (int i = 0; i < Config.cacheSwap.n_configured; i++)
-        result += store(i)->minSize();
+    for (int i = 0; i < Config.cacheSwap.n_configured; i++) {
+        if (dir(i).doReportStat())
+            result += store(i)->minSize();
+    }
+
+    return result;
+}
+
+uint64_t
+StoreHashIndex::currentSize() const
+{
+    uint64_t result = 0;
+
+    for (int i = 0; i < Config.cacheSwap.n_configured; i++) {
+        if (dir(i).doReportStat())
+            result += store(i)->currentSize();
+    }
+
+    return result;
+}
+
+uint64_t
+StoreHashIndex::currentCount() const
+{
+    uint64_t result = 0;
+
+    for (int i = 0; i < Config.cacheSwap.n_configured; i++) {
+        if (dir(i).doReportStat())
+            result += store(i)->currentCount();
+    }
 
     return result;
 }
index 0ae8ef17805a68ff4c1c1bce7327091a3af45d5f..1c3edea5f6e356af4b1c94e1e886a2c43dacc2c7 100644 (file)
@@ -111,7 +111,7 @@ storeCleanup(void *datanotused)
     if (currentSearch->isDone()) {
         debugs(20, 1, "  Completed Validation Procedure");
         debugs(20, 1, "  Validated " << validated << " Entries");
-        debugs(20, 1, "  store_swap_size = " << store_swap_size);
+        debugs(20, 1, "  store_swap_size = " << Store::Root().currentSize());
         StoreController::store_dirs_rebuilding--;
         assert(0 == StoreController::store_dirs_rebuilding);