openresty / lua-nginx-module

Embed the Power of Lua into NGINX HTTP servers
https://openresty.org/
11.24k stars 2.02k forks source link

set new key to ngx.shared.dict result in expired old key becoming nil #1300

Open jinquanni opened 6 years ago

jinquanni commented 6 years ago

reproduce script (using resty-cli)

resty --shdict='dogs 1m' -e 'local dict = ngx.shared.dogs dict:set("test004", 444, 5, 1) dict:set("test005", 555, 5, 1) dict:set("test001", 111, 5, 1) dict:set("test002", 222, 5, 1) dict:set("test003", 333, 5, 1)

print("---------get all key in 5s") print("test001: ",dict:get_stale("test001")) print("test002: ",dict:get_stale("test002")) print("test003: ",dict:get_stale("test003")) print("test004: ",dict:get_stale("test004")) print("test005: ",dict:get_stale("test005"))

ngx.sleep(6) print("--------sleep 6s, get all keys that already expired") print("test001: ",dict:get_stale("test001")) print("test002: ",dict:get_stale("test002")) print("test003: ",dict:get_stale("test003")) print("test004: ",dict:get_stale("test004")) print("test005: ",dict:get_stale("test005"))

print("--------set a new key, then get all keys that already expired") dict:set("test006", 666, 5, 1) print("test001: ",dict:get_stale("test001")) print("test002: ",dict:get_stale("test002")) print("test003: ",dict:get_stale("test003")) print("test004: ",dict:get_stale("test004")) print("test005: ",dict:get_stale("test005")) '

result

---------get all key in 5s test001: 1111false test002: 2221false test003: 3331false test004: 4441false test005: 5551false --------sleep 6s, get all keys that already expired test001: 1111true test002: 2221true test003: 3331true test004: 4441true test005: 5551true --------set a new key, then get all keys that already expired test001: test002: test003: 3331true test004: 4441true test005: 5551true

As a result, I think the result should be that all key can get data, but now, “test001“ and ”test002” do not get data(nil), and these key are set after “test003”、“test004“、”test005”。 Any idea are welcome!

spacewander commented 6 years ago

@jinquanni

Note that the value of an expired key is not guaranteed to be available so one should never rely on the availability of expired items.

https://github.com/openresty/lua-nginx-module#ngxshareddictget_stale

ngx_lua will remove expired key to make room for the new one.

jinquanni commented 6 years ago

@spacewander Thank you for your reply first,and i have read the links you gave before。What's troubling me now is the rule of remove expired key。Random or LRU or Time? Can I control this by any Parameters?

tokers commented 6 years ago

@jinquanni Most shdict APIs(except something like get_stale) will try to remove some expired entries(at most for three) when they are called. In additionally, when new item are being allocated and the memory space (for this shdict) is not enough, at least one entry will be removed forcibly (except something like safe_set), according to the LRU algorithm.

jinquanni commented 6 years ago

@tokers Thank you for your reply first I change shdict zone from 1m to 100m, and change set to safe_set , The phenomenon still exists

(1) Most shdict APIs(except something like get_stale) will try to remove some expired entries(at most for three) when they are called ---------The APIs here includes set and safe_set, right? (2)If the above description is correct, why do I need additional rules("when new item are being allocated and the memory space (for this shdict) is not enough, at least one entry will be removed forcibly (except something like safe_set), according to the LRU algorithm.")? I can not see the significance of this additional rule.

tokers commented 6 years ago

(1) Most shdict APIs(except something like get_stale) will try to remove some expired entries(at most for three) when they are called ---------The APIs here includes set and safe_set, right?

Yes.

(2)If the above description is correct, why do I need additional rules("when new item are being allocated and the memory space (for this shdict) is not enough, at least one entry will be removed forcibly (except something like safe_set), according to the LRU algorithm.")? I can not see the significance of this additional rule.

This is about the situation that the shared memory is lack.

--------set a new key, then get all keys that already expired test001: test002: test003: 3331true test004: 4441true test005: 5551true

The ngx.shared.DICT.set will try to remove some expired keys. So the test001 and test002 are hit according to the LRU algorithm.

jinquanni commented 6 years ago

@tokers hello again I change shdict zone from 1m to 100m, and change set to safe_set , it's mean memory is enough,but The phenomenon still exists? It confuses me。

In other words, I want to use share.DICT to cache data from redis. When redis is exceptions, I hope to always get the data in the cache (Even if a key expires, it does not want to be removed.).

What should I do?

tokers commented 6 years ago

@jinquanni Keys were removed because they were expired, not because of the memory size. Also, safe_set ensures it will not override the unexpired items.

See https://github.com/openresty/lua-nginx-module#ngxshareddictsafe_set for more details.

jinquanni commented 6 years ago

@tokers ok i see, thank you very much! By the way, do you know is there other ways can ensures expired items donot be removed. Or is there a plan to implement such an interface in ngx.shared.DICT?

tokers commented 6 years ago

@jinquanni

By the way, do you know is there other ways can ensures expired items donot be removed. Or is there a plan to implement such an interface in ngx.shared.DICT?

I think there is no such plan. Since you don't want to expired items be removed, why not just let these items never expire?

jinquanni commented 6 years ago

@tokers But if it is never expired, the data in the cache will not be refreshed. What I want to achieve is that when redis is normal, caching will expire in 10 seconds, that’s mean every 10 seconds, I will visit redis once and refresh the cache. But when redis is abnormal, I want to always have data available, whether it expires or not.

tokers commented 6 years ago

@jinquanni You may need another work-around way. Anyway, I think thee shdict APIs is no problem there.