2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 47 Store Directory Routines */
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"
21 #include "UFSSwapLogParser.h"
29 CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs
,RebuildState
);
31 Fs::Ufs::RebuildState::RebuildState(RefCount
<UFSSwapDir
> aSwapDir
) :
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.
53 int clean
= 0; //TODO: change to bool
54 int zeroLengthLog
= 0;
55 FILE *fp
= sd
->openTmpSwapLog(&clean
, &zeroLengthLog
);
57 if (fp
&& !zeroLengthLog
)
58 LogParser
= Fs::Ufs::UFSSwapLogParser::GetUFSSwapLogParser(fp
);
60 if (LogParser
== nullptr ) {
68 flags
.clean
= (clean
!= 0);
72 flags
.need_to_validate
= true;
74 counts
.updateStartTime(current_time
);
76 debugs(47, DBG_IMPORTANT
, "Rebuilding storage in " << sd
->path
<< " (" <<
77 (clean
? "clean log" : (LogParser
? "dirty log" : "no log")) << ")");
80 Fs::Ufs::RebuildState::~RebuildState()
82 sd
->closeTmpSwapLog();
89 Fs::Ufs::RebuildState::RebuildStep(void *data
)
91 RebuildState
*rb
= (RebuildState
*)data
;
95 // delay storeRebuildComplete() when reconfiguring to protect storeCleanup()
96 if (!rb
->isDone() || reconfiguring
)
97 eventAdd("storeRebuild", RebuildStep
, rb
, 0.01, 1);
99 storeRebuildComplete(&rb
->counts
);
104 /// load entries from swap.state or files until we run out of entries or time
106 Fs::Ufs::RebuildState::rebuildStep()
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
;
114 const int totalEntries
= LogParser
? LogParser
->SwapLogEntries() : -1;
118 rebuildFromSwapLog();
120 rebuildFromDirectory();
122 // TODO: teach storeRebuildProgress to handle totalEntries <= 0
123 if (totalEntries
> 0 && (n_read
% 4000 == 0))
124 storeRebuildProgress(sd
->index
, totalEntries
, n_read
);
126 if (opt_foreground_rebuild
)
127 continue; // skip "few entries at a time" check below
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");
139 /// process one cache file
141 Fs::Ufs::RebuildState::rebuildFromDirectory()
143 cache_key key
[SQUID_MD5_DIGEST_LENGTH
];
147 debugs(47, 3, "DIR #" << sd
->index
);
152 fd
= getNextFile(&filn
, &size
);
155 debugs(47, DBG_IMPORTANT
, "Done scanning " << sd
->path
<< " dir (" <<
156 n_read
<< " entries)");
164 /* lets get file stats here */
168 if (fstat(fd
, &sb
) < 0) {
170 debugs(47, DBG_IMPORTANT
, MYNAME
<< "fstat(FD " << fd
<< "): " << xstrerr(xerrno
));
172 --store_open_disk_fd
;
178 buf
.init(SM_PAGE_SIZE
, SM_PAGE_SIZE
);
179 if (!storeRebuildLoadEntry(fd
, sd
->index
, buf
, counts
))
182 const uint64_t expectedSize
= sb
.st_size
> 0 ?
183 static_cast<uint64_t>(sb
.st_size
) : 0;
186 const bool parsed
= storeRebuildParseEntry(buf
, tmpe
, key
, counts
,
190 --store_open_disk_fd
;
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
);
201 // XXX: shouldn't this be a call to commonUfsUnlink?
202 sd
->unlinkFile(filn
); // should we unlink in all failure cases?
217 /// if the loaded entry metadata is still relevant, indexes the entry
219 Fs::Ufs::RebuildState::addIfFresh(const cache_key
*key
,
221 uint64_t swap_file_sz
,
229 if (!evictStaleAndContinue(key
, lastref
, counts
.dupcount
))
233 const auto addedEntry
= sd
->addDiskRestore(key
,
242 0 /* XXX: unused */);
243 storeDirSwapLog(addedEntry
, SWAP_LOG_ADD
);
246 /// Evicts a matching entry if it was last touched before caller's maxRef.
247 /// \returns false only if the matching entry was touched at or after maxRef,
248 /// indicating that the caller has supplied outdated maxRef.
250 Fs::Ufs::RebuildState::evictStaleAndContinue(const cache_key
*candidateKey
, const time_t maxRef
, int &staleCount
)
252 if (auto *indexedEntry
= Store::Root().peek(candidateKey
)) {
254 if (indexedEntry
->lastref
>= maxRef
) {
255 indexedEntry
->abandon("Fs::Ufs::RebuildState::evictStaleAndContinue");
261 indexedEntry
->release(true); // evict previously indexedEntry
267 /// process one swap log entry
269 Fs::Ufs::RebuildState::rebuildFromSwapLog()
271 StoreSwapLogData swapData
;
273 if (LogParser
->ReadRecord(swapData
) != 1) {
274 debugs(47, DBG_IMPORTANT
, "Done reading " << sd
->path
<< " swaplog (" << n_read
<< " entries)");
284 if (!swapData
.sane()) {
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
297 swapData
.swap_filen
&= 0x00FFFFFF;
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
);
304 if (swapData
.op
== SWAP_LOG_ADD
) {
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
);
312 x
= ::log(static_cast<double>(++counts
.bad_log_op
)) / ::log(10.0);
314 if (0.0 == x
- (double) (int) x
)
315 debugs(47, DBG_IMPORTANT
, "WARNING: " << counts
.bad_log_op
<< " invalid swap log entries found");
322 ++counts
.scancount
; // XXX: should not this be incremented earlier?
324 if (!sd
->validFileno(swapData
.swap_filen
, 0)) {
329 if (EBIT_TEST(swapData
.flags
, KEY_PRIVATE
)) {
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).
342 addIfFresh(swapData
.key
,
344 swapData
.swap_file_sz
,
354 Fs::Ufs::RebuildState::getNextFile(sfileno
* filn_p
, int *)
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) <<
366 while (fd
< 0 && done
== 0) {
369 if (!flags
.init
) { /* initialize, open first file */
370 // XXX: 0's should not be needed, constructor inits now
376 assert(Config
.cacheSwap
.n_configured
> 0);
379 if (0 == in_dir
) { /* we need to read in a new directory */
380 fullpath
.Printf("%s/%02X/%02X",
387 td
= opendir(fullpath
.c_str());
393 debugs(47, DBG_IMPORTANT
, "ERROR: " << MYNAME
<< "in opendir (" << fullpath
<< "): " << xstrerr(xerrno
));
395 entry
= readdir(td
); /* skip . and .. */
398 if (entry
== nullptr && errno
== ENOENT
)
399 debugs(47, DBG_IMPORTANT
, "WARNING: directory does not exist!");
400 debugs(47, 3, "Directory " << fullpath
);
404 if (td
!= nullptr && (entry
= readdir(td
)) != nullptr) {
407 if (sscanf(entry
->d_name
, "%x", &fn
) != 1) {
408 debugs(47, 3, "invalid entry " << entry
->d_name
);
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
);
421 if (sd
->mapBitTest(fn
)) {
422 debugs(47, 3, "Locked, continuing with next.");
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
);
433 debugs(47, DBG_IMPORTANT
, "ERROR: " << MYNAME
<< "opening " << fullfilename
<< ": " << xstrerr(xerrno
));
435 ++store_open_disk_fd
;
447 if (sd
->validL2(++curlvl2
))
452 if (sd
->validL1(++curlvl1
))
465 Fs::Ufs::RebuildState::error() const
471 Fs::Ufs::RebuildState::isDone() const