MemObject.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 19 Store Memory Primitives */
10 
11 #include "squid.h"
12 #include "comm/Connection.h"
13 #include "Generic.h"
14 #include "globals.h"
15 #include "HttpReply.h"
16 #include "MemBuf.h"
17 #include "MemObject.h"
18 #include "SquidConfig.h"
19 #include "Store.h"
20 #include "StoreClient.h"
21 
22 #if USE_DELAY_POOLS
23 #include "DelayPools.h"
24 #endif
25 
26 /* TODO: make this global or private */
27 #if URL_CHECKSUM_DEBUG
28 static unsigned int url_checksum(const char *url);
29 unsigned int
30 url_checksum(const char *url)
31 {
32  unsigned int ck;
33  SquidMD5_CTX M;
34  static unsigned char digest[16];
35  SquidMD5Init(&M);
36  SquidMD5Update(&M, (unsigned char *) url, strlen(url));
37  SquidMD5Final(digest, &M);
38  memcpy(&ck, digest, sizeof(ck));
39  return ck;
40 }
41 
42 #endif
43 
45 
46 size_t
48 {
49  return Pool().inUseCount();
50 }
51 
52 const char *
54 {
55  if (storeId_.isEmpty()) {
56  debugs(20, DBG_IMPORTANT, "ERROR: Squid BUG: Missing MemObject::storeId value");
57  dump();
58  storeId_ = "[unknown_URI]";
59  }
60  return storeId_.c_str();
61 }
62 
63 const char *
65 {
66  return logUri_.isEmpty() ? storeId() : logUri_.c_str();
67 }
68 
69 bool
71 {
72  return !storeId_.isEmpty();
73 }
74 
75 void
76 MemObject::setUris(char const *aStoreId, char const *aLogUri, const HttpRequestMethod &aMethod)
77 {
78  if (hasUris())
79  return;
80 
81  storeId_ = aStoreId;
82  debugs(88, 3, this << " storeId: " << storeId_);
83 
84  // fast pointer comparison for a common storeCreateEntry(url,url,...) case
85  if (!aLogUri || aLogUri == aStoreId)
86  logUri_.clear(); // use storeId_ by default to minimize copying
87  else
88  logUri_ = aLogUri;
89 
90  method = aMethod;
91 
92 #if URL_CHECKSUM_DEBUG
93  chksum = url_checksum(urlXXX());
94 #endif
95 }
96 
98 {
99  debugs(20, 3, "MemObject constructed, this=" << this);
100  ping_reply_callback = nullptr;
101  memset(&start_ping, 0, sizeof(start_ping));
102  reply_ = new HttpReply;
103 }
104 
106 {
107  debugs(20, 3, "MemObject destructed, this=" << this);
108 
109 #if URL_CHECKSUM_DEBUG
110  checkUrlChecksum();
111 #endif
112 
113  assert(xitTable.index < 0);
114  assert(memCache.index < 0);
115  assert(swapout.sio == nullptr);
116 
118 }
119 
120 HttpReply &
122 {
124  return *reply_;
125 }
126 
127 void
129 {
130  assert(r);
131  reply_ = r;
132  updatedReply_ = nullptr;
133 }
134 
135 void
136 MemObject::write(const StoreIOBuffer &writeBuffer)
137 {
138  debugs(19, 6, "memWrite: offset " << writeBuffer.offset << " len " << writeBuffer.length);
139 
140  /* We don't separate out mime headers yet, so ensure that the first
141  * write is at offset 0 - where they start
142  */
143  assert (data_hdr.endOffset() || writeBuffer.offset == 0);
144 
145  assert (data_hdr.write (writeBuffer));
146 }
147 
148 void
150 {
151  data_hdr.dump();
152 
153  debugs(20, DBG_IMPORTANT, "MemObject->start_ping: " << start_ping);
154  debugs(20, DBG_IMPORTANT, "MemObject->inmem_hi: " << data_hdr.endOffset());
155  debugs(20, DBG_IMPORTANT, "MemObject->inmem_lo: " << inmem_lo);
156  debugs(20, DBG_IMPORTANT, "MemObject->nclients: " << nclients);
157  debugs(20, DBG_IMPORTANT, "MemObject->reply: " << reply_);
158  debugs(20, DBG_IMPORTANT, "MemObject->updatedReply: " << updatedReply_);
159  debugs(20, DBG_IMPORTANT, "MemObject->appliedUpdates: " << appliedUpdates);
160  debugs(20, DBG_IMPORTANT, "MemObject->request: " << request);
161  debugs(20, DBG_IMPORTANT, "MemObject->logUri: " << logUri_);
162  debugs(20, DBG_IMPORTANT, "MemObject->storeId: " << storeId_);
163 }
164 
165 struct LowestMemReader : public unary_function<store_client, void> {
166  LowestMemReader(int64_t seed):current(seed) {}
167 
168  void operator() (store_client const &x) {
169  if (x.getType() == STORE_MEM_CLIENT)
171  }
172 
173  int64_t current;
174 };
175 
176 struct StoreClientStats : public unary_function<store_client, void> {
177  StoreClientStats(MemBuf *anEntry):where(anEntry),index(0) {}
178 
179  void operator()(store_client const &x) {
180  x.dumpStats(where, index);
181  ++index;
182  }
183 
185  size_t index;
186 };
187 
188 void
190 {
191  mb->appendf("\t" SQUIDSBUFPH " %s\n", SQUIDSBUFPRINT(method.image()), logUri());
192  if (!vary_headers.isEmpty())
193  mb->appendf("\tvary_headers: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(vary_headers));
194  mb->appendf("\tinmem_lo: %" PRId64 "\n", inmem_lo);
195  mb->appendf("\tinmem_hi: %" PRId64 "\n", data_hdr.endOffset());
196  mb->appendf("\tswapout: %" PRId64 " bytes queued\n", swapout.queue_offset);
197 
198  if (swapout.sio.getRaw())
199  mb->appendf("\tswapout: %" PRId64 " bytes written\n", (int64_t) swapout.sio->offset());
200 
201  if (xitTable.index >= 0)
202  mb->appendf("\ttransient index: %d state: %d\n", xitTable.index, xitTable.io);
203  if (memCache.index >= 0)
204  mb->appendf("\tmem-cache index: %d state: %d offset: %" PRId64 "\n", memCache.index, memCache.io, memCache.offset);
205  if (object_sz >= 0)
206  mb->appendf("\tobject_sz: %" PRId64 "\n", object_sz);
207 
208  StoreClientStats statsVisitor(mb);
209 
210  for_each<StoreClientStats>(clients, statsVisitor);
211 }
212 
213 int64_t
215 {
216  return data_hdr.endOffset();
217 }
218 
219 void
221 {
222  const int hdr_sz = endOffset();
223  assert(hdr_sz >= 0);
224  assert(reply_);
225  reply_->hdr_sz = hdr_sz;
226 }
227 
228 int64_t
230 {
231  if (object_sz < 0)
232  return endOffset();
233 
234  return object_sz;
235 }
236 
237 int64_t
239 {
240  if (object_sz >= 0) {
241  debugs(20, 7, object_sz << " frozen by complete()");
242  return object_sz;
243  }
244 
245  const auto hdr_sz = baseReply().hdr_sz;
246 
247  // Cannot predict future length using an empty/unset or HTTP/0 reply.
248  // For any HTTP/1 reply, hdr_sz is positive -- status-line cannot be empty.
249  if (hdr_sz <= 0)
250  return -1;
251 
252  const auto clen = baseReply().bodySize(method);
253  if (clen < 0) {
254  debugs(20, 7, "unknown; hdr: " << hdr_sz);
255  return -1;
256  }
257 
258  const auto messageSize = clen + hdr_sz;
259  debugs(20, 7, messageSize << " hdr: " << hdr_sz << " clen: " << clen);
260  return messageSize;
261 }
262 
263 void
265 {
266  assert(swapout.sio == nullptr);
268  inmem_lo = 0;
269  /* Should we check for clients? */
270  assert(reply_);
271  reply_->reset();
272  updatedReply_ = nullptr;
273  appliedUpdates = false;
274 }
275 
276 int64_t
278 {
279  LowestMemReader lowest (endOffset() + 1);
280 
281  for_each <LowestMemReader>(clients, lowest);
282 
283  return lowest.current;
284 }
285 
286 /* XXX: This is wrong. It breaks *badly* on range combining */
287 bool
289 {
290  const auto savedHttpHeaders = baseReply().hdr_sz;
291  const bool canRead = endOffset() - savedHttpHeaders <
293 
294  if (!canRead) {
295  debugs(19, 5, "no: " << endOffset() << '-' << savedHttpHeaders <<
296  " < " << lowestMemReaderOffset() << '+' << Config.readAheadGap);
297  }
298 
299  return canRead;
300 }
301 
302 void
304 {
305  ++nclients;
306  dlinkAdd(aClient, &aClient->node, &clients);
307 }
308 
309 #if URL_CHECKSUM_DEBUG
310 void
311 MemObject::checkUrlChecksum () const
312 {
313  assert(chksum == url_checksum(urlXXX()));
314 }
315 
316 #endif
317 
318 /*
319  * How much of the object data is on the disk?
320  */
321 int64_t
323 {
324  /*
325  * NOTE: storeOffset() represents the disk file size,
326  * not the amount of object data on disk.
327  *
328  * If we don't have at least 'swap_hdr_sz' bytes
329  * then none of the object data is on disk.
330  *
331  * This should still be safe if swap_hdr_sz == 0,
332  * meaning we haven't even opened the swapout file
333  * yet.
334  */
335 
336  if (swapout.sio.getRaw() == nullptr)
337  return 0;
338 
339  int64_t nwritten = swapout.sio->offset();
340 
341  if (nwritten <= (int64_t)swap_hdr_sz)
342  return 0;
343 
344  return (nwritten - swap_hdr_sz);
345 }
346 
347 int64_t
349 {
350  /*
351  * Careful. lowest_offset can be greater than endOffset(), such
352  * as in the case of a range request.
353  */
354  int64_t lowest_offset = lowestMemReaderOffset();
355 
356  // XXX: Remove the last (Config.onoff.memory_cache_first-based) condition
357  // and update keepForLocalMemoryCache() accordingly. The caller wants to
358  // remove all local memory that is safe to remove. Honoring caching
359  // preferences is its responsibility. Our responsibility is safety. The
360  // situation was different when ff4b33f added that condition -- there was no
361  // keepInLocalMemory/keepForLocalMemoryCache() call guard back then.
362  if (endOffset() < lowest_offset ||
363  endOffset() - inmem_lo > (int64_t)Config.Store.maxInMemObjSize ||
364  (swap && !Config.onoff.memory_cache_first))
365  return lowest_offset;
366 
367  return inmem_lo;
368 }
369 
370 void
372 {
373  int64_t new_mem_lo = policyLowestOffsetToKeep(1);
374  /*
375  * We should only free up to what we know has been written
376  * to disk, not what has been queued for writing. Otherwise
377  * there will be a chunk of the data which is not in memory
378  * and is not yet on disk.
379  * The -1 makes sure the page isn't freed until storeSwapOut has
380  * walked to the next page.
381  */
382  int64_t on_disk;
383 
384  if ((on_disk = objectBytesOnDisk()) - 1 < new_mem_lo)
385  new_mem_lo = on_disk - 1;
386 
387  if (new_mem_lo == -1)
388  new_mem_lo = 0; /* the above might become -1 */
389 
390  data_hdr.freeDataUpto(new_mem_lo);
391 
392  inmem_lo = new_mem_lo;
393 }
394 
395 void
397 {
398  if (const int64_t new_mem_lo = policyLowestOffsetToKeep(false)) {
399  assert (new_mem_lo > 0);
400  data_hdr.freeDataUpto(new_mem_lo);
401  inmem_lo = new_mem_lo;
402  } // else we should not trim anything at this time
403 }
404 
405 bool
407 {
409  /* XXX : make this higher level */
410  debugs (19, result ? 4 :3, "MemObject::isContiguous: Returning " << (result ? "true" : "false"));
411  return result;
412 }
413 
414 int
415 MemObject::mostBytesWanted(int max, bool ignoreDelayPools) const
416 {
417 #if USE_DELAY_POOLS
418  if (!ignoreDelayPools) {
419  /* identify delay id with largest allowance */
420  DelayId largestAllowance = mostBytesAllowed ();
421  return largestAllowance.bytesWanted(0, max);
422  }
423 #else
424  (void)ignoreDelayPools;
425 #endif
426 
427  return max;
428 }
429 
430 void
431 MemObject::setNoDelay(bool const newValue)
432 {
433 #if USE_DELAY_POOLS
434 
435  for (dlink_node *node = clients.head; node; node = node->next) {
436  store_client *sc = (store_client *) node->data;
437  sc->delayId.setNoDelay(newValue);
438  }
439 #else
440  (void)newValue;
441 #endif
442 }
443 
444 void
446 {
447 #if USE_DELAY_POOLS
448  if (readAheadPolicyCanRead()) {
449  if (DelayId mostAllowedId = mostBytesAllowed()) {
450  mostAllowedId.delayRead(aRead);
451  return;
452  }
453  }
454 #endif
455  deferredReads.delay(aRead);
456 }
457 
458 void
460 {
462 }
463 
464 #if USE_DELAY_POOLS
465 DelayId
467 {
468  int j;
469  int jmax = -1;
470  DelayId result;
471 
472  for (dlink_node *node = clients.head; node; node = node->next) {
473  store_client *sc = (store_client *) node->data;
474 
475  j = sc->bytesWanted();
476 
477  if (j > jmax) {
478  jmax = j;
479  result = sc->delayId;
480  }
481  }
482 
483  return result;
484 }
485 
486 #endif
487 
488 int64_t
490 {
491  return endOffset() - swapout.queue_offset;
492 }
493 
int64_t freeDataUpto(int64_t)
Definition: stmem.cc:81
int64_t queue_offset
number of bytes sent to SwapDir for writing
Definition: MemObject.h:161
int hdr_sz
Definition: Message.h:81
Definition: parse.c:104
Store::IoStatus io
current I/O state
Definition: MemObject.h:190
HttpReply & adjustableBaseReply()
Definition: MemObject.cc:121
bool readAheadPolicyCanRead() const
Definition: MemObject.cc:288
LowestMemReader(int64_t seed)
Definition: MemObject.cc:166
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
StoreClientStats(MemBuf *anEntry)
Definition: MemObject.cc:177
SQUIDCEXTERN void SquidMD5Init(struct SquidMD5Context *context)
Definition: md5.c:73
const char * storeId() const
Definition: MemObject.cc:53
int memory_cache_first
Definition: SquidConfig.h:335
bool isEmpty() const
Definition: SBuf.h:435
mem_hdr data_hdr
Definition: MemObject.h:148
int mostBytesWanted(int max, bool ignoreDelayPools) const
Definition: MemObject.cc:415
struct node * next
Definition: parse.c:105
void trimUnSwappable()
Definition: MemObject.cc:396
struct timeval start_ping
Definition: MemObject.h:207
int bytesWanted(int min, int max) const
Definition: DelayId.cc:126
DelayedAsyncCalls deferredReads
Definition: MemObject.h:233
int64_t offset
bytes written/read to/from the memory cache so far
Definition: MemObject.h:199
int64_t bodySize(const HttpRequestMethod &) const
Definition: HttpReply.cc:377
MemCache memCache
current [shared] memory caching state for the entry
Definition: MemObject.h:203
int64_t offset
Definition: StoreIOBuffer.h:58
StoreIOState::Pointer sio
Definition: MemObject.h:162
void delay(const AsyncCallPointer &)
stores the given call to schedule it at schedule() or destruction time
void setNoDelay(bool const newValue)
Definition: MemObject.cc:431
int64_t expectedReplySize() const
Definition: MemObject.cc:238
DelayId mostBytesAllowed() const
Definition: MemObject.cc:466
bool isContiguous() const
Definition: MemObject.cc:406
const A & max(A const &lhs, A const &rhs)
struct SquidConfig::@97 onoff
C * getRaw() const
Definition: RefCount.h:89
const SBuf & image() const
bool write(StoreIOBuffer const &)
Definition: stmem.cc:303
int64_t endOffset() const
Definition: MemObject.cc:214
SQUIDCEXTERN void SquidMD5Final(uint8_t digest[16], struct SquidMD5Context *context)
int64_t availableForSwapOut() const
buffered bytes we have not swapped out yet
Definition: MemObject.cc:489
RemovalPolicy * mem_policy
Definition: MemObject.cc:44
int64_t inmem_lo
Definition: MemObject.h:149
void clear()
Definition: SBuf.cc:175
void replaceBaseReply(const HttpReplyPointer &r)
Definition: MemObject.cc:128
int64_t endOffset() const
Definition: stmem.cc:45
const char * logUri() const
client request URI used for logging; storeId() by default
Definition: MemObject.cc:64
Definition: Range.h:18
void operator()(store_client const &x)
Definition: MemObject.cc:168
@ STORE_MEM_CLIENT
Definition: enums.h:68
HttpReplyPointer reply_
Definition: MemObject.h:227
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
SBuf vary_headers
Definition: MemObject.h:221
const HttpReply & baseReply() const
Definition: MemObject.h:60
SBuf storeId_
StoreId for our entry (usually request URI)
Definition: MemObject.h:230
Definition: MemBuf.h:23
int64_t policyLowestOffsetToKeep(bool swap) const
Definition: MemObject.cc:348
int32_t index
entry position inside the in-transit table
Definition: MemObject.h:189
int64_t current
Definition: MemObject.cc:173
void reset() override
Definition: HttpReply.cc:59
void write(const StoreIOBuffer &buf)
Definition: MemObject.cc:136
XitTable xitTable
current [shared] memory caching state for the entry
Definition: MemObject.h:192
off_t offset() const
Definition: StoreIOState.h:48
struct SquidConfig::@95 Store
HttpRequestMethod method
Definition: MemObject.h:147
#define assert(EX)
Definition: assert.h:17
void dump() const
Definition: MemObject.cc:149
void dump() const
Definition: stmem.cc:359
void schedule()
schedules and forgets all async calls previously stored by delay()
SBuf logUri_
URI used for logging (usually request URI)
Definition: MemObject.h:231
Store::IoStatus io
current I/O state
Definition: MemObject.h:201
int64_t size() const
Definition: MemObject.cc:229
static size_t inUseCount()
Definition: MemObject.cc:47
static int sc[16]
Definition: smbdes.c:121
const char * c_str()
Definition: SBuf.cc:516
void freeContent()
Definition: stmem.cc:59
void delayRead(const AsyncCallPointer &)
Definition: MemObject.cc:445
size_t maxInMemObjSize
Definition: SquidConfig.h:268
MemBuf * where
Definition: MemObject.cc:184
int getType() const
Definition: store_client.cc:91
bool appliedUpdates
Definition: MemObject.h:90
void markEndOfReplyHeaders()
sets baseReply().hdr_sz (i.e. written reply headers size) to endOffset()
Definition: MemObject.cc:220
bool hasContigousContentRange(Range< int64_t > const &range) const
Definition: stmem.cc:245
int32_t index
entry position inside the memory cache
Definition: MemObject.h:198
dlink_node node
Definition: StoreClient.h:139
auto discardableHttpEnd() const
the client will not use HTTP response bytes with lower offsets (if any)
Definition: StoreClient.h:82
bool hasUris() const
whether setUris() has been called
Definition: MemObject.cc:70
void dumpStats(MemBuf *output, int clientNumber) const
int64_t object_sz
Definition: MemObject.h:215
void trimSwappable()
Definition: MemObject.cc:371
void addClient(store_client *)
Definition: MemObject.cc:303
HttpReplyPointer updatedReply_
Definition: MemObject.h:228
#define DBG_IMPORTANT
Definition: Stream.h:38
#define PRId64
Definition: types.h:104
SQUIDCEXTERN void SquidMD5Update(struct SquidMD5Context *context, const void *buf, unsigned len)
Definition: md5.c:89
void kickReads()
Definition: MemObject.cc:459
int64_t readAheadGap
Definition: SquidConfig.h:98
int64_t lowestMemReaderOffset() const
Definition: MemObject.cc:277
const char * urlXXX() const
Definition: MemObject.h:138
IRCB * ping_reply_callback
Definition: MemObject.h:208
void reset()
Definition: MemObject.cc:264
int64_t objectBytesOnDisk() const
Definition: MemObject.cc:322
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
void stat(MemBuf *mb) const
Definition: MemObject.cc:189
HttpRequestPointer request
Definition: MemObject.h:205
void setUris(char const *aStoreId, char const *aLogUri, const HttpRequestMethod &aMethod)
Definition: MemObject.cc:76
size_t swap_hdr_sz
Definition: MemObject.h:216
int nclients
Definition: MemObject.h:156
void operator()(store_client const &x)
Definition: MemObject.cc:179
#define SQUIDSBUFPH
Definition: SBuf.h:31
class SquidConfig Config
Definition: SquidConfig.cc:12
dlink_list clients
Definition: MemObject.h:150
SwapOut swapout
Definition: MemObject.h:169

 

Introduction

Documentation

Support

Miscellaneous