Merge commit '5dcf04a15d1ed9db6f2b36ea1703552264b6f0cd' into dev

This commit is contained in:
Théophile Diot 2024-01-31 15:02:55 +01:00
commit da2e2493e8
No known key found for this signature in database
GPG key ID: 248FEA4BAE400D06
23 changed files with 1000 additions and 1692 deletions

View file

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

View file

@ -1,18 +1,31 @@
# 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.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]
@ -255,6 +268,7 @@ 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-2022 Thibault Charbonnier
Copyright (c) 2017-2024 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,6 +165,8 @@ 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)
@ -337,17 +339,18 @@ 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 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**.
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**.
The third return value is a number which is set if no error was encountered.
It indicated the level at which the value was fetched: `1` for L1, `2` for L2,
It indicates the level at which the value was fetched: `1` for L1, `2` for L2,
and `3` for L3.
If an error is encountered, this method returns `nil` plus a string describing
the error.
If, however, an error is encountered, then this method returns `nil` in `value`
and a string describing the error in `err`.
The first argument `key` is a string. Each value must be stored under a unique
key.
@ -442,14 +445,20 @@ 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 hit_lvl == -1 then
if err ~= nil then
-- error
elseif 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.0",
_VERSION = "2.6.1",
_AUTHOR = "Thibault Charbonnier",
_LICENSE = "MIT",
_URL = "https://github.com/thibaultcha/lua-resty-mlcache",
@ -394,12 +394,8 @@ function _M.new(name, shm, opts)
end
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 function l1_serialize(value, l1_serializer)
if value ~= nil and l1_serializer then
local ok, err
ok, value, err = pcall(l1_serializer, value)
if not ok then
@ -415,6 +411,21 @@ local function set_lru(self, key, value, ttl, neg_ttl, 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
@ -555,6 +566,11 @@ 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
@ -574,6 +590,11 @@ 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
@ -687,6 +708,7 @@ 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.0-1"
version = "2.6.1-1"
source = {
url = "git://github.com/thibaultcha/lua-resty-mlcache",
tag = "2.6.0"
url = "git+https://github.com/thibaultcha/lua-resty-mlcache",
tag = "2.6.1"
}
description = {
summary = "Layered caching library for OpenResty",

View file

@ -1,46 +1,18 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
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()
}
};
plan tests => repeat_each() * blocks() * 5;
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"
@ -48,96 +20,80 @@ __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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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())
}
}
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- ignore_response_body
--- error_log
received event from my_channel: hello world
--- no_error_log
[error]
[crit]
[alert]
=== TEST 3: broadcast() runs event callback in protected mode
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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())
}
}
--- request
GET /t
--- response_body
--- ignore_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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
}
}
--- config
location = /t {
location /t {
content_by_lua_block {
local ok, err = pcall(ipc.poll, ipc, false)
if not ok then
@ -145,50 +101,42 @@ qq{
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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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())
}
}
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- ignore_response_body
--- 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]
@ -197,16 +145,14 @@ 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 eval
qq{
lua_package_path "$::pwd/lib/?.lua;;";
lua_shared_dict ipc 32k;
--- http_config
lua_shared_dict ipc_shm 32k;
init_by_lua_block {
require "resty.core"
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "my_channel event: ", data)
@ -218,9 +164,8 @@ qq{
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)
@ -234,8 +179,6 @@ qq{
ngx.say("ipc.idx: ", ipc.idx)
}
}
--- request
GET /t
--- response_body
ipc.idx: 0
ipc.idx: 34
@ -248,47 +191,38 @@ my_channel event: first broadcast
=== TEST 7: poll() does not execute events from self (same pid)
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc"))
ipc = assert(mlcache_ipc.new("ipc_shm"))
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())
}
}
--- request
GET /t
--- response_body
--- ignore_response_body
--- no_error_log
[error]
received event from my_channel: hello world
[error]
[crit]
[alert]
=== TEST 8: poll() runs all registered callbacks for a channel
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback 1 from my_channel: ", data)
@ -302,68 +236,55 @@ qq{
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())
}
}
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- ignore_response_body
--- 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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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())
}
}
--- request
GET /t
--- response_body
--- ignore_response_body
--- no_error_log
[error]
callback from my_channel: hello world
[error]
[crit]
[alert]
=== TEST 10: poll() runs all callbacks from all channels
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
ipc:subscribe("my_channel", function(data)
ngx.log(ngx.NOTICE, "callback 1 from my_channel: ", data)
@ -381,50 +302,44 @@ qq{
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())
}
}
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- error_log
--- ignore_response_body
--- grep_error_log eval: qr/callback \d+ from [^,]*/
--- grep_error_log_out
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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
}
}
--- config
location = /t {
location /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
assert(ngx.shared.ipc:set("lua-resty-ipc:index", false))
assert(ngx.shared.ipc_shm:set("lua-resty-ipc:index", false))
local ok, err = ipc:poll()
if not ok then
@ -432,33 +347,29 @@ qq{
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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
}
}
--- config
location = /t {
location /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
ngx.shared.ipc:delete(1)
ngx.shared.ipc:flush_expired()
ngx.shared.ipc_shm:delete(1)
ngx.shared.ipc_shm:flush_expired()
local ok, err = ipc:poll()
if not ok then
@ -466,44 +377,40 @@ qq{
end
}
}
--- 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/,
]
--- 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]
=== TEST 13: poll() reaches custom timeout
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", true))
}
}
--- config
location = /t {
location /t {
content_by_lua_block {
assert(ipc:broadcast("my_channel", "msg 1"))
ngx.shared.ipc:delete(1)
ngx.shared.ipc:flush_expired()
ngx.shared.ipc_shm:delete(1)
ngx.shared.ipc_shm:flush_expired()
local ok, err = ipc:poll(0.01)
if not ok then
@ -511,83 +418,75 @@ qq{
end
}
}
--- 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/,
]
--- 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]
=== TEST 14: poll() logs errors and continue if event has been tampered with
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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:set(1, false))
assert(ngx.shared.ipc_shm:set(1, false))
assert(ipc:poll())
}
}
--- request
GET /t
--- response_body
--- ignore_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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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:delete(1)
ngx.shared.ipc:flush_expired()
ngx.shared.ipc_shm:delete(1)
ngx.shared.ipc_shm:flush_expired()
local ok, err = ipc:poll()
if not ok then
@ -595,36 +494,31 @@ qq{
end
}
}
--- request
GET /t
--- response_body
--- ignore_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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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())
@ -642,8 +536,6 @@ qq{
assert(ipc:poll())
}
}
--- request
GET /t
--- ignore_response_body
--- error_log
callback from my_channel: third broadcast
@ -655,63 +547,57 @@ callback from my_channel: second broadcast
=== TEST 17: poll() JITs
--- http_config eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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
}
}
--- request
GET /t
--- response_body
--- ignore_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 eval
qq{
$::HttpConfig
--- http_config
init_worker_by_lua_block {
local mlcache_ipc = require "resty.mlcache.ipc"
ipc = assert(mlcache_ipc.new("ipc", true))
ipc = assert(mlcache_ipc.new("ipc_shm", 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
}
}
--- request
GET /t
--- response_body
--- ignore_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,35 +1,26 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
repeat_each(2);
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;
};
plan tests => repeat_each() * blocks() * 3;
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
@ -38,119 +29,103 @@ GET /t
=== 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.log(ngx.ERR, err)
ngx.say(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.log(ngx.ERR, err)
ngx.say(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.log(ngx.ERR, err)
ngx.say(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.log(ngx.ERR, err)
ngx.say(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.log(ngx.ERR, err)
ngx.say(err)
end
}
}
--- request
GET /t
--- response_body
--- error_log
ipc_shm must be a string
opts.ipc_shm must be a string
--- no_error_log
[error]
=== 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"
@ -160,8 +135,6 @@ ipc_shm must be a string
end
}
}
--- request
GET /t
--- ignore_response_body
--- error_log eval
qr/\[error\] .*? no such lua_shared_dict: ipc/
@ -171,9 +144,8 @@ 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"
@ -183,8 +155,6 @@ qr/\[error\] .*? no such lua_shared_dict: ipc/
end
}
}
--- request
GET /t
--- response_body
opts.ipc must be a table
--- no_error_log
@ -193,9 +163,8 @@ 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"
@ -208,8 +177,6 @@ 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
@ -218,9 +185,8 @@ 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"
@ -248,8 +214,6 @@ 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
@ -260,9 +224,8 @@ 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"
@ -280,8 +243,6 @@ 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
@ -290,9 +251,8 @@ 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"
@ -320,8 +280,6 @@ 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
@ -331,9 +289,8 @@ 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"
@ -351,8 +308,6 @@ purge | channel: mlcache:purge:name | handler: function
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok
--- no_error_log
@ -361,9 +316,8 @@ 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"
@ -375,8 +329,6 @@ ok
end
}
}
--- request
GET /t
--- response_body
--- error_log
@ -385,9 +337,8 @@ 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"
@ -395,31 +346,28 @@ opts.lru_size must be a number
ttl = ""
})
if not ok then
ngx.log(ngx.ERR, err)
ngx.say(err)
end
local ok, err = pcall(mlcache.new, "name", "cache_shm", {
ttl = -1
})
if not ok then
ngx.log(ngx.ERR, err)
ngx.say(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"
@ -427,31 +375,28 @@ opts.ttl must be >= 0
neg_ttl = ""
})
if not ok then
ngx.log(ngx.ERR, err)
ngx.say(err)
end
local ok, err = pcall(mlcache.new, "name", "cache_shm", {
neg_ttl = -1
})
if not ok then
ngx.log(ngx.ERR, err)
ngx.say(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"
@ -459,23 +404,20 @@ opts.neg_ttl must be >= 0
resty_lock_opts = false,
})
if not ok then
ngx.log(ngx.ERR, err)
ngx.say(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"
@ -495,8 +437,6 @@ 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
@ -507,9 +447,8 @@ 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"
@ -521,8 +460,6 @@ opts.shm_set_tries must be >= 1
end
}
}
--- request
GET /t
--- response_body
opts.shm_miss must be a string
--- no_error_log
@ -531,9 +468,8 @@ 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"
@ -545,8 +481,6 @@ 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
@ -555,9 +489,8 @@ 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"
@ -571,8 +504,6 @@ no such lua_shared_dict for opts.shm_miss: foo
ngx.say(type(cache.neg_ttl))
}
}
--- request
GET /t
--- response_body
table
number
@ -583,9 +514,8 @@ 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"
@ -597,8 +527,6 @@ 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,49 +1,21 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
workers(2);
#repeat_each(2);
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()
}
};
plan tests => repeat_each() * (blocks() * 4);
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"
@ -59,19 +31,17 @@ __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"
@ -90,19 +60,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
--- http_config eval: $::HttpConfig
--- main_config
timer_resolution 10ms;
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -141,20 +111,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
--- http_config eval: $::HttpConfig
--- main_config
timer_resolution 10ms;
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -181,20 +151,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
--- http_config eval: $::HttpConfig
--- main_config
timer_resolution 10ms;
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -231,20 +201,18 @@ 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"
@ -291,20 +259,18 @@ 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"
@ -331,19 +297,17 @@ 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"
@ -361,21 +325,18 @@ ttl: \d* nil_val: nil
end
}
}
--- request
GET /t
--- response_body
val: 123456
--- no_error_log
[error]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):13 loop\]/
--- no_error_log
[error]
=== TEST 9: peek() JITs on miss
--- http_config eval: $::HttpConfig
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -389,21 +350,18 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):13 loop\]/
end
}
}
--- request
GET /t
--- response_body
--- no_error_log
[error]
--- ignore_response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):6 loop\]/
--- no_error_log
[error]
[crit]
=== 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"
@ -430,21 +388,19 @@ 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"
@ -477,21 +433,19 @@ 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"
@ -518,21 +472,19 @@ 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"
@ -565,21 +517,19 @@ 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", {
@ -607,8 +557,6 @@ stale: true
end
}
}
--- request
GET /t
--- response_body_like chomp
remaining_ttl: -\d\.\d+
data: 123
@ -618,13 +566,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", {
@ -653,8 +601,6 @@ data: 123
end
}
}
--- request
GET /t
--- response_body_like chomp
remaining_ttl: -\d\.\d+
data: nil
@ -664,3 +610,4 @@ remaining_ttl: -\d\.\d+
data: nil
--- no_error_log
[error]
[crit]

View file

@ -1,49 +1,21 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
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 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()
}
};
plan tests => repeat_each() * blocks() * 3;
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"
@ -53,8 +25,6 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no polling configured, specify opts.ipc_shm or opts.ipc.poll
--- no_error_log
@ -63,9 +33,8 @@ 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"
@ -83,8 +52,6 @@ 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
@ -93,9 +60,8 @@ 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"
@ -108,10 +74,8 @@ called poll() with args: 3.5
end
}
}
--- request
GET /t
--- ignore_response_body
--- no_error_log
[error]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):8 loop\]/
--- no_error_log
[error]

View file

@ -1,29 +1,20 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
#repeat_each(2);
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;
};
plan tests => repeat_each() * blocks() * 4;
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"
@ -33,19 +24,17 @@ __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"
@ -59,19 +48,17 @@ 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"
@ -102,20 +89,18 @@ 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"
@ -145,19 +130,17 @@ 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"
@ -176,19 +159,17 @@ 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"
@ -229,8 +210,6 @@ cache lru value after set(): 123
ngx.say("value from get(): ", value)
}
}
--- request
GET /t
--- response_body
calling get()
value from get(): 123
@ -241,13 +220,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"
@ -294,8 +273,6 @@ value from get(): 123
ngx.say("value from get(): ", value)
}
}
--- request
GET /t
--- response_body
calling get()
value from get(): nil
@ -306,13 +283,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()
@ -372,8 +349,6 @@ 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
@ -384,9 +359,8 @@ 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"
@ -430,20 +404,18 @@ 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"
@ -487,20 +459,18 @@ 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"
@ -533,21 +503,18 @@ 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
[error]
[warn]
[error]
=== 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"
@ -582,19 +549,17 @@ 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"
@ -614,11 +579,10 @@ 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,31 +1,21 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
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;
lua_shared_dict ipc_shm 1m;
};
plan tests => repeat_each() * blocks() * 3;
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"
@ -35,8 +25,6 @@ __DATA__
ngx.say(err)
}
}
--- request
GET /t
--- response_body
no ipc to propagate deletion, specify opts.ipc_shm or opts.ipc
--- no_error_log
@ -45,9 +33,8 @@ 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"
@ -59,8 +46,6 @@ 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
@ -69,9 +54,8 @@ 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"
@ -121,8 +105,6 @@ key must be a string
ngx.say("from callback: ", data)
}
}
--- request
GET /t
--- response_body
in callback
from callback: 123
@ -138,9 +120,8 @@ 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"
@ -203,8 +184,6 @@ from callback: 456
ngx.say("from callback again: ", data)
}
}
--- request
GET /t
--- response_body
in callback
from callback: nil
@ -220,9 +199,8 @@ 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"
@ -242,8 +220,6 @@ 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,49 +1,21 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
workers(2);
#repeat_each(2);
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()
}
};
plan tests => repeat_each() * blocks() * 3;
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"
@ -55,8 +27,6 @@ __DATA__
end
}
}
--- request
GET /t
--- response_body
opts.l1_serializer must be a function
--- no_error_log
@ -65,9 +35,8 @@ 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"
@ -90,8 +59,6 @@ opts.l1_serializer must be a function
ngx.say(data)
}
}
--- request
GET /t
--- response_body
transform("foo")
--- no_error_log
@ -100,9 +67,8 @@ 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"
@ -128,10 +94,7 @@ transform("foo")
end
}
}
--- request
GET /t
--- response_body
--- ignore_response_body
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):18 loop\]/
--- no_error_log
@ -140,9 +103,8 @@ 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"
@ -171,8 +133,6 @@ 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")
@ -184,9 +144,8 @@ 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"
@ -216,8 +175,6 @@ calls: 1
ngx.say("calls: ", calls)
}
}
--- request
GET /t
--- response_body
transform("foo")
transform("foo")
@ -229,9 +186,8 @@ 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"
@ -258,8 +214,6 @@ calls: 3
ngx.say(data)
}
}
--- request
GET /t
--- response_body
transform_boolean("false")
--- no_error_log
@ -267,10 +221,161 @@ transform_boolean("false")
=== TEST 7: l1_serializer is called in protected mode (L2 miss)
--- http_config eval: $::HttpConfig
=== TEST 7: l1_serializer is called on lock timeout
--- 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
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 {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -292,8 +397,6 @@ transform_boolean("false")
ngx.say(data)
}
}
--- request
GET /t
--- response_body_like
l1_serializer threw an error: .*?: cannot transform
--- no_error_log
@ -301,10 +404,9 @@ l1_serializer threw an error: .*?: cannot transform
=== TEST 8: l1_serializer is called in protected mode (L2 hit)
--- http_config eval: $::HttpConfig
=== TEST 10: l1_serializer is called in protected mode (L2 hit)
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -332,8 +434,6 @@ 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
@ -341,10 +441,9 @@ l1_serializer threw an error: .*?: cannot transform
=== TEST 9: l1_serializer is not called for L2+L3 misses (no record)
--- http_config eval: $::HttpConfig
=== TEST 11: l1_serializer is not called for L2+L3 misses (no record)
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -390,8 +489,6 @@ 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
@ -400,10 +497,9 @@ l1_serializer called for L2 negative hit: false
=== TEST 10: l1_serializer is not supposed to return a nil value
--- http_config eval: $::HttpConfig
=== TEST 12: l1_serializer is not supposed to return a nil value
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -422,8 +518,6 @@ 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
@ -431,10 +525,9 @@ l1_serializer returned a nil value
=== TEST 11: l1_serializer can return nil + error
--- http_config eval: $::HttpConfig
=== TEST 13: l1_serializer can return nil + error
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -456,8 +549,6 @@ l1_serializer returned a nil value
ngx.say("data: ", data)
}
}
--- request
GET /t
--- response_body
l1_serializer: cannot transform
data: nil
@ -466,10 +557,9 @@ data: nil
=== TEST 12: l1_serializer can be given as a get() argument
--- http_config eval: $::HttpConfig
=== TEST 14: l1_serializer can be given as a get() argument
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -492,8 +582,6 @@ data: nil
ngx.say(data)
}
}
--- request
GET /t
--- response_body
transform("foo")
--- no_error_log
@ -501,10 +589,9 @@ transform("foo")
=== TEST 13: l1_serializer as get() argument has precedence over the constructor one
--- http_config eval: $::HttpConfig
=== TEST 15: l1_serializer as get() argument has precedence over the constructor one
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -540,8 +627,6 @@ transform("foo")
ngx.say(data)
}
}
--- request
GET /t
--- response_body
get_argument("foo")
constructor("bar")
@ -550,10 +635,9 @@ constructor("bar")
=== TEST 14: get() validates l1_serializer is a function
--- http_config eval: $::HttpConfig
=== TEST 16: get() validates l1_serializer is a function
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -571,8 +655,6 @@ constructor("bar")
end
}
}
--- request
GET /t
--- response_body
opts.l1_serializer must be a function
--- no_error_log
@ -580,10 +662,9 @@ opts.l1_serializer must be a function
=== TEST 15: set() calls l1_serializer
--- http_config eval: $::HttpConfig
=== TEST 17: set() calls l1_serializer
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -613,8 +694,6 @@ opts.l1_serializer must be a function
ngx.say(value)
}
}
--- request
GET /t
--- response_body
transform("value")
--- no_error_log
@ -622,10 +701,9 @@ transform("value")
=== TEST 16: set() calls l1_serializer for boolean false values
--- http_config eval: $::HttpConfig
=== TEST 18: set() calls l1_serializer for boolean false values
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -655,8 +733,6 @@ transform("value")
ngx.say(value)
}
}
--- request
GET /t
--- response_body
transform_boolean("false")
--- no_error_log
@ -664,10 +740,9 @@ transform_boolean("false")
=== TEST 17: l1_serializer as set() argument has precedence over the constructor one
--- http_config eval: $::HttpConfig
=== TEST 19: l1_serializer as set() argument has precedence over the constructor one
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -701,8 +776,6 @@ transform_boolean("false")
ngx.say(value)
}
}
--- request
GET /t
--- response_body
set_argument("value")
--- no_error_log
@ -710,10 +783,9 @@ set_argument("value")
=== TEST 18: set() validates l1_serializer is a function
--- http_config eval: $::HttpConfig
=== TEST 20: set() validates l1_serializer is a function
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -733,8 +805,6 @@ set_argument("value")
end
}
}
--- request
GET /t
--- response_body
opts.l1_serializer must be a function
--- no_error_log

View file

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

View file

@ -1,28 +1,20 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
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 ipc_shm 1m;
};
plan tests => repeat_each() * blocks() * 3;
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"
@ -33,8 +25,6 @@ __DATA__
cache_1.lru == cache_2.lru)
}
}
--- request
GET /t
--- response_body
lua-resty-lru instances are the same: true
--- no_error_log
@ -43,9 +33,8 @@ 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"
@ -56,8 +45,6 @@ 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
@ -66,9 +53,8 @@ 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"
@ -104,8 +90,6 @@ lua-resty-lru instances are the same: false
ngx.say((cache_2.lru:get("key")))
}
}
--- request
GET /t
--- response_body
123
nil
@ -115,9 +99,8 @@ 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"
@ -153,8 +136,6 @@ 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
@ -166,9 +147,8 @@ 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"
@ -215,8 +195,6 @@ 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
@ -230,9 +208,8 @@ 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)
@ -276,8 +253,6 @@ 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
@ -289,9 +264,8 @@ 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"
@ -322,8 +296,6 @@ 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
@ -333,9 +305,8 @@ 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"
@ -366,8 +337,6 @@ 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,51 +1,21 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::Util;
use t::TestMLCache;
workers(2);
#repeat_each(2);
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()
}
};
plan tests => repeat_each() * blocks() * 4;
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"
@ -62,20 +32,18 @@ __DATA__
assert(cache:update())
}
}
--- request
GET /t
--- ignore_response_body
--- no_error_log
[error]
--- error_log
received event from invalidations: my_key
--- no_error_log
[error]
[crit]
=== 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"
@ -98,22 +66,19 @@ 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", {
@ -125,20 +90,18 @@ received event from invalidations: my_key
end
}
}
--- request
GET /t
--- ignore_response_body
--- no_error_log
[error]
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):7 loop\]/
--- no_error_log
[error]
[crit]
=== 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"
@ -167,8 +130,6 @@ 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
@ -176,13 +137,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"
@ -211,8 +172,6 @@ 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
@ -221,14 +180,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::Util::skip_openresty('>=', '1.13.6.2')
--- http_config eval: $::HttpConfig
--- skip_eval: 3: t::TestMLCache::skip_openresty('>=', '1.13.6.2')
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -257,8 +216,6 @@ 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
@ -266,14 +223,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::Util::skip_openresty('<', '1.13.6.2')
--- http_config eval: $::HttpConfig
--- skip_eval: 3: t::TestMLCache::skip_openresty('<', '1.13.6.2')
--- config
location = /t {
location /t {
content_by_lua_block {
local mlcache = require "resty.mlcache"
@ -306,8 +263,6 @@ 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()
@ -317,3 +272,4 @@ both instances use the same lru: true
lru didn't change after purge: true
--- no_error_log
[error]
[crit]

View file

@ -1,45 +1,18 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
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()
}
};
plan tests => repeat_each() * blocks() * 3;
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"
@ -51,8 +24,6 @@ __DATA__
end
}
}
--- request
GET /t
--- response_body
opts.shm_locks must be a string
--- no_error_log
@ -61,9 +32,8 @@ 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"
@ -75,8 +45,6 @@ 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
@ -85,9 +53,8 @@ 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"
@ -107,8 +74,6 @@ 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,29 +1,20 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::TestMLCache;
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"
@ -42,20 +33,18 @@ __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"))
@ -79,20 +68,18 @@ 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", {
@ -162,8 +149,6 @@ opts.resurrect_ttl must be >= 0
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -193,13 +178,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", {
@ -243,8 +228,6 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -257,13 +240,14 @@ 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", {
@ -317,8 +301,6 @@ 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
@ -338,13 +320,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", {
@ -406,8 +388,6 @@ hit_lvl: 4
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: nil
@ -432,13 +412,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", {
@ -501,8 +481,6 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: nil
@ -527,13 +505,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", {
@ -593,8 +571,6 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -619,13 +595,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", {
@ -677,8 +653,6 @@ hit_lvl: 3
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -696,13 +670,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
@ -767,8 +741,6 @@ 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
@ -778,17 +750,16 @@ hit_lvl: 4
data: 456
err: nil
hit_lvl: 1
--- no_error_log
[error]
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
--- no_error_log
[error]
=== 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
@ -853,8 +824,6 @@ 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
@ -864,17 +833,16 @@ hit_lvl: 4
data: nil
err: nil
hit_lvl: 1
--- no_error_log
[error]
--- error_log eval
qr/\[warn\] .*? could not acquire callback lock: timeout/
--- no_error_log
[error]
=== 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", {
@ -920,8 +888,6 @@ qr/\[warn\] .*? could not acquire callback lock: timeout/
ngx.say("hit_lvl: ", hit_lvl)
}
}
--- request
GET /t
--- response_body
-> 1st get()
data: 123
@ -939,13 +905,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", {
@ -970,21 +936,18 @@ hit_lvl: nil
ngx.say("cb return values: ", data, " ", err)
}
}
--- request
GET /t
--- response_body
cb return values: 123 nil
--- no_error_log
[error]
--- error_log eval
qr/\[warn\] .*? callback returned an error \(table: 0x[[:xdigit:]]+\)/
--- no_error_log
[error]
=== 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()
@ -1039,9 +1002,8 @@ 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,53 +1,21 @@
# vim:set ts=4 sts=4 sw=4 et ft=:
use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);
use strict;
use lib '.';
use t::Util;
no_long_string();
use t::TestMLCache;
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"
@ -58,8 +26,6 @@ __DATA__
ngx.say("bulk.n: ", bulk.n)
}
}
--- request
GET /t
--- response_body
type: table
size: 0
@ -70,9 +36,8 @@ 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"
@ -83,8 +48,6 @@ bulk.n: 0
ngx.say("bulk.n: ", bulk.n)
}
}
--- request
GET /t
--- response_body
type: table
size: 0
@ -95,9 +58,8 @@ 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"
@ -119,8 +81,6 @@ 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
@ -132,9 +92,8 @@ 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"))
@ -160,8 +119,6 @@ bulk\.n: 3
end
}
}
--- request
GET /t
--- response_body
1 nil 3
2 nil 3
@ -172,9 +129,8 @@ GET /t
=== 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"))
@ -195,8 +151,6 @@ GET /t
end
}
}
--- request
GET /t
--- response_body
1 1 nil 3
2 2 nil 3
@ -207,9 +161,8 @@ GET /t
=== 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"
@ -219,8 +172,6 @@ GET /t
end
}
}
--- request
GET /t
--- response_body
res must have res.n field; is this a get_bulk() result?
--- no_error_log

View file

@ -0,0 +1,103 @@
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

@ -1,51 +0,0 @@
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: