Proxy_cache_lock allow multiple requests to remote server in some cases

My understanding of proxy_cache_lock is that only one request should be
passed to the proxied server for a given uri, even if many requests from
the
same uri/key are hitting nginx while it is being refreshed.

When the cache folder specified in the proxy_cache_path is empty, it
works
well and behave like I described above.
However, if the element in the cache already exists, but is expired
(according to proxy_cache_valid configuration), all concurrent requests
will
hit the proxied server and the resource will be downloaded multiple
times.

Here is my config:

proxy_cache_path /usr/share/nginx/cache levels=1:2 keys_zone=CACHE:10m
max_size=2g inactive=1440m;

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;

    root /usr/share/nginx/html;
    index index.html index.htm;

    server_name localhost;

    location /videos {

            proxy_cache            CACHE;
            proxy_cache_valid      200  15s;
            proxy_cache_revalidate on;
            proxy_cache_lock       on;
            proxy_cache_lock_timeout   30s;
            proxy_cache_lock_age   30s;
            proxy_cache_use_stale  error timeout invalid_header
                                   http_500 http_502 http_503 

http_504;

            proxy_pass http://origin_server/videos;
    }

}

Basically what I want to do is to be able to take advantage of the
“proxy_cache_revalidate on” to force a If-Modified-Since request, but
only
one request should go and fetch the new element from the proxied server,
even if multiple requests are coming in for the same uri/key and the
cache
is expired.

To be more specific, in my case, the resources downloaded are videos
between
1MB to 10MB in size, so they take some time to download and saving
bandwidth
is important, and only one request should be done, not multiple (to the
proxied server).

Using “proxy_cache_use_stale updating” is also not an option since I
want
all requests that are coming simultaneously to wait and use the new
resource
when there is a new one returned from the proxied server.

Is there something I am doing wrong, or is this the expected behavior?
Is
there a way to do what I am trying to do with nginx?

I am using nginx 1.8.1 on Ubuntu Server 14.04 x64.

Regards,
Jeeeff

Posted at Nginx Forum:

Anyone else noticed the same behavior?

I wasn’t sure if that kind of behavior was correct, but as I said the
lock
works properly and only one request get forwarded to the backend server
when
there is no cached item in the cache (that is: the cache is empty), so
to me
the behavior should be the same when the cache is being refreshed (I
don’t
see why it would be different?), but that is not the case… so it looks
like a defect to me.

I also tried to confirm if other cache software do the same thing, such
as
varnish, and in theory they have this “waiting queue” which should
guarantee
that only one request gets forwarded, but there is a bug right now which
actually makes the thing even worst than Nginx (at least Nginx sends a
If-Modified-Since, but varnish just do a full request for all concurrent
requests, their bug tracking ticket is
Varnish HTTP Cache — Varnish HTTP Cache). The workaround
mentioned
does no even provide the same behavior (it hits the old cache instead of
waiting the queued request), so does not work for me.

Sorry for mentioning about a third party product such as varnish on this
forum, but since I wasn’t receiving any response, I wanted to see if
other
solutions had this functionality (and confirm my post made sense…),
but
then I am now back with Nginx as it works better (and is easier to use)
anyway, at least until the bug is resolved on varnish. If this issue is
resolved on nginx, that would be even better for me… nginx is more
flexible and easier to use.

Posted at Nginx Forum:

Hello!

On Thu, Jan 28, 2016 at 07:13:11PM -0500, jeeeff wrote:

My understanding of proxy_cache_lock is that only one request should be
passed to the proxied server for a given uri, even if many requests from the
same uri/key are hitting nginx while it is being refreshed.

When the cache folder specified in the proxy_cache_path is empty, it works
well and behave like I described above.
However, if the element in the cache already exists, but is expired
(according to proxy_cache_valid configuration), all concurrent requests will
hit the proxied server and the resource will be downloaded multiple times.

That’s expected, see Alphabetical index of directives

: When enabled, only one request at a time will be allowed to
: populate a new cache element identified according to the
: proxy_cache_key directive by passing a request to a proxied
: server.

Note “a new cache element”. It does not work while updating cache
elements, this is not implemented.

[…]

Using “proxy_cache_use_stale updating” is also not an option since I want
all requests that are coming simultaneously to wait and use the new resource
when there is a new one returned from the proxied server.

Is there something I am doing wrong, or is this the expected behavior? Is
there a way to do what I am trying to do with nginx?

See above, this is expected behaviour - as of now proxy_cache_lock
does nothing while updating cache elements. Using
“proxy_cache_use_stale updating” is recommended if you need to
reduce concurrency while updating cache elements.

If “proxy_cache_use_stale updating” doesn’t work for you, you may
try extending “proxy_cache_lock” to also cover updating. Current
implementation doesn’t try to do this to reduce complexity.


Maxim D.
http://nginx.org/

Hello!

On Fri, Feb 05, 2016 at 11:02:31AM -0500, jeeeff wrote:

Anyone else noticed the same behavior?

I wasn’t sure if that kind of behavior was correct, but as I said the lock
works properly and only one request get forwarded to the backend server when
there is no cached item in the cache (that is: the cache is empty), so to me
the behavior should be the same when the cache is being refreshed (I don’t
see why it would be different?), but that is not the case… so it looks
like a defect to me.

[…]

You can find the detailed response to your original message here:

http://mailman.nginx.org/pipermail/nginx/2016-January/049734.html

Unfortunately, forum interface is broken and is unable to show it
in the correct thread. Consider using the mailing list directly
instead, see nginx: support.

Additionally, it’s also unlikely that you’ll see this message as
well, due to the same forum bug.


Maxim D.
http://nginx.org/

Maxim D. Wrote:

Additionally, it’s also unlikely that you’ll see this message as
well, due to the same forum bug.


Maxim D.
http://nginx.org/

Thanks a lot for the info!
Yes, after I re-read the documentation, I kind of understood that
“populate
a new element” could have been interpreted the wrong way.
Anyway, now I know for sure what the implemented behavior is, and that I
am
not making a mistake or missing some configuration.

And as you suggested, I also thought of looking at the code to see if
that
could be fixed/implemented, that is, if I get some time to get into the
project source code…

Jeeeff

Posted at Nginx Forum: