]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_rebuild.cc
protos.h refactoring, part one.
[thirdparty/squid.git] / src / store_rebuild.cc
CommitLineData
886f2785 1
8638fc66 2/*
262a0e14 3 * $Id$
8638fc66 4 *
5 * DEBUG: section 20 Store Rebuild Routines
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
8638fc66 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
8638fc66 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
26ac0430 24 *
8638fc66 25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
26ac0430 29 *
8638fc66 30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
8638fc66 34 */
35
582c2af2 36#include "squid.h"
a553a5a3 37#include "event.h"
582c2af2
FC
38#include "globals.h"
39#include "md5.h"
40#include "protos.h"
e4f1fdae 41#include "StatCounters.h"
e6ccf245 42#include "Store.h"
fc54b8d2 43#include "store_key_md5.h"
d3b3ab85 44#include "SwapDir.h"
c8f4eac4 45#include "StoreSearch.h"
985c86bc 46#include "SquidTime.h"
f09f5b26 47
21d845b1
FC
48#if HAVE_ERRNO_H
49#include <errno.h>
50#endif
b2c141d4 51static struct _store_rebuild_data counts;
62e76326 52
b2c141d4 53static struct timeval rebuild_start;
9bea1d5b 54static void storeCleanup(void *);
f09f5b26 55
26ac0430 56typedef struct {
5a797987 57 /* total number of "swap.state" entries that will be read */
58 int total;
59 /* number of entries read so far */
60 int scanned;
2fadd50d 61} store_rebuild_progress;
5a797987 62
63static store_rebuild_progress *RebuildProgress = NULL;
64
68ea4c8b 65static int
9bea1d5b 66storeCleanupDoubleCheck(StoreEntry * e)
68ea4c8b 67{
c8f4eac4 68 SwapDir *SD = dynamic_cast<SwapDir *>(INDEXSD(e->swap_dirn));
d3b3ab85 69 return (SD->doubleCheck(*e));
68ea4c8b 70}
71
12784378 72static void
9bea1d5b 73storeCleanup(void *datanotused)
f09f5b26 74{
9bea1d5b 75 static int store_errors = 0;
c8f4eac4 76 static StoreSearchPointer currentSearch;
77 static int validated = 0;
78
79 if (currentSearch == NULL || currentSearch->isDone())
80 currentSearch = Store::Root().search(NULL, NULL);
81
82 size_t statCount = 500;
83
26031546 84 // TODO: Avoid the loop (and ENTRY_VALIDATED) unless opt_store_doublecheck.
c8f4eac4 85 while (statCount-- && !currentSearch->isDone() && currentSearch->next()) {
c8f4eac4 86 StoreEntry *e;
87
88 e = currentSearch->currentItem();
89
90 if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
91 continue;
92
93 /*
5f33b71d 94 * Calling StoreEntry->release() has no effect because we're
c8f4eac4 95 * still in 'store_rebuilding' state
96 */
97 if (e->swap_filen < 0)
98 continue;
99
100 if (opt_store_doublecheck)
101 if (storeCleanupDoubleCheck(e))
5db6bf73 102 ++store_errors;
c8f4eac4 103
104 EBIT_SET(e->flags, ENTRY_VALIDATED);
105
106 /*
107 * Only set the file bit if we know its a valid entry
108 * otherwise, set it in the validation procedure
109 */
e2851fe7 110
c8f4eac4 111 if ((++validated & 0x3FFFF) == 0)
112 /* TODO format the int with with a stream operator */
e0236918 113 debugs(20, DBG_IMPORTANT, " " << validated << " Entries Validated so far.");
f09f5b26 114 }
62e76326 115
c8f4eac4 116 if (currentSearch->isDone()) {
e0236918
FC
117 debugs(20, DBG_IMPORTANT, " Completed Validation Procedure");
118 debugs(20, DBG_IMPORTANT, " Validated " << validated << " Entries");
119 debugs(20, DBG_IMPORTANT, " store_swap_size = " << Store::Root().currentSize() / 1024.0 << " KB");
5e263176 120 --StoreController::store_dirs_rebuilding;
bef81ea5 121 assert(0 == StoreController::store_dirs_rebuilding);
c8f4eac4 122
2f6fcab1
AR
123 if (opt_store_doublecheck && store_errors) {
124 fatalf("Quitting after finding %d cache index inconsistencies. " \
125 "Removing cache index will force its slow rebuild. " \
126 "Removing -S will let Squid start with an inconsistent " \
127 "cache index (at your own risk).\n", store_errors);
128 }
c8f4eac4 129
130 if (store_digest)
131 storeDigestNoteStoreReady();
132
133 currentSearch = NULL;
134 } else
135 eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
f09f5b26 136}
137
f09f5b26 138/* meta data recreated from disk image in swap directory */
b2c141d4 139void
62e76326 140
9bea1d5b 141storeRebuildComplete(struct _store_rebuild_data *dc)
f09f5b26 142{
9bea1d5b 143 double dt;
144 counts.objcount += dc->objcount;
145 counts.expcount += dc->expcount;
146 counts.scancount += dc->scancount;
147 counts.clashcount += dc->clashcount;
148 counts.dupcount += dc->dupcount;
149 counts.cancelcount += dc->cancelcount;
150 counts.invalid += dc->invalid;
151 counts.badflags += dc->badflags;
152 counts.bad_log_op += dc->bad_log_op;
153 counts.zero_object_sz += dc->zero_object_sz;
154 /*
b07b21cc 155 * When store_dirs_rebuilding == 1, it means we are done reading
9bea1d5b 156 * or scanning all cache_dirs. Now report the stats and start
157 * the validation (storeCleanup()) thread.
158 */
62e76326 159
e4b1808b 160 if (StoreController::store_dirs_rebuilding > 1)
62e76326 161 return;
162
9bea1d5b 163 dt = tvSubDsec(rebuild_start, current_time);
62e76326 164
e0236918
FC
165 debugs(20, DBG_IMPORTANT, "Finished rebuilding storage from disk.");
166 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.scancount << " Entries scanned");
167 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.invalid << " Invalid entries.");
168 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.badflags << " With invalid flags.");
169 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.objcount << " Objects loaded.");
170 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.expcount << " Objects expired.");
171 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.cancelcount << " Objects cancelled.");
172 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.dupcount << " Duplicate URLs purged.");
173 debugs(20, DBG_IMPORTANT, " " << std::setw(7) << counts.clashcount << " Swapfile clashes avoided.");
174 debugs(20, DBG_IMPORTANT, " Took "<< std::setw(3)<< std::setprecision(2) << dt << " seconds ("<< std::setw(6) <<
bf8fe701 175 ((double) counts.objcount / (dt > 0.0 ? dt : 1.0)) << " objects/sec).");
e0236918 176 debugs(20, DBG_IMPORTANT, "Beginning Validation Procedure");
62e76326 177
9bea1d5b 178 eventAdd("storeCleanup", storeCleanup, NULL, 0.0, 1);
62e76326 179
9bea1d5b 180 xfree(RebuildProgress);
62e76326 181
9bea1d5b 182 RebuildProgress = NULL;
f09f5b26 183}
184
b2c141d4 185/*
186 * this is ugly. We don't actually start any rebuild threads here,
187 * but only initialize counters, etc. The rebuild threads are
188 * actually started by the filesystem "fooDirInit" function.
189 */
f09f5b26 190void
9bea1d5b 191storeRebuildStart(void)
f09f5b26 192{
9bea1d5b 193 memset(&counts, '\0', sizeof(counts));
194 rebuild_start = current_time;
195 /*
b07b21cc 196 * Note: store_dirs_rebuilding is initialized to 1.
26ac0430
AJ
197 *
198 * When we parse the configuration and construct each swap dir,
bef81ea5 199 * the construction of that raises the rebuild count.
200 *
9bea1d5b 201 * This prevents us from trying to write clean logs until we
bef81ea5 202 * finished rebuilding - including after a reconfiguration that opens an
203 * existing swapdir. The corresponding decrement * occurs in
204 * storeCleanup(), when it is finished.
9bea1d5b 205 */
e6ccf245 206 RebuildProgress = (store_rebuild_progress *)xcalloc(Config.cacheSwap.n_configured,
62e76326 207 sizeof(store_rebuild_progress));
5a797987 208}
209
210/*
211 * A fs-specific rebuild procedure periodically reports its
212 * progress.
213 */
214void
9bea1d5b 215storeRebuildProgress(int sd_index, int total, int sofar)
5a797987 216{
9bea1d5b 217 static time_t last_report = 0;
218 double n = 0.0;
219 double d = 0.0;
62e76326 220
9bea1d5b 221 if (sd_index < 0)
62e76326 222 return;
223
9bea1d5b 224 if (sd_index >= Config.cacheSwap.n_configured)
62e76326 225 return;
226
9bea1d5b 227 if (NULL == RebuildProgress)
62e76326 228 return;
229
9bea1d5b 230 RebuildProgress[sd_index].total = total;
62e76326 231
9bea1d5b 232 RebuildProgress[sd_index].scanned = sofar;
62e76326 233
9bea1d5b 234 if (squid_curtime - last_report < 15)
62e76326 235 return;
236
5db6bf73 237 for (sd_index = 0; sd_index < Config.cacheSwap.n_configured; ++sd_index) {
62e76326 238 n += (double) RebuildProgress[sd_index].scanned;
239 d += (double) RebuildProgress[sd_index].total;
5a797987 240 }
62e76326 241
e0236918 242 debugs(20, DBG_IMPORTANT, "Store rebuilding is "<< std::setw(4)<< std::setprecision(2) << 100.0 * n / d << "% complete");
9bea1d5b 243 last_report = squid_curtime;
f09f5b26 244}
e2851fe7
AR
245
246#include "fde.h"
247#include "StoreMetaUnpacker.h"
248#include "StoreMeta.h"
249#include "Generic.h"
250
251struct InitStoreEntry : public unary_function<StoreMeta, void> {
252 InitStoreEntry(StoreEntry *anEntry, cache_key *aKey):what(anEntry),index(aKey) {}
253
254 void operator()(StoreMeta const &x) {
255 switch (x.getType()) {
256
257 case STORE_META_KEY:
258 assert(x.length == SQUID_MD5_DIGEST_LENGTH);
259 memcpy(index, x.value, SQUID_MD5_DIGEST_LENGTH);
260 break;
261
262 case STORE_META_STD:
263 struct old_metahdr {
264 time_t timestamp;
265 time_t lastref;
266 time_t expires;
267 time_t lastmod;
268 size_t swap_file_sz;
89924985
AR
269 uint16_t refcount;
270 uint16_t flags;
e2851fe7
AR
271 } *tmp;
272 tmp = (struct old_metahdr *)x.value;
273 assert(x.length == STORE_HDR_METASIZE_OLD);
274 what->timestamp = tmp->timestamp;
275 what->lastref = tmp->lastref;
276 what->expires = tmp->expires;
277 what->lastmod = tmp->lastmod;
278 what->swap_file_sz = tmp->swap_file_sz;
279 what->refcount = tmp->refcount;
280 what->flags = tmp->flags;
281 break;
282
283 case STORE_META_STD_LFS:
284 assert(x.length == STORE_HDR_METASIZE);
285 memcpy(&what->timestamp, x.value, STORE_HDR_METASIZE);
286 break;
287
288 default:
289 break;
290 }
291 }
292
293 StoreEntry *what;
294 cache_key *index;
295};
296
297bool
aa1a691e
AR
298storeRebuildLoadEntry(int fd, int diskIndex, MemBuf &buf,
299 struct _store_rebuild_data &counts)
e2851fe7
AR
300{
301 if (fd < 0)
302 return false;
303
aa1a691e 304 assert(buf.hasSpace()); // caller must allocate
e2851fe7 305
aa1a691e 306 const int len = FD_READ_METHOD(fd, buf.space(), buf.spaceSize());
5db6bf73 307 ++ statCounter.syscalls.disk.reads;
aa1a691e
AR
308 if (len < 0) {
309 const int xerrno = errno;
28edca61 310 debugs(47, DBG_IMPORTANT, "WARNING: cache_dir[" << diskIndex << "]: " <<
9199139f 311 "Ignoring cached entry after meta data read failure: " << xstrerr(xerrno));
e2851fe7
AR
312 return false;
313 }
314
aa1a691e
AR
315 buf.appended(len);
316 return true;
317}
318
319bool
320storeRebuildParseEntry(MemBuf &buf, StoreEntry &tmpe, cache_key *key,
321 struct _store_rebuild_data &counts,
322 uint64_t expectedSize)
323{
e2851fe7 324 int swap_hdr_len = 0;
aa1a691e 325 StoreMetaUnpacker aBuilder(buf.content(), buf.contentSize(), &swap_hdr_len);
e2851fe7
AR
326 if (aBuilder.isBufferZero()) {
327 debugs(47,5, HERE << "skipping empty record.");
328 return false;
329 }
330
331 if (!aBuilder.isBufferSane()) {
28edca61 332 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring malformed cache entry.");
e2851fe7
AR
333 return false;
334 }
335
336 StoreMeta *tlv_list = aBuilder.createStoreMeta();
337 if (!tlv_list) {
28edca61
AR
338 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring cache entry with invalid " <<
339 "meta data");
e2851fe7
AR
340 return false;
341 }
342
aa1a691e
AR
343 // TODO: consume parsed metadata?
344
e2851fe7
AR
345 debugs(47,7, HERE << "successful swap meta unpacking");
346 memset(key, '\0', SQUID_MD5_DIGEST_LENGTH);
347
348 InitStoreEntry visitor(&tmpe, key);
349 for_each(*tlv_list, visitor);
350 storeSwapTLVFree(tlv_list);
351 tlv_list = NULL;
352
353 if (storeKeyNull(key)) {
28edca61 354 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring keyless cache entry");
e2851fe7
AR
355 return false;
356 }
357
358 tmpe.key = key;
359 /* check sizes */
360
361 if (expectedSize > 0) {
362 if (tmpe.swap_file_sz == 0) {
363 tmpe.swap_file_sz = expectedSize;
364 } else if (tmpe.swap_file_sz == (uint64_t)(expectedSize - swap_hdr_len)) {
365 tmpe.swap_file_sz = expectedSize;
366 } else if (tmpe.swap_file_sz != expectedSize) {
28edca61
AR
367 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring cache entry due to a " <<
368 "SIZE MISMATCH " << tmpe.swap_file_sz << "!=" << expectedSize);
e2851fe7
AR
369 return false;
370 }
9199139f 371 } else if (tmpe.swap_file_sz <= 0) {
28edca61
AR
372 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring cache entry with " <<
373 "unknown size: " << tmpe);
aa1a691e 374 return false;
e2851fe7
AR
375 }
376
377 if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
5db6bf73 378 ++ counts.badflags;
e2851fe7
AR
379 return false;
380 }
381
382 return true;
383}
384
385bool
386storeRebuildKeepEntry(const StoreEntry &tmpe, const cache_key *key,
387 struct _store_rebuild_data &counts)
388{
389 /* this needs to become
390 * 1) unpack url
391 * 2) make synthetic request with headers ?? or otherwise search
392 * for a matching object in the store
393 * TODO FIXME change to new async api
394 * TODO FIXME I think there is a race condition here with the
395 * async api :
396 * store A reads in object foo, searchs for it, and finds nothing.
397 * store B reads in object foo, searchs for it, finds nothing.
398 * store A gets called back with nothing, so registers the object
399 * store B gets called back with nothing, so registers the object,
400 * which will conflict when the in core index gets around to scanning
401 * store B.
402 *
403 * this suggests that rather than searching for duplicates, the
404 * index rebuild should just assume its the most recent accurate
405 * store entry and whoever indexes the stores handles duplicates.
406 */
407 if (StoreEntry *e = Store::Root().get(key)) {
408
409 if (e->lastref >= tmpe.lastref) {
410 /* key already exists, old entry is newer */
411 /* keep old, ignore new */
5db6bf73 412 ++counts.dupcount;
a46219b7
AR
413
414 // For some stores, get() creates/unpacks a store entry. Signal
415 // such stores that we will no longer use the get() result:
416 e->lock();
417 e->unlock();
418
e2851fe7
AR
419 return false;
420 } else {
421 /* URL already exists, this swapfile not being used */
422 /* junk old, load new */
423 e->release(); /* release old entry */
5db6bf73 424 ++counts.dupcount;
e2851fe7
AR
425 }
426 }
427
428 return true;
429}