using $upstream* variables inside map directive

K
  • 7 May '14
Hello,
I'm trying to avoid caching of small responses from upstreams using map:
map $upstream_http_content_length $dontcache {
default 0;
~^\d\d$ 1;
~^\d$ 1;
}

Unfortunatelly, nginx seems to ignore $upstream* variables at the map
processing stage, hence variables like $upstream_http_content_length or
$upstream_response_length stay empty when map directive is processed (this
can be observed in debug log as "http map started" message). In case I use
non-upstream related variables, a map works as expected.

Question: is there any way to use $upstream* vars inside the map directive,
or maybe someone can offer alternative way to detect small upstream response
in order to bypass cache?

Thank you.

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,249880,249880#msg-249880
J
  • 7 May '14
On 7 May 2014 06:38, Kirill K. <nginx-forum at nginx.us> wrote:
> Hello,
> I'm trying to avoid caching of small responses from upstreams using map:
> map $upstream_http_content_length $dontcache {
> default 0;
> ~^\d\d$ 1;
> ~^\d$ 1;
> }
>
> Unfortunatelly, nginx seems to ignore $upstream* variables at the map
> processing stage, hence variables like $upstream_http_content_length or
> $upstream_response_length stay empty when map directive is processed (this
> can be observed in debug log as "http map started" message). In case I use
> non-upstream related variables, a map works as expected.
>
> Question: is there any way to use $upstream* vars inside the map directive,
> or maybe someone can offer alternative way to detect small upstream response
> in order to bypass cache?

I don't explicitly know how to achieve what you're trying to, but I
seem to recall mention on this list that a map's value gets stuck
(per-request) the first time it's evaluated. I might be
misremembering, but this does ring a bell.

So - is your map somehow being evaluated /before/ the upstream vars
are available? Does your config perhaps cause it to be evaluated when
the initial request arrives, to see if the response should be served
from cache; then the request is proxy_pass'd, then after receiving a
response the caching bypass config is examined but to no avail as the
map has "stuck" with the initially set value?

Sorry I can't be more specific - I'm sure others can help more definitively!

J
K
  • 7 May '14
Probably that's the case, and I'm not sure if there's a way to use map
inside upstream {...} or other context apart from http {...}, which makes
your theory sound correct.
What confuses me most: I googled a bit, and using map w/
$upstream_response_length is the most common way offered to avoid caching of
small (or zero-sized) responses, yet it just does not work in a real life
scenario...

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,249880,249892#msg-249892
R
  • 7 May '14
On Wed, May 07, 2014 at 01:38:04AM -0400, Kirill K. wrote:
> Hello,
> I'm trying to avoid caching of small responses from upstreams using map:
> map $upstream_http_content_length $dontcache {
> default 0;
> ~^\d\d$ 1;
> ~^\d$ 1;
> }
> 
> Unfortunatelly, nginx seems to ignore $upstream* variables at the map
> processing stage, hence variables like $upstream_http_content_length or
> $upstream_response_length stay empty when map directive is processed (this
> can be observed in debug log as "http map started" message). In case I use
> non-upstream related variables, a map works as expected.
> 
> Question: is there any way to use $upstream* vars inside the map directive,
> or maybe someone can offer alternative way to detect small upstream response
> in order to bypass cache?

If you use $dontcache with proxy_cache_bypass, then it's expected
behavior.  At the time proxy_cache_bypass is evaluated, there's no
response yet, so the $upstream_http_* do not exist.

If you try to use $dontcache with proxy_no_cache ONLY, it'll work,
because the latter is evaluated _after_ obtaining a response.

If you use it both with proxy_cache_bypass and proxy_no_cache,
please realize that using it with proxy_cache_bypass makes no
sense, and then the fact that "map" creates the so-called
cacheable variables plays its role.

I have a patch for "map" that makes map variables "volatile".
If you absolutely need such a "map" behavior, I can send it
to you for testing, but better limit the use of $upstream_http_*
to only proxy_no_cache.
K
  • 7 May '14
Thanks, Ruslan,
Thing is, I tried to "debug" whether $dontcache is being set at all by
exposing it via response headers (along with content-length), and it shows
that $upstream_response_length is ignored by map completely, i.e. no matter
where I use $dontcache, it will never get any value different from default
(i.e. 0). Even though $upstream_response_length  is validated correctly (and
can be exposed in headers), the map directive just ignores it.

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,249880,249895#msg-249895
R
  • 7 May '14
On Wed, May 07, 2014 at 08:53:56AM -0400, Kirill K. wrote:
> Thanks, Ruslan,
> Thing is, I tried to "debug" whether $dontcache is being set at all by
> exposing it via response headers (along with content-length), and it shows
> that $upstream_response_length is ignored by map completely, i.e. no matter
> where I use $dontcache, it will never get any value different from default
> (i.e. 0). Even though $upstream_response_length  is validated correctly (and
> can be exposed in headers), the map directive just ignores it.

I tested that your map works when used ONLY in proxy_no_cache,
and it indeed DTRT: responses with content length less than
100 aren't cached.

Here's the config snippet:

: http {
:     proxy_cache_path proxy_cache keys_zone=proxy_cache:10m;
: 
:     map $upstream_http_content_length $dontcache {
:         default 0;
:         ~^\d\d$ 1;
:         ~^\d$ 1;
:     }
: 
:     server {
:         listen 8000;
: 
:         location / {
:             proxy_pass http://127.0.0.1:8001;
:             proxy_cache proxy_cache;
:             proxy_cache_valid 300m;
:             proxy_no_cache $dontcache;
: 
:             add_header X-DontCache $dontcache;
:         }
:     }
: 
:     server {
:         listen 8001;
: 
:         return 200 "ok";
:     }
: }

$ curl -i http://127.0.0.1:8000/test
HTTP/1.1 200 OK
Server: nginx/1.7.1
Date: Wed, 07 May 2014 14:34:19 GMT
Content-Type: text/plain
Content-Length: 2
Connection: keep-alive
X-DontCache: 1

ok

And there will be no file in "proxy_cache" directory.
K
  • 7 May '14
Ruslan, you're a hero! 
I just commented the following line in my existing config
#proxy_cache_bypass $dontcache;
and everything works now!

I won't be able to comprehend such nginx's behaviour w/o your help, greatly
appreciated.

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,249880,249912#msg-249912
U
  • 11 Nov '20
Ruslan, could you send that patch for "map"? I would like to check it.

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,249880,289960#msg-289960
R
  • 11 Nov '20
On Wed, Nov 11, 2020 at 12:55:30PM -0500, unoobee wrote:
> Ruslan, could you send that patch for "map"? I would like to check it.

The "volatile" parameter of the "map" directive is available since
nginx version 1.11.7.
U
  • 12 Nov '20
I tried using $upstream_http_content_length inside the map directive with
the "volatile" parameter to specify the proxy_cache behavior, but the map
still uses the default value.
Is there any way to set the proxy_cache behavior depending on
$upstream_http_content_length via the map directive?

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,249880,289963#msg-289963
F
  • 12 Nov '20
On Thu, Nov 12, 2020 at 02:33:49AM -0500, unoobee wrote:

Hi there,

> I tried using $upstream_http_content_length inside the map directive with
> the "volatile" parameter to specify the proxy_cache behavior, but the map
> still uses the default value.

What's your config?

> Is there any way to set the proxy_cache behavior depending on
> $upstream_http_content_length via the map directive?

What proxy_cache behavior do you want to set?

You could reasonably set proxy_no_cache, because that only applies after
accessing upstream.

You can't usefully set proxy_cache, or proxy_cache_bypass, or
proxy_cache_key, because they are all consulted before the decision on
whether or not to access upstream has been made.

Cheers,

    f
-- 
Francis Daly        francis at daoine.org
U
  • 12 Nov '20
My configuration looks like this:

proxy_cache_path /cache/ssd              keys_zone=ssd_cache:10m levels=1:2
inactive=600s max_size=100m;
proxy_cache_path /cache/hdd              keys_zone=hdd_cache:10m levels=1:2
inactive=600s max_size=100m;

upstream backend {
        server www.test.com:443;
}

server {
    listen 80;
    server_name test.com;

        location / {
            proxy_pass https://backend;
            proxy_redirect https://backend/ /;
            proxy_set_header Host $host;
            proxy_cache $cache;
        }
}

map $upstream_http_content_length $cache  {
       volatile;
        ~^\d\d\b    ssd_cache;
        default     hdd_cache;
}

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,249880,289965#msg-289965
F
  • 12 Nov '20
On Thu, Nov 12, 2020 at 04:58:31AM -0500, unoobee wrote:

Hi there,

> My configuration looks like this:

Thanks for this.

It looks like you are setting "proxy_cache" to always try to read from
"hdd_cache"; but you want it to sometimes write to "ssd_cache" instead.

And you are reporting that it does not ever write to "ssd_cache".

Is that correct?

If so -- given that it will only ever read from "hdd_cache", what would
be the benefit in writing to somewhere else?

I'm not certain what you're trying to achieve. Perhaps describing that,
might make it clear whether it can be done?

    f
-- 
Francis Daly        francis at daoine.org
U
  • 12 Nov '20
> And you are reporting that it does not ever write to "ssd_cache".
Yes, this is correct. I want to choose the cache location based on the size
of the cached file

I want to get the behavior described in the article, but only with the file
size in the map directive, I assume I need $sent_http_content_length or
$upstream_http_content_length.

https://www.nginx.com/blog/cache-placement-strategies-nginx-plus/

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,249880,289971#msg-289971
F
  • 12 Nov '20
On Thu, Nov 12, 2020 at 11:00:09AM -0500, unoobee wrote:

Hi there,

> > And you are reporting that it does not ever write to "ssd_cache".
> Yes, this is correct. I want to choose the cache location based on the size
> of the cached file

Logically, you can't.

You can only choose the cache location based on something in the request;
not on something in the response.

> I want to get the behavior described in the article, but only with the file
> size in the map directive, I assume I need $sent_http_content_length or
> $upstream_http_content_length.

You could try changing all your urls to include the file size; and then
choose a cache location based on the sizes in the request url.

But that is unlikely to be convenient.

Good luck with it,

    f
-- 
Francis Daly        francis at daoine.org