RebuildState.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 "base/IoManip.h"
13 #include "fs_io.h"
14 #include "globals.h"
15 #include "RebuildState.h"
16 #include "SquidConfig.h"
17 #include "store/Disks.h"
18 #include "store_key_md5.h"
19 #include "store_rebuild.h"
20 #include "StoreSwapLogData.h"
21 #include "tools.h"
22 #include "UFSSwapLogParser.h"
23 
24 #include <cerrno>
25 #include <cmath>
26 #if HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29 
31 
33  sd(aSwapDir),
34  n_read(0),
35  LogParser(nullptr),
36  curlvl1(0),
37  curlvl2(0),
38  in_dir(0),
39  done(0),
40  fn(0),
41  entry(nullptr),
42  td(nullptr),
43  fromLog(true),
44  _done(false),
45  cbdata(nullptr)
46 {
47 
48  /*
49  * If the swap.state file exists in the cache_dir, then
50  * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll
51  * use commonUfsDirRebuildFromDirectory() to open up each file
52  * and suck in the meta data.
53  */
54  int clean = 0; //TODO: change to bool
55  int zeroLengthLog = 0;
56  FILE *fp = sd->openTmpSwapLog(&clean, &zeroLengthLog);
57 
58  if (fp && !zeroLengthLog)
60 
61  if (LogParser == nullptr ) {
62  fromLog = false;
63 
64  if (fp != nullptr)
65  fclose(fp);
66 
67  } else {
68  fromLog = true;
69  flags.clean = (clean != 0);
70  }
71 
72  if (!clean)
73  flags.need_to_validate = true;
74 
76 
77  debugs(47, DBG_IMPORTANT, "Rebuilding storage in " << sd->path << " (" <<
78  (clean ? "clean log" : (LogParser ? "dirty log" : "no log")) << ")");
79 }
80 
82 {
83  sd->closeTmpSwapLog();
84 
85  if (LogParser)
86  delete LogParser;
87 }
88 
89 void
91 {
92  RebuildState *rb = (RebuildState *)data;
93  if (!reconfiguring)
94  rb->rebuildStep();
95 
96  // delay storeRebuildComplete() when reconfiguring to protect storeCleanup()
97  if (!rb->isDone() || reconfiguring)
98  eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1);
99  else {
101  delete rb;
102  }
103 }
104 
106 void
108 {
109  // Balance our desire to maximize the number of entries processed at once
110  // (and, hence, minimize overheads and total rebuild time) with a
111  // requirement to also process Coordinator events, disk I/Os, etc.
112  const int maxSpentMsec = 50; // keep small: most RAM I/Os are under 1ms
113  const timeval loopStart = current_time;
114 
115  const int totalEntries = LogParser ? LogParser->SwapLogEntries() : -1;
116 
117  while (!isDone()) {
118  if (fromLog)
119  rebuildFromSwapLog();
120  else
121  rebuildFromDirectory();
122 
123  // TODO: teach storeRebuildProgress to handle totalEntries <= 0
124  if (totalEntries > 0 && (n_read % 4000 == 0))
125  storeRebuildProgress(sd->index, totalEntries, n_read);
126 
128  continue; // skip "few entries at a time" check below
129 
130  getCurrentTime();
131  const double elapsedMsec = tvSubMsec(loopStart, current_time);
132  if (elapsedMsec > maxSpentMsec || elapsedMsec < 0) {
133  debugs(47, 5, "pausing after " << n_read << " entries in " <<
134  elapsedMsec << "ms; " << (elapsedMsec/n_read) << "ms per entry");
135  break;
136  }
137  }
138 }
139 
141 void
143 {
145 
146  struct stat sb;
147  int fd = -1;
148  debugs(47, 3, "DIR #" << sd->index);
149 
150  assert(fd == -1);
151  sfileno filn = 0;
152  int size;
153  fd = getNextFile(&filn, &size);
154 
155  if (fd == -2) {
156  debugs(47, DBG_IMPORTANT, "Done scanning " << sd->path << " dir (" <<
157  n_read << " entries)");
158  _done = true;
159  return;
160  } else if (fd < 0) {
161  return;
162  }
163 
164  assert(fd > -1);
165  /* lets get file stats here */
166 
167  ++n_read;
168 
169  if (fstat(fd, &sb) < 0) {
170  int xerrno = errno;
171  debugs(47, DBG_IMPORTANT, MYNAME << "fstat(FD " << fd << "): " << xstrerr(xerrno));
172  file_close(fd);
174  fd = -1;
175  return;
176  }
177 
178  MemBuf buf;
180  if (!storeRebuildLoadEntry(fd, sd->index, buf, counts))
181  return;
182 
183  const uint64_t expectedSize = sb.st_size > 0 ?
184  static_cast<uint64_t>(sb.st_size) : 0;
185 
186  StoreEntry tmpe;
187  const bool parsed = storeRebuildParseEntry(buf, tmpe, key, counts,
188  expectedSize);
189 
190  file_close(fd);
192  fd = -1;
193 
194  bool accepted = parsed && tmpe.swap_file_sz > 0;
195  if (parsed && !accepted) {
196  debugs(47, DBG_IMPORTANT, "WARNING: Ignoring ufs cache entry with " <<
197  "unknown size: " << tmpe);
198  accepted = false;
199  }
200 
201  if (!accepted) {
202  // XXX: shouldn't this be a call to commonUfsUnlink?
203  sd->unlinkFile(filn); // should we unlink in all failure cases?
204  return;
205  }
206 
207  addIfFresh(key,
208  filn,
209  tmpe.swap_file_sz,
210  tmpe.expires,
211  tmpe.timestamp,
212  tmpe.lastref,
213  tmpe.lastModified(),
214  tmpe.refcount,
215  tmpe.flags);
216 }
217 
219 void
221  sfileno file_number,
222  uint64_t swap_file_sz,
223  time_t expires,
224  time_t timestamp,
225  time_t lastref,
226  time_t lastmod,
227  uint32_t refcount,
228  uint16_t newFlags)
229 {
230  if (!evictStaleAndContinue(key, lastref, counts.dupcount))
231  return;
232 
233  ++counts.objcount;
234  const auto addedEntry = sd->addDiskRestore(key,
235  file_number,
236  swap_file_sz,
237  expires,
238  timestamp,
239  lastref,
240  lastmod,
241  refcount,
242  newFlags,
243  0 /* XXX: unused */);
245 }
246 
250 bool
251 Fs::Ufs::RebuildState::evictStaleAndContinue(const cache_key *candidateKey, const time_t maxRef, int &staleCount)
252 {
253  if (auto *indexedEntry = Store::Root().peek(candidateKey)) {
254 
255  if (indexedEntry->lastref >= maxRef) {
256  indexedEntry->abandon("Fs::Ufs::RebuildState::evictStaleAndContinue");
257  ++counts.clashcount;
258  return false;
259  }
260 
261  ++staleCount;
262  indexedEntry->release(true); // evict previously indexedEntry
263  }
264 
265  return true;
266 }
267 
269 void
271 {
272  StoreSwapLogData swapData;
273 
274  if (LogParser->ReadRecord(swapData) != 1) {
275  debugs(47, DBG_IMPORTANT, "Done reading " << sd->path << " swaplog (" << n_read << " entries)");
276  LogParser->Close();
277  delete LogParser;
278  LogParser = nullptr;
279  _done = true;
280  return;
281  }
282 
283  ++n_read;
284 
285  if (!swapData.sane()) {
286  ++counts.invalid;
287  return;
288  }
289 
290  /*
291  * BC: during 2.4 development, we changed the way swap file
292  * numbers are assigned and stored. The high 16 bits used
293  * to encode the SD index number. There used to be a call
294  * to storeDirProperFileno here that re-assigned the index
295  * bits. Now, for backwards compatibility, we just need
296  * to mask it off.
297  */
298  swapData.swap_filen &= 0x00FFFFFF;
299 
300  debugs(47, 3, swap_log_op_str[(int) swapData.op] << " " <<
301  storeKeyText(swapData.key) << " " <<
302  asHex(swapData.swap_filen).upperCase().minDigits(8));
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 << ": /" <<
360  asHex(curlvl1).upperCase().minDigits(2) << "/" <<
361  asHex(curlvl2).upperCase().minDigits(2));
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, asHex(fn).upperCase().minDigits(8) <<
414  " does not belong in " << sd->index << "/" <<
415  asHex(curlvl1).upperCase().minDigits(2) << "/" <<
416  asHex(curlvl2).upperCase().minDigits(2));
417  continue;
418  }
419 
420  if (sd->mapBitTest(fn)) {
421  debugs(47, 3, "Locked, continuing with next.");
422  continue;
423  }
424 
425  fullfilename.Printf(SQUIDSBUFPH "/%s",
426  SQUIDSBUFPRINT(fullpath), entry->d_name);
427  debugs(47, 3, "Opening " << fullfilename);
428  fd = file_open(fullfilename.c_str(), O_RDONLY | O_BINARY);
429 
430  if (fd < 0) {
431  int xerrno = errno;
432  debugs(47, DBG_IMPORTANT, "ERROR: " << MYNAME << "opening " << fullfilename << ": " << xstrerr(xerrno));
433  } else
435 
436  continue;
437  }
438 
439  if (td != nullptr)
440  closedir(td);
441 
442  td = nullptr;
443 
444  in_dir = 0;
445 
446  if (sd->validL2(++curlvl2))
447  continue;
448 
449  curlvl2 = 0;
450 
451  if (sd->validL1(++curlvl1))
452  continue;
453 
454  curlvl1 = 0;
455 
456  done = 1;
457  }
458 
459  *filn_p = fn;
460  return fd;
461 }
462 
463 bool
465 {
466  return false;
467 }
468 
469 bool
471 {
472  return _done;
473 }
474 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void storeRebuildProgress(int sd_index, int total, int sofar)
struct Fs::Ufs::RebuildState::Flags flags
#define SM_PAGE_SIZE
Definition: defines.h:63
void rebuildFromDirectory()
process one cache file
time_t timestamp
Definition: Store.h:223
unsigned char key[SQUID_MD5_DIGEST_LENGTH]
unsigned char cache_key
Store key.
Definition: forward.h:29
void log(char *format,...)
bool storeRebuildParseEntry(MemBuf &buf, StoreEntry &tmpe, cache_key *key, StoreRebuildData &stats, uint64_t expectedSize)
@ KEY_PRIVATE
Definition: enums.h:97
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
void updateStartTime(const timeval &dirStartTime)
maintain earliest initiation time across multiple indexing cache_dirs
Definition: cbdata.cc:37
uint16_t flags
Definition: Store.h:231
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:1115
time_t expires
Definition: Store.h:225
Store::DiskConfig cacheSwap
Definition: SquidConfig.h:423
void file_close(int fd)
Definition: fs_io.cc:93
@ SWAP_LOG_ADD
Definition: swap_log_op.h:14
int store_open_disk_fd
@ SWAP_LOG_DEL
Definition: swap_log_op.h:15
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:51
void storeRebuildComplete(StoreRebuildData *dc)
virtual bool isDone() const
#define SQUID_MD5_DIGEST_LENGTH
Definition: md5.h:66
#define O_BINARY
Definition: defines.h:134
int size
Definition: ModDevPoll.cc:69
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:18
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
void rebuildFromSwapLog()
process one swap log entry
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
uint16_t refcount
Definition: Store.h:230
Definition: MemBuf.h:23
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
RefCount< UFSSwapDir > sd
Definition: RebuildState.h:37
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition: IoManip.h:169
int reconfiguring
#define assert(EX)
Definition: assert.h:17
bool storeRebuildLoadEntry(int fd, int diskIndex, MemBuf &buf, StoreRebuildData &)
loads entry from disk; fills supplied memory buffer on success
CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs, RebuildState)
SwappedTime timestamp
const char * swap_log_op_str[]
signed_int32_t sfileno
Definition: forward.h:22
void storeDirSwapLog(const StoreEntry *e, int op)
Definition: Disks.cc:839
Fs::Ufs::UFSSwapLogParser * LogParser
Definition: RebuildState.h:40
StoreRebuildData counts
Definition: RebuildState.h:59
static StoreEntry * addedEntry(Store::Disk *aStore, String name, String, String)
static StoreRebuildData counts
uint64_t swap_file_sz
Definition: Store.h:229
#define DBG_IMPORTANT
Definition: Stream.h:38
const char * storeKeyText(const cache_key *key)
#define MYNAME
Definition: Stream.h:219
bool evictStaleAndContinue(const cache_key *candidateKey, const time_t maxRef, int &staleCount)
bool sane() const
consistency self-check: whether the data appears to make sense
int getNextFile(sfileno *, int *size)
int opt_foreground_rebuild
void lastModified(const time_t when)
Definition: Store.h:175
int file_open(const char *path, int mode)
Definition: fs_io.cc:65
static UFSSwapLogParser * GetUFSSwapLogParser(FILE *fp)
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
time_t lastref
Definition: Store.h:224
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
#define SQUIDSBUFPH
Definition: SBuf.h:31
RebuildState(const RefCount< UFSSwapDir > &sd)
Definition: RebuildState.cc:32
virtual bool error() const
class SquidConfig Config
Definition: SquidConfig.cc:12
static EVH RebuildStep
Definition: RebuildState.h:29
void rebuildStep()
load entries from swap.state or files until we run out of entries or time
Controller & Root()
safely access controller singleton
Definition: Controller.cc:926

 

Introduction

Documentation

Support

Miscellaneous