Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
19 changes: 18 additions & 1 deletion lib/resty/lrucache.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -164,6 +168,7 @@ function _M.new(size)
node2key = {},
num_items = 0,
max_items = size,
evict_cb = evict_cb,
}
return setmetatable(self, mt)
end
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion lib/resty/lrucache/pureffi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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".
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions t/009-evict-call-back.t
Original file line number Diff line number Diff line change
@@ -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
53 changes: 53 additions & 0 deletions t/100-pureffi/009-evict-call-back.t
Original file line number Diff line number Diff line change
@@ -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