From 60517af9b60aa4d764d19bddf92c90fb6bc61f3b Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Mon, 8 Jan 2018 15:48:56 -0800 Subject: [PATCH] feature: shdict:incr(): added the "exptime" argument to set the expiry of values when they are first created via the "init" argument. --- .travis.yml | 2 +- lib/resty/core/shdict.lua | 33 ++++-- t/shdict.t | 244 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 07a336ef4..4610da83b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,7 +59,7 @@ install: - git clone https://github.com/openresty/openresty.git ../openresty - git clone https://github.com/openresty/openresty-devel-utils.git - git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module - - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module + - git clone -b feat/incr-exptime-ffi https://github.com/thibaultcha/lua-nginx-module.git ../lua-nginx-module - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module - git clone https://github.com/openresty/lua-resty-lrucache.git diff --git a/lib/resty/core/shdict.lua b/lib/resty/core/shdict.lua index b9fc0447d..55f2a88a9 100644 --- a/lib/resty/core/shdict.lua +++ b/lib/resty/core/shdict.lua @@ -29,7 +29,7 @@ ffi.cdef[[ int ngx_http_lua_ffi_shdict_incr(void *zone, const unsigned char *key, size_t key_len, double *value, char **err, int has_init, double init, - int *forcible); + long init_ttl, int *forcible); int ngx_http_lua_ffi_shdict_store(void *zone, int op, const unsigned char *key, size_t key_len, int value_type, @@ -337,7 +337,7 @@ local function shdict_get_stale(zone, key) end -local function shdict_incr(zone, key, value, init) +local function shdict_incr(zone, key, value, init, init_ttl) zone = check_zone(zone) if key == nil then @@ -361,8 +361,6 @@ local function shdict_incr(zone, key, value, init) end num_value[0] = value - local has_init - if init then local typ = type(init) if typ ~= "number" then @@ -372,22 +370,39 @@ local function shdict_incr(zone, key, value, init) return error("bad init arg: number expected, got " .. typ) end end + end - has_init = 1 + if init_ttl ~= nil then + local typ = type(init_ttl) + if typ ~= "number" then + init_ttl = tonumber(init_ttl) + + if not init_ttl then + error("bad init_ttl arg: number expected, got " .. typ, 2) + end + end + + if init_ttl < 0 then + error('bad "init_ttl" argument', 2) + end + + if not init then + error('must provide "init" when providing "init_ttl"', 2) + end else - has_init = 0 - init = 0 + init_ttl = 0 end local rc = C.ngx_http_lua_ffi_shdict_incr(zone, key, key_len, num_value, - errmsg, has_init, init, + errmsg, init and 1 or 0, + init or 0, init_ttl * 1000, forcible) if rc ~= 0 then -- ~= NGX_OK return nil, ffi_str(errmsg[0]) end - if has_init == 0 then + if not init then return tonumber(num_value[0]) end diff --git a/t/shdict.t b/t/shdict.t index 550ecdfd6..4279409f5 100644 --- a/t/shdict.t +++ b/t/shdict.t @@ -1284,3 +1284,247 @@ free_page_bytes: (?:0|32768) [error] [alert] [crit] + + + +=== TEST 39: incr bad init_ttl argument +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + local pok, err = pcall(dogs.incr, dogs, "foo", 1, 0, -1) + if not pok then + ngx.say("not ok: ", err) + return + end + + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +not ok: bad "init_ttl" argument +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 40: incr init_ttl argument is not a number +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + local pok, err = pcall(dogs.incr, dogs, "foo", 1, 0, "bar") + if not pok then + ngx.say("not ok: ", err) + return + end + + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +not ok: bad init_ttl arg: number expected, got string +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 41: incr init_ttl argument without init +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + local pok, err = pcall(dogs.incr, dogs, "foo", 1, nil, 0.001) + if not pok then + ngx.say("not ok: ", err) + return + end + + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +not ok: must provide "init" when providing "init_ttl" +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 42: incr key with init_ttl (key exists) +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + + local res, err = dogs:incr("foo", 10502, 0, 0.001) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + + ngx.sleep(0.002) + + ngx.say("foo after incr init_ttl = ", dogs:get("foo")) + } + } +--- request +GET /t +--- response_body +incr: 10534 nil +foo = 10534 +foo after incr init_ttl = 10534 +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 43: incr key with init and init_ttl (key not exists) +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:flush_all() + + local res, err = dogs:incr("foo", 10502, 1, 0.001) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + + ngx.sleep(0.002) + + ngx.say("foo after init_ttl = ", dogs:get("foo")) + } + } +--- request +GET /t +--- response_body +incr: 10503 nil +foo = 10503 +foo after init_ttl = nil +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 44: incr key with init and init_ttl as string (key not exists) +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:flush_all() + + local res, err = dogs:incr("foo", 10502, 1, "0.001") + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + + ngx.sleep(0.002) + + ngx.say("foo after init_ttl = ", dogs:get("foo")) + } + } +--- request +GET /t +--- response_body +incr: 10503 nil +foo = 10503 +foo after init_ttl = nil +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 45: incr key with init and init_ttl (key expired and size matched) +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + for i = 1, 20 do + dogs:set("bar" .. i, i, 0.002) + end + dogs:set("foo", 32, 0.002) + ngx.sleep(0.003) + + local res, err = dogs:incr("foo", 10502, 0, 0.001) + ngx.say("incr: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + + ngx.sleep(0.002) + + ngx.say("foo after init_ttl = ", dogs:get("foo")) + } + } +--- request +GET /t +--- response_body +incr: 10502 nil +foo = 10502 +foo after init_ttl = nil +--- no_error_log +[error] +[alert] +[crit] + + + +=== TEST 46: incr key with init and init_ttl (forcibly override other valid entries) +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:flush_all() + + local long_prefix = string.rep("1234567890", 100) + for i = 1, 1000 do + local success, err, forcible = dogs:set(long_prefix .. i, i) + if forcible then + dogs:delete(long_prefix .. i) + break + end + end + + local res, err, forcible = dogs:incr(long_prefix .. "bar", 10502, 0) + ngx.say("incr: ", res, " ", err, " ", forcible) + + local res, err, forcible = dogs:incr(long_prefix .. "foo", 10502, 0, 0.001) + ngx.say("incr: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get(long_prefix .. "foo")) + + ngx.sleep(0.002) + ngx.say("foo after init_ttl = ", dogs:get("foo")) + } + } +--- request +GET /t +--- response_body +incr: 10502 nil false +incr: 10502 nil true +foo = 10502 +foo after init_ttl = nil +--- no_error_log +[error] +[alert] +[crit]