Merge commit 'd9a2f6c2e3b67cf4501d46da9f2260e939321fe6' into dev

This commit is contained in:
Théophile Diot 2024-01-31 15:03:11 +01:00
commit 285247402c
No known key found for this signature in database
GPG key ID: 248FEA4BAE400D06
24 changed files with 1694 additions and 1002 deletions

View file

@ -145,9 +145,9 @@
},
{
"id": "lua-resty-mlcache",
"name": "lua-resty-mlcache v2.6.1",
"name": "lua-resty-mlcache v2.6.0",
"url": "https://github.com/thibaultcha/lua-resty-mlcache.git",
"commit": "ab418e4237b083aaa9b7c9c43782b1900aabbf5b"
"commit": "f140f56663cbdb9cdd247d29f75c299c702ff6b4"
},
{
"id": "lua-resty-openssl",

View file

@ -23,8 +23,7 @@ jobs:
matrix:
os: [ubuntu-latest]
openresty:
- 1.25.3.1
- 1.21.4.3
- 1.21.4.1
- 1.19.9.1
- 1.19.3.2
- 1.17.8.2

View file

@ -1,31 +1,18 @@
# Table of Contents
- [2.6.1](#261)
- [2.6.0](#260)
- [2.5.0](#250)
- [2.4.1](#241)
- [2.4.0](#240)
- [2.3.0](#230)
- [2.2.1](#221)
- [2.2.0](#220)
- [2.1.0](#210)
- [2.0.2](#202)
- [2.0.1](#201)
- [2.0.0](#200)
- [1.0.1](#101)
- [1.0.0](#100)
## [2.6.1]
> Released on: 2024/01/30
#### Fixed
- Ensure the `l1_serializer` callback option is properly invoked in a couple of
`get()` edge-cases.
[#123](https://github.com/thibaultcha/lua-resty-mlcache/pull/123)
[Back to TOC](#table-of-contents)
- [2.6.0](#2.6.0)
- [2.5.0](#2.5.0)
- [2.4.1](#2.4.1)
- [2.4.0](#2.4.0)
- [2.3.0](#2.3.0)
- [2.2.1](#2.2.1)
- [2.2.0](#2.2.0)
- [2.1.0](#2.1.0)
- [2.0.2](#2.0.2)
- [2.0.1](#2.0.1)
- [2.0.0](#2.0.0)
- [1.0.1](#1.0.1)
- [1.0.0](#1.0.0)
## [2.6.0]
@ -268,7 +255,6 @@ Initial release.
[Back to TOC](#table-of-contents)
[2.6.1]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.6.0...2.6.1
[2.6.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.5.0...2.6.0
[2.5.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.4.1...2.5.0
[2.4.1]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.4.0...2.4.1

View file

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2024 Thibault Charbonnier
Copyright (c) 2017-2022 Thibault Charbonnier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -165,8 +165,6 @@ Tests matrix results:
| `1.17.8.x` | :heavy_check_mark:
| `1.19.3.x` | :heavy_check_mark:
| `1.19.9.x` | :heavy_check_mark:
| `1.21.4.x` | :heavy_check_mark:
| `1.25.3.x` | :heavy_check_mark:
| > | not tested
[Back to TOC](#table-of-contents)
@ -339,18 +337,17 @@ Perform a cache lookup. This is the primary and most efficient method of this
module. A typical pattern is to *not* call [set()](#set), and let [get()](#get)
perform all the work.
When this method succeeds, it returns `value` and `err` is set to `nil`.
**Because `nil` values from the L3 callback can be cached (i.e. "negative
caching"), `value` can be `nil` albeit already cached. Hence, one must note to
check the second return value `err` to determine if this method succeeded or
not**.
When this method succeeds, it returns `value` and no error. **Because `nil`
values from the L3 callback can be cached (i.e. "negative caching"), `value` can
be nil albeit already cached. Hence, one must rely on the second return value
`err` to determine if this method succeeded or not**.
The third return value is a number which is set if no error was encountered.
It indicates the level at which the value was fetched: `1` for L1, `2` for L2,
It indicated the level at which the value was fetched: `1` for L1, `2` for L2,
and `3` for L3.
If, however, an error is encountered, then this method returns `nil` in `value`
and a string describing the error in `err`.
If an error is encountered, this method returns `nil` plus a string describing
the error.
The first argument `key` is a string. Each value must be stored under a unique
key.
@ -445,20 +442,14 @@ in the cache **and** no callback is provided, `get()` will return `nil, nil,
with return values such as `nil, nil, 1`, where 1 signifies a **negative cached
item** found in L1 (cached `nil`).
Not providing a `callback` function allows implementing cache lookup patterns
that are guaranteed to be on-cpu for a more constant, smoother latency tail end
(e.g. with values refreshed in background timers via `set()`).
```lua
local value, err, hit_lvl = cache:get("key")
if value == nil then
if err ~= nil then
-- error
elseif hit_lvl == -1 then
if hit_lvl == -1 then
-- miss (no value)
else
-- negative hit (cached `nil` value)
end
-- negative hit (cached `nil`)
end
```

View file

@ -174,7 +174,7 @@ end
local _M = {
_VERSION = "2.6.1",
_VERSION = "2.6.0",
_AUTHOR = "Thibault Charbonnier",
_LICENSE = "MIT",
_URL = "https://github.com/thibaultcha/lua-resty-mlcache",
@ -394,8 +394,12 @@ function _M.new(name, shm, opts)
end
local function l1_serialize(value, l1_serializer)
if value ~= nil and l1_serializer then
local function set_lru(self, key, value, ttl, neg_ttl, l1_serializer)
if value == nil then
ttl = neg_ttl
value = CACHE_MISS_SENTINEL_LRU
elseif l1_serializer then
local ok, err
ok, value, err = pcall(l1_serializer, value)
if not ok then
@ -411,21 +415,6 @@ local function l1_serialize(value, l1_serializer)
end
end
return value
end
local function set_lru(self, key, value, ttl, neg_ttl, l1_serializer)
local value, err = l1_serialize(value, l1_serializer)
if err then
return nil, err
end
if value == nil then
value = CACHE_MISS_SENTINEL_LRU
ttl = neg_ttl
end
if ttl == 0 then
-- indefinite ttl for lua-resty-lrucache is 'nil'
ttl = nil
@ -566,11 +555,6 @@ local function get_shm_set_lru(self, key, shm_key, l1_serializer)
end
if went_stale then
value, err = l1_serialize(value, l1_serializer)
if err then
return nil, err
end
return value, nil, went_stale
end
@ -590,11 +574,6 @@ local function get_shm_set_lru(self, key, shm_key, l1_serializer)
-- value has less than 1ms of lifetime in the shm, avoid
-- setting it in LRU which would be wasteful and could
-- indefinitely cache the value when ttl == 0
value, err = l1_serialize(value, l1_serializer)
if err then
return nil, err
end
return value, nil, nil, is_stale
end
end
@ -708,7 +687,6 @@ end
local function run_callback(self, key, shm_key, data, ttl, neg_ttl,
went_stale, l1_serializer, resurrect_ttl, shm_set_tries, cb, ...)
local lock, err = resty_lock:new(self.shm_locks, self.resty_lock_opts)
if not lock then
return nil, "could not create lock: " .. err

View file

@ -1,8 +1,8 @@
package = "lua-resty-mlcache"
version = "2.6.1-1"
version = "2.6.0-1"
source = {
url = "git+https://github.com/thibaultcha/lua-resty-mlcache",
tag = "2.6.1"
url = "git://github.com/thibaultcha/lua-resty-mlcache",
tag = "2.6.0"
}
description = {
summary = "Layered caching library for OpenResty",

View file

@ -1,18 +1,46 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
plan tests => repeat_each() * blocks() * 5;
workers(1);
plan tests => repeat_each() * (blocks() * 5);
our $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict ipc 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: new() ensures shm exists
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
@ -20,80 +48,96 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no such lua_shared_dict: foo
--- no_error_log
[error]
[crit]
[alert]
=== TEST 2: broadcast() sends an event through shm
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "received event from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "hello world"))
assert(ipc:poll())
}
}
--- ignore_response_body
--- error_log
received event from my_channel: hello world
--- request
GET /t
--- response_body
--- no_error_log
[error]
[crit]
[alert]
--- error_log
received event from my_channel: hello world
=== TEST 3: broadcast() runs event callback in protected mode
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
error("my callback had an error")
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "hello world"))
assert(ipc:poll())
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- error_log eval
qr/\[error\] .*? \[ipc\] callback for channel 'my_channel' threw a Lua error: init_worker_by_lua(.*?)?:\d: my callback had an error/
qr/\[error\] .*? \[ipc\] callback for channel 'my_channel' threw a Lua error: init_worker_by_lua:\d: my callback had an error/
--- no_error_log
lua entry thread aborted: runtime error
[crit]
[alert]
=== TEST 4: poll() catches invalid timeout arg
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
local ok, err = pcall(ipc.poll, ipc, false)
if not ok then
@ -101,42 +145,50 @@ lua entry thread aborted: runtime error
end
}
}
--- request
GET /t
--- response_body
timeout must be a number
--- no_error_log
[error]
[crit]
[alert]
=== TEST 5: poll() catches up with all events
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "received event from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
assert(ipc:broadcast("my_channel", "msg 2"))
assert(ipc:broadcast("my_channel", "msg 3"))
assert(ipc:poll())
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- error_log
received event from my_channel: msg 1
received event from my_channel: msg 2
received event from my_channel: msg 3
--- no_error_log
[error]
@ -145,14 +197,16 @@ This ensures new workers spawned during a master process' lifecycle do not
attempt to replay all events from index 0.
https://github.com/thibaultcha/lua-resty-mlcache/issues/87
https://github.com/thibaultcha/lua-resty-mlcache/issues/93
--- http_config
lua_shared_dict ipc_shm 32k;
--- http_config eval
qq{
lua_package_path "$::pwd/lib/?.lua;;";
lua_shared_dict ipc 32k;
init_by_lua_block {
require "resty.core"
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "my_channel event: ", data)
@ -164,8 +218,9 @@ https://github.com/thibaultcha/lua-resty-mlcache/issues/93
assert(ipc:broadcast("my_channel", string.rep(".", 2^10)))
end
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
ngx.say("ipc.idx: ", ipc.idx)
@ -179,6 +234,8 @@ https://github.com/thibaultcha/lua-resty-mlcache/issues/93
ngx.say("ipc.idx: ", ipc.idx)
}
}
--- request
GET /t
--- response_body
ipc.idx: 0
ipc.idx: 34
@ -191,38 +248,47 @@ my_channel event: first broadcast
=== TEST 7: poll() does not execute events from self (same pid)
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm"))
ipc = assert(mlcache_ipc.new("ipc"))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "received event from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "hello world"))
assert(ipc:poll())
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- no_error_log
received event from my_channel: hello world
[error]
[crit]
[alert]
received event from my_channel: hello world
=== TEST 8: poll() runs all registered callbacks for a channel
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback 1 from my_channel: ", data)
@ -236,55 +302,68 @@ received event from my_channel: hello world
ngx.log(ngx.NOTICE, "callback 3 from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "hello world"))
assert(ipc:poll())
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- error_log
callback 1 from my_channel: hello world
callback 2 from my_channel: hello world
callback 3 from my_channel: hello world
--- no_error_log
[error]
=== TEST 9: poll() exits when no event to poll
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:poll())
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- no_error_log
callback from my_channel: hello world
[error]
[crit]
[alert]
callback from my_channel: hello world
=== TEST 10: poll() runs all callbacks from all channels
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback 1 from my_channel: ", data)
@ -302,44 +381,50 @@ callback from my_channel: hello world
ngx.log(ngx.NOTICE, "callback 2 from other_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "hello world"))
assert(ipc:broadcast("other_channel", "hello ipc"))
assert(ipc:broadcast("other_channel", "hello ipc 2"))
assert(ipc:poll())
}
}
--- ignore_response_body
--- grep_error_log eval: qr/callback \d+ from [^,]*/
--- grep_error_log_out
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- error_log
callback 1 from my_channel: hello world
callback 2 from my_channel: hello world
callback 1 from other_channel: hello ipc
callback 2 from other_channel: hello ipc
callback 1 from other_channel: hello ipc 2
callback 2 from other_channel: hello ipc 2
--- no_error_log
[error]
[crit]
[alert]
=== TEST 11: poll() catches tampered shm (by third-party users)
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
assert(ngx.shared.ipc_shm:set("lua-resty-ipc:index", false))
assert(ngx.shared.ipc:set("lua-resty-ipc:index", false))
local ok, err = ipc:poll()
if not ok then
@ -347,29 +432,33 @@ callback 2 from other_channel: hello ipc 2
end
}
}
--- request
GET /t
--- response_body
index is not a number, shm tampered with
--- no_error_log
[error]
[crit]
[alert]
=== TEST 12: poll() retries getting an event until timeout
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
ngx.shared.ipc_shm:delete(1)
ngx.shared.ipc_shm:flush_expired()
ngx.shared.ipc:delete(1)
ngx.shared.ipc:flush_expired()
local ok, err = ipc:poll()
if not ok then
@ -377,40 +466,44 @@ index is not a number, shm tampered with
end
}
}
--- ignore_response_body
--- grep_error_log eval: qr/((\[error\] .*?)|(\[ipc\] no event data at index '\d+', retrying .*?))[^,]*/
--- grep_error_log_out eval
qr/\[ipc\] no event data at index '1', retrying in: 0\.001s
\[ipc\] no event data at index '1', retrying in: 0\.002s
\[ipc\] no event data at index '1', retrying in: 0\.004s
\[ipc\] no event data at index '1', retrying in: 0\.008s
\[ipc\] no event data at index '1', retrying in: 0\.016s
\[ipc\] no event data at index '1', retrying in: 0\.032s
\[ipc\] no event data at index '1', retrying in: 0\.064s
\[ipc\] no event data at index '1', retrying in: 0\.128s
\[ipc\] no event data at index '1', retrying in: 0\.045s
\[error\] .*? could not poll: timeout/
--- no_error_log
[warn]
[crit]
[alert]
--- request
GET /t
--- response_body
--- error_log eval
[
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.001s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.002s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.004s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.008s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.016s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.032s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.064s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.128s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.045s/,
qr/\[error\] .*? could not poll: timeout/,
]
=== TEST 13: poll() reaches custom timeout
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
ngx.shared.ipc_shm:delete(1)
ngx.shared.ipc_shm:flush_expired()
ngx.shared.ipc:delete(1)
ngx.shared.ipc:flush_expired()
local ok, err = ipc:poll(0.01)
if not ok then
@ -418,75 +511,83 @@ qr/\[ipc\] no event data at index '1', retrying in: 0\.001s
end
}
}
--- ignore_response_body
--- grep_error_log eval: qr/((\[error\] .*?)|(\[ipc\] no event data at index '\d+', retrying .*?))[^,]*/
--- grep_error_log_out eval
qr/\[ipc\] no event data at index '1', retrying in: 0\.001s
\[ipc\] no event data at index '1', retrying in: 0\.002s
\[ipc\] no event data at index '1', retrying in: 0\.004s
\[ipc\] no event data at index '1', retrying in: 0\.003s
\[error\] .*? could not poll: timeout/
--- no_error_log
[warn]
[crit]
[alert]
--- request
GET /t
--- response_body
--- error_log eval
[
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.001s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.002s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.004s/,
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.003s/,
qr/\[error\] .*? could not poll: timeout/,
]
=== TEST 14: poll() logs errors and continue if event has been tampered with
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
assert(ipc:broadcast("my_channel", "msg 2"))
assert(ngx.shared.ipc_shm:set(1, false))
assert(ngx.shared.ipc:set(1, false))
assert(ipc:poll())
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- error_log eval
[
qr/\[error\] .*? \[ipc\] event at index '1' is not a string, shm tampered with/,
qr/\[notice\] .*? callback from my_channel: msg 2/,
]
--- no_error_log
[warn]
[crit]
=== TEST 15: poll() is safe to be called in contexts that don't support ngx.sleep()
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
return 200;
log_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
ngx.shared.ipc_shm:delete(1)
ngx.shared.ipc_shm:flush_expired()
ngx.shared.ipc:delete(1)
ngx.shared.ipc:flush_expired()
local ok, err = ipc:poll()
if not ok then
@ -494,31 +595,36 @@ qr/\[ipc\] no event data at index '1', retrying in: 0\.001s
end
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- error_log eval
[
qr/\[info\] .*? \[ipc\] no event data at index '1', retrying in: 0\.001s/,
qr/\[warn\] .*? \[ipc\] could not sleep before retry: API disabled in the context of log_by_lua/,
qr/\[error\] .*? could not poll: timeout/,
]
--- no_error_log
[crit]
=== TEST 16: poll() guards self.idx from growing beyond the current shm idx
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
assert(ipc:broadcast("other_channel", ""))
assert(ipc:poll())
@ -536,6 +642,8 @@ qr/\[ipc\] no event data at index '1', retrying in: 0\.001s
assert(ipc:poll())
}
}
--- request
GET /t
--- ignore_response_body
--- error_log
callback from my_channel: third broadcast
@ -547,57 +655,63 @@ callback from my_channel: second broadcast
=== TEST 17: poll() JITs
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
for i = 1, 10e3 do
assert(ipc:poll())
end
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):2 loop\]/
--- no_error_log
[warn]
[error]
[crit]
=== TEST 18: broadcast() JITs
--- http_config
--- http_config eval
qq{
$::HttpConfig
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc = assert(mlcache_ipc.new("ipc", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback from my_channel: ", data)
end)
}
}
--- config
location /t {
location = /t {
content_by_lua_block {
for i = 1, 10e3 do
assert(ipc:broadcast("my_channel", "hello world"))
end
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):2 loop\]/
--- no_error_log
[warn]
[error]
[crit]

View file

@ -1,26 +1,35 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3) + 4;
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
};
run_tests();
__DATA__
=== TEST 1: module has version number
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
ngx.say(mlcache._VERSION)
}
}
--- request
GET /t
--- response_body_like
\d+\.\d+\.\d+
--- no_error_log
@ -29,103 +38,119 @@ __DATA__
=== TEST 2: new() validates name
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local ok, err = pcall(mlcache.new)
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
name must be a string
--- no_error_log
[error]
=== TEST 3: new() validates shm
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local ok, err = pcall(mlcache.new, "name")
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
shm must be a string
--- no_error_log
[error]
=== TEST 4: new() validates opts
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local ok, err = pcall(mlcache.new, "name", "cache_shm", "foo")
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
opts must be a table
--- no_error_log
[error]
=== TEST 5: new() ensures shm exists
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache, err = mlcache.new("name", "foo")
if not cache then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
no such lua_shared_dict: foo
--- no_error_log
[error]
=== TEST 6: new() supports ipc_shm option and validates it
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local ok, err = pcall(mlcache.new, "name", "cache_shm", { ipc_shm = 1 })
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
opts.ipc_shm must be a string
--- no_error_log
[error]
--- error_log
ipc_shm must be a string
=== TEST 7: new() supports opts.ipc_shm and ensures it exists
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -135,6 +160,8 @@ opts.ipc_shm must be a string
end
}
}
--- request
GET /t
--- ignore_response_body
--- error_log eval
qr/\[error\] .*? no such lua_shared_dict: ipc/
@ -144,8 +171,9 @@ qr/\[error\] .*? no such lua_shared_dict: ipc/
=== TEST 8: new() supports ipc options and validates it
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -155,6 +183,8 @@ qr/\[error\] .*? no such lua_shared_dict: ipc/
end
}
}
--- request
GET /t
--- response_body
opts.ipc must be a table
--- no_error_log
@ -163,8 +193,9 @@ opts.ipc must be a table
=== TEST 9: new() prevents both opts.ipc_shm and opts.ipc to be given
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -177,6 +208,8 @@ opts.ipc must be a table
end
}
}
--- request
GET /t
--- response_body
cannot specify both of opts.ipc_shm and opts.ipc
--- no_error_log
@ -185,8 +218,9 @@ cannot specify both of opts.ipc_shm and opts.ipc
=== TEST 10: new() validates ipc.register_listeners + ipc.broadcast + ipc.poll (type: custom)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -214,6 +248,8 @@ cannot specify both of opts.ipc_shm and opts.ipc
end
}
}
--- request
GET /t
--- response_body
opts.ipc.register_listeners must be a function
opts.ipc.broadcast must be a function
@ -224,8 +260,9 @@ opts.ipc.poll must be a function
=== TEST 11: new() ipc.register_listeners can return nil + err (type: custom)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -243,6 +280,8 @@ opts.ipc.poll must be a function
end
}
}
--- request
GET /t
--- response_body_like
failed to initialize custom IPC \(opts\.ipc\.register_listeners returned an error\): something happened
--- no_error_log
@ -251,8 +290,9 @@ failed to initialize custom IPC \(opts\.ipc\.register_listeners returned an erro
=== TEST 12: new() calls ipc.register_listeners with events array (type: custom)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -280,6 +320,8 @@ failed to initialize custom IPC \(opts\.ipc\.register_listeners returned an erro
end
}
}
--- request
GET /t
--- response_body
invalidation | channel: mlcache:invalidations:name | handler: function
purge | channel: mlcache:purge:name | handler: function
@ -289,8 +331,9 @@ purge | channel: mlcache:purge:name | handler: function
=== TEST 13: new() ipc.poll is optional (some IPC libraries might not need it
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -308,6 +351,8 @@ purge | channel: mlcache:purge:name | handler: function
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok
--- no_error_log
@ -316,8 +361,9 @@ ok
=== TEST 14: new() validates opts.lru_size
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -329,6 +375,8 @@ ok
end
}
}
--- request
GET /t
--- response_body
--- error_log
@ -337,8 +385,9 @@ opts.lru_size must be a number
=== TEST 15: new() validates opts.ttl
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -346,28 +395,31 @@ opts.lru_size must be a number
ttl = ""
})
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
local ok, err = pcall(mlcache.new, "name", "cache_shm", {
ttl = -1
})
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
opts.ttl must be a number
opts.ttl must be >= 0
--- no_error_log
[error]
=== TEST 16: new() validates opts.neg_ttl
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -375,28 +427,31 @@ opts.ttl must be >= 0
neg_ttl = ""
})
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
local ok, err = pcall(mlcache.new, "name", "cache_shm", {
neg_ttl = -1
})
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
opts.neg_ttl must be a number
opts.neg_ttl must be >= 0
--- no_error_log
[error]
=== TEST 17: new() validates opts.resty_lock_opts
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -404,20 +459,23 @@ opts.neg_ttl must be >= 0
resty_lock_opts = false,
})
if not ok then
ngx.say(err)
ngx.log(ngx.ERR, err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
opts.resty_lock_opts must be a table
--- no_error_log
[error]
=== TEST 18: new() validates opts.shm_set_tries
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -437,6 +495,8 @@ opts.resty_lock_opts must be a table
end
}
}
--- request
GET /t
--- response_body
opts.shm_set_tries must be a number
opts.shm_set_tries must be >= 1
@ -447,8 +507,9 @@ opts.shm_set_tries must be >= 1
=== TEST 19: new() validates opts.shm_miss
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -460,6 +521,8 @@ opts.shm_set_tries must be >= 1
end
}
}
--- request
GET /t
--- response_body
opts.shm_miss must be a string
--- no_error_log
@ -468,8 +531,9 @@ opts.shm_miss must be a string
=== TEST 20: new() ensures opts.shm_miss exists
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -481,6 +545,8 @@ opts.shm_miss must be a string
end
}
}
--- request
GET /t
--- response_body
no such lua_shared_dict for opts.shm_miss: foo
--- no_error_log
@ -489,8 +555,9 @@ no such lua_shared_dict for opts.shm_miss: foo
=== TEST 21: new() creates an mlcache object with default attributes
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -504,6 +571,8 @@ no such lua_shared_dict for opts.shm_miss: foo
ngx.say(type(cache.neg_ttl))
}
}
--- request
GET /t
--- response_body
table
number
@ -514,8 +583,9 @@ number
=== TEST 22: new() accepts user-provided LRU instances via opts.lru
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local pureffi_lrucache = require "resty.lrucache.pureffi"
@ -527,6 +597,8 @@ number
ngx.say("lru is user-provided: ", cache.lru == my_lru)
}
}
--- request
GET /t
--- response_body
lru is user-provided: true
--- no_error_log

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,49 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
workers(2);
#repeat_each(2);
plan tests => repeat_each() * (blocks() * 4);
plan tests => repeat_each() * (blocks() * 3) + 2;
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict cache_shm_miss 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: peek() validates key
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -31,17 +59,19 @@ __DATA__
end
}
}
--- request
GET /t
--- response_body
key must be a string
--- no_error_log
[error]
[crit]
=== TEST 2: peek() returns nil if a key has never been fetched before
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -60,19 +90,19 @@ key must be a string
ngx.say("ttl: ", ttl)
}
}
--- request
GET /t
--- response_body
ttl: nil
--- no_error_log
[error]
[crit]
=== TEST 3: peek() returns the remaining ttl if a key has been fetched before
--- main_config
timer_resolution 10ms;
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -111,20 +141,20 @@ ttl: nil
ngx.say("ttl: ", math.ceil(ttl))
}
}
--- request
GET /t
--- response_body
ttl: 19
ttl: 18
--- no_error_log
[error]
[crit]
=== TEST 4: peek() returns a negative ttl when a key expired
--- main_config
timer_resolution 10ms;
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -151,20 +181,20 @@ ttl: 18
ngx.say("ttl: ", math.ceil(ttl))
}
}
--- request
GET /t
--- response_body
ttl: -1
ttl: -2
--- no_error_log
[error]
[crit]
=== TEST 5: peek() returns remaining ttl if shm_miss is specified
--- main_config
timer_resolution 10ms;
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -201,18 +231,20 @@ ttl: -2
ngx.say("ttl: ", math.ceil(ttl))
}
}
--- request
GET /t
--- response_body
ttl: 19
ttl: 18
--- no_error_log
[error]
[crit]
=== TEST 6: peek() returns the value if a key has been fetched before
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -259,18 +291,20 @@ ttl: 18
ngx.say("ttl: ", math.ceil(ttl), " nil_val: ", val)
}
}
--- request
GET /t
--- response_body_like
ttl: \d* val: 123
ttl: \d* nil_val: nil
--- no_error_log
[error]
[crit]
=== TEST 7: peek() returns the value if shm_miss is specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -297,17 +331,19 @@ ttl: \d* nil_val: nil
ngx.say("ttl: ", math.ceil(ttl), " nil_val: ", val)
}
}
--- request
GET /t
--- response_body_like
ttl: \d* nil_val: nil
--- no_error_log
[error]
[crit]
=== TEST 8: peek() JITs on hit
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -325,18 +361,21 @@ ttl: \d* nil_val: nil
end
}
}
--- request
GET /t
--- response_body
val: 123456
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):13 loop\]/
--- no_error_log
[error]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):13 loop\]/
=== TEST 9: peek() JITs on miss
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -350,18 +389,21 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):13 loop\]/
end
}
}
--- ignore_response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):6 loop\]/
--- request
GET /t
--- response_body
--- no_error_log
[error]
[crit]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):6 loop\]/
=== TEST 10: peek() returns nil if a value expired
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -388,19 +430,21 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):6 loop\]/
ngx.say("stale: ", stale)
}
}
--- request
GET /t
--- response_body
ttl: nil
data: nil
stale: nil
--- no_error_log
[error]
[crit]
=== TEST 11: peek() returns nil if a value expired in 'shm_miss'
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -433,19 +477,21 @@ stale: nil
ngx.say("stale: ", stale)
}
}
--- request
GET /t
--- response_body
ttl: nil
data: nil
stale: nil
--- no_error_log
[error]
[crit]
=== TEST 12: peek() accepts stale arg and returns stale values
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -472,19 +518,21 @@ stale: nil
ngx.say("stale: ", stale)
}
}
--- request
GET /t
--- response_body_like chomp
ttl: -0\.\d+
data: 123
stale: true
--- no_error_log
[error]
[crit]
=== TEST 13: peek() accepts stale arg and returns stale values from 'shm_miss'
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -517,19 +565,21 @@ stale: true
ngx.say("stale: ", stale)
}
}
--- request
GET /t
--- response_body_like chomp
ttl: -0\.\d+
data: nil
stale: true
--- no_error_log
[error]
[crit]
=== TEST 14: peek() does not evict stale items from L2 shm
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -557,6 +607,8 @@ stale: true
end
}
}
--- request
GET /t
--- response_body_like chomp
remaining_ttl: -\d\.\d+
data: 123
@ -566,13 +618,13 @@ remaining_ttl: -\d\.\d+
data: 123
--- no_error_log
[error]
[crit]
=== TEST 15: peek() does not evict stale negative data from L2 shm_miss
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -601,6 +653,8 @@ data: 123
end
}
}
--- request
GET /t
--- response_body_like chomp
remaining_ttl: -\d\.\d+
data: nil
@ -610,4 +664,3 @@ remaining_ttl: -\d\.\d+
data: nil
--- no_error_log
[error]
[crit]

View file

@ -1,21 +1,49 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
workers(2);
#repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3);
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict ipc_shm 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: update() errors if no ipc
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -25,6 +53,8 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no polling configured, specify opts.ipc_shm or opts.ipc.poll
--- no_error_log
@ -33,8 +63,9 @@ no polling configured, specify opts.ipc_shm or opts.ipc.poll
=== TEST 2: update() calls ipc poll() with timeout arg
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -52,6 +83,8 @@ no polling configured, specify opts.ipc_shm or opts.ipc.poll
assert(cache:update(3.5, "not me"))
}
}
--- request
GET /t
--- response_body
called poll() with args: 3.5
--- no_error_log
@ -60,8 +93,9 @@ called poll() with args: 3.5
=== TEST 3: update() JITs when no events to catch up
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -74,8 +108,10 @@ called poll() with args: 3.5
end
}
}
--- request
GET /t
--- ignore_response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):8 loop\]/
--- no_error_log
[error]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):8 loop\]/

View file

@ -1,20 +1,29 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
#repeat_each(2);
plan tests => repeat_each() * blocks() * 4;
plan tests => repeat_each() * (blocks() * 3) + 2;
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict cache_shm_miss 1m;
lua_shared_dict ipc_shm 1m;
};
run_tests();
__DATA__
=== TEST 1: set() errors if no ipc
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -24,17 +33,19 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no ipc to propagate update, specify opts.ipc_shm or opts.ipc
--- no_error_log
[error]
[crit]
=== TEST 2: set() validates key
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -48,17 +59,19 @@ no ipc to propagate update, specify opts.ipc_shm or opts.ipc
end
}
}
--- request
GET /t
--- response_body
key must be a string
--- no_error_log
[error]
[crit]
=== TEST 3: set() puts a value directly in shm
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -89,18 +102,20 @@ key must be a string
ngx.say("cache lru value after get(): ", value_lru)
}
}
--- request
GET /t
--- response_body
value from get(): 123
cache lru value after get(): 123
--- no_error_log
[error]
[crit]
=== TEST 4: set() puts a negative hit directly in shm_miss if specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -130,17 +145,19 @@ cache lru value after get(): 123
ngx.say("value from get(): ", value)
}
}
--- request
GET /t
--- response_body
value from get(): nil
--- no_error_log
[error]
[crit]
=== TEST 5: set() puts a value directly in its own LRU
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -159,17 +176,19 @@ value from get(): nil
ngx.say("cache lru value after set(): ", value_lru)
}
}
--- request
GET /t
--- response_body
cache lru value after set(): 123
--- no_error_log
[error]
[crit]
=== TEST 6: set() respects 'ttl' for non-nil values
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -210,6 +229,8 @@ cache lru value after set(): 123
ngx.say("value from get(): ", value)
}
}
--- request
GET /t
--- response_body
calling get()
value from get(): 123
@ -220,13 +241,13 @@ callback called
value from get(): 123
--- no_error_log
[error]
[crit]
=== TEST 7: set() respects 'neg_ttl' for nil values
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -273,6 +294,8 @@ value from get(): 123
ngx.say("value from get(): ", value)
}
}
--- request
GET /t
--- response_body
calling get()
value from get(): nil
@ -283,13 +306,13 @@ callback called
value from get(): nil
--- no_error_log
[error]
[crit]
=== TEST 8: set() respects 'set_shm_tries'
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local dict = ngx.shared.cache_shm
dict:flush_all()
@ -349,6 +372,8 @@ value from get(): nil
ngx.say("callback was called: ", cb_called ~= nil)
}
}
--- request
GET /t
--- response_body
type of data in shm: string
callback was called: false
@ -359,8 +384,9 @@ callback was called: false
=== TEST 9: set() with shm_miss can set a nil where a value was
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -404,18 +430,20 @@ callback was called: false
ngx.say("value from get() after set(): ", value)
}
}
--- request
GET /t
--- response_body
initial value from get(): 123
value from get() after set(): nil
--- no_error_log
[error]
[crit]
=== TEST 10: set() with shm_miss can set a value where a nil was
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -459,18 +487,20 @@ value from get() after set(): nil
ngx.say("value from get() after set(): ", value)
}
}
--- request
GET /t
--- response_body
initial value from get(): nil
value from get() after set(): 123
--- no_error_log
[error]
[crit]
=== TEST 11: set() returns 'no memory' errors upon fragmentation in the shm
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -503,18 +533,21 @@ value from get() after set(): 123
ngx.say(err)
}
}
--- request
GET /t
--- response_body
nil
could not write to lua_shared_dict 'cache_shm': no memory
--- no_error_log
[warn]
[error]
[warn]
=== TEST 12: set() does not set LRU upon shm insertion error
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -549,17 +582,19 @@ could not write to lua_shared_dict 'cache_shm': no memory
ngx.say(data)
}
}
--- request
GET /t
--- response_body
nil
--- no_error_log
[error]
[crit]
=== TEST 13: set() calls broadcast() with invalidated key
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -579,10 +614,11 @@ nil
assert(cache:set("my_key", nil, nil))
}
}
--- request
GET /t
--- response_body
channel: mlcache:invalidations:my_mlcache
data: my_key
other args:
--- no_error_log
[error]
[crit]

View file

@ -1,21 +1,31 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
workers(2);
#repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3);
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict cache_shm_miss 1m;
lua_shared_dict ipc_shm 1m;
};
run_tests();
__DATA__
=== TEST 1: delete() errors if no ipc
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -25,6 +35,8 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no ipc to propagate deletion, specify opts.ipc_shm or opts.ipc
--- no_error_log
@ -33,8 +45,9 @@ no ipc to propagate deletion, specify opts.ipc_shm or opts.ipc
=== TEST 2: delete() validates key
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -46,6 +59,8 @@ no ipc to propagate deletion, specify opts.ipc_shm or opts.ipc
ngx.say(err)
}
}
--- request
GET /t
--- response_body
key must be a string
--- no_error_log
@ -54,8 +69,9 @@ key must be a string
=== TEST 3: delete() removes a cached value from LRU + shm
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -105,6 +121,8 @@ key must be a string
ngx.say("from callback: ", data)
}
}
--- request
GET /t
--- response_body
in callback
from callback: 123
@ -120,8 +138,9 @@ from callback: 456
=== TEST 4: delete() removes a cached nil from shm_miss if specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -184,6 +203,8 @@ from callback: 456
ngx.say("from callback again: ", data)
}
}
--- request
GET /t
--- response_body
in callback
from callback: nil
@ -199,8 +220,9 @@ from callback again: 456
=== TEST 5: delete() calls broadcast with invalidated key
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -220,6 +242,8 @@ from callback again: 456
assert(cache:delete("my_key"))
}
}
--- request
GET /t
--- response_body
channel: mlcache:invalidations:my_mlcache
data: my_key

View file

@ -1,21 +1,49 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
workers(2);
#repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3) + 1;
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict ipc_shm 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: l1_serializer is validated by the constructor
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -27,6 +55,8 @@ __DATA__
end
}
}
--- request
GET /t
--- response_body
opts.l1_serializer must be a function
--- no_error_log
@ -35,8 +65,9 @@ opts.l1_serializer must be a function
=== TEST 2: l1_serializer is called on L1+L2 cache misses
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -59,6 +90,8 @@ opts.l1_serializer must be a function
ngx.say(data)
}
}
--- request
GET /t
--- response_body
transform("foo")
--- no_error_log
@ -67,8 +100,9 @@ transform("foo")
=== TEST 3: get() JITs when hit of scalar value coming from shm with l1_serializer
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -94,7 +128,10 @@ transform("foo")
end
}
}
--- ignore_response_body
--- request
GET /t
--- response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):18 loop\]/
--- no_error_log
@ -103,8 +140,9 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):18 loop\]/
=== TEST 4: l1_serializer is not called on L1 hits
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -133,6 +171,8 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):18 loop\]/
ngx.say("calls: ", calls)
}
}
--- request
GET /t
--- response_body
transform("foo")
transform("foo")
@ -144,8 +184,9 @@ calls: 1
=== TEST 5: l1_serializer is called on each L2 hit
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -175,6 +216,8 @@ calls: 1
ngx.say("calls: ", calls)
}
}
--- request
GET /t
--- response_body
transform("foo")
transform("foo")
@ -186,8 +229,9 @@ calls: 3
=== TEST 6: l1_serializer is called on boolean false hits
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -214,6 +258,8 @@ calls: 3
ngx.say(data)
}
}
--- request
GET /t
--- response_body
transform_boolean("false")
--- no_error_log
@ -221,161 +267,10 @@ transform_boolean("false")
=== TEST 7: l1_serializer is called on lock timeout
=== TEST 7: l1_serializer is called in protected mode (L2 miss)
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
-- insert 2 dummy values to ensure that lock acquisition (which
-- uses shm:set) will _not_ evict out stale cached value
ngx.shared.cache_shm:set(1, true, 0.2)
ngx.shared.cache_shm:set(2, true, 0.2)
local mlcache = require "resty.mlcache"
local cache_1 = assert(mlcache.new("my_mlcache", "cache_shm", {
ttl = 0.3,
resurrect_ttl = 0.3,
l1_serializer = function(s)
return "from cache_1"
end,
}))
local cache_2 = assert(mlcache.new("my_mlcache", "cache_shm", {
ttl = 0.3,
resurrect_ttl = 0.3,
l1_serializer = function(s)
return "from cache_2"
end,
resty_lock_opts = {
timeout = 0.2
}
}))
local function cb(delay, return_val)
if delay then
ngx.sleep(delay)
end
return return_val or 123
end
-- cache in shm
local data, err, hit_lvl = cache_1:get("my_key", nil, cb)
assert(data == "from cache_1")
assert(err == nil)
assert(hit_lvl == 3)
-- make shm + LRU expire
ngx.sleep(0.3)
local t1 = ngx.thread.spawn(function()
-- trigger L3 callback again, but slow to return this time
cache_1:get("my_key", nil, cb, 0.3, 456)
end)
local t2 = ngx.thread.spawn(function()
-- make this mlcache wait on other's callback, and timeout
local data, err, hit_lvl = cache_2:get("my_key", nil, cb)
ngx.say("data: ", data)
ngx.say("err: ", err)
ngx.say("hit_lvl: ", hit_lvl)
end)
assert(ngx.thread.wait(t1))
assert(ngx.thread.wait(t2))
ngx.say()
ngx.say("-> subsequent get()")
data, err, hit_lvl = cache_2:get("my_key", nil, cb, nil, 123)
ngx.say("data: ", data)
ngx.say("err: ", err)
ngx.say("hit_lvl: ", hit_lvl) -- should be 1 since LRU instances are shared by mlcache namespace, and t1 finished
}
}
--- response_body
data: from cache_2
err: nil
hit_lvl: 4
-> subsequent get()
data: from cache_1
err: nil
hit_lvl: 1
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
=== TEST 8: l1_serializer is called when value has < 1ms remaining_ttl
--- config
location /t {
content_by_lua_block {
local forced_now = ngx.now()
ngx.now = function()
return forced_now
end
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
ttl = 0.2,
l1_serializer = function(s)
return "override"
end,
}))
local function cb(v)
return v or 42
end
local data, err = cache:get("key", nil, cb)
assert(data == "override", err or "invalid data value: " .. data)
-- drop L1 cache value
cache.lru:delete("key")
-- advance 0.2 second in the future, and simulate another :get()
-- call; the L2 shm entry will still be alive (as its clock is
-- not faked), but mlcache will compute a remaining_ttl of 0;
-- In such cases, we should _not_ cache the value indefinitely in
-- the L1 LRU cache.
forced_now = forced_now + 0.2
local data, err, hit_lvl = cache:get("key", nil, cb)
assert(data == "override", err or "invalid data value: " .. data)
ngx.say("+0.200s hit_lvl: ", hit_lvl)
-- the value is not cached in LRU (too short ttl anyway)
data, err, hit_lvl = cache:get("key", nil, cb)
assert(data == "override", err or "invalid data value: " .. data)
ngx.say("+0.200s hit_lvl: ", hit_lvl)
-- make it expire in shm (real wait)
ngx.sleep(0.201)
data, err, hit_lvl = cache:get("key", nil, cb, 91)
assert(data == "override", err or "invalid data value: " .. data)
ngx.say("+0.201s hit_lvl: ", hit_lvl)
}
}
--- response_body
+0.200s hit_lvl: 2
+0.200s hit_lvl: 2
+0.201s hit_lvl: 3
--- no_error_log
[error]
=== TEST 9: l1_serializer is called in protected mode (L2 miss)
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -397,6 +292,8 @@ qr/\[warn\] .*? could not acquire callback lock: timeout/
ngx.say(data)
}
}
--- request
GET /t
--- response_body_like
l1_serializer threw an error: .*?: cannot transform
--- no_error_log
@ -404,9 +301,10 @@ l1_serializer threw an error: .*?: cannot transform
=== TEST 10: l1_serializer is called in protected mode (L2 hit)
=== TEST 8: l1_serializer is called in protected mode (L2 hit)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -434,6 +332,8 @@ l1_serializer threw an error: .*?: cannot transform
ngx.say(data)
}
}
--- request
GET /t
--- response_body_like
l1_serializer threw an error: .*?: cannot transform
--- no_error_log
@ -441,9 +341,10 @@ l1_serializer threw an error: .*?: cannot transform
=== TEST 11: l1_serializer is not called for L2+L3 misses (no record)
=== TEST 9: l1_serializer is not called for L2+L3 misses (no record)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -489,6 +390,8 @@ l1_serializer threw an error: .*?: cannot transform
ngx.say("l1_serializer called for L2 negative hit: ", called)
}
}
--- request
GET /t
--- response_body
l1_serializer called for L3 miss: false
l1_serializer called for L2 negative hit: false
@ -497,9 +400,10 @@ l1_serializer called for L2 negative hit: false
=== TEST 12: l1_serializer is not supposed to return a nil value
=== TEST 10: l1_serializer is not supposed to return a nil value
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -518,6 +422,8 @@ l1_serializer called for L2 negative hit: false
ngx.say(err)
}
}
--- request
GET /t
--- response_body_like
l1_serializer returned a nil value
--- no_error_log
@ -525,9 +431,10 @@ l1_serializer returned a nil value
=== TEST 13: l1_serializer can return nil + error
=== TEST 11: l1_serializer can return nil + error
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -549,6 +456,8 @@ l1_serializer returned a nil value
ngx.say("data: ", data)
}
}
--- request
GET /t
--- response_body
l1_serializer: cannot transform
data: nil
@ -557,9 +466,10 @@ data: nil
=== TEST 14: l1_serializer can be given as a get() argument
=== TEST 12: l1_serializer can be given as a get() argument
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -582,6 +492,8 @@ data: nil
ngx.say(data)
}
}
--- request
GET /t
--- response_body
transform("foo")
--- no_error_log
@ -589,9 +501,10 @@ transform("foo")
=== TEST 15: l1_serializer as get() argument has precedence over the constructor one
=== TEST 13: l1_serializer as get() argument has precedence over the constructor one
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -627,6 +540,8 @@ transform("foo")
ngx.say(data)
}
}
--- request
GET /t
--- response_body
get_argument("foo")
constructor("bar")
@ -635,9 +550,10 @@ constructor("bar")
=== TEST 16: get() validates l1_serializer is a function
=== TEST 14: get() validates l1_serializer is a function
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -655,6 +571,8 @@ constructor("bar")
end
}
}
--- request
GET /t
--- response_body
opts.l1_serializer must be a function
--- no_error_log
@ -662,9 +580,10 @@ opts.l1_serializer must be a function
=== TEST 17: set() calls l1_serializer
=== TEST 15: set() calls l1_serializer
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -694,6 +613,8 @@ opts.l1_serializer must be a function
ngx.say(value)
}
}
--- request
GET /t
--- response_body
transform("value")
--- no_error_log
@ -701,9 +622,10 @@ transform("value")
=== TEST 18: set() calls l1_serializer for boolean false values
=== TEST 16: set() calls l1_serializer for boolean false values
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -733,6 +655,8 @@ transform("value")
ngx.say(value)
}
}
--- request
GET /t
--- response_body
transform_boolean("false")
--- no_error_log
@ -740,9 +664,10 @@ transform_boolean("false")
=== TEST 19: l1_serializer as set() argument has precedence over the constructor one
=== TEST 17: l1_serializer as set() argument has precedence over the constructor one
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -776,6 +701,8 @@ transform_boolean("false")
ngx.say(value)
}
}
--- request
GET /t
--- response_body
set_argument("value")
--- no_error_log
@ -783,9 +710,10 @@ set_argument("value")
=== TEST 20: set() validates l1_serializer is a function
=== TEST 18: set() validates l1_serializer is a function
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -805,6 +733,8 @@ set_argument("value")
end
}
}
--- request
GET /t
--- response_body
opts.l1_serializer must be a function
--- no_error_log

View file

@ -1,20 +1,31 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use lib '.';
use t::TestMLCache;
use t::Util;
#repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3);
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict cache_shm_miss 1m;
lua_shared_dict ipc_shm 1m;
};
run_tests();
__DATA__
=== TEST 1: purge() errors if no ipc
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -24,6 +35,8 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no ipc to propagate purge, specify opts.ipc_shm or opts.ipc
--- no_error_log
@ -32,8 +45,9 @@ no ipc to propagate purge, specify opts.ipc_shm or opts.ipc
=== TEST 2: purge() deletes all items from L1 + L2 (sanity 1/2)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -66,6 +80,8 @@ no ipc to propagate purge, specify opts.ipc_shm or opts.ipc
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok
--- no_error_log
@ -74,8 +90,9 @@ ok
=== TEST 3: purge() deletes all items from L1 (sanity 2/2)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -104,6 +121,8 @@ ok
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok
--- no_error_log
@ -112,9 +131,10 @@ ok
=== TEST 4: purge() deletes all items from L1 with a custom LRU
--- skip_eval: 3: t::TestMLCache::skip_openresty('<', '1.13.6.2')
--- skip_eval: 3: t::Util::skip_openresty('<', '1.13.6.2')
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local lrucache = require "resty.lrucache"
@ -148,6 +168,8 @@ ok
ngx.say("lru instance is the same one: ", lru == cache.lru)
}
}
--- request
GET /t
--- response_body
ok
lru instance is the same one: true
@ -157,9 +179,10 @@ lru instance is the same one: true
=== TEST 5: purge() is prevented if custom LRU does not support flush_all()
--- skip_eval: 3: t::TestMLCache::skip_openresty('>', '1.13.6.1')
--- skip_eval: 3: t::Util::skip_openresty('>', '1.13.6.1')
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local lrucache = require "resty.lrucache"
@ -178,6 +201,8 @@ lru instance is the same one: true
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
cannot purge when using custom LRU cache with OpenResty < 1.13.6.2
--- no_error_log
@ -186,8 +211,9 @@ cannot purge when using custom LRU cache with OpenResty < 1.13.6.2
=== TEST 6: purge() deletes all items from shm_miss is specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -223,6 +249,8 @@ cannot purge when using custom LRU cache with OpenResty < 1.13.6.2
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok
--- no_error_log
@ -231,8 +259,9 @@ ok
=== TEST 7: purge() does not call shm:flush_expired() by default
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
do
local cache_shm = ngx.shared.cache_shm
@ -255,6 +284,8 @@ ok
assert(cache:purge())
}
}
--- request
GET /t
--- response_body_unlike
flush_expired called with 'max_count'
--- no_error_log
@ -263,8 +294,9 @@ flush_expired called with 'max_count'
=== TEST 8: purge() calls shm:flush_expired() if argument specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
do
local cache_shm = ngx.shared.cache_shm
@ -289,6 +321,8 @@ flush_expired called with 'max_count'
assert(cache:purge(true))
}
}
--- request
GET /t
--- response_body
flush_expired called with 'max_count': nil
--- no_error_log
@ -297,8 +331,9 @@ flush_expired called with 'max_count': nil
=== TEST 9: purge() calls shm:flush_expired() if shm_miss is specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
do
local cache_shm = ngx.shared.cache_shm
@ -324,6 +359,8 @@ flush_expired called with 'max_count': nil
assert(cache:purge(true))
}
}
--- request
GET /t
--- response_body
flush_expired called with 'max_count': nil
flush_expired called with 'max_count': nil
@ -333,8 +370,9 @@ flush_expired called with 'max_count': nil
=== TEST 10: purge() calls broadcast() on purge channel
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -354,6 +392,8 @@ flush_expired called with 'max_count': nil
assert(cache:purge())
}
}
--- request
GET /t
--- response_body
channel: mlcache:purge:my_mlcache
data:

View file

@ -1,20 +1,28 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3);
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict ipc_shm 1m;
};
run_tests();
__DATA__
=== TEST 1: multiple instances with the same name have same lua-resty-lru instance
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -25,6 +33,8 @@ __DATA__
cache_1.lru == cache_2.lru)
}
}
--- request
GET /t
--- response_body
lua-resty-lru instances are the same: true
--- no_error_log
@ -33,8 +43,9 @@ lua-resty-lru instances are the same: true
=== TEST 2: multiple instances with different names have different lua-resty-lru instances
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -45,6 +56,8 @@ lua-resty-lru instances are the same: true
cache_1.lru == cache_2.lru)
}
}
--- request
GET /t
--- response_body
lua-resty-lru instances are the same: false
--- no_error_log
@ -53,8 +66,9 @@ lua-resty-lru instances are the same: false
=== TEST 3: garbage-collected instances also GC their lru instance
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -90,6 +104,8 @@ lua-resty-lru instances are the same: false
ngx.say((cache_2.lru:get("key")))
}
}
--- request
GET /t
--- response_body
123
nil
@ -99,8 +115,9 @@ nil
=== TEST 4: multiple instances with different names get() of the same key are isolated
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -136,6 +153,8 @@ nil
ngx.say("cache_2 shm has: ", shm_2_value)
}
}
--- request
GET /t
--- response_body
cache_1 lru has: value A
cache_2 lru has: value B
@ -147,8 +166,9 @@ cache_2 shm has: value B
=== TEST 5: multiple instances with different names delete() of the same key are isolated
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -195,6 +215,8 @@ cache_2 shm has: value B
ngx.say("cache_2 lru has: ", lru_v_2)
}
}
--- request
GET /t
--- response_body
cache_1 shm has a value: true
delete from cache_1
@ -208,8 +230,9 @@ cache_2 lru has: value B
=== TEST 6: multiple instances with different names peek() of the same key are isolated
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
-- must reset the shm so that when repeated, this tests doesn't
-- return unpredictible TTLs (0.9xxxs)
@ -253,6 +276,8 @@ cache_2 lru has: value B
ngx.say("cache_2 value: ", val)
}
}
--- request
GET /t
--- response_body
cache_1 ttl: 1
cache_1 value: value A
@ -264,8 +289,9 @@ cache_2 value: value B
=== TEST 7: non-namespaced instances use different delete() broadcast channel
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -296,6 +322,8 @@ cache_2 value: value B
assert(cache_2:delete("my_key"))
}
}
--- request
GET /t
--- response_body
cache_1 channel: mlcache:invalidations:my_mlcache_1
cache_2 channel: mlcache:invalidations:my_mlcache_2
@ -305,8 +333,9 @@ cache_2 channel: mlcache:invalidations:my_mlcache_2
=== TEST 8: non-namespaced instances use different purge() broadcast channel
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -337,6 +366,8 @@ cache_2 channel: mlcache:invalidations:my_mlcache_2
assert(cache_2:purge())
}
}
--- request
GET /t
--- response_body
cache_1 channel: mlcache:purge:my_mlcache_1
cache_2 channel: mlcache:purge:my_mlcache_2

View file

@ -1,21 +1,51 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use lib '.';
use t::TestMLCache;
use t::Util;
workers(2);
#repeat_each(2);
plan tests => repeat_each() * blocks() * 4;
plan tests => repeat_each() * (blocks() * 3) + 2;
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict ipc_shm 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: update() with ipc_shm catches up with invalidation events
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -32,18 +62,20 @@ __DATA__
assert(cache:update())
}
}
--- request
GET /t
--- ignore_response_body
--- error_log
received event from invalidations: my_key
--- no_error_log
[error]
[crit]
--- error_log
received event from invalidations: my_key
=== TEST 2: update() with ipc_shm timeouts when waiting for too long
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -66,19 +98,22 @@ received event from invalidations: my_key
end
}
}
--- request
GET /t
--- response_body
could not poll ipc events: timeout
--- error_log
received event from invalidations: my_key
--- no_error_log
[error]
received event from invalidations: my_other
--- error_log
received event from invalidations: my_key
=== TEST 3: update() with ipc_shm JITs when no events to catch up
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -90,18 +125,20 @@ received event from invalidations: my_other
end
}
}
--- request
GET /t
--- ignore_response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):7 loop\]/
--- no_error_log
[error]
[crit]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):7 loop\]/
=== TEST 4: set() with ipc_shm invalidates other workers' LRU cache
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -130,6 +167,8 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):7 loop\]/
assert(cache_clone:update())
}
}
--- request
GET /t
--- response_body
calling update on cache
called lru:delete() with key: my_key
@ -137,13 +176,13 @@ calling update on cache_clone
called lru:delete() with key: my_key
--- no_error_log
[error]
[crit]
=== TEST 5: delete() with ipc_shm invalidates other workers' LRU cache
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -172,6 +211,8 @@ called lru:delete() with key: my_key
assert(cache_clone:update())
}
}
--- request
GET /t
--- response_body
called lru:delete() with key: my_key
calling update on cache
@ -180,14 +221,14 @@ calling update on cache_clone
called lru:delete() with key: my_key
--- no_error_log
[error]
[crit]
=== TEST 6: purge() with mlcache_shm invalidates other workers' LRU cache (OpenResty < 1.13.6.2)
--- skip_eval: 3: t::TestMLCache::skip_openresty('>=', '1.13.6.2')
--- skip_eval: 3: t::Util::skip_openresty('>=', '1.13.6.2')
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -216,6 +257,8 @@ called lru:delete() with key: my_key
ngx.say("cache_clone has new lru: ", cache_clone.lru ~= lru_clone)
}
}
--- request
GET /t
--- response_body
cache has new lru: true
cache_clone still has same lru: true
@ -223,14 +266,14 @@ calling update on cache_clone
cache_clone has new lru: true
--- no_error_log
[error]
[crit]
=== TEST 7: purge() with mlcache_shm invalidates other workers' LRU cache (OpenResty >= 1.13.6.2)
--- skip_eval: 3: t::TestMLCache::skip_openresty('<', '1.13.6.2')
--- skip_eval: 3: t::Util::skip_openresty('<', '1.13.6.2')
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -263,6 +306,8 @@ cache_clone has new lru: true
ngx.say("lru didn't change after purge: ", cache.lru == lru)
}
}
--- request
GET /t
--- response_body
both instances use the same lru: true
called lru:flush_all()
@ -272,4 +317,3 @@ both instances use the same lru: true
lru didn't change after purge: true
--- no_error_log
[error]
[crit]

View file

@ -1,18 +1,45 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
plan tests => repeat_each() * blocks() * 3;
plan tests => repeat_each() * (blocks() * 3);
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict locks_shm 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: new() validates opts.shm_locks
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -24,6 +51,8 @@ __DATA__
end
}
}
--- request
GET /t
--- response_body
opts.shm_locks must be a string
--- no_error_log
@ -32,8 +61,9 @@ opts.shm_locks must be a string
=== TEST 2: new() ensures opts.shm_locks exists
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -45,6 +75,8 @@ opts.shm_locks must be a string
end
}
}
--- request
GET /t
--- response_body
no such lua_shared_dict for opts.shm_locks: foo
--- no_error_log
@ -53,8 +85,9 @@ no such lua_shared_dict for opts.shm_locks: foo
=== TEST 3: get() stores resty-locks in opts.shm_locks if specified
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -74,6 +107,8 @@ no such lua_shared_dict for opts.shm_locks: foo
cache:get("key", nil, cb)
}
}
--- request
GET /t
--- response_body
1: lua-resty-mlcache:lock:namekey
--- no_error_log

View file

@ -1,20 +1,29 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use lib '.';
use t::TestMLCache;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
plan tests => repeat_each() * (blocks() * 3 + 3);
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
lua_shared_dict cache_shm_miss 1m;
};
no_long_string();
log_level('warn');
plan tests => repeat_each() * blocks() * 4;
run_tests();
__DATA__
=== TEST 1: new() validates 'opts.resurrect_ttl' (number && >= 0)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -33,18 +42,20 @@ __DATA__
end
}
}
--- request
GET /t
--- response_body
opts.resurrect_ttl must be a number
opts.resurrect_ttl must be >= 0
--- no_error_log
[error]
[crit]
=== TEST 2: get() validates 'opts.resurrect_ttl' (number && >= 0)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm"))
@ -68,18 +79,20 @@ opts.resurrect_ttl must be >= 0
end
}
}
--- request
GET /t
--- response_body
opts.resurrect_ttl must be a number
opts.resurrect_ttl must be >= 0
--- no_error_log
[error]
[crit]
=== TEST 3: get() resurrects a stale value upon callback soft error for 'resurrect_ttl' instance option
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -149,6 +162,8 @@ opts.resurrect_ttl must be >= 0
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -178,13 +193,13 @@ err: nil
hit_lvl: 3
--- no_error_log
[error]
[crit]
=== TEST 4: get() logs soft callback error with warn level when resurrecting
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -228,6 +243,8 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -240,14 +257,13 @@ err: nil
hit_lvl: 4
--- error_log eval
qr/\[warn\] .*? callback returned an error \(some error\) but stale value found/
--- no_error_log
[error]
=== TEST 5: get() accepts 'opts.resurrect_ttl' option to override instance option
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -301,6 +317,8 @@ qr/\[warn\] .*? callback returned an error \(some error\) but stale value found/
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -320,13 +338,13 @@ err: nil
hit_lvl: 4
--- no_error_log
[error]
[crit]
=== TEST 6: get() resurrects a nil stale value (negative cache)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -388,6 +406,8 @@ hit_lvl: 4
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: nil
@ -412,13 +432,13 @@ err: nil
hit_lvl: 3
--- no_error_log
[error]
[crit]
=== TEST 7: get() resurrects a nil stale value (negative cache) in 'opts.shm_miss'
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -481,6 +501,8 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: nil
@ -505,13 +527,13 @@ err: nil
hit_lvl: 3
--- no_error_log
[error]
[crit]
=== TEST 8: get() ignores cb return values upon stale value resurrection
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -571,6 +593,8 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -595,13 +619,13 @@ err: nil
hit_lvl: 3
--- no_error_log
[error]
[crit]
=== TEST 9: get() does not resurrect a stale value when callback throws error
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -653,6 +677,8 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -670,13 +696,13 @@ err: nil
hit_lvl: 3
--- no_error_log
[error]
[crit]
=== TEST 10: get() returns error and data on lock timeout but does not resurrect
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
-- insert 2 dummy values to ensure that lock acquisition (which
-- uses shm:set) will _not_ evict out stale cached value
@ -741,6 +767,8 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl) -- should be 1 since LRU instances are shared by mlcache namespace, and t1 finished
}
}
--- request
GET /t
--- response_body
data: 123
err: nil
@ -750,16 +778,17 @@ hit_lvl: 4
data: 456
err: nil
hit_lvl: 1
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
--- no_error_log
[error]
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
=== TEST 11: get() returns nil cached item on callback lock timeout
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
-- insert 2 dummy values to ensure that lock acquisition (which
-- uses shm:set) will _not_ evict out stale cached value
@ -824,6 +853,8 @@ qr/\[warn\] .*? could not acquire callback lock: timeout/
ngx.say("hit_lvl: ", hit_lvl) -- should be 1 since LRU instances are shared by mlcache namespace, and t1 finished
}
}
--- request
GET /t
--- response_body
data: nil
err: nil
@ -833,16 +864,17 @@ hit_lvl: 4
data: nil
err: nil
hit_lvl: 1
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
--- no_error_log
[error]
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
=== TEST 12: get() does not resurrect a stale value if no 'resurrect_ttl' is set on the instance
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -888,6 +920,8 @@ qr/\[warn\] .*? could not acquire callback lock: timeout/
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -905,13 +939,13 @@ err: some error
hit_lvl: nil
--- no_error_log
[error]
[crit]
=== TEST 13: get() callback can return nil + err (non-string) safely with opts.resurrect_ttl
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm", {
@ -936,18 +970,21 @@ hit_lvl: nil
ngx.say("cb return values: ", data, " ", err)
}
}
--- request
GET /t
--- response_body
cb return values: 123 nil
--- error_log eval
qr/\[warn\] .*? callback returned an error \(table: 0x[[:xdigit:]]+\)/
--- no_error_log
[error]
--- error_log eval
qr/\[warn\] .*? callback returned an error \(table: 0x[[:xdigit:]]+\)/
=== TEST 14: get() returns stale hit_lvl when retrieved from shm on last ms (see GH PR #58)
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local forced_now = ngx.now()
ngx.now = function()
@ -1002,8 +1039,9 @@ qr/\[warn\] .*? callback returned an error \(table: 0x[[:xdigit:]]+\)/
ngx.say("+0.200s after resurrect hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
+0.200s after resurrect hit_lvl: 4
--- no_error_log
[error]
[crit]

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,53 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use strict;
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use lib '.';
use t::TestMLCache;
use t::Util;
no_long_string();
workers(2);
#repeat_each(2);
plan tests => repeat_each() * blocks() * 3;
my $pwd = cwd();
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict cache_shm 1m;
#lua_shared_dict cache_shm_miss 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
run_tests();
__DATA__
=== TEST 1: new_bulk() creates a bulk
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -26,6 +58,8 @@ __DATA__
ngx.say("bulk.n: ", bulk.n)
}
}
--- request
GET /t
--- response_body
type: table
size: 0
@ -36,8 +70,9 @@ bulk.n: 0
=== TEST 2: new_bulk() creates a bulk with narr in arg #1
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -48,6 +83,8 @@ bulk.n: 0
ngx.say("bulk.n: ", bulk.n)
}
}
--- request
GET /t
--- response_body
type: table
size: 0
@ -58,8 +95,9 @@ bulk.n: 0
=== TEST 3: bulk:add() adds bulk operations
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -81,6 +119,8 @@ bulk.n: 0
ngx.say("bulk.n: ", bulk.n)
}
}
--- request
GET /t
--- response_body_like
key_1 nil function: 0x[0-9a-fA-F]+ 1
key_2 nil function: 0x[0-9a-fA-F]+ 2
@ -92,8 +132,9 @@ bulk\.n: 3
=== TEST 4: bulk:add() can be given to get_bulk()
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm"))
@ -119,6 +160,8 @@ bulk\.n: 3
end
}
}
--- request
GET /t
--- response_body
1 nil 3
2 nil 3
@ -129,8 +172,9 @@ bulk\.n: 3
=== TEST 5: each_bulk_res() iterates over get_bulk() results
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
local cache = assert(mlcache.new("my_mlcache", "cache_shm"))
@ -151,6 +195,8 @@ bulk\.n: 3
end
}
}
--- request
GET /t
--- response_body
1 1 nil 3
2 2 nil 3
@ -161,8 +207,9 @@ bulk\.n: 3
=== TEST 6: each_bulk_res() throws an error on unrocognized res
--- http_config eval: $::HttpConfig
--- config
location /t {
location = /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -172,6 +219,8 @@ bulk\.n: 3
end
}
}
--- request
GET /t
--- response_body
res must have res.n field; is this a get_bulk() result?
--- no_error_log

View file

@ -1,103 +0,0 @@
package t::TestMLCache;
use strict;
use Test::Nginx::Socket::Lua -Base;
use Cwd qw(cwd);
our $pwd = cwd();
our @EXPORT = qw(
$pwd
skip_openresty
);
my $PackagePath = qq{lua_package_path "$pwd/lib/?.lua;;";};
my $HttpConfig = qq{
lua_shared_dict cache_shm 1m;
lua_shared_dict cache_shm_miss 1m;
lua_shared_dict locks_shm 1m;
lua_shared_dict ipc_shm 1m;
init_by_lua_block {
-- local verbose = true
local verbose = false
local outfile = "$Test::Nginx::Util::ErrLogFile"
-- local outfile = "/tmp/v.log"
if verbose then
local dump = require "jit.dump"
dump.on(nil, outfile)
else
local v = require "jit.v"
v.on(outfile)
end
require "resty.core"
-- jit.opt.start("hotloop=1")
-- jit.opt.start("loopunroll=1000000")
-- jit.off()
}
};
add_block_preprocessor(sub {
my $block = shift;
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
my $http_config = $block->http_config || '';
$http_config .= $PackagePath;
if ($http_config !~ m/init_by_lua_block/) {
$http_config .= $HttpConfig;
}
$block->set_value("http_config", $http_config);
});
sub get_openresty_canon_version (@) {
sprintf "%d.%03d%03d%03d", $_[0], $_[1], $_[2], $_[3];
}
sub get_openresty_version () {
my $NginxBinary = $ENV{TEST_NGINX_BINARY} || 'nginx';
my $out = `$NginxBinary -V 2>&1`;
if (!defined $out || $? != 0) {
bail_out("Failed to get the version of the OpenResty in PATH");
die;
}
if ($out =~ m{openresty[^/]*/(\d+)\.(\d+)\.(\d+)\.(\d+)}s) {
return get_openresty_canon_version($1, $2, $3, $4);
}
if ($out =~ m{nginx[^/]*/(\d+)\.(\d+)\.(\d+)}s) {
return;
}
bail_out("Failed to parse the output of \"nginx -V\": $out\n");
die;
}
sub skip_openresty ($$) {
my ($op, $ver) = @_;
my $OpenrestyVersion = get_openresty_version();
if ($ver =~ m{(\d+)\.(\d+)\.(\d+)\.(\d+)}s) {
$ver = get_openresty_canon_version($1, $2, $3, $4);
} else {
bail_out("Invalid skip_openresty() arg: $ver");
die;
}
if (defined $OpenrestyVersion and eval "$OpenrestyVersion $op $ver") {
return 1;
}
}
no_long_string();
1;

View file

@ -0,0 +1,51 @@
use strict;
package t::Util;
sub get_openresty_canon_version (@) {
sprintf "%d.%03d%03d%03d", $_[0], $_[1], $_[2], $_[3];
}
sub get_openresty_version () {
my $NginxBinary = $ENV{TEST_NGINX_BINARY} || 'nginx';
my $out = `$NginxBinary -V 2>&1`;
if (!defined $out || $? != 0) {
bail_out("Failed to get the version of the OpenResty in PATH");
die;
}
if ($out =~ m{openresty[^/]*/(\d+)\.(\d+)\.(\d+)\.(\d+)}s) {
return get_openresty_canon_version($1, $2, $3, $4);
}
if ($out =~ m{nginx[^/]*/(\d+)\.(\d+)\.(\d+)}s) {
return;
}
bail_out("Failed to parse the output of \"nginx -V\": $out\n");
die;
}
sub skip_openresty {
my ($op, $ver) = @_;
my $OpenrestyVersion = get_openresty_version();
if ($ver =~ m{(\d+)\.(\d+)\.(\d+)\.(\d+)}s) {
$ver = get_openresty_canon_version($1, $2, $3, $4);
} else {
bail_out("Invalid skip_openresty() arg: $ver");
die;
}
if (defined $OpenrestyVersion and eval "$OpenrestyVersion $op $ver") {
return 1;
}
return;
}
our @EXPORT = qw(
skip_openresty
);
1;
# vim: set ft=perl: