]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_rebuild.cc
cache_log_message directive (#775)
[thirdparty/squid.git] / src / store_rebuild.cc
CommitLineData
8638fc66 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
8638fc66 7 */
8
bbc27441
AJ
9/* DEBUG: section 20 Store Rebuild Routines */
10
582c2af2 11#include "squid.h"
c59baaa8 12#include "DebugMessages.h"
a553a5a3 13#include "event.h"
582c2af2
FC
14#include "globals.h"
15#include "md5.h"
602d9612
A
16#include "SquidConfig.h"
17#include "SquidTime.h"
e4f1fdae 18#include "StatCounters.h"
e6ccf245 19#include "Store.h"
b3f7fd88 20#include "store/Disk.h"
35a28a37 21#include "store_digest.h"
602d9612 22#include "store_key_md5.h"
687f5275 23#include "store_rebuild.h"
c8f4eac4 24#include "StoreSearch.h"
ed6e9fb9
AJ
25// for tvSubDsec() which should be in SquidTime.h
26#include "util.h"
f09f5b26 27
1a30fdf5
AJ
28#include <cerrno>
29
211e9ce2 30static StoreRebuildData counts;
62e76326 31
9bea1d5b 32static void storeCleanup(void *);
f09f5b26 33
8ecbe78d
EB
34// TODO: Either convert to Progress or replace with StoreRebuildData.
35// TODO: Handle unknown totals (UFS cache_dir that lost swap.state) correctly.
26ac0430 36typedef struct {
5a797987 37 /* total number of "swap.state" entries that will be read */
38 int total;
39 /* number of entries read so far */
40 int scanned;
2fadd50d 41} store_rebuild_progress;
5a797987 42
43static store_rebuild_progress *RebuildProgress = NULL;
44
8ecbe78d
EB
45void
46StoreRebuildData::updateStartTime(const timeval &dirStartTime)
47{
48 startTime = started() ? std::min(startTime, dirStartTime) : dirStartTime;
49}
50
12784378 51static void
ced8def3 52storeCleanup(void *)
f09f5b26 53{
9bea1d5b 54 static int store_errors = 0;
c8f4eac4 55 static StoreSearchPointer currentSearch;
56 static int validated = 0;
52dce2fe 57 static int seen = 0;
c8f4eac4 58
59 if (currentSearch == NULL || currentSearch->isDone())
2745fea5 60 currentSearch = Store::Root().search();
c8f4eac4 61
62 size_t statCount = 500;
63
26031546 64 // TODO: Avoid the loop (and ENTRY_VALIDATED) unless opt_store_doublecheck.
c8f4eac4 65 while (statCount-- && !currentSearch->isDone() && currentSearch->next()) {
c8f4eac4 66 StoreEntry *e;
67
68 e = currentSearch->currentItem();
69
52dce2fe
AR
70 ++seen;
71
c8f4eac4 72 if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
73 continue;
74
75 /*
5f33b71d 76 * Calling StoreEntry->release() has no effect because we're
c8f4eac4 77 * still in 'store_rebuilding' state
78 */
4310f8b0 79 if (!e->hasDisk())
c8f4eac4 80 continue;
81
82 if (opt_store_doublecheck)
5d84beb5 83 if (e->disk().doubleCheck(*e))
5db6bf73 84 ++store_errors;
c8f4eac4 85
86 EBIT_SET(e->flags, ENTRY_VALIDATED);
87
88 /*
89 * Only set the file bit if we know its a valid entry
90 * otherwise, set it in the validation procedure
91 */
e2851fe7 92
c8f4eac4 93 if ((++validated & 0x3FFFF) == 0)
94 /* TODO format the int with with a stream operator */
e0236918 95 debugs(20, DBG_IMPORTANT, " " << validated << " Entries Validated so far.");
f09f5b26 96 }
62e76326 97
c8f4eac4 98 if (currentSearch->isDone()) {
52dce2fe 99 debugs(20, 2, "Seen: " << seen << " entries");
c59baaa8
EB
100 debugs(20, Important(43), "Completed Validation Procedure" <<
101 Debug::Extra << "Validated " << validated << " Entries" <<
102 Debug::Extra << "store_swap_size = " << (Store::Root().currentSize()/1024.0) << " KB");
5e263176 103 --StoreController::store_dirs_rebuilding;
bef81ea5 104 assert(0 == StoreController::store_dirs_rebuilding);
c8f4eac4 105
2f6fcab1
AR
106 if (opt_store_doublecheck && store_errors) {
107 fatalf("Quitting after finding %d cache index inconsistencies. " \
108 "Removing cache index will force its slow rebuild. " \
109 "Removing -S will let Squid start with an inconsistent " \
110 "cache index (at your own risk).\n", store_errors);
111 }
c8f4eac4 112
113 if (store_digest)
114 storeDigestNoteStoreReady();
115
116 currentSearch = NULL;
117 } else
118 eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
f09f5b26 119}
120
f09f5b26 121/* meta data recreated from disk image in swap directory */
b2c141d4 122void
62e76326 123
211e9ce2 124storeRebuildComplete(StoreRebuildData *dc)
f09f5b26 125{
8ecbe78d
EB
126 if (dc) {
127 counts.objcount += dc->objcount;
128 counts.expcount += dc->expcount;
129 counts.scancount += dc->scancount;
130 counts.clashcount += dc->clashcount;
131 counts.dupcount += dc->dupcount;
132 counts.cancelcount += dc->cancelcount;
133 counts.invalid += dc->invalid;
134 counts.badflags += dc->badflags;
135 counts.bad_log_op += dc->bad_log_op;
136 counts.zero_object_sz += dc->zero_object_sz;
137 counts.validations += dc->validations;
138 counts.updateStartTime(dc->startTime);
139 }
140 // else the caller was not responsible for indexing its cache_dir
141
142 assert(StoreController::store_dirs_rebuilding > 1);
143 --StoreController::store_dirs_rebuilding;
144
9bea1d5b 145 /*
b07b21cc 146 * When store_dirs_rebuilding == 1, it means we are done reading
9bea1d5b 147 * or scanning all cache_dirs. Now report the stats and start
148 * the validation (storeCleanup()) thread.
149 */
62e76326 150
e4b1808b 151 if (StoreController::store_dirs_rebuilding > 1)
62e76326 152 return;
153
8ecbe78d 154 const auto dt = tvSubDsec(counts.startTime, current_time);
62e76326 155
c59baaa8
EB
156 debugs(20, Important(46), "Finished rebuilding storage from disk." <<
157 Debug::Extra << std::setw(7) << counts.scancount << " Entries scanned" <<
158 Debug::Extra << std::setw(7) << counts.invalid << " Invalid entries" <<
159 Debug::Extra << std::setw(7) << counts.badflags << " With invalid flags" <<
160 Debug::Extra << std::setw(7) << counts.objcount << " Objects loaded" <<
161 Debug::Extra << std::setw(7) << counts.expcount << " Objects expired" <<
162 Debug::Extra << std::setw(7) << counts.cancelcount << " Objects canceled" <<
163 Debug::Extra << std::setw(7) << counts.dupcount << " Duplicate URLs purged" <<
164 Debug::Extra << std::setw(7) << counts.clashcount << " Swapfile clashes avoided" <<
165 Debug::Extra << "Took " << std::setprecision(2) << dt << " seconds (" <<
bf8fe701 166 ((double) counts.objcount / (dt > 0.0 ? dt : 1.0)) << " objects/sec).");
c59baaa8 167 debugs(20, Important(56), "Beginning Validation Procedure");
62e76326 168
9bea1d5b 169 eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
62e76326 170
9bea1d5b 171 xfree(RebuildProgress);
62e76326 172
9bea1d5b 173 RebuildProgress = NULL;
f09f5b26 174}
175
b2c141d4 176/*
177 * this is ugly. We don't actually start any rebuild threads here,
178 * but only initialize counters, etc. The rebuild threads are
179 * actually started by the filesystem "fooDirInit" function.
180 */
f09f5b26 181void
9bea1d5b 182storeRebuildStart(void)
f09f5b26 183{
b56b37cf 184 counts = StoreRebuildData(); // reset counters
9bea1d5b 185 /*
b07b21cc 186 * Note: store_dirs_rebuilding is initialized to 1.
26ac0430
AJ
187 *
188 * When we parse the configuration and construct each swap dir,
bef81ea5 189 * the construction of that raises the rebuild count.
190 *
9bea1d5b 191 * This prevents us from trying to write clean logs until we
bef81ea5 192 * finished rebuilding - including after a reconfiguration that opens an
193 * existing swapdir. The corresponding decrement * occurs in
194 * storeCleanup(), when it is finished.
9bea1d5b 195 */
e6ccf245 196 RebuildProgress = (store_rebuild_progress *)xcalloc(Config.cacheSwap.n_configured,
62e76326 197 sizeof(store_rebuild_progress));
5a797987 198}
199
200/*
201 * A fs-specific rebuild procedure periodically reports its
202 * progress.
203 */
204void
9bea1d5b 205storeRebuildProgress(int sd_index, int total, int sofar)
5a797987 206{
9bea1d5b 207 static time_t last_report = 0;
8ecbe78d 208 // TODO: Switch to int64_t and fix handling of unknown totals.
9bea1d5b 209 double n = 0.0;
210 double d = 0.0;
62e76326 211
9bea1d5b 212 if (sd_index < 0)
62e76326 213 return;
214
9bea1d5b 215 if (sd_index >= Config.cacheSwap.n_configured)
62e76326 216 return;
217
9bea1d5b 218 if (NULL == RebuildProgress)
62e76326 219 return;
220
9bea1d5b 221 RebuildProgress[sd_index].total = total;
62e76326 222
9bea1d5b 223 RebuildProgress[sd_index].scanned = sofar;
62e76326 224
9bea1d5b 225 if (squid_curtime - last_report < 15)
62e76326 226 return;
227
5db6bf73 228 for (sd_index = 0; sd_index < Config.cacheSwap.n_configured; ++sd_index) {
62e76326 229 n += (double) RebuildProgress[sd_index].scanned;
230 d += (double) RebuildProgress[sd_index].total;
5a797987 231 }
62e76326 232
c59baaa8 233 debugs(20, Important(57), "Indexing cache entries: " << Progress(n, d));
9bea1d5b 234 last_report = squid_curtime;
f09f5b26 235}
e2851fe7 236
8ecbe78d
EB
237void
238Progress::print(std::ostream &os) const
239{
240 if (goal > 0) {
9fa45f86 241 const auto savedPrecision = os.precision(2);
8ecbe78d 242 const auto percent = 100.0 * completed / goal;
9fa45f86 243 os << percent << "% (" << completed << " out of " << goal << ")";
70ac5b29 244 (void)os.precision(savedPrecision);
8ecbe78d
EB
245 } else if (!completed && !goal) {
246 os << "nothing to do";
247 } else {
248 // unknown (i.e. negative) or buggy (i.e. zero when completed != 0) goal
249 os << completed;
250 }
251}
252
e2851fe7 253#include "fde.h"
e2851fe7 254#include "Generic.h"
602d9612
A
255#include "StoreMeta.h"
256#include "StoreMetaUnpacker.h"
e2851fe7
AR
257
258struct InitStoreEntry : public unary_function<StoreMeta, void> {
259 InitStoreEntry(StoreEntry *anEntry, cache_key *aKey):what(anEntry),index(aKey) {}
260
261 void operator()(StoreMeta const &x) {
262 switch (x.getType()) {
263
264 case STORE_META_KEY:
265 assert(x.length == SQUID_MD5_DIGEST_LENGTH);
266 memcpy(index, x.value, SQUID_MD5_DIGEST_LENGTH);
267 break;
268
269 case STORE_META_STD:
270 struct old_metahdr {
271 time_t timestamp;
272 time_t lastref;
273 time_t expires;
274 time_t lastmod;
275 size_t swap_file_sz;
89924985
AR
276 uint16_t refcount;
277 uint16_t flags;
e2851fe7
AR
278 } *tmp;
279 tmp = (struct old_metahdr *)x.value;
280 assert(x.length == STORE_HDR_METASIZE_OLD);
281 what->timestamp = tmp->timestamp;
282 what->lastref = tmp->lastref;
283 what->expires = tmp->expires;
438b41ba 284 what->lastModified(tmp->lastmod);
e2851fe7
AR
285 what->swap_file_sz = tmp->swap_file_sz;
286 what->refcount = tmp->refcount;
287 what->flags = tmp->flags;
288 break;
289
290 case STORE_META_STD_LFS:
291 assert(x.length == STORE_HDR_METASIZE);
292 memcpy(&what->timestamp, x.value, STORE_HDR_METASIZE);
293 break;
294
295 default:
296 break;
297 }
298 }
299
300 StoreEntry *what;
301 cache_key *index;
302};
303
304bool
3254023f 305storeRebuildLoadEntry(int fd, int diskIndex, MemBuf &buf, StoreRebuildData &)
e2851fe7
AR
306{
307 if (fd < 0)
308 return false;
309
aa1a691e 310 assert(buf.hasSpace()); // caller must allocate
e2851fe7 311
aa1a691e 312 const int len = FD_READ_METHOD(fd, buf.space(), buf.spaceSize());
5db6bf73 313 ++ statCounter.syscalls.disk.reads;
aa1a691e
AR
314 if (len < 0) {
315 const int xerrno = errno;
28edca61 316 debugs(47, DBG_IMPORTANT, "WARNING: cache_dir[" << diskIndex << "]: " <<
9199139f 317 "Ignoring cached entry after meta data read failure: " << xstrerr(xerrno));
e2851fe7
AR
318 return false;
319 }
320
aa1a691e
AR
321 buf.appended(len);
322 return true;
323}
324
325bool
326storeRebuildParseEntry(MemBuf &buf, StoreEntry &tmpe, cache_key *key,
3254023f 327 StoreRebuildData &stats,
aa1a691e
AR
328 uint64_t expectedSize)
329{
e2851fe7 330 int swap_hdr_len = 0;
aa1a691e 331 StoreMetaUnpacker aBuilder(buf.content(), buf.contentSize(), &swap_hdr_len);
e2851fe7
AR
332 if (aBuilder.isBufferZero()) {
333 debugs(47,5, HERE << "skipping empty record.");
334 return false;
335 }
336
4c2f8b72
EB
337 StoreMeta *tlv_list = nullptr;
338 try {
339 tlv_list = aBuilder.createStoreMeta();
340 } catch (const std::exception &e) {
341 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring store entry because " << e.what());
e2851fe7
AR
342 return false;
343 }
4c2f8b72 344 assert(tlv_list);
e2851fe7 345
aa1a691e
AR
346 // TODO: consume parsed metadata?
347
539283df 348 debugs(47,7, "successful swap meta unpacking; swap_file_sz=" << tmpe.swap_file_sz);
e2851fe7
AR
349 memset(key, '\0', SQUID_MD5_DIGEST_LENGTH);
350
351 InitStoreEntry visitor(&tmpe, key);
352 for_each(*tlv_list, visitor);
353 storeSwapTLVFree(tlv_list);
354 tlv_list = NULL;
355
356 if (storeKeyNull(key)) {
28edca61 357 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring keyless cache entry");
e2851fe7
AR
358 return false;
359 }
360
361 tmpe.key = key;
362 /* check sizes */
363
364 if (expectedSize > 0) {
365 if (tmpe.swap_file_sz == 0) {
366 tmpe.swap_file_sz = expectedSize;
367 } else if (tmpe.swap_file_sz == (uint64_t)(expectedSize - swap_hdr_len)) {
368 tmpe.swap_file_sz = expectedSize;
369 } else if (tmpe.swap_file_sz != expectedSize) {
28edca61
AR
370 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring cache entry due to a " <<
371 "SIZE MISMATCH " << tmpe.swap_file_sz << "!=" << expectedSize);
e2851fe7
AR
372 return false;
373 }
9199139f 374 } else if (tmpe.swap_file_sz <= 0) {
52dce2fe
AR
375 // if caller cannot handle unknown sizes, it must check after the call.
376 debugs(47, 7, "unknown size: " << tmpe);
e2851fe7
AR
377 }
378
379 if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
3254023f 380 ++ stats.badflags;
e2851fe7
AR
381 return false;
382 }
383
384 return true;
385}
386