* WGs marked with an * asterisk has had at least one new draft made available during the last 5 days

Ticket #223 (closed design: fixed)

Opened 4 years ago

Last modified 15 months ago

Allowing heuristic caching for new status codes

Reported by: mnot@pobox.com Owned by:
Priority: normal Milestone: 22
Component: p6-cache Severity: In WG Last Call
Keywords: Cc:
Origin:

Description

p6 2.3.1.1 defines heuristic caching as applying to a closed set of status codes. New status codes should be able to define that they allow heuristics to be used.

Change History

comment:1 Changed 4 years ago by mnot@pobox.com

From [914]:

Allow new status codes to define whether they allow heuristic freshness (see #223).

comment:2 Changed 4 years ago by mnot@pobox.com

  • Status changed from new to closed
  • Resolution set to incorporated
  • Component changed from non-specific to p6-cache

comment:3 Changed 4 years ago by julian.reschke@gmx.de

From [916]:

Note change in [914] relating to issue 223 (see #223)

comment:4 Changed 4 years ago by mnot@pobox.com

  • Milestone changed from unassigned to 11

comment:5 Changed 4 years ago by mnot@pobox.com

  • Status changed from closed to reopened
  • Resolution incorporated deleted

comment:6 Changed 4 years ago by mnot@pobox.com

  • Status changed from reopened to closed
  • Resolution set to fixed

comment:7 Changed 19 months ago by fielding@gbiv.com

  • Status changed from closed to reopened
  • Resolution fixed deleted
  • Milestone changed from 11 to 22

I ran across this again while revisiting the status code definitions and cannot understand why the original change was made (to which this ticket was responding).

RFC2616 allowed heuristics to be applied for any code that did not explicitly disallow caching or cache heuristics. p6 and p2 currently reverses that to specify a small number of codes as allowing heuristics, which is incorrect when compared to both practice and compatibility with HTTP/1.x.

The small patch applied here was insufficient. The text from RFC2616 (sec. 13.2.2 and 13.2.4) needs to be restored in p6 and the unnecessary bits removed from p2.

comment:8 Changed 19 months ago by mnot@pobox.com

p6 4.1.2 currently says:

If no explicit expiration time is present in a stored response that has a status code whose definition allows heuristic freshness to be used (including the following in Section 6 of [Part2]: 200 (OK), 203 (Non-Authoritative Information), 206 (Partial Content), 300 (Multiple Choices), 301 (Moved Permanently) and 410 (Gone)), a cache may calculate a heuristic expiration time.

This was sourced from 2616 13.4, which says:

A response received with a status code of 200, 203, 206, 300, 301 or 410 MAY be stored by a cache and used in reply to a subsequent request, subject to the expiration mechanism, unless a cache-control directive prohibits caching... A response received with any other status code (e.g. status codes 302 and 307) MUST NOT be returned in a reply to a subsequent request unless there are cache-control directives or another header(s) that explicitly allow it.

Despite the writing style, it's pretty clear to me from this that heuristics are applied only to those status codes which allow it.

So, what are you looking for here? I can see taking the laundry list of status codes out of the sentence above and making them into another paragraph or note, because they're just for convenience, and distract from the main point. I can't see making heuristic freshness opt-out. What bits do you want taken out of p2?

comment:9 Changed 18 months ago by mnot@pobox.com

  • Severity changed from Active WG Document to In WG Last Call

comment:10 Changed 18 months ago by fielding@gbiv.com

I meant the text which contradicts that specific list in RFC2616, which we still have in p6 section 4.1.

Since origin servers do not always provide explicit expiration times, a cache MAY assign a heuristic expiration time when an explicit time is not specified, employing algorithms that use other header field values (such as the Last-Modified time) to estimate a plausible expiration time. This specification does not provide specific algorithms, but does impose worst-case constraints on their results.

I remember that as the original model that I defined for HTTP/1.0. I don't have any memory of that list of status codes introduced in RFC2616 (it was not in RFC2068).

My suggestion is that we delete the list of codes and each such paragraph in the status codes. They are not needed for interoperability. We should only have notes when heuristics are not allowed. So, that would change the first paragraph of 4.1.2 to:

If no explicit expiration time is present in a stored response and the status code does not explicitly disallow heuristic caching, a cache MAY calculate a heuristic expiration time.

Short and sweet. BTW, I have never seen the 113 warn-code used in practice.

comment:11 Changed 18 months ago by mnot@pobox.com

If you're saying that the MAY above can be misinterpreted to grant the use of heuristics too broadly, I agree and will fix.

I have a real problem with changing heuristics to opt-opt; every implementation I've seen has an explicit list of heuristic-available codes, not a default-allow heuristic. Besides which, they'll have to keep a list anyway, since codes they don't recognise will have to be treated as if heuristics aren't allowed.

comment:12 Changed 18 months ago by fielding@gbiv.com

In HTTP, if an origin server does not make effort to mark a response with a specific expiration time, then recipients are free to cache as long as the semantics are understood and make sense to do so. It is the server's responsibility to narrow that window if the server considers expiration important. That is what the first MAY says, and it was properly vetted with the WG (many times over).

Yes, every implementation contains a list of status codes that this cache considers not worth caching. That is how it is supposed to work. Requiring that every cache implementation have the same cache heuristics is an entirely different issue: that is saying we know the context and needs of the cache better than the person who installed the cache. Limiting them to an arbitrary set of status codes is just plain stupid given that status codes are extensible; it is not an interoperability requirement, since the codes that actually need specific cache behavior will define it, and doesn't belong in our spec. This is no different from the nonsense about caching URIs with query components that we removed earlier.

comment:13 Changed 18 months ago by fielding@gbiv.com

FTR, there is no such restriction on heuristic expiration in Apache Traffic Server, though there is a restriction on what codes can be cached at all (which doesn't come close to matching the list in 13.4):

 258 is_negative_caching_appropriate(HttpTransact::State* s)
 259 {
 260   if (!s->txn_conf->negative_caching_enabled || !s->hdr_info.server_response.valid())
 261     return false;
 262 
 263   switch (s->hdr_info.server_response.status_get()) {
 264   case HTTP_STATUS_NO_CONTENT:
 265   case HTTP_STATUS_USE_PROXY:
 266   case HTTP_STATUS_BAD_REQUEST:
 267   case HTTP_STATUS_FORBIDDEN:
 268   case HTTP_STATUS_NOT_FOUND:
 269   case HTTP_STATUS_METHOD_NOT_ALLOWED:
 270   case HTTP_STATUS_REQUEST_URI_TOO_LONG:
 271   case HTTP_STATUS_INTERNAL_SERVER_ERROR:
 272   case HTTP_STATUS_NOT_IMPLEMENTED:
 273   case HTTP_STATUS_BAD_GATEWAY:
 274   case HTTP_STATUS_SERVICE_UNAVAILABLE:
 275   case HTTP_STATUS_GATEWAY_TIMEOUT:
 276     return true;
 277   default:
 278     break;
 279   }
 280 
 281   return false;
 282 }

comment:14 Changed 18 months ago by mnot@pobox.com

I feel like we're arguing past each other. You say "Limiting them to an arbitrary set of status codes is just plain stupid given that status codes are extensible"; in fact the set *is* extensible, and it's not a static list; it's just a list of codes *in this spec* which say they're able to be used with a heuristic.

As such, I think there are only (!) three things to discuss here:

  1. Whether heuristic freshness should be opt-in or opt-out for new status codes.
  1. The application of heuristics to status codes we define. I.e., which ones get it, which ones don't.
  1. Changes in wording to clarify. Always welcome.

Are you talking about this, or are we completely failing to communicate?

Regarding 1, almost everyone I talk to finds heuristic caching surprising, and making surprising things opt-out is extremely anti-social. It's also going to be wordier, because it'll have to be documented as "opt-in, and check what you understand." If it's opt-opt, an implementation doesn't need to check what they understand, because the default is that heuristics aren't used. It'll also mean we're potentially changing the caching semantics for other, extension status codes that were written with the assumption that they wouldn't be heuristically cached.

Regarding 2, I'm fine going here, and IIRC we brought this up a while back, but there wasn't much appetite to discuss. If you want to suggest changes, that's great, we can discuss. Note that without this discussion, making #1 opt-out means that EVERYTHING that currently is silent on heuristics gets a "this status code does not allow heuristic caching".

comment:15 Changed 18 months ago by mnot@pobox.com

Oh, and FWIW the list for negative caching is exactly the same in Squid, because they're both derived from the same codebase way back when.

comment:16 Changed 18 months ago by fielding@gbiv.com

I think that I figured out what problems I am having with the text ...

Originally, each status code would indicate whether or not it is cacheable by default and each method would indicate whether or not it is cacheable by default, and if both are true then the response message is cacheable unless indicated otherwise. Any response that is cacheable and does not have an explicit expiration time allows for a heuristic to calculate freshness. Jeff changed that to an explicit list of codes that are default cacheable and all others (including extensions) are non-cacheable by default.

RFC2616 13.4 Response Cacheability:

A response received with a status code of 200, 203, 206, 300, 301 or 410 MAY be stored by a cache and used in reply to a subsequent request, subject to the expiration mechanism, unless a cache-control directive prohibits caching. However, a cache that does not support the Range and Content-Range headers MUST NOT cache 206 (Partial Content) responses.

A response received with any other status code (e.g. status codes 302 and 307) MUST NOT be returned in a reply to a subsequent request unless there are cache-control directives or another header(s) that explicitly allow it. For example, these include the following: an Expires header (section 14.21); a "max-age", "s-maxage", "must- revalidate", "proxy-revalidate", "public" or "private" cache-control directive (section 14.9).

In our spec for p2, we have removed the "cacheable unless indicated otherwise" and "only cacheable if indicated by a Cache-Control or Expires" lines from the status code definitions. We now only have "Caches MAY use a heuristic to determine freshness" where it used to say "cacheable unless indicated otherwise". There are two problems with this:

1) If a response only has "Cache-Control: public" without Expires or max-age, heuristic expiration is supposed to be used to determine freshness, yet we make it look like a heuristic can only be used with certain codes.

2) There are a lot of codes that are not supposed to be cacheable (100, 101, 201, ...) and that really isn't the same as saying they can't use heuristic freshness. RFC2616 says they are not cacheable unless indicated otherwise based on the reasonable theory that an origin server won't add a cache-control or expires to a code that doesn't make sense to cache.

Suggested changes to p2:

  • Add to status code intro p2 sec.6:

Status codes that are defined as cacheable by default (e.g., 200, 203, 206, 300, 301, and 410 in this specification) can be reused by a cache with heuristic expiration unless otherwise indicated by the method definition or explicit cache controls [Part6]; all other status codes are not cacheable by default.

  • For each of the above listed codes, replace "Caches MAY use a heuristic ..." with "Responses with this status code are cacheable unless otherwise indicated by the method semantics or explicit cache controls [Part6]."

Suggested changes to p6:

  • 4.1.2, replace entire first paragraph with

A cache MUST NOT use heuristics to determine freshness unless no explicit expiration time is present in a stored response and either the status code is defined as cacheable by default (including the following in Section 6 of [Part2]: 200 (OK), 203 (Non-Authoritative Information), 206 (Partial Content), 300 (Multiple Choices), 301 (Moved Permanently) and 410 (Gone)) or the response has been explicitly marked as cacheable (e.g., using the "public" cache-directive without max-age).

comment:17 Changed 18 months ago by fielding@gbiv.com

From [2166]:

Indicate certain status codes as cacheable by default instead of heuristics allowed; Use requirement on heuristics to allow them to be used only when cacheable by default or indicated as cacheable via public directive; Addresses #223

comment:18 Changed 18 months ago by fielding@gbiv.com

  • Status changed from reopened to closed
  • Resolution set to incorporated

Since the above is mostly editorial (except for the bit about cache-control public allowing heuristics), I have gone ahead and committed so that we can close out the first WGLC comments. Feel free to tweak it if you disagree, or save it for a new issue during the next WGLC.

comment:19 Changed 18 months ago by mnot@pobox.com

  • Status changed from closed to reopened
  • Resolution incorporated deleted

These edits are horrible. Cacheability is defined by section 3 of part 6; here you're re-defining a parallel model. This is going to cause even more confusion. "Cacheable by default" doesn't make any sense in that model.

If we decide to go this way, it would be much clearer to say, roughly, "if you can store it according to section 3, and there isn't explicit freshness information available, you can use a heuristic."

The only other possible interpretation AFAICT is that "cacheable by default" is a magical phrase which really indicates "can use a heuristic," which is clearly not where you want to be, because you just removed that text.

So, if you really want to go down this road, we should:

  • Confirm with the WG that we're going to make heuristics available to *all* status codes that allow caching
  • Tweak the language in section 3 to clarify when a status code is considered cacheable
  • Review the status codes we define to make sure they clearly denote whether they're cacheable (e.g., most of 4xx and 5xx don't say a word, currently)
  • Separately, address #430

However, I'd note that doing so is going to require us to review every status code -- something we've already shied away from once -- and is going to result in a situation where a status code can't be made explicitly cacheable without making it subject to heuristics, which may be dangerous for some (e.g., 500, 400).

For example, if a 400 is used to indicate that the Host header is missing, it shouldn't be cached. However, there are other uses of 400 where it may make sense (especially if it's being used as the most generic case of 4xx). What is a server to do? Always send CC: no-store on 400 responses? No one does that today (not even apache.org).

I fail to see why it's so important to conflate a status code's general cacheability and the ability to use a heuristic with it.

Roy, please respond here, and then we can take it to the list. Let's figure out consensus there before making more edits.

comment:20 follow-up: ↓ 21 Changed 18 months ago by fielding@gbiv.com

Section 3 of p6 defines storage. Yes, it appears to have neglected to include "public", so we will have to fix it there as well. Changing the meaning of existing protocol elements is not an option. All we are allowed to do is update the description and remove the cruft.

Yes, cacheable by default means you can store in any cache and use a heuristic expiration unless otherwise noted. As previously mentioned, that has always been the definition of HTTP caching, in all of the specifications, so there really is no point arguing with me about it. For example, I just pulled a book about HTTP off my bookshelf (REST in Practice by Webber et al.) which uses an example of

Cache-Control: public, max-age="0"

specifically because public "doesn't determine a freshness value" (p.165).

In 2616, the list of specific status codes in the section on Cacheability defined which ones are cacheable by default. You changed that to "allow heuristics", which is not the same thing. What we agreed to change is that new codes can define for themselves whether they are cacheable by default. The distinction between "allow heuristics" and "cacheable by default" is, very specifically, that the latter allows for public and private to override non-cacheable (allowing heuristics if not otherwise specified by the origin server via a more specific cache directive like max-age, must-revalidate, or s-max-age).

The only codes we need to check are the new ones defined since 2616, and only if they need to be added to the set of cacheable by default. That is hardly a difficult task. IIRC, 308 is the only one that would apply.

Reverting the protocol change is not open to WG review -- changing the meaning of public is outside the scope of the charter. We can have all sorts of discussions about *how* to describe the correct semantics, but we will not go forward with a change to the semantics. Our editorial text needs to be consistent with the protocol, which means if the editorial "model" fails to describe the protocol then we change the model.

Regarding 400, the current text says "all other status codes are not cacheable by default", which means the only way it would be allowed to be cached is if the origin server specifically tells recipients to cache it. Whether that involves heuristics or not is irrelevant -- if the server does not want it reused, it won't mark the response as reusable.

When cache-control was first added to HTTP, there was a requirement that it not override requirements given in the method and status code (i.e., if a status code said it MUST NOT be cached, then cache-control would be unable to make it cacheable). Likewise, there were specific requirements on precedence of cache directives. Both were removed by the WG at some point before RFC2068. However, RFC2616 does have a general rule for precedence in section 13.1.3 that I overlooked:

The Cache-Control header allows a client or server to transmit a variety of directives in either requests or responses. These directives typically override the default caching algorithms. As a general rule, if there is any apparent conflict between header values, the most restrictive interpretation is applied (that is, the one that is most likely to preserve semantic transparency). However, in some cases, cache-control directives are explicitly specified as weakening the approximation of semantic transparency (for example, "max-stale" or "public").

so the opposite rule that exists for directive extensions (extensions can override more restrictive standard directives) does not apply when we are considering two standard directives. Hence, no-store and no-cache should still override public.

comment:21 in reply to: ↑ 20 Changed 18 months ago by mnot@pobox.com

Replying to fielding@…:

Yes, cacheable by default means you can store in any cache and use a heuristic expiration unless otherwise noted. As previously mentioned, that has always been the definition of HTTP caching, in all of the specifications, so there really is no point arguing with me about it.

... just as there's no point offering me advice to unconditionally trust your interpretation of the spec.

In 2616, the list of specific status codes in the section on Cacheability defined which ones are cacheable by default. You changed that to "allow heuristics", which is not the same thing. What we agreed to change is that new codes can define for themselves whether they are cacheable by default.

Actually, it happened before that, when I did the big rewrite. This issue was opened afterwards, IIRC.

The distinction between "allow heuristics" and "cacheable by default" is, very specifically, that the latter allows for public and private to override non-cacheable (allowing heuristics if not otherwise specified by the origin server via a more specific cache directive like max-age, must-revalidate, or s-max-age).

The only codes we need to check are the new ones defined since 2616, and only if they need to be added to the set of cacheable by default. That is hardly a difficult task. IIRC, 308 is the only one that would apply.

What about the status codes that TS, Squid and many others allow to be negatively cached? I think we need an issue to discuss that; yes, it may qualify as a semantic change, but it will improve interop (no cache is going to stop applying a heuristic to 404s just to make us happy).

Reverting the protocol change is not open to WG review -- changing the meaning of public is outside the scope of the charter. We can have all sorts of discussions about *how* to describe the correct semantics, but we will not go forward with a change to the semantics. Our editorial text needs to be consistent with the protocol, which means if the editorial "model" fails to describe the protocol then we change the model.

That's your opinion, and I'll certainly take it into account. However, you don't get to define what the protocol is alone -- it's not just what's in your head.

Regarding 400, the current text says "all other status codes are not cacheable by default", which means the only way it would be allowed to be cached is if the origin server specifically tells recipients to cache it. Whether that involves heuristics or not is irrelevant -- if the server does not want it reused, it won't mark the response as reusable.

OK. So Squid and TS are non-conformant here?

When cache-control was first added to HTTP, there was a requirement that it not override requirements given in the method and status code (i.e., if a status code said it MUST NOT be cached, then cache-control would be unable to make it cacheable). Likewise, there were specific requirements on precedence of cache directives. Both were removed by the WG at some point before RFC2068. However, RFC2616 does have a general rule for precedence in section 13.1.3 that I overlooked:

The Cache-Control header allows a client or server to transmit a variety of directives in either requests or responses. These directives typically override the default caching algorithms. As a general rule, if there is any apparent conflict between header values, the most restrictive interpretation is applied (that is, the one that is most likely to preserve semantic transparency). However, in some cases, cache-control directives are explicitly specified as weakening the approximation of semantic transparency (for example, "max-stale" or "public").

so the opposite rule that exists for directive extensions (extensions can override more restrictive standard directives) does not apply when we are considering two standard directives. Hence, no-store and no-cache should still override public.

If that's your interpretation, I'm less worried.

Next steps:

  • I'll send a message to list explaining why we re-opened this issue (basically, I read too much into this phrase when re-writing)
  • I'll do an edit to clean up (and it will be just editorial)
  • You'll review
  • I'll open a separate issue examining whether we need to define other status codes as cacheable by default, etc.
  • We'll do #430 separately

comment:22 Changed 18 months ago by mnot@pobox.com

From [2179]:

Tidy up discussion of heuristic freshness, with follow-on effects in the definition of 'public', the storage algorithm, and considerations for new status codes. Addresses #223

comment:23 Changed 18 months ago by mnot@pobox.com

OK, edits made, and I'm about to send a message to list.

I changed the phrase "cacheable by default" to just "cacheable", since that's the wording you used in p2. However, I'm kind of not happy with either wording; in the caching model, as you so rightly point out, *everything* is cacheable, if it has explicit freshness assigned to it.

I can see changing all instances to "cacheable by default", but that phrase is awkward. This is what drove me to originally use "heuristically cacheable."

comment:24 Changed 18 months ago by mnot@pobox.com

  • Status changed from reopened to closed
  • Resolution set to incorporated

comment:25 Changed 15 months ago by mnot@pobox.com

  • Status changed from closed to reopened
  • Resolution incorporated deleted

comment:26 Changed 15 months ago by mnot@pobox.com

  • Status changed from reopened to closed
  • Resolution set to fixed
Note: See TracTickets for help on using tickets.