diff --git a/README.markdown b/README.markdown index c4da2f8..a520a3b 100644 --- a/README.markdown +++ b/README.markdown @@ -45,9 +45,14 @@ local _M = {} -- alternatively: local lrucache = require "resty.lrucache.pureffi" local lrucache = require "resty.lrucache" +local function call_back(key, value) + ngx.log(ngx.ERR, "evict:", key, ":", value) +end + -- we need to initialize the cache on the lua module level so that -- it can be shared by all the requests served by each nginx worker process: -local c, err = lrucache.new(200) -- allow up to 200 items in the cache +local c, err = lrucache.new(2, call_back) -- allow up to 2 items in the cache +-- local c, err = lrucache.new(2, nil, call_back) -- for `resty.lrucache.pureffi` if not c then error("failed to create the cache: " .. (err or "unknown")) end @@ -61,6 +66,12 @@ function _M.go() c:set("dog", { age = 10 }, 0.1) -- expire in 0.1 sec c:delete("dog") + c:set("dog", 32) + c:set("parrto", 12) -- in log "evict:cat:56" + c:set("cat", 56, 0.1) -- in log "evict:dog:32" + ngx.sleep(1) + ngx.say("cat:", c:get("cat")) -- in log "evict:cat:56" + c:flush_all() -- flush all the cached data end @@ -151,7 +162,9 @@ local lrucache = require "resty.lrucache.pureffi" new --- -`syntax: cache, err = lrucache.new(max_items [, load_factor])` +`syntax: cache, err = lrucache.new(max_items [, evict_cb])` + +`syntax: cache, err = lrucache.new(max_items [, load_factor, evict_cb])` Creates a new cache instance. Upon failure, returns `nil` and a string describing the error. @@ -167,6 +180,11 @@ saturated to 1; likewise, if load-factor is smaller than `0.1`, it will be clamped to `0.1`). This argument is only meaningful for `resty.lrucache.pureffi`. +The `evict_cb` argument specifies the item's destory call back. When use `set` +add new item or `get`(item with ttl), may trigger remove old item, with this +call back you can do some clean up job. The `evict_cb` will receive two param: +key and value. + [Back to TOC](#table-of-contents) set diff --git a/lib/resty/lrucache.lua b/lib/resty/lrucache.lua index 147842a..7bc46d4 100644 --- a/lib/resty/lrucache.lua +++ b/lib/resty/lrucache.lua @@ -151,11 +151,15 @@ local function ptr2num(ptr) end -function _M.new(size) +function _M.new(size, evict_cb) if size < 1 then return nil, "size too small" end + if evict_cb and type(evict_cb) ~= "function" then + return nil, "evict_cb type error" + end + local self = { hasht = {}, free_queue = queue_init(size), @@ -164,6 +168,7 @@ function _M.new(size) node2key = {}, num_items = 0, max_items = size, + evict_cb = evict_cb, } return setmetatable(self, mt) end @@ -195,6 +200,12 @@ function _M.get(self, key) if node.expire >= 0 and node.expire < ngx_now() then -- print("expired: ", node.expire, " > ", ngx_now()) + if self.evict_cb then + local ok, err = pcall(self.evict_cb, key, val) + if not ok then + ngx.log(ngx.ERR, "evict expire key:", key, " fail:", err) + end + end return nil, val, node.user_flags end @@ -241,6 +252,12 @@ function _M.set(self, key, value, ttl, flags) -- print(key, ": evicting oldkey: ", oldkey, ", oldnode: ", -- tostring(node)) if oldkey then + if self.evict_cb then + local ok, err = pcall(self.evict_cb, oldkey, hasht[oldkey]) + if not ok then + ngx.log(ngx.ERR, "evict old key:", oldkey, " fail:", err) + end + end hasht[oldkey] = nil key2node[oldkey] = nil end diff --git a/lib/resty/lrucache/pureffi.lua b/lib/resty/lrucache/pureffi.lua index fc1103e..070fbb7 100644 --- a/lib/resty/lrucache/pureffi.lua +++ b/lib/resty/lrucache/pureffi.lua @@ -318,11 +318,15 @@ local mt = { __index = _M } -- load-factor is specified, it will be clamped to the range of [0.1, 1](i.e. -- if load-factor is greater than 1, it will be saturated to 1, likewise, -- if load-factor is smaller than 0.1, it will be clamped to 0.1). -function _M.new(size, load_factor) +function _M.new(size, load_factor, evict_cb) if size < 1 then return nil, "size too small" end + if evict_cb and type(evict_cb) ~= "function" then + return nil, "evict_cb type error" + end + -- Determine bucket size, which must be power of two. local load_f = load_factor if not load_factor then @@ -350,6 +354,7 @@ function _M.new(size, load_factor) val_v = new_tab(size, 0), bucket_v = ffi_new(int_array_t, bucket_sz), num_items = 0, + evict_cb = evict_cb, } -- "note_v" is an array of all the nodes used in the LRU queue. Exprpession -- node_v[i] evaluates to the element of ID "i". @@ -489,6 +494,12 @@ function _M.get(self, key) local expire = node.expire if expire >= 0 and expire < ngx_now() then -- print("expired: ", node.expire, " > ", ngx_now()) + if self.evict_cb then + local ok, err = pcall(self.evict_cb, key, self.val_v[node_id]) + if not ok then + ngx.log(ngx.ERR, "evict expire key:", key, " fail:", err) + end + end return nil, self.val_v[node_id], node.user_flags end @@ -526,6 +537,12 @@ function _M.set(self, key, value, ttl, flags) -- evict the least recently used key -- assert(not queue_is_empty(self.cache_queue)) node = queue_last(self.cache_queue) + if self.evict_cb then + local ok, err = pcall(self.evict_cb, self.key_v[node.id], self.val_v[node.id]) + if not ok then + ngx.log(ngx.ERR, "evict old key:", self.key_v[node.id], " fail:", err) + end + end remove_key(self, self.key_v[node.id]) else -- take a free queue node diff --git a/t/009-evict-call-back.t b/t/009-evict-call-back.t new file mode 100644 index 0000000..fabfa57 --- /dev/null +++ b/t/009-evict-call-back.t @@ -0,0 +1,53 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib '.'; +use t::TestLRUCache; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: evict with call back +--- config + location = /t { + content_by_lua_block { + local function evict_cb(k,v) + ngx.say(k, v) + end + local lrucache = require "resty.lrucache" + local c = lrucache.new(1, evict_cb) + + collectgarbage() + + c:set("dog", 12) + c:set("cat", 14) + } + } +--- response_body +dog12 + + + +=== TEST 2: evict with call back, ttl +--- config + location = /t { + content_by_lua_block { + local function evict_cb(k,v) + ngx.say(k, v) + end + local lrucache = require "resty.lrucache" + local c = lrucache.new(1, evict_cb) + + collectgarbage() + + c:set("dog", 12, 0.1) + ngx.sleep(1) + c:get("dog") + } + } +--- response_body +dog12 diff --git a/t/100-pureffi/009-evict-call-back.t b/t/100-pureffi/009-evict-call-back.t new file mode 100644 index 0000000..252969d --- /dev/null +++ b/t/100-pureffi/009-evict-call-back.t @@ -0,0 +1,53 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib '.'; +use t::TestLRUCache; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: evict with call back +--- config + location = /t { + content_by_lua_block { + local function evict_cb(k,v) + ngx.say(k, v) + end + local lrucache = require "resty.lrucache.pureffi" + local c = lrucache.new(1, nil, evict_cb) + + collectgarbage() + + c:set("dog", 12) + c:set("cat", 14) + } + } +--- response_body +dog12 + + + +=== TEST 2: evict with call back, ttl +--- config + location = /t { + content_by_lua_block { + local function evict_cb(k,v) + ngx.say(k, v) + end + local lrucache = require "resty.lrucache.pureffi" + local c = lrucache.new(1, nil, evict_cb) + + collectgarbage() + + c:set("dog", 12, 1) + ngx.sleep(1) + c:get("dog") + } + } +--- response_body +dog12