[nginx] Cache: support for stale-while-revalidate and stale-if-error.

Roman Arutyunyan arut at nginx.com
Fri Feb 10 13:58:32 UTC 2017


details:   http://hg.nginx.org/nginx/rev/9a9e13686869
branches:  
changeset: 6905:9a9e13686869
user:      Roman Arutyunyan <arut at nginx.com>
date:      Thu Dec 22 14:25:34 2016 +0300
description:
Cache: support for stale-while-revalidate and stale-if-error.

Previously, there was no way to enable the proxy_cache_use_stale behavior by
reading the backend response.  Now, stale-while-revalidate and stale-if-error
Cache-Control extensions (RFC 5861) are supported.  They specify, how long a
stale response can be used when a cache entry is being updated, or in case of
an error.

diffstat:

 src/http/ngx_http_cache.h      |    9 ++-
 src/http/ngx_http_file_cache.c |    8 ++
 src/http/ngx_http_upstream.c   |  117 ++++++++++++++++++++++++++++++----------
 3 files changed, 103 insertions(+), 31 deletions(-)

diffs (261 lines):

diff -r 5e2423bce883 -r 9a9e13686869 src/http/ngx_http_cache.h
--- a/src/http/ngx_http_cache.h	Wed Feb 08 19:36:03 2017 +0300
+++ b/src/http/ngx_http_cache.h	Thu Dec 22 14:25:34 2016 +0300
@@ -27,7 +27,7 @@
 #define NGX_HTTP_CACHE_ETAG_LEN      42
 #define NGX_HTTP_CACHE_VARY_LEN      42
 
-#define NGX_HTTP_CACHE_VERSION       3
+#define NGX_HTTP_CACHE_VERSION       4
 
 
 typedef struct {
@@ -71,6 +71,8 @@ struct ngx_http_cache_s {
 
     ngx_file_uniq_t                  uniq;
     time_t                           valid_sec;
+    time_t                           updating_sec;
+    time_t                           error_sec;
     time_t                           last_modified;
     time_t                           date;
 
@@ -114,12 +116,17 @@ struct ngx_http_cache_s {
     unsigned                         purged:1;
     unsigned                         reading:1;
     unsigned                         secondary:1;
+
+    unsigned                         stale_updating:1;
+    unsigned                         stale_error:1;
 };
 
 
 typedef struct {
     ngx_uint_t                       version;
     time_t                           valid_sec;
+    time_t                           updating_sec;
+    time_t                           error_sec;
     time_t                           last_modified;
     time_t                           date;
     uint32_t                         crc32;
diff -r 5e2423bce883 -r 9a9e13686869 src/http/ngx_http_file_cache.c
--- a/src/http/ngx_http_file_cache.c	Wed Feb 08 19:36:03 2017 +0300
+++ b/src/http/ngx_http_file_cache.c	Thu Dec 22 14:25:34 2016 +0300
@@ -601,6 +601,8 @@ ngx_http_file_cache_read(ngx_http_reques
     c->buf->last += n;
 
     c->valid_sec = h->valid_sec;
+    c->updating_sec = h->updating_sec;
+    c->error_sec = h->error_sec;
     c->last_modified = h->last_modified;
     c->date = h->date;
     c->valid_msec = h->valid_msec;
@@ -632,6 +634,8 @@ ngx_http_file_cache_read(ngx_http_reques
     now = ngx_time();
 
     if (c->valid_sec < now) {
+        c->stale_updating = c->valid_sec + c->updating_sec >= now;
+        c->stale_error = c->valid_sec + c->error_sec >= now;
 
         ngx_shmtx_lock(&cache->shpool->mutex);
 
@@ -1252,6 +1256,8 @@ ngx_http_file_cache_set_header(ngx_http_
 
     h->version = NGX_HTTP_CACHE_VERSION;
     h->valid_sec = c->valid_sec;
+    h->updating_sec = c->updating_sec;
+    h->error_sec = c->error_sec;
     h->last_modified = c->last_modified;
     h->date = c->date;
     h->crc32 = c->crc32;
@@ -1513,6 +1519,8 @@ ngx_http_file_cache_update_header(ngx_ht
 
     h.version = NGX_HTTP_CACHE_VERSION;
     h.valid_sec = c->valid_sec;
+    h.updating_sec = c->updating_sec;
+    h.error_sec = c->error_sec;
     h.last_modified = c->last_modified;
     h.date = c->date;
     h.crc32 = c->crc32;
diff -r 5e2423bce883 -r 9a9e13686869 src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c	Wed Feb 08 19:36:03 2017 +0300
+++ b/src/http/ngx_http_upstream.c	Thu Dec 22 14:25:34 2016 +0300
@@ -871,7 +871,9 @@ ngx_http_upstream_cache(ngx_http_request
 
     case NGX_HTTP_CACHE_UPDATING:
 
-        if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) {
+        if ((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
+            || c->stale_updating)
+        {
             u->cache_status = rc;
             rc = NGX_OK;
 
@@ -894,6 +896,9 @@ ngx_http_upstream_cache(ngx_http_request
     case NGX_HTTP_CACHE_STALE:
 
         c->valid_sec = 0;
+        c->updating_sec = 0;
+        c->error_sec = 0;
+
         u->buffer.start = NULL;
         u->cache_status = NGX_HTTP_CACHE_EXPIRED;
 
@@ -2340,7 +2345,7 @@ ngx_http_upstream_test_next(ngx_http_req
 #if (NGX_HTTP_CACHE)
 
         if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
-            && (u->conf->cache_use_stale & un->mask))
+            && ((u->conf->cache_use_stale & un->mask) || r->cache->stale_error))
         {
             ngx_int_t  rc;
 
@@ -2364,14 +2369,17 @@ ngx_http_upstream_test_next(ngx_http_req
         && u->cache_status == NGX_HTTP_CACHE_EXPIRED
         && u->conf->cache_revalidate)
     {
-        time_t     now, valid;
+        time_t     now, valid, updating, error;
         ngx_int_t  rc;
 
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream not modified");
 
         now = ngx_time();
+
         valid = r->cache->valid_sec;
+        updating = r->cache->updating_sec;
+        error = r->cache->error_sec;
 
         rc = u->reinit_request(r);
 
@@ -2385,6 +2393,8 @@ ngx_http_upstream_test_next(ngx_http_req
 
         if (valid == 0) {
             valid = r->cache->valid_sec;
+            updating = r->cache->updating_sec;
+            error = r->cache->error_sec;
         }
 
         if (valid == 0) {
@@ -2397,6 +2407,9 @@ ngx_http_upstream_test_next(ngx_http_req
 
         if (valid) {
             r->cache->valid_sec = valid;
+            r->cache->updating_sec = updating;
+            r->cache->error_sec = error;
+
             r->cache->date = now;
 
             ngx_http_file_cache_update_header(r);
@@ -4132,7 +4145,7 @@ ngx_http_upstream_next(ngx_http_request_
 #if (NGX_HTTP_CACHE)
 
         if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
-            && (u->conf->cache_use_stale & ft_type))
+            && ((u->conf->cache_use_stale & ft_type) || r->cache->stale_error))
         {
             ngx_int_t  rc;
 
@@ -4507,32 +4520,76 @@ ngx_http_upstream_process_cache_control(
         offset = 8;
     }
 
-    if (p == NULL) {
-        return NGX_OK;
-    }
-
-    n = 0;
-
-    for (p += offset; p < last; p++) {
-        if (*p == ',' || *p == ';' || *p == ' ') {
-            break;
-        }
-
-        if (*p >= '0' && *p <= '9') {
-            n = n * 10 + *p - '0';
-            continue;
-        }
-
-        u->cacheable = 0;
-        return NGX_OK;
-    }
-
-    if (n == 0) {
-        u->cacheable = 0;
-        return NGX_OK;
-    }
-
-    r->cache->valid_sec = ngx_time() + n;
+    if (p) {
+        n = 0;
+
+        for (p += offset; p < last; p++) {
+            if (*p == ',' || *p == ';' || *p == ' ') {
+                break;
+            }
+
+            if (*p >= '0' && *p <= '9') {
+                n = n * 10 + *p - '0';
+                continue;
+            }
+
+            u->cacheable = 0;
+            return NGX_OK;
+        }
+
+        if (n == 0) {
+            u->cacheable = 0;
+            return NGX_OK;
+        }
+
+        r->cache->valid_sec = ngx_time() + n;
+    }
+
+    p = ngx_strlcasestrn(start, last, (u_char *) "stale-while-revalidate=",
+                         23 - 1);
+
+    if (p) {
+        n = 0;
+
+        for (p += 23; p < last; p++) {
+            if (*p == ',' || *p == ';' || *p == ' ') {
+                break;
+            }
+
+            if (*p >= '0' && *p <= '9') {
+                n = n * 10 + *p - '0';
+                continue;
+            }
+
+            u->cacheable = 0;
+            return NGX_OK;
+        }
+
+        r->cache->updating_sec = n;
+        r->cache->error_sec = n;
+    }
+
+    p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
+
+    if (p) {
+        n = 0;
+
+        for (p += 15; p < last; p++) {
+            if (*p == ',' || *p == ';' || *p == ' ') {
+                break;
+            }
+
+            if (*p >= '0' && *p <= '9') {
+                n = n * 10 + *p - '0';
+                continue;
+            }
+
+            u->cacheable = 0;
+            return NGX_OK;
+        }
+
+        r->cache->error_sec = n;
+    }
     }
 #endif
 


More information about the nginx-devel mailing list