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)
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
88void
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
105void
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
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
140void
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
218void
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
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
249bool
250Fs::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");
257 return false;
258 }
259
260 ++staleCount;
261 indexedEntry->release(true); // evict previously indexedEntry
262 }
263
264 return true;
265}
266
268void
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)) {
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).
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
353int
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
464bool
466{
467 return false;
468}
469
470bool
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:421
uint16_t flags
Definition: Store.h:232
time_t expires
Definition: Store.h:226
void lastModified(const time_t when)
Definition: Store.h:176
time_t timestamp
Definition: Store.h:224
time_t lastref
Definition: Store.h:225
uint64_t swap_file_sz
Definition: Store.h:230
uint16_t refcount
Definition: Store.h:231
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:926
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