#include "ufscommon.h"
#include "Store.h"
#include "fde.h"
+#include "SquidTime.h"
#include "StoreMeta.h"
#include "Generic.h"
#include "StoreMetaUnpacker.h"
RebuildState::RebuildState (RefCount<UFSSwapDir> aSwapDir) : sd (aSwapDir),LogParser(NULL), e(NULL), fromLog(true), _done (false)
{
- speed = opt_foreground_rebuild ? 1 << 30 : 50;
/*
* If the swap.state file exists in the cache_dir, then
* we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll
rb->rebuildStep();
if (!rb->isDone())
- eventAdd("storeRebuild", RebuildStep, rb, 0.0, 1);
+ eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1);
else {
StoreController::store_dirs_rebuilding--;
storeRebuildComplete(&rb->counts);
}
}
+/// load entries from swap.state or files until we run out of entries or time
void
RebuildState::rebuildStep()
{
- if (fromLog)
- rebuildFromSwapLog();
- else
- rebuildFromDirectory();
+ currentEntry(NULL);
+
+ // Balance our desire to maximize the number of entries processed at once
+ // (and, hence, minimize overheads and total rebuild time) with a
+ // requirement to also process Coordinator events, disk I/Os, etc.
+ const int maxSpentMsec = 50; // keep small: most RAM I/Os are under 1ms
+ const timeval loopStart = current_time;
+
+ const int totalEntries = LogParser ? LogParser->SwapLogEntries() : -1;
+
+ while (!isDone()) {
+ if (fromLog)
+ rebuildFromSwapLog();
+ else
+ rebuildFromDirectory();
+
+ // TODO: teach storeRebuildProgress to handle totalEntries <= 0
+ if (totalEntries > 0 && (n_read % 4000 == 0))
+ storeRebuildProgress(sd->index, totalEntries, n_read);
+
+ if (opt_foreground_rebuild)
+ continue; // skip "few entries at a time" check below
+
+ getCurrentTime();
+ const double elapsedMsec = tvSubMsec(loopStart, current_time);
+ if (elapsedMsec > maxSpentMsec || elapsedMsec < 0) {
+ debugs(47, 5, HERE << "pausing after " << n_read << " entries in " <<
+ elapsedMsec << "ms; " << (elapsedMsec/n_read) << "ms per entry");
+ break;
+ }
+ }
}
+/// process one cache file
void
RebuildState::rebuildFromDirectory()
{
- currentEntry(NULL);
cache_key key[SQUID_MD5_DIGEST_LENGTH];
struct stat sb;
assert(this != NULL);
debugs(47, 3, "commonUfsDirRebuildFromDirectory: DIR #" << sd->index);
- for (int count = 0; count < speed; count++) {
assert(fd == -1);
sfileno filn = 0;
int size;
_done = true;
return;
} else if (fd < 0) {
- continue;
+ return;
}
assert(fd > -1);
file_close(fd);
store_open_disk_fd--;
fd = -1;
- continue;
+ return;
}
MemBuf buf;
if (!loaded) {
// XXX: shouldn't this be a call to commonUfsUnlink?
sd->unlinkFile(filn); // should we unlink in all failure cases?
- continue;
+ return;
}
if (!storeRebuildKeepEntry(tmpe, key, counts))
- continue;
+ return;
counts.objcount++;
// tmpe.dump(5);
tmpe.flags, /* flags */
(int) flags.clean));
storeDirSwapLog(currentEntry(), SWAP_LOG_ADD);
- }
-
}
StoreEntry *
e = newValue;
}
+/// process one swap log entry
void
RebuildState::rebuildFromSwapLog()
{
- currentEntry (NULL);
- double x;
- /* load a number of objects per invocation */
-
- for (int count = 0; count < speed; count++) {
StoreSwapLogData swapData;
if (LogParser->ReadRecord(swapData) != 1) {
n_read++;
if (swapData.op <= SWAP_LOG_NOP)
- continue;
+ return;
if (swapData.op >= SWAP_LOG_MAX)
- continue;
+ return;
/*
* BC: during 2.4 development, we changed the way swap file
counts.objcount--;
counts.cancelcount++;
}
- continue;
+ return;
} else {
+ const double
x = ::log(static_cast<double>(++counts.bad_log_op)) / ::log(10.0);
if (0.0 == x - (double) (int) x)
counts.invalid++;
- continue;
+ return;
}
- if ((++counts.scancount & 0xFFF) == 0) {
-
- int swap_entries = LogParser->SwapLogEntries();
-
- if (0 != swap_entries )
- storeRebuildProgress(sd->index,
- swap_entries, n_read);
- }
+ ++counts.scancount; // XXX: should not this be incremented earlier?
if (!sd->validFileno(swapData.swap_filen, 0)) {
counts.invalid++;
- continue;
+ return;
}
if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) {
counts.badflags++;
- continue;
+ return;
}
/* this needs to become
if (used && !disk_entry_newer) {
/* log entry is old, ignore it */
counts.clashcount++;
- continue;
+ return;
} else if (used && currentEntry() && currentEntry()->swap_filen == swapData.swap_filen && currentEntry()->swap_dirn == sd->index) {
/* swapfile taken, same URL, newer, update meta */
debug_trap("commonUfsDirRebuildFromSwapLog: bad condition");
debugs(47, 1, "\tSee " << __FILE__ << ":" << __LINE__);
}
- continue;
+ return;
} else if (used) {
/* swapfile in use, not by this URL, log entry is newer */
/* This is sorta bad: the log entry should NOT be newer at this
* and the validation procedure hasn't run. */
assert(flags.need_to_validate);
counts.clashcount++;
- continue;
+ return;
} else if (currentEntry() && !disk_entry_newer) {
/* key already exists, current entry is newer */
/* keep old, ignore new */
counts.dupcount++;
- continue;
+ return;
} else if (currentEntry()) {
/* key already exists, this swapfile not being used */
/* junk old, load new */
(int) flags.clean));
storeDirSwapLog(currentEntry(), SWAP_LOG_ADD);
- }
-
}
int
return fd;
}
-void
-RebuildState::next(void (aCallback)(void *aCallbackDataarg), void *aCallbackData)
-{
- /* for now, we don't cache at all */
- speed = 1;
- currentEntry(NULL);
-
- while (!isDone() && currentEntry() == NULL)
- rebuildStep();
-
- aCallback(aCallbackData);
-}
-
-bool
-RebuildState::next()
-{
- return false;
-}
-
bool
RebuildState::error() const
{