Squid Web Cache master
Loading...
Searching...
No Matches
RebuildState.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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)
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
89void
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
106void
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
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
141void
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
219void
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
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
250bool
251Fs::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");
258 return false;
259 }
260
261 ++staleCount;
262 indexedEntry->release(true); // evict previously indexedEntry
263 }
264
265 return true;
266}
267
269void
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)) {
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 << ": /" <<
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
463bool
465{
466 return false;
467}
468
469bool
471{
472 return _done;
473}
474
void storeDirSwapLog(const StoreEntry *e, int op)
Definition Disks.cc:833
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition IoManip.h:169
int size
Definition ModDevPoll.cc:70
#define SQUIDSBUFPH
Definition SBuf.h:31
#define SQUIDSBUFPRINT(s)
Definition SBuf.h:32
class SquidConfig Config
void log(char *format,...)
#define assert(EX)
Definition assert.h:17
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
Definition cbdata.h:333
RefCount< UFSSwapDir > sd
void rebuildFromDirectory()
process one cache file
RebuildState(const RefCount< UFSSwapDir > &sd)
void rebuildFromSwapLog()
process one swap log entry
virtual bool error() const
StoreRebuildData counts
bool evictStaleAndContinue(const cache_key *candidateKey, const time_t maxRef, int &staleCount)
Fs::Ufs::UFSSwapLogParser * LogParser
virtual bool isDone() const
struct Fs::Ufs::RebuildState::Flags flags
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)
static UFSSwapLogParser * GetUFSSwapLogParser(FILE *fp)
void init(mb_size_t szInit, mb_size_t szMax)
Definition MemBuf.cc:93
Store::DiskConfig cacheSwap
uint16_t flags
Definition Store.h:231
time_t expires
Definition Store.h:225
void lastModified(const time_t when)
Definition Store.h:175
time_t timestamp
Definition Store.h:223
time_t lastref
Definition Store.h:224
uint64_t swap_file_sz
Definition Store.h:229
uint16_t refcount
Definition Store.h:230
void updateStartTime(const timeval &dirStartTime)
maintain earliest initiation time across multiple indexing cache_dirs
unsigned char key[SQUID_MD5_DIGEST_LENGTH]
bool sane() const
consistency self-check: whether the data appears to make sense
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define SM_PAGE_SIZE
Definition defines.h:63
#define O_BINARY
Definition defines.h:134
#define EBIT_TEST(flag, bit)
Definition defines.h:67
@ KEY_PRIVATE
Definition enums.h:97
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:66
void file_close(int fd)
Definition fs_io.cc:92
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
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)
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)
void storeRebuildProgress(const int sd_index_raw, const int total, const int sofar)
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:18
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:51
const char * xstrerr(int error)
Definition xstrerror.cc:83