]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Do not treat tiny virgin response buffer space specially (#2056)
authorEduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Wed, 23 Apr 2025 12:59:03 +0000 (12:59 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Wed, 23 Apr 2025 15:27:02 +0000 (15:27 +0000)
A long series of commits sprinkled Squid code with special treatment of
virgin response buffer space equal to one or two bytes:

* 2005 commit 2afaba07 adds a 2-byte hack to HttpStateData. The hack
  avoids stalling transactions by never asking
  StoreEntry::delayAwareRead() to read 1 byte.

* 2006 commit 253cacc adds the same 2-byte hack to Ftp::Client.

* 2007 commit 478cfe99 adds a 2-byte hack to
  Icap::ModXact::virginConsume() so that ICAP transactions do not stall
  when HttpStateData hack prevents new virgin bytes from being read into
  a BodyPipe buffer that has just one or two space bytes left. The ICAP
  hack was probably one-byte more aggressive than necessary...

* 2011 commit 0ad2b63b adds +1 byte hacks to ServerStateData and
  ClientHttpRequest to work around the same StoreEntry::delayAwareRead()
  problem leading to excessive buffering with slow clients.

* A followup 2012 commit 4dc2b072 adjusts StoreEntry::delayAwareRead()
  to allow 1-byte reads, making _all_ of the above hacks unnecessary.
  However, that commit only removed +1 byte hacks added in 2011, missing
  HttpStateData and ICAP hacks added in 2005, 2006 and 2007, probably
  because that 2012 work focused on fixing the bug introduced by 2011
  commit (that was triggered by slow client-Squid communication).

We now remove the remaining known hacks that worked around
StoreEntry::delayAwareRead() inability to read 1 byte (i.e. the first
three hacks on the above list).

These changes also happen to fix transaction stalls with read_ahead_gap
set to 1 and probably some other rare use cases: Transactions could
stall because inBuf.spaceSize() may be 1 for an emptied inBuf, leading
to maybeMakeSpaceAvailable() returning zero due to `read_size < 2` being
true. That zero result prevents HttpStateData from making progress by
reading new virgin response bytes. Mistreatment of "emptied inBuf" cases
was exposed by 2023 commit 50c5af88 changes. More work is needed to
address other, more prominent "emptied inBuf" cases.

src/adaptation/icap/ModXact.cc
src/clients/FtpClient.cc
src/http.cc

index e03c9c21df9bad2432ffcdce41a1e2bd06b2e726..f7201fb8b3b5ff1d92f731717526a51a6a62a39f 100644 (file)
@@ -435,10 +435,7 @@ void Adaptation::Icap::ModXact::virginConsume()
     BodyPipe &bp = *virgin.body_pipe;
     const bool wantToPostpone = isRepeatable || canStartBypass || protectGroupBypass;
 
-    // Why > 2? HttpState does not use the last bytes in the buffer
-    // because Client::delayRead() is arguably broken. See
-    // HttpStateData::maybeReadVirginBody for more details.
-    if (wantToPostpone && bp.buf().spaceSize() > 2) {
+    if (wantToPostpone && bp.buf().spaceSize() > 0) {
         // Postponing may increase memory footprint and slow the HTTP side
         // down. Not postponing may increase the number of ICAP errors
         // if the ICAP service fails. We may also use "potential" space to
index 3cd38db070df1e433a761e949388a9f2a9f3ba59..a667cd5df89f72a4fa924407fd2dc0594efefefa 100644 (file)
@@ -929,7 +929,7 @@ Ftp::Client::maybeReadVirginBody()
 
     debugs(9, 9, "FTP may read up to " << read_sz << " bytes");
 
-    if (read_sz < 2) // see http.cc
+    if (!read_sz)
         return;
 
     data.read_pending = true;
index 6411c292034dd0a99d264b8bec365b4d4750add1..f15cf5b5efb0d4cd63d4f89a2748e4de50a9e301 100644 (file)
@@ -1699,7 +1699,7 @@ HttpStateData::maybeMakeSpaceAvailable(const size_t maxReadSize)
     // how much we want to read
     const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), maxReadSize);
 
-    if (read_size < 2) {
+    if (!read_size) {
         debugs(11, 7, "will not read up to " << read_size << " into buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
         return 0;
     }