store_rebuild.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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 20 Store Rebuild Routines */
10
11#include "squid.h"
12#include "debug/Messages.h"
13#include "event.h"
14#include "fde.h"
15#include "globals.h"
16#include "md5.h"
17#include "SquidConfig.h"
18#include "StatCounters.h"
19#include "Store.h"
20#include "store/Disk.h"
21#include "store/SwapMetaIn.h"
22#include "store_digest.h"
23#include "store_key_md5.h"
24#include "store_rebuild.h"
25#include "StoreSearch.h"
26#include "time/gadgets.h"
27
28#include <cerrno>
29
31
32static void storeCleanup(void *);
33
34// TODO: Either convert to Progress or replace with StoreRebuildData.
35// TODO: Handle unknown totals (UFS cache_dir that lost swap.state) correctly.
36typedef struct {
37 /* total number of "swap.state" entries that will be read */
38 int total;
39 /* number of entries read so far */
42
44
45void
46StoreRebuildData::updateStartTime(const timeval &dirStartTime)
47{
48 startTime = started() ? std::min(startTime, dirStartTime) : dirStartTime;
49}
50
51static void
53{
54 static int store_errors = 0;
55 static StoreSearchPointer currentSearch;
56 static int validated = 0;
57 static int seen = 0;
58
59 if (currentSearch == nullptr || currentSearch->isDone())
60 currentSearch = Store::Root().search();
61
62 size_t statCount = 500;
63
64 // TODO: Avoid the loop (and ENTRY_VALIDATED) unless opt_store_doublecheck.
65 while (statCount-- && !currentSearch->isDone() && currentSearch->next()) {
66 StoreEntry *e;
67
68 e = currentSearch->currentItem();
69
70 ++seen;
71
73 continue;
74
75 /*
76 * Calling StoreEntry->release() has no effect because we're
77 * still in 'store_rebuilding' state
78 */
79 if (!e->hasDisk())
80 continue;
81
83 if (e->disk().doubleCheck(*e))
84 ++store_errors;
85
87
88 /*
89 * Only set the file bit if we know its a valid entry
90 * otherwise, set it in the validation procedure
91 */
92
93 if ((++validated & 0x3FFFF) == 0)
94 /* TODO format the int with with a stream operator */
95 debugs(20, DBG_IMPORTANT, " " << validated << " Entries Validated so far.");
96 }
97
98 if (currentSearch->isDone()) {
99 debugs(20, 2, "Seen: " << seen << " entries");
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");
105
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 }
112
113 if (store_digest)
115
116 currentSearch = nullptr;
117 } else
118 eventAdd("storeCleanup", storeCleanup, nullptr, 0.0, 1);
119}
120
121/* meta data recreated from disk image in swap directory */
122void
123
125{
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
144
145 /*
146 * When store_dirs_rebuilding == 1, it means we are done reading
147 * or scanning all cache_dirs. Now report the stats and start
148 * the validation (storeCleanup()) thread.
149 */
150
152 return;
153
154 const auto dt = tvSubDsec(counts.startTime, current_time);
155
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 (" <<
166 ((double) counts.objcount / (dt > 0.0 ? dt : 1.0)) << " objects/sec).");
167 debugs(20, Important(56), "Beginning Validation Procedure");
168
169 eventAdd("storeCleanup", storeCleanup, nullptr, 0.0, 1);
170
172
173 RebuildProgress = nullptr;
174}
175
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 */
181void
183{
184 counts = StoreRebuildData(); // reset counters
185 /*
186 * Note: store_dirs_rebuilding is initialized to 1.
187 *
188 * When we parse the configuration and construct each swap dir,
189 * the construction of that raises the rebuild count.
190 *
191 * This prevents us from trying to write clean logs until we
192 * finished rebuilding - including after a reconfiguration that opens an
193 * existing swapdir. The corresponding decrement * occurs in
194 * storeCleanup(), when it is finished.
195 */
197 sizeof(store_rebuild_progress));
198}
199
200/*
201 * A fs-specific rebuild procedure periodically reports its
202 * progress.
203 */
204void
205storeRebuildProgress(int sd_index, int total, int sofar)
206{
207 static time_t last_report = 0;
208 // TODO: Switch to int64_t and fix handling of unknown totals.
209 double n = 0.0;
210 double d = 0.0;
211
212 if (sd_index < 0)
213 return;
214
215 if (sd_index >= Config.cacheSwap.n_configured)
216 return;
217
218 if (nullptr == RebuildProgress)
219 return;
220
221 RebuildProgress[sd_index].total = total;
222
223 RebuildProgress[sd_index].scanned = sofar;
224
225 if (squid_curtime - last_report < 15)
226 return;
227
228 for (sd_index = 0; sd_index < Config.cacheSwap.n_configured; ++sd_index) {
229 n += (double) RebuildProgress[sd_index].scanned;
230 d += (double) RebuildProgress[sd_index].total;
231 }
232
233 debugs(20, Important(57), "Indexing cache entries: " << Progress(n, d));
234 last_report = squid_curtime;
235}
236
237void
238Progress::print(std::ostream &os) const
239{
240 if (goal > 0) {
241 const auto savedPrecision = os.precision(2);
242 const auto percent = 100.0 * completed / goal;
243 os << percent << "% (" << completed << " out of " << goal << ")";
244 (void)os.precision(savedPrecision);
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
253bool
254storeRebuildLoadEntry(int fd, int diskIndex, MemBuf &buf, StoreRebuildData &)
255{
256 if (fd < 0)
257 return false;
258
259 assert(buf.hasSpace()); // caller must allocate
260
261 const int len = FD_READ_METHOD(fd, buf.space(), buf.spaceSize());
263 if (len < 0) {
264 const int xerrno = errno;
265 debugs(47, DBG_IMPORTANT, "WARNING: cache_dir[" << diskIndex << "]: " <<
266 "Ignoring cached entry after meta data read failure: " << xstrerr(xerrno));
267 return false;
268 }
269
270 buf.appended(len);
271 return true;
272}
273
274bool
277 uint64_t expectedSize)
278{
279 uint64_t swap_hdr_len = 0;
280
281 tmpe.key = nullptr;
282
283 try {
284 swap_hdr_len = Store::UnpackIndexSwapMeta(buf, tmpe, key);
285 } catch (...) {
286 debugs(47, Important(65), "WARNING: Indexer ignores a cache_dir entry: " << CurrentException);
287 return false;
288 }
289
290 // TODO: consume parsed metadata?
291
292 debugs(47,7, "successful swap meta unpacking; swap_file_sz=" << tmpe.swap_file_sz);
293
294 if (!tmpe.key) {
295 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring keyless cache entry");
296 return false;
297 }
298
299 /* check sizes */
300
301 if (expectedSize > 0) {
302 if (tmpe.swap_file_sz == 0) {
303 tmpe.swap_file_sz = expectedSize;
304 } else if (tmpe.swap_file_sz == (uint64_t)(expectedSize - swap_hdr_len)) {
305 tmpe.swap_file_sz = expectedSize;
306 } else if (tmpe.swap_file_sz != expectedSize) {
307 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring cache entry due to a " <<
308 "SIZE MISMATCH " << tmpe.swap_file_sz << "!=" << expectedSize);
309 return false;
310 }
311 } else if (tmpe.swap_file_sz <= 0) {
312 // if caller cannot handle unknown sizes, it must check after the call.
313 debugs(47, 7, "unknown size: " << tmpe);
314 }
315
316 if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
317 ++ stats.badflags;
318 return false;
319 }
320
321 return true;
322}
323
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define assert(EX)
Definition: assert.h:17
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1313
Definition: MemBuf.h:24
mb_size_t spaceSize() const
Definition: MemBuf.cc:155
char * space()
returns buffer after data; does not check space existence
Definition: MemBuf.h:57
bool hasSpace() const
Definition: MemBuf.h:72
void appended(mb_size_t sz)
updates content size after external append
Definition: MemBuf.cc:226
advancement of work that consists of (usually known number) of similar steps
Definition: store_rebuild.h:47
int64_t goal
the known total number of work steps (or negative)
Definition: store_rebuild.h:56
void print(std::ostream &os) const
brief progress report suitable for level-0/1 debugging
int64_t completed
the number of finished work steps
Definition: store_rebuild.h:55
Store::DiskConfig cacheSwap
Definition: SquidConfig.h:423
struct StatCounters::@130 syscalls
struct StatCounters::@130::@134 disk
uint16_t flags
Definition: Store.h:232
bool hasDisk(const sdirno dirn=-1, const sfileno filen=-1) const
Definition: store.cc:1915
Store::Disk & disk() const
the disk this entry is [being] cached on; asserts for entries w/o a disk
Definition: store.cc:1906
uint64_t swap_file_sz
Definition: Store.h:230
cache_dir(s) indexing statistics
Definition: store_rebuild.h:20
void updateStartTime(const timeval &dirStartTime)
maintain earliest initiation time across multiple indexing cache_dirs
bool started() const
whether we have worked on indexing this(these) cache_dir(s) before
Definition: store_rebuild.h:26
timeval startTime
absolute time when the rebuild was initiated
Definition: store_rebuild.h:42
int64_t validations
the number of validated cache entries, slots
Definition: store_rebuild.h:41
virtual void next(void(callback)(void *cbdata), void *cbdata)=0
virtual StoreEntry * currentItem()=0
virtual bool isDone() const =0
static int store_dirs_rebuilding
the number of cache_dirs being rebuilt; TODO: move to Disks::Rebuilding
Definition: Controller.h:134
StoreSearch * search()
Definition: Controller.cc:211
virtual bool doubleCheck(StoreEntry &)
Definition: Disk.cc:46
A const & min(A const &lhs, A const &rhs)
#define Important(id)
Definition: Messages.h:93
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define EBIT_SET(flag, bit)
Definition: defines.h:67
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
@ ENTRY_VALIDATED
Definition: enums.h:113
@ 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
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
int opt_store_doublecheck
CacheDigest * store_digest
class Ping::pingStats_ stats
Controller & Root()
safely access controller singleton
Definition: Controller.cc:938
size_t UnpackIndexSwapMeta(const MemBuf &, StoreEntry &, cache_key *)
Definition: SwapMetaIn.cc:243
#define xfree
unsigned char cache_key
Store key.
Definition: forward.h:29
void storeDigestNoteStoreReady(void)
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)
static store_rebuild_progress * RebuildProgress
void storeRebuildStart(void)
static void storeCleanup(void *)
void EVH void double
Definition: stub_event.cc:16
double tvSubDsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:44
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:17
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors