Jacobian
Jacobian

Reputation: 10862

Is it a good practice to cache Redis data in ngx.shared

I have some Lua code embedded in nginx. In this code I get some small data from Redis cache. Now I wonder, if it is a good practice to cache this data (already cached in some sense) in nginx, using ngx.shared construct? Are there any pros and cons of doing it this way? In pseudo-code I expect to have something like:

local cache = ngx.shared.cache
local cached_key = cache:get("cached_key")
if cached_key == nil then
    ... get data from Redis
    cache:set("cached_key", cached_key)
end

Upvotes: 0

Views: 1123

Answers (1)

Alban Linard
Alban Linard

Reputation: 1047

As stated in the documentation ngx.shared is a space shared among all the workers of the nginx server.

All the listed operations are atomic, so you only have to bother about race conditions if you use two operations on ngx.shared one after the other. In this case, they should be protected using ngx.semaphore.

The pros:

  • Using ngx.shared provides faster access to the data, because you avoid a request/response loop to the Redis server.
  • Even if you need a ngx.semaphore you can expect faster access to the data (but i have no benchmark to provide).

The cons:

  • The ngx.shared cache provides inaccurate data, as your local cache does not reflect the current Redis value. This is not always a crucial point, as there can always be a delta between the values used in the worker and the value stored in Redis.
  • Data stored in ngx.shared can be inconsistent, which is more important. For instance it can store x=true and y=false whereas in Redis x and y have always the same value. It depends on how you update your local cache.
  • You have to handle yourself the cache, by updating the values in your cache whenever they are sent to Redis. This can be easily done by wrapping the redis functions. Expect bugs if you handle updates by putting it after each call to redis.get, because you (or someone) will forget it.
  • You also have to handle reads: whenever a value is not found in your ngx.cache, you have to automatically read it from Redis. Expect bugs if you handle reads by putting them after each call to cache.get, because you (or someone) will forget it.

For the two last points, you can easily write a small wrapper module.

As a conclusion:

  • If your server runs only one instance, with one or several workers, using ngx.shared is interesting, as you can always have a cache of your Redis data that is always up-to-date.
  • If your server runs several instances and having an always up-to-date cache is mandatory, or if you could have consistency problems, then you should avoid caching using ngx.shared.
  • In all cases, if the size of your data can be huge, make sure to provide a way to clean it before memory consumption is too high. If you cannot provide cleaning, then you should not use ngx.shared.

Also, do not forget to store the cached value within a local variable, in order to avoid geting it again and again, and thus to improve efficiency.

Upvotes: 3

Related Questions