diff --git a/lib/resty/lock.lua b/lib/resty/lock.lua index cf305cb..0187c30 100644 --- a/lib/resty/lock.lua +++ b/lib/resty/lock.lua @@ -87,13 +87,14 @@ function _M.new(_, dict_name, opts) cdata.key_id = 0 cdata.dict_id = ref_obj(dict) - local timeout, exptime, step, ratio, max_step + local timeout, exptime, step, ratio, max_step, safe_add if opts then timeout = opts.timeout exptime = opts.exptime step = opts.step ratio = opts.ratio max_step = opts.max_step + safe_add = opts.safe_add end if not exptime then @@ -107,6 +108,7 @@ function _M.new(_, dict_name, opts) local self = { cdata = cdata, dict = dict, + dict_add = safe_add and dict.safe_add or dict.add, timeout = timeout or 5, exptime = exptime, step = step or 0.001, @@ -128,7 +130,8 @@ function _M.lock(self, key) return nil, "locked" end local exptime = self.exptime - local ok, err = dict:add(key, true, exptime) + local dict_add = self.dict_add + local ok, err = dict_add(dict, key, true, exptime) if ok then cdata.key_id = ref_obj(key) if not shdict_mt then @@ -154,7 +157,7 @@ function _M.lock(self, key) elapsed = elapsed + step timeout = timeout - step - local ok, err = dict:add(key, true, exptime) + local ok, err = dict_add(dict, key, true, exptime) if ok then cdata.key_id = ref_obj(key) if not shdict_mt then diff --git a/t/sanity.t b/t/sanity.t index b04497c..97be7b7 100644 --- a/t/sanity.t +++ b/t/sanity.t @@ -468,3 +468,136 @@ lock 2: unlock: nil, unlocked --- no_error_log [error] + + +=== TEST 14: serial lock and unlock with safe_add +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua ' + local lock = require "resty.lock" + for i = 1, 2 do + local lock = lock:new("cache_locks", {safe_add = true}) + local elapsed, err = lock:lock("foo") + ngx.say("lock: ", elapsed, ", ", err) + local ok, err = lock:unlock() + if not ok then + ngx.say("failed to unlock: ", err) + end + ngx.say("unlock: ", ok) + end + '; + } +--- request +GET /t +--- response_body +lock: 0, nil +unlock: 1 +lock: 0, nil +unlock: 1 + +--- no_error_log +[error] + + + +=== TEST 15: the "safe_add" option is true: exhausting the shm zone memory +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua ' + local lock = require "resty.lock" + local cache = ngx.shared.cache_locks + + cache:flush_all() + function full_dict( dict ) + for i = 1, 10000 do + local ok, err = dict:safe_set(string.rep("2", 2) .. i, string.rep("2", 5) .. i) + if not ok then + return + end + end + end + full_dict(cache) + + local lock = lock:new("cache_locks", {safe_add = true, timeout = 0}) + local elapsed, err = lock:lock("foo") + ngx.say("lock: ", elapsed, ", ", err) + '; + } +--- request +GET /t +--- response_body +lock: nil, no memory + +--- no_error_log +[error] + + + +=== TEST 16: the "safe_add" option is false: exhausting the shm zone memory +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua ' + local lock = require "resty.lock" + local cache = ngx.shared.cache_locks + + cache:flush_all() + function full_dict( dict ) + for i = 1, 10000 do + local ok, err = dict:safe_set(string.rep("2", 2) .. i, string.rep("2", 5) .. i) + if not ok then + return + end + end + end + full_dict(cache) + + local lock = lock:new("cache_locks", {safe_add = false, timeout = 0}) + local elapsed, err = lock:lock("foo") + ngx.say("lock: ", elapsed, ", ", err) + '; + } +--- request +GET /t +--- response_body +lock: 0, nil + +--- no_error_log +[error] + + + +=== TEST 17: the "safe_add" option is off by default: exhausting the shm zone memory +--- http_config eval: $::HttpConfig +--- config + location = /t { + content_by_lua ' + local lock = require "resty.lock" + local cache = ngx.shared.cache_locks + + cache:flush_all() + function full_dict( dict ) + for i = 1, 10000 do + local ok, err = dict:safe_set(string.rep("2", 2) .. i, string.rep("2", 5) .. i) + if not ok then + return + end + end + end + full_dict(cache) + + local lock = lock:new("cache_locks", {timeout = 0}) + local elapsed, err = lock:lock("foo") + ngx.say("lock: ", elapsed, ", ", err) + '; + } +--- request +GET /t +--- response_body +lock: 0, nil + +--- no_error_log +[error] +