RebuildState.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 47 Store Directory Routines */
10 
11 #include "squid.h"
12 #include "fs_io.h"
13 #include "globals.h"
14 #include "RebuildState.h"
15 #include "SquidConfig.h"
16 #include "store/Disks.h"
17 #include "store_key_md5.h"
18 #include "store_rebuild.h"
19 #include "StoreSwapLogData.h"
20 #include "tools.h"
21 #include "UFSSwapLogParser.h"
22 
23 #include <cerrno>
24 #include <cmath>
25 #if HAVE_SYS_STAT_H
26 #include <sys/stat.h>
27 #endif
28 
30 
32  sd(aSwapDir),
33  n_read(0),
34  LogParser(nullptr),
35  curlvl1(0),
36  curlvl2(0),
37  in_dir(0),
38  done(0),
39  fn(0),
40  entry(nullptr),
41  td(nullptr),
42  fromLog(true),
43  _done(false),
44  cbdata(nullptr)
45 {
46 
47  /*
48  * If the swap.state file exists in the cache_dir, then
49  * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll
50  * use commonUfsDirRebuildFromDirectory() to open up each file
51  * and suck in the meta data.
52  */
53  int clean = 0; //TODO: change to bool
54  int zeroLengthLog = 0;
55  FILE *fp = sd->openTmpSwapLog(&clean, &zeroLengthLog);
56 
57  if (fp && !zeroLengthLog)
59 
60  if (LogParser == nullptr ) {
61  fromLog = false;
62 
63  if (fp != nullptr)
64  fclose(fp);
65 
66  } else {
67  fromLog = true;
68  flags.clean = (clean != 0);
69  }
70 
71  if (!clean)
72  flags.need_to_validate = true;
73 
75 
76  debugs(47, DBG_IMPORTANT, "Rebuilding storage in " << sd->path << " (" <<
77  (clean ? "clean log" : (LogParser ? "dirty log" : "no log")) << ")");
78 }
79 
81 {
82  sd->closeTmpSwapLog();
83 
84  if (LogParser)
85  delete LogParser;
86 }
87 
88 void
90 {
91  RebuildState *rb = (RebuildState *)data;
92  if (!reconfiguring)
93  rb->rebuildStep();
94 
95  // delay storeRebuildComplete() when reconfiguring to protect storeCleanup()
96  if (!rb->isDone() || reconfiguring)
97  eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1);
98  else {
100  delete rb;
101  }
102 }
103 
105 void
107 {
108  // Balance our desire to maximize the number of entries processed at once
109  // (and, hence, minimize overheads and total rebuild time) with a
110  // requirement to also process Coordinator events, disk I/Os, etc.
111  const int maxSpentMsec = 50; // keep small: most RAM I/Os are under 1ms
112  const timeval loopStart = current_time;
113 
114  const int totalEntries = LogParser ? LogParser->SwapLogEntries() : -1;
115 
116  while (!isDone()) {
117  if (fromLog)
118  rebuildFromSwapLog();
119  else
120  rebuildFromDirectory();
121 
122  // TODO: teach storeRebuildProgress to handle totalEntries <= 0
123  if (totalEntries > 0 && (n_read % 4000 == 0))
124  storeRebuildProgress(sd->index, totalEntries, n_read);
125 
127  continue; // skip "few entries at a time" check below
128 
129  getCurrentTime();
130  const double elapsedMsec = tvSubMsec(loopStart, current_time);
131  if (elapsedMsec > maxSpentMsec || elapsedMsec < 0) {
132  debugs(47, 5, "pausing after " << n_read << " entries in " <<
133  elapsedMsec << "ms; " << (elapsedMsec/n_read) << "ms per entry");
134  break;
135  }
136  }
137 }
138 
140 void
142 {
144 
145  struct stat sb;
146  int fd = -1;
147  debugs(47, 3, "DIR #" << sd->index);
148 
149  assert(fd == -1);
150  sfileno filn = 0;
151  int size;
152  fd = getNextFile(&filn, &size);
153 
154  if (fd == -2) {
155  debugs(47, DBG_IMPORTANT, "Done scanning " << sd->path << " dir (" <<
156  n_read << " entries)");
157  _done = true;
158  return;
159  } else if (fd < 0) {
160  return;
161  }
162 
163  assert(fd > -1);
164  /* lets get file stats here */
165 
166  ++n_read;
167 
168  if (fstat(fd, &sb) < 0) {
169  int xerrno = errno;
170  debugs(47, DBG_IMPORTANT, MYNAME << "fstat(FD " << fd << "): " << xstrerr(xerrno));
171  file_close(fd);
173  fd = -1;
174  return;
175  }
176 
177  MemBuf buf;
179  if (!storeRebuildLoadEntry(fd, sd->index, buf, counts))
180  return;
181 
182  const uint64_t expectedSize = sb.st_size > 0 ?
183  static_cast<uint64_t>(sb.st_size) : 0;
184 
185  StoreEntry tmpe;
186  const bool parsed = storeRebuildParseEntry(buf, tmpe, key, counts,
187  expectedSize);
188 
189  file_close(fd);
191  fd = -1;
192 
193  bool accepted = parsed && tmpe.swap_file_sz > 0;
194  if (parsed && !accepted) {
195  debugs(47, DBG_IMPORTANT, "WARNING: Ignoring ufs cache entry with " <<
196  "unknown size: " << tmpe);
197  accepted = false;
198  }
199 
200  if (!accepted) {
201  // XXX: shouldn't this be a call to commonUfsUnlink?
202  sd->unlinkFile(filn); // should we unlink in all failure cases?
203  return;
204  }
205 
206  addIfFresh(key,
207  filn,
208  tmpe.swap_file_sz,
209  tmpe.expires,
210  tmpe.timestamp,
211  tmpe.lastref,
212  tmpe.lastModified(),
213  tmpe.refcount,
214  tmpe.flags);
215 }
216 
218 void
220  sfileno file_number,
221  uint64_t swap_file_sz,
222  time_t expires,
223  time_t timestamp,
224  time_t lastref,
225  time_t lastmod,
226  uint32_t refcount,
227  uint16_t newFlags)
228 {
229  if (!evictStaleAndContinue(key, lastref, counts.dupcount))
230  return;
231 
232  ++counts.objcount;
233  const auto addedEntry = sd->addDiskRestore(key,
234  file_number,
235  swap_file_sz,
236  expires,
237  timestamp,
238  lastref,
239  lastmod,
240  refcount,
241  newFlags,
242  0 /* XXX: unused */);
244 }
245 
249 bool
250 Fs::Ufs::RebuildState::evictStaleAndContinue(const cache_key *candidateKey, const time_t maxRef, int &staleCount)
251 {
252  if (auto *indexedEntry = Store::Root().peek(candidateKey)) {
253 
254  if (indexedEntry->lastref >= maxRef) {
255  indexedEntry->abandon("Fs::Ufs::RebuildState::evictStaleAndContinue");
256  ++counts.clashcount;
257  return false;
258  }
259 
260  ++staleCount;
261  indexedEntry->release(true); // evict previously indexedEntry
262  }
263 
264  return true;
265 }
266 
268 void
270 {
271  StoreSwapLogData swapData;
272 
273  if (LogParser->ReadRecord(swapData) != 1) {
274  debugs(47, DBG_IMPORTANT, "Done reading " << sd->path << " swaplog (" << n_read << " entries)");
275  LogParser->Close();
276  delete LogParser;
277  LogParser = nullptr;
278  _done = true;
279  return;
280  }
281 
282  ++n_read;
283 
284  if (!swapData.sane()) {
285  ++counts.invalid;
286  return;
287  }
288 
289  /*
290  * BC: during 2.4 development, we changed the way swap file
291  * numbers are assigned and stored. The high 16 bits used
292  * to encode the SD index number. There used to be a call
293  * to storeDirProperFileno here that re-assigned the index
294  * bits. Now, for backwards compatibility, we just need
295  * to mask it off.
296  */
297  swapData.swap_filen &= 0x00FFFFFF;
298 
299  debugs(47, 3, swap_log_op_str[(int) swapData.op] << " " <<
300  storeKeyText(swapData.key) << " "<< std::setfill('0') <<
301  std::hex << std::uppercase << std::setw(8) <<
302  swapData.swap_filen);
303 
304  if (swapData.op == SWAP_LOG_ADD) {
305  (void) 0;
306  } else if (swapData.op == SWAP_LOG_DEL) {
307  // remove any older or same-age entry; +1 covers same-age entries
308  (void)evictStaleAndContinue(swapData.key, swapData.lastref+1, counts.cancelcount);
309  return;
310  } else {
311  const double
312  x = ::log(static_cast<double>(++counts.bad_log_op)) / ::log(10.0);
313 
314  if (0.0 == x - (double) (int) x)
315  debugs(47, DBG_IMPORTANT, "WARNING: " << counts.bad_log_op << " invalid swap log entries found");
316 
317  ++counts.invalid;
318 
319  return;
320  }
321 
322  ++counts.scancount; // XXX: should not this be incremented earlier?
323 
324  if (!sd->validFileno(swapData.swap_filen, 0)) {
325  ++counts.invalid;
326  return;
327  }
328 
329  if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) {
330  ++counts.badflags;
331  return;
332  }
333 
334  if (sd->mapBitTest(swapData.swap_filen)) {
335  // While we were scanning the logs, some (unrelated) entry was added to
336  // our disk using our logged swap_filen. We could change our swap_filen
337  // and move the store file, but there is no Store API to do that (TODO).
338  ++counts.clashcount;
339  return;
340  }
341 
342  addIfFresh(swapData.key,
343  swapData.swap_filen,
344  swapData.swap_file_sz,
345  swapData.expires,
346  swapData.timestamp,
347  swapData.lastref,
348  swapData.lastmod,
349  swapData.refcount,
350  swapData.flags);
351 }
352 
353 int
355 {
356  int fd = -1;
357  int dirs_opened = 0;
358  debugs(47, 3, "flag=" << flags.init << ", " <<
359  sd->index << ": /"<< std::setfill('0') << std::hex <<
360  std::uppercase << std::setw(2) << curlvl1 << "/" << std::setw(2) <<
361  curlvl2);
362 
363  if (done)
364  return -2;
365 
366  while (fd < 0 && done == 0) {
367  fd = -1;
368 
369  if (!flags.init) { /* initialize, open first file */
370  // XXX: 0's should not be needed, constructor inits now
371  done = 0;
372  curlvl1 = 0;
373  curlvl2 = 0;
374  in_dir = 0;
375  flags.init = true;
377  }
378 
379  if (0 == in_dir) { /* we need to read in a new directory */
380  fullpath.Printf("%s/%02X/%02X",
381  sd->path,
382  curlvl1, curlvl2);
383 
384  if (dirs_opened)
385  return -1;
386 
387  td = opendir(fullpath.c_str());
388 
389  ++dirs_opened;
390 
391  if (!td) {
392  int xerrno = errno;
393  debugs(47, DBG_IMPORTANT, "ERROR: " << MYNAME << "in opendir (" << fullpath << "): " << xstrerr(xerrno));
394  } else {
395  entry = readdir(td); /* skip . and .. */
396  entry = readdir(td);
397 
398  if (entry == nullptr && errno == ENOENT)
399  debugs(47, DBG_IMPORTANT, "WARNING: directory does not exist!");
400  debugs(47, 3, "Directory " << fullpath);
401  }
402  }
403 
404  if (td != nullptr && (entry = readdir(td)) != nullptr) {
405  ++in_dir;
406 
407  if (sscanf(entry->d_name, "%x", &fn) != 1) {
408  debugs(47, 3, "invalid entry " << entry->d_name);
409  continue;
410  }
411 
412  if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) {
413  debugs(47, 3, std::setfill('0') <<
414  std::hex << std::uppercase << std::setw(8) << fn <<
415  " does not belong in " << std::dec << sd->index << "/" <<
416  curlvl1 << "/" << curlvl2);
417 
418  continue;
419  }
420 
421  if (sd->mapBitTest(fn)) {
422  debugs(47, 3, "Locked, continuing with next.");
423  continue;
424  }
425 
426  fullfilename.Printf(SQUIDSBUFPH "/%s",
427  SQUIDSBUFPRINT(fullpath), entry->d_name);
428  debugs(47, 3, "Opening " << fullfilename);
429  fd = file_open(fullfilename.c_str(), O_RDONLY | O_BINARY);
430 
431  if (fd < 0) {
432  int xerrno = errno;
433  debugs(47, DBG_IMPORTANT, "ERROR: " << MYNAME << "opening " << fullfilename << ": " << xstrerr(xerrno));
434  } else
436 
437  continue;
438  }
439 
440  if (td != nullptr)
441  closedir(td);
442 
443  td = nullptr;
444 
445  in_dir = 0;
446 
447  if (sd->validL2(++curlvl2))
448  continue;
449 
450  curlvl2 = 0;
451 
452  if (sd->validL1(++curlvl1))
453  continue;
454 
455  curlvl1 = 0;
456 
457  done = 1;
458  }
459 
460  *filn_p = fn;
461  return fd;
462 }
463 
464 bool
466 {
467  return false;
468 }
469 
470 bool
472 {
473  return _done;
474 }
475 
void storeDirSwapLog(const StoreEntry *e, int op)
Definition: Disks.cc:835
#define true
Definition: GnuRegex.c:241
#define false
Definition: GnuRegex.c:240
int size
Definition: ModDevPoll.cc:75
CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs, RebuildState)
#define SQUIDSBUFPH
Definition: SBuf.h:31
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
class SquidConfig Config
Definition: SquidConfig.cc:12
void log(char *format,...)
#define assert(EX)
Definition: assert.h:19
RefCount< UFSSwapDir > sd
Definition: RebuildState.h:37
void rebuildFromDirectory()
process one cache file
void rebuildFromSwapLog()
process one swap log entry
virtual bool error() const
StoreRebuildData counts
Definition: RebuildState.h:59
bool evictStaleAndContinue(const cache_key *candidateKey, const time_t maxRef, int &staleCount)
Fs::Ufs::UFSSwapLogParser * LogParser
Definition: RebuildState.h:40
virtual bool isDone() const
struct Fs::Ufs::RebuildState::Flags flags
RebuildState(RefCount< UFSSwapDir > sd)
Definition: RebuildState.cc:31
static EVH RebuildStep
Definition: RebuildState.h:29
int getNextFile(sfileno *, int *size)
void rebuildStep()
load entries from swap.state or files until we run out of entries or time
void addIfFresh(const cache_key *key, sfileno file_number, uint64_t swap_file_sz, time_t expires, time_t timestamp, time_t lastref, time_t lastmod, uint32_t refcount, uint16_t flags)
if the loaded entry metadata is still relevant, indexes the entry
static bool FilenoBelongsHere(int fn, int cachedir, int level1dir, int level2dir)
Definition: UFSSwapDir.cc:1116
static UFSSwapLogParser * GetUFSSwapLogParser(FILE *fp)
Definition: MemBuf.h:24
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Store::DiskConfig cacheSwap
Definition: SquidConfig.h:429
uint16_t flags
Definition: Store.h:230
time_t expires
Definition: Store.h:224
void lastModified(const time_t when)
Definition: Store.h:174
time_t timestamp
Definition: Store.h:222
time_t lastref
Definition: Store.h:223
uint64_t swap_file_sz
Definition: Store.h:228
uint16_t refcount
Definition: Store.h:229
void updateStartTime(const timeval &dirStartTime)
maintain earliest initiation time across multiple indexing cache_dirs
unsigned char key[SQUID_MD5_DIGEST_LENGTH]
SwappedTime timestamp
bool sane() const
consistency self-check: whether the data appears to make sense
Definition: cbdata.cc:60
#define MYNAME
Definition: Stream.h:238
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define SM_PAGE_SIZE
Definition: defines.h:65
#define O_BINARY
Definition: defines.h:136
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
@ KEY_PRIVATE
Definition: enums.h:102
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
int file_open(const char *path, int mode)
Definition: fs_io.cc:45
void file_close(int fd)
Definition: fs_io.cc:73
int store_open_disk_fd
int opt_foreground_rebuild
int reconfiguring
#define SQUID_MD5_DIGEST_LENGTH
Definition: md5.h:66
Controller & Root()
safely access controller singleton
Definition: Controller.cc:934
static struct stat sb
Definition: squidclient.cc:71
unsigned char cache_key
Store key.
Definition: forward.h:29
signed_int32_t sfileno
Definition: forward.h:22
const char * storeKeyText(const cache_key *key)
bool storeRebuildParseEntry(MemBuf &buf, StoreEntry &tmpe, cache_key *key, StoreRebuildData &stats, uint64_t expectedSize)
void storeRebuildProgress(int sd_index, int total, int sofar)
bool storeRebuildLoadEntry(int fd, int diskIndex, MemBuf &buf, StoreRebuildData &)
loads entry from disk; fills supplied memory buffer on success
static StoreRebuildData counts
void storeRebuildComplete(StoreRebuildData *dc)
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
const char * swap_log_op_str[]
@ SWAP_LOG_DEL
Definition: swap_log_op.h:15
@ SWAP_LOG_ADD
Definition: swap_log_op.h:14
static StoreEntry * addedEntry(Store::Disk *aStore, String name, String, String)
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:17
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:51
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors