diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 470a09d5f..3b2e9adca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 743993ab6..9b1c4b8dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/LICENSE b/LICENSE index 873a00e75..a817d3fa2 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/README.md b/README.md index 183c4666b..785ceb9e6 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/lib/resty/mlcache.lua b/lib/resty/mlcache.lua index 7f66af0bf..136a9efc9 100644 --- a/lib/resty/mlcache.lua +++ b/lib/resty/mlcache.lua @@ -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 diff --git a/lua-resty-mlcache-2.6.0-1.rockspec b/lua-resty-mlcache-2.6.1-1.rockspec similarity index 92% rename from lua-resty-mlcache-2.6.0-1.rockspec rename to lua-resty-mlcache-2.6.1-1.rockspec index 4821f51a0..f0c6b6b86 100644 --- a/lua-resty-mlcache-2.6.0-1.rockspec +++ b/lua-resty-mlcache-2.6.1-1.rockspec @@ -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", diff --git a/t/00-ipc.t b/t/00-ipc.t index f2e947dc4..7f4c883f8 100644 --- a/t/00-ipc.t +++ b/t/00-ipc.t @@ -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] diff --git a/t/01-new.t b/t/01-new.t index 93ce3e4f8..8e52421c8 100644 --- a/t/01-new.t +++ b/t/01-new.t @@ -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 diff --git a/t/02-get.t b/t/02-get.t index 40de0efc3..9453bce08 100644 --- a/t/02-get.t +++ b/t/02-get.t @@ -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) + 9; - -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: get() validates key ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -63,19 +31,17 @@ __DATA__ end } } ---- request -GET /t --- response_body key must be a string --- no_error_log [error] +[crit] === TEST 2: get() accepts callback as nil or function ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -96,19 +62,17 @@ key must be a string end } } ---- request -GET /t --- response_body --- no_error_log [error] +[crit] === TEST 3: get() rejects callbacks not nil or function ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -129,20 +93,18 @@ GET /t end } } ---- request -GET /t --- response_body callback must be nil or a function callback must be nil or a function --- no_error_log [error] +[crit] === TEST 4: get() validates opts ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -158,19 +120,17 @@ callback must be nil or a function end } } ---- request -GET /t --- response_body opts must be a table --- no_error_log [error] +[crit] === TEST 5: get() calls callback in protected mode with stack traceback ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -190,8 +150,6 @@ opts must be a table end } } ---- request -GET /t --- response_body_like chomp callback threw an error: .*? oops stack traceback: @@ -199,13 +157,13 @@ stack traceback: \s+content_by_lua\(nginx\.conf:\d+\):\d+: in function --- no_error_log [error] +[crit] === TEST 6: get() is resilient to callback runtime errors with non-string arguments ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -226,20 +184,18 @@ stack traceback: end } } ---- request -GET /t --- response_body_like callback threw an error: userdata: NULL callback threw an error: table: 0x[0-9a-fA-F]+ --- no_error_log [error] +[crit] === TEST 7: get() caches a number ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -286,21 +242,19 @@ callback threw an error: table: 0x[0-9a-fA-F]+ ngx.say("from shm: ", type(data), " ", data) } } ---- request -GET /t --- response_body from callback: number 123 from lru: number 123 from shm: number 123 --- no_error_log [error] +[crit] === TEST 8: get() caches a boolean (true) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -347,21 +301,19 @@ from shm: number 123 ngx.say("from shm: ", type(data), " ", data) } } ---- request -GET /t --- response_body from callback: boolean true from lru: boolean true from shm: boolean true --- no_error_log [error] +[crit] === TEST 9: get() caches a boolean (false) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -408,21 +360,19 @@ from shm: boolean true ngx.say("from shm: ", type(data), " ", data) } } ---- request -GET /t --- response_body from callback: boolean false from lru: boolean false from shm: boolean false --- no_error_log [error] +[crit] === TEST 10: get() caches nil ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -469,21 +419,19 @@ from shm: boolean false ngx.say("from shm: ", type(data), " ", data) } } ---- request -GET /t --- response_body from callback: nil nil from lru: nil nil from shm: nil nil --- no_error_log [error] +[crit] === TEST 11: get() caches nil in 'shm_miss' if specified ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm local dict_miss = ngx.shared.cache_shm_miss @@ -548,8 +496,6 @@ from shm: nil nil ngx.say("value in lru is a sentinel nil value: ", v ~= nil) } } ---- request -GET /t --- response_body from callback: nil nil no value in shm: true @@ -558,13 +504,13 @@ from shm: nil nil value in lru is a sentinel nil value: true --- no_error_log [error] +[crit] === TEST 12: get() caches a string ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -611,21 +557,19 @@ value in lru is a sentinel nil value: true ngx.say("from shm: ", type(data), " ", data) } } ---- request -GET /t --- response_body from callback: string hello world from lru: string hello world from shm: string hello world --- no_error_log [error] +[crit] === TEST 13: get() caches a table ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local cjson = require "cjson" local mlcache = require "resty.mlcache" @@ -676,21 +620,19 @@ from shm: string hello world ngx.say("from shm: ", type(data), " ", data.hello, " ", data.subt.foo) } } ---- request -GET /t --- response_body from callback: table world bar from lru: table world bar from shm: table world bar --- no_error_log [error] +[crit] === TEST 14: get() errors when caching an unsupported type ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local cjson = require "cjson" local mlcache = require "resty.mlcache" @@ -712,8 +654,6 @@ from shm: table world bar end } } ---- request -GET /t --- error_code: 500 --- error_log eval qr/\[error\] .*?mlcache\.lua:\d+: cannot cache value of type userdata/ @@ -721,9 +661,8 @@ qr/\[error\] .*?mlcache\.lua:\d+: cannot cache value of type userdata/ === TEST 15: get() calls callback with args ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -746,23 +685,26 @@ qr/\[error\] .*?mlcache\.lua:\d+: cannot cache value of type userdata/ ngx.say(data) } } ---- request -GET /t --- response_body 3 --- no_error_log [error] +[crit] === TEST 16: get() caches hit for 'ttl' from LRU (in ms) ---- 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", { ttl = 0.3 })) + local cache = assert(mlcache.new("my_mlcache", "cache_shm", { + ttl = 0.3, + l1_serializer = function(s) + return "override" + end + })) local function cb() ngx.say("in callback") @@ -770,33 +712,31 @@ GET /t end local data = assert(cache:get("key", nil, cb)) - assert(data == 123) + assert(data == "override") ngx.sleep(0.2) data = assert(cache:get("key", nil, cb)) - assert(data == 123) + assert(data == "override") ngx.sleep(0.2) - data = assert(cache:get("key", nil, cb)) - assert(data == 123) + local data, err, lvl = assert(cache:get("key", nil, cb)) + assert(data == "override") } } ---- request -GET /t --- response_body in callback in callback --- no_error_log [error] +[crit] === TEST 17: get() caches miss (nil) for 'neg_ttl' from LRU (in ms) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -827,20 +767,18 @@ in callback assert(data == nil) } } ---- request -GET /t --- response_body in callback in callback --- no_error_log [error] +[crit] === TEST 18: get() caches for 'opts.ttl' from LRU (in ms) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -865,20 +803,18 @@ in callback assert(data == 123) } } ---- request -GET /t --- response_body in callback in callback --- no_error_log [error] +[crit] === TEST 19: get() caches for 'opts.neg_ttl' from LRU (in ms) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -906,20 +842,18 @@ in callback assert(data == nil) } } ---- request -GET /t --- response_body in callback in callback --- no_error_log [error] +[crit] === TEST 20: get() with ttl of 0 means indefinite caching ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -952,21 +886,19 @@ in callback ngx.say("in shm after exp: ", data) } } ---- request -GET /t --- response_body in callback in LRU after exp: 123 in shm after exp: 123 --- no_error_log [error] +[crit] === TEST 21: get() with neg_ttl of 0 means indefinite caching for nil values ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1001,21 +933,19 @@ in shm after exp: 123 ngx.say("in shm after exp: ", tostring(data)) } } ---- request -GET /t --- response_body_like in callback in LRU after exp: table: \S+ in shm after exp: nil --- no_error_log [error] +[crit] === TEST 22: get() errors when ttl < 0 ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1036,19 +966,17 @@ in shm after exp: nil end } } ---- request -GET /t --- response_body opts.ttl must be >= 0 --- no_error_log [error] +[crit] === TEST 23: get() errors when neg_ttl < 0 ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1069,19 +997,17 @@ opts.ttl must be >= 0 end } } ---- request -GET /t --- response_body opts.neg_ttl must be >= 0 --- no_error_log [error] +[crit] === TEST 24: get() shm -> LRU caches for 'opts.ttl - since' in ms ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1126,20 +1052,18 @@ opts.neg_ttl must be >= 0 end } } ---- request -GET /t --- response_body is not expired in LRU: 123 is stale in LRU: 123 --- no_error_log [error] +[crit] === TEST 25: get() shm -> LRU caches non-nil for 'indefinite' if ttl is 0 ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1171,19 +1095,17 @@ is stale in LRU: 123 end } } ---- request -GET /t --- response_body is not expired in LRU: 123 --- no_error_log [error] +[crit] === TEST 26: get() shm -> LRU caches for 'opts.neg_ttl - since' in ms ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1230,20 +1152,18 @@ is not expired in LRU: 123 end } } ---- request -GET /t --- response_body_like is not expired in LRU: table: \S+ is stale in LRU: table: \S+ --- no_error_log [error] +[crit] === TEST 27: get() shm -> LRU caches nil for 'indefinite' if neg_ttl is 0 ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1274,19 +1194,17 @@ is stale in LRU: table: \S+ -- data is a table (nil sentinel value) so rely on stale instead } } ---- request -GET /t --- response_body is stale in LRU: nil --- no_error_log [error] +[crit] === TEST 28: get() returns hit level ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1310,21 +1228,19 @@ is stale in LRU: nil ngx.say("hit level from shm: ", hit_lvl) } } ---- request -GET /t --- response_body hit level from callback: 3 hit level from LRU: 1 hit level from shm: 2 --- no_error_log [error] +[crit] === TEST 29: get() returns hit level for nil hits ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1348,22 +1264,20 @@ hit level from shm: 2 ngx.say("hit level from shm: ", hit_lvl) } } ---- request -GET /t --- response_body hit level from callback: 3 hit level from LRU: 1 hit level from shm: 2 --- no_error_log [error] +[crit] === TEST 30: get() returns hit level for boolean false hits ---- skip_eval: 3: t::Util::skip_openresty('<', '1.11.2.3') ---- http_config eval: $::HttpConfig +--- skip_eval: 3: t::TestMLCache::skip_openresty('<', '1.11.2.3') --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1387,21 +1301,19 @@ hit level from shm: 2 ngx.say("hit level from shm: ", hit_lvl) } } ---- request -GET /t --- response_body hit level from callback: 3 hit level from LRU: 1 hit level from shm: 2 --- no_error_log [error] +[crit] === TEST 31: get() JITs when hit coming from LRU ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1417,21 +1329,18 @@ hit level from shm: 2 end } } ---- request -GET /t ---- response_body - +--- ignore_response_body --- error_log eval qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):10 loop\]/ --- no_error_log [error] +[crit] === TEST 32: get() JITs when hit of scalar value coming from shm ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1477,10 +1386,7 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):10 loop\]/ end } } ---- request -GET /t ---- response_body - +--- ignore_response_body --- error_log eval [ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):18 loop\]/, @@ -1489,14 +1395,14 @@ GET /t ] --- no_error_log [error] +[crit] === TEST 33: get() JITs when hit of table value coming from shm --- SKIP: blocked until l2_serializer ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1515,21 +1421,18 @@ GET /t 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 [error] +[crit] === TEST 34: get() JITs when miss coming from LRU ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1546,21 +1449,18 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):18 loop\]/ end } } ---- request -GET /t ---- response_body - +--- ignore_response_body --- error_log eval qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):10 loop\]/ --- no_error_log [error] +[crit] === TEST 35: get() JITs when miss coming from shm ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1579,21 +1479,18 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):10 loop\]/ end } } ---- request -GET /t ---- response_body - +--- ignore_response_body --- error_log eval qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):10 loop\]/ --- no_error_log [error] +[crit] === TEST 36: get() callback can return nil + err (string) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1620,20 +1517,18 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):10 loop\]/ end } } ---- request -GET /t --- response_body cb return values: nil an error occurred cb2 return values: foo an error occurred again --- no_error_log [error] +[crit] === TEST 37: get() callback can return nil + err (non-string) safely ---- 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")) @@ -1659,20 +1554,18 @@ cb2 return values: foo an error occurred again end } } ---- request -GET /t --- response_body_like chomp cb return values: nil table: 0x[[:xdigit:]]+ cb2 return values: foo table: 0x[[:xdigit:]]+ --- no_error_log [error] +[crit] === TEST 38: get() callback can return nil + err (table) and will call __tostring ---- 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")) @@ -1693,19 +1586,17 @@ cb2 return values: foo table: 0x[[:xdigit:]]+ end } } ---- request -GET /t --- response_body cb return values: nil hello from __tostring --- no_error_log [error] +[crit] === TEST 39: get() callback's 3th return value can override the ttl ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1743,20 +1634,18 @@ cb return values: nil hello from __tostring assert(data == 2) } } ---- request -GET /t --- response_body in callback 1 in callback 2 --- no_error_log [error] +[crit] === TEST 40: get() callback's 3th return value can override the neg_ttl ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1794,20 +1683,18 @@ in callback 2 assert(data == 1) } } ---- request -GET /t --- response_body in callback 1 in callback 2 --- no_error_log [error] +[crit] === TEST 41: get() ignores invalid callback 3rd return value (not number) ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -1869,8 +1756,6 @@ in callback 2 assert(data == 1) } } ---- request -GET /t --- response_body Test A: string TTL return value for positive data is ignored in positive callback @@ -1880,13 +1765,13 @@ in negative callback in positive callback --- no_error_log [error] +[crit] === TEST 42: get() passes 'resty_lock_opts' for L3 calls ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local resty_lock = require "resty.lock" local mlcache = require "resty.mlcache" @@ -1917,19 +1802,17 @@ in positive callback end } } ---- request -GET /t --- response_body was given 'opts.resty_lock_opts': true --- no_error_log [error] +[crit] === TEST 43: get() errors on lock timeout ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { access_by_lua_block { ngx.shared.cache_shm:set(1, true, 0.2) ngx.shared.cache_shm:set(2, true, 0.2) @@ -1990,8 +1873,6 @@ was given 'opts.resty_lock_opts': true 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: could not acquire callback lock: timeout @@ -2003,13 +1884,13 @@ err: nil hit_lvl: 1 --- no_error_log [error] +[crit] === TEST 44: get() returns data even if failed to set in shm ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm local mlcache = require "resty.mlcache" @@ -2047,8 +1928,6 @@ hit_lvl: 1 ngx.say("data type: ", type(data)) } } ---- request -GET /t --- response_body data type: string --- error_log eval @@ -2059,9 +1938,8 @@ qr/\[warn\] .*? could not write to lua_shared_dict 'cache_shm' after 3 tries \(n === TEST 45: get() errors on invalid opts.shm_set_tries ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -2087,21 +1965,19 @@ qr/\[warn\] .*? could not write to lua_shared_dict 'cache_shm' after 3 tries \(n end } } ---- request -GET /t --- response_body opts.shm_set_tries must be a number opts.shm_set_tries must be >= 1 opts.shm_set_tries must be >= 1 --- no_error_log [error] +[crit] === TEST 46: get() with default shm_set_tries to LRU evict items when a large value is being cached ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm dict:flush_all() @@ -2158,8 +2034,6 @@ opts.shm_set_tries must be >= 1 ngx.say("callback was called: ", cb_calls, " times") } } ---- request -GET /t --- response_body type of data in shm: string callback was called: 1 times @@ -2170,9 +2044,8 @@ callback was called: 1 times === TEST 47: get() respects instance opts.shm_set_tries to LRU evict items when a large value is being cached ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm dict:flush_all() @@ -2231,8 +2104,6 @@ callback was called: 1 times ngx.say("callback was called: ", cb_calls, " times") } } ---- request -GET /t --- response_body type of data in shm: string callback was called: 1 times @@ -2243,9 +2114,8 @@ callback was called: 1 times === TEST 48: get() accepts opts.shm_set_tries to LRU evict items when a large value is being cached ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm dict:flush_all() @@ -2304,8 +2174,6 @@ callback was called: 1 times ngx.say("callback was called: ", cb_calls, " times") } } ---- request -GET /t --- response_body type of data in shm: string callback was called: 1 times @@ -2316,9 +2184,8 @@ callback was called: 1 times === TEST 49: get() caches data in L1 LRU even if failed to set in shm ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm dict:flush_all() @@ -2368,21 +2235,19 @@ callback was called: 1 times ngx.say("is stale: ", stale ~= nil) } } ---- request -GET /t --- response_body type of data in LRU: string sleeping... is stale: true --- no_error_log [error] +[crit] === TEST 50: get() does not cache value in LRU indefinitely 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() @@ -2433,21 +2298,19 @@ is stale: true ngx.say("+0.201s hit_lvl: ", hit_lvl) } } ---- request -GET /t --- response_body +0.200s hit_lvl: 2 +0.200s hit_lvl: 2 +0.201s hit_lvl: 3 --- no_error_log [error] +[crit] === TEST 51: get() bypass cache for negative callback TTL ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -2497,8 +2360,6 @@ GET /t assert(hit_level == 3) } } ---- request -GET /t --- response_body Test A: negative TTL return value for positive data bypasses cache in positive callback @@ -2508,13 +2369,13 @@ in negative callback in negative callback --- no_error_log [error] +[crit] === TEST 52: get() nil callback returns positive cached items from L1/L2 ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -2568,8 +2429,6 @@ in negative callback ngx.say("hit_lvl: ", hit_lvl) } } ---- request -GET /t --- response_body -> miss data: nil @@ -2592,13 +2451,13 @@ err: nil hit_lvl: 1 --- no_error_log [error] +[crit] === TEST 53: get() nil callback returns negative cached items from L1/L2 ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -2652,8 +2511,6 @@ hit_lvl: 1 ngx.say("hit_lvl: ", hit_lvl) } } ---- request -GET /t --- response_body -> miss data: nil @@ -2676,13 +2533,13 @@ err: nil hit_lvl: 1 --- no_error_log [error] +[crit] === TEST 54: get() JITs on misses without a callback ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" @@ -2693,10 +2550,9 @@ hit_lvl: 1 end } } ---- request -GET /t --- ignore_response_body --- error_log eval qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):6 loop\]/ --- no_error_log [error] +[crit] diff --git a/t/03-peek.t b/t/03-peek.t index bee4b9da0..f2326ea95 100644 --- a/t/03-peek.t +++ b/t/03-peek.t @@ -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] diff --git a/t/04-update.t b/t/04-update.t index 2c599253f..6533ee62e 100644 --- a/t/04-update.t +++ b/t/04-update.t @@ -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] diff --git a/t/05-set.t b/t/05-set.t index b5597d723..40e370f83 100644 --- a/t/05-set.t +++ b/t/05-set.t @@ -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] diff --git a/t/06-delete.t b/t/06-delete.t index 8d4644aae..b81002655 100644 --- a/t/06-delete.t +++ b/t/06-delete.t @@ -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 diff --git a/t/07-l1_serializer.t b/t/07-l1_serializer.t index 0f9f0d475..a896cc014 100644 --- a/t/07-l1_serializer.t +++ b/t/07-l1_serializer.t @@ -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 diff --git a/t/08-purge.t b/t/08-purge.t index f63b32e7d..40aa283d2 100644 --- a/t/08-purge.t +++ b/t/08-purge.t @@ -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: diff --git a/t/09-isolation.t b/t/09-isolation.t index 0ce0b40b9..34a43693b 100644 --- a/t/09-isolation.t +++ b/t/09-isolation.t @@ -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 diff --git a/t/10-ipc_shm.t b/t/10-ipc_shm.t index 20de9f597..de6ef6ae9 100644 --- a/t/10-ipc_shm.t +++ b/t/10-ipc_shm.t @@ -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] diff --git a/t/11-locks_shm.t b/t/11-locks_shm.t index ba443e920..b4cf77090 100644 --- a/t/11-locks_shm.t +++ b/t/11-locks_shm.t @@ -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 diff --git a/t/12-resurrect-stale.t b/t/12-resurrect-stale.t index 218643e98..687ebd5a1 100644 --- a/t/12-resurrect-stale.t +++ b/t/12-resurrect-stale.t @@ -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] diff --git a/t/13-get_bulk.t b/t/13-get_bulk.t index 7b6498a91..157aee8b2 100644 --- a/t/13-get_bulk.t +++ b/t/13-get_bulk.t @@ -1,53 +1,39 @@ # 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; -no_long_string(); +add_block_preprocessor(sub { + my $block = shift; + + if (!defined $block->no_error_log) { + $block->set_value("no_error_log", qq{[error] +[crit] +[alert] +[emerg] +stub +stub +stub +stub +stub +}); + } +}); workers(2); - #repeat_each(2); -plan tests => repeat_each() * ((blocks() * 3) + 12 * 3); # n * 3 -> for debug error_log concurrency tests -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() * 11; run_tests(); __DATA__ === TEST 1: get_bulk() validates 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")) @@ -58,19 +44,14 @@ __DATA__ end } } ---- request -GET /t --- response_body bulk must be a table ---- no_error_log -[error] === TEST 2: get_bulk() ensures bulk has n field ---- 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")) @@ -84,19 +65,14 @@ bulk must be a table end } } ---- request -GET /t --- response_body bulk must have n field ---- no_error_log -[error] === TEST 3: get_bulk() validates operations keys ---- 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")) @@ -111,19 +87,14 @@ bulk must have n field end } } ---- request -GET /t --- response_body key at index 5 must be a string for operation 2 (got boolean) ---- no_error_log -[error] === TEST 4: get_bulk() validates operations callbacks ---- 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")) @@ -147,20 +118,15 @@ key at index 5 must be a string for operation 2 (got boolean) end } } ---- request -GET /t --- response_body callback at index 3 must be a function for operation 1 (got nil) callback at index 7 must be a function for operation 2 (got boolean) ---- no_error_log -[error] === TEST 5: get_bulk() validates opts argument ---- 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")) @@ -188,20 +154,15 @@ callback at index 7 must be a function for operation 2 (got boolean) ngx.say("ok") } } ---- request -GET /t --- response_body opts must be a table ok ---- no_error_log -[error] === TEST 6: get_bulk() validates opts.concurrency ---- 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")) @@ -247,22 +208,17 @@ ok ngx.say("ok") } } ---- request -GET /t --- response_body opts.concurrency must be a number opts.concurrency must be > 0 opts.concurrency must be > 0 ok ---- no_error_log -[error] === TEST 7: get_bulk() multiple fetch L3 ---- 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")) @@ -285,21 +241,16 @@ ok end } } ---- request -GET /t --- response_body 1 nil 3 2 nil 3 3 nil 3 ---- no_error_log -[error] === TEST 8: get_bulk() multiple fetch L2 ---- 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")) @@ -330,21 +281,16 @@ GET /t end } } ---- request -GET /t --- response_body 1 nil 2 2 nil 2 3 nil 2 ---- no_error_log -[error] === TEST 9: get_bulk() multiple fetch L1 ---- 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")) @@ -371,21 +317,16 @@ GET /t end } } ---- request -GET /t --- response_body 1 nil 1 2 nil 1 3 nil 1 ---- no_error_log -[error] === TEST 10: get_bulk() multiple fetch L1/single fetch L3 ---- 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")) @@ -411,21 +352,16 @@ GET /t end } } ---- request -GET /t --- response_body 1 nil 1 2 nil 1 3 nil 3 ---- no_error_log -[error] === TEST 11: get_bulk() multiple fetch L1/single fetch L3 (with nils) ---- 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")) @@ -453,21 +389,16 @@ GET /t end } } ---- request -GET /t --- response_body nil nil 1 nil nil 1 nil nil 3 ---- no_error_log -[error] === TEST 12: get_bulk() mixed fetch L1/L2/L3 ---- 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")) @@ -496,21 +427,16 @@ nil nil 3 end } } ---- request -GET /t --- response_body 1 nil 1 2 nil 2 3 nil 3 ---- no_error_log -[error] === TEST 13: get_bulk() mixed fetch L1/L2/L3 (with nils) ---- 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")) @@ -541,21 +467,16 @@ GET /t end } } ---- request -GET /t --- response_body nil nil 1 nil nil 2 nil nil 3 ---- no_error_log -[error] === TEST 14: get_bulk() returns callback-returned errors ---- 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")) @@ -578,21 +499,16 @@ nil nil 3 end } } ---- request -GET /t --- response_body 1 nil 3 2 nil 3 nil some error nil ---- no_error_log -[error] === TEST 15: get_bulk() returns callback runtime errors ---- 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")) @@ -615,23 +531,18 @@ nil some error nil end } } ---- request -GET /t --- response_body_like 1 nil 3 2 nil 3 nil callback threw an error: some error stack traceback: .*? nil ---- no_error_log -[error] === TEST 16: get_bulk() runs L3 callback on expired keys ---- 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")) @@ -663,20 +574,15 @@ stack traceback: end } } ---- request -GET /t --- response_body 2 nil 3 3 nil 3 ---- no_error_log -[error] === TEST 17: get_bulk() honors ttl and neg_ttl instance attributes ---- 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", { @@ -707,23 +613,18 @@ GET /t ngx.say("key_b: ", value, " (ttl: ", ttl, ")") } } ---- request -GET /t --- response_body 1 nil 3 nil nil 3 key_a: 1 (ttl: 0.2) key_b: nil (ttl: 0.3) ---- no_error_log -[error] === TEST 18: get_bulk() validates operations ttl and neg_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")) @@ -747,20 +648,15 @@ key_b: nil (ttl: 0.3) end } } ---- request -GET /t --- response_body options at index 2 for operation 1 are invalid: opts.ttl must be a number options at index 6 for operation 2 are invalid: opts.neg_ttl must be a number ---- no_error_log -[error] === TEST 19: get_bulk() accepts ttl and neg_ttl for each operation ---- 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", { @@ -791,23 +687,18 @@ options at index 6 for operation 2 are invalid: opts.neg_ttl must be a number ngx.say("key_b: ", value, " (ttl: ", ttl, ")") } } ---- request -GET /t --- response_body 1 nil 3 nil nil 3 key_a: 1 (ttl: 0.4) key_b: nil (ttl: 0.8) ---- no_error_log -[error] === TEST 20: get_bulk() honors ttl from callback return values ---- 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", { @@ -837,23 +728,18 @@ key_b: nil (ttl: 0.8) ngx.say("key_b: ", value, " (ttl: ", ttl, ")") } } ---- request -GET /t --- response_body 1 nil 3 2 nil 3 key_a: 1 (ttl: 0.2) key_b: 2 (ttl: 1) ---- no_error_log -[error] === TEST 21: get_bulk() honors resurrect_ttl instance attribute ---- 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", { @@ -899,23 +785,18 @@ key_b: 2 (ttl: 1) ngx.say(string.format("key_b: %d ttl: %.2f", value, ttl)) } } ---- request -GET /t --- response_body_like 1 nil 4 3 nil 3 key_a: 1 ttl: 0\.(?:2|1)\d+ key_b: 3 ttl: 0\.(?:1|0)\d+ ---- no_error_log -[error] === TEST 22: get_bulk() accepts resurrect_ttl for each operation ---- 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", { @@ -961,23 +842,18 @@ key_b: 3 ttl: 0\.(?:1|0)\d+ ngx.say(string.format("key_b: %d ttl: %.2f", value, ttl)) } } ---- request -GET /t --- response_body_like 1 nil 4 3 nil 3 key_a: 1 ttl: 0\.(?:2|1)\d+ key_b: 3 ttl: 0\.(?:1|0)\d+ ---- no_error_log -[error] === TEST 23: get_bulk() honors l1_serializer instance attribute ---- 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", { @@ -1003,20 +879,15 @@ key_b: 3 ttl: 0\.(?:1|0)\d+ end } } ---- request -GET /t --- response_body hello nil 3 world nil 3 ---- no_error_log -[error] === TEST 24: get_bulk() accepts l1_serializer for each operation ---- 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", { @@ -1045,20 +916,15 @@ world nil 3 end } } ---- request -GET /t --- response_body hello nil 3 world nil 3 ---- no_error_log -[error] === TEST 25: get_bulk() honors shm_set_tries instance attribute ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm dict:flush_all() @@ -1102,20 +968,15 @@ world nil 3 end } } ---- request -GET /t --- ignore_response_body ---- no_error_log -[error] --- error_log could not write to lua_shared_dict 'cache_shm' after 1 tries (no memory) === TEST 26: get_bulk() accepts shm_set_tries for each operation ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local dict = ngx.shared.cache_shm dict:flush_all() @@ -1159,20 +1020,15 @@ could not write to lua_shared_dict 'cache_shm' after 1 tries (no memory) end } } ---- request -GET /t --- ignore_response_body ---- no_error_log -[error] --- error_log could not write to lua_shared_dict 'cache_shm' after 1 tries (no memory) === TEST 27: get_bulk() operations wait on lock if another thread is fetching the same key ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache_1 = assert(mlcache.new("my_mlcache", "cache_shm")) @@ -1225,8 +1081,6 @@ could not write to lua_shared_dict 'cache_shm' after 1 tries (no memory) end } } ---- request -GET /t --- response_body t1 hello 3 @@ -1234,15 +1088,12 @@ hello 3 t2 hello nil 3 hello nil 2 ---- no_error_log -[error] === TEST 28: get_bulk() operations reports timeout on lock if another thread is fetching the same key ---- http_config eval: $::HttpConfig --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache_1 = assert(mlcache.new("my_mlcache", "cache_shm")) @@ -1297,8 +1148,6 @@ hello nil 2 end } } ---- request -GET /t --- response_body t1 hello 3 @@ -1306,16 +1155,13 @@ hello 3 t2 hello nil 3 nil could not acquire callback lock: timeout nil ---- no_error_log -[error] === TEST 29: get_bulk() opts.concurrency: default is 3 (with 3 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1334,24 +1180,26 @@ nil could not acquire callback lock: timeout nil }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 2 threads to run 3 callbacks thread 1 running callbacks 1 to 1 thread 2 running callbacks 2 to 2 main thread running callbacks 3 to 3 --- no_error_log +[warn] [error] +[crit] +[alert] +[emerg] +stub === TEST 30: get_bulk() opts.concurrency: default is 3 (with 6 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1373,24 +1221,26 @@ main thread running callbacks 3 to 3 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 2 threads to run 6 callbacks thread 1 running callbacks 1 to 2 thread 2 running callbacks 3 to 4 main thread running callbacks 5 to 6 --- no_error_log +[warn] [error] +[crit] +[alert] +[emerg] +stub === TEST 31: get_bulk() opts.concurrency: default is 3 (with 7 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1413,24 +1263,26 @@ main thread running callbacks 5 to 6 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 2 threads to run 7 callbacks thread 1 running callbacks 1 to 3 thread 2 running callbacks 4 to 6 main thread running callbacks 7 to 7 --- no_error_log +[warn] [error] +[crit] +[alert] +[emerg] +stub === TEST 32: get_bulk() opts.concurrency: default is 3 (with 1 op) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1447,24 +1299,26 @@ main thread running callbacks 7 to 7 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 0 threads to run 1 callbacks main thread running callbacks 1 to 1 --- no_error_log [warn] [error] +[crit] [alert] +[emerg] +stub +stub +stub === TEST 33: get_bulk() opts.concurrency: 1 (with 3 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1483,24 +1337,26 @@ main thread running callbacks 1 to 1 }, { concurrency = 1 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 0 threads to run 3 callbacks main thread running callbacks 1 to 3 --- no_error_log [warn] [error] +[crit] [alert] +[emerg] +stub +stub +stub === TEST 34: get_bulk() opts.concurrency: 1 (with 6 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1522,24 +1378,26 @@ main thread running callbacks 1 to 3 }, { concurrency = 1 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 0 threads to run 6 callbacks main thread running callbacks 1 to 6 --- no_error_log [warn] [error] +[crit] [alert] +[emerg] +stub +stub +stub === TEST 35: get_bulk() opts.concurrency: 6 (with 3 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1558,9 +1416,7 @@ main thread running callbacks 1 to 6 }, { concurrency = 6 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 2 threads to run 3 callbacks thread 1 running callbacks 1 to 1 @@ -1568,14 +1424,18 @@ thread 2 running callbacks 2 to 2 main thread running callbacks 3 to 3 --- no_error_log [error] +[crit] +[alert] +[emerg] +stub +stub === TEST 36: get_bulk() opts.concurrency: 6 (with 6 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1597,9 +1457,7 @@ main thread running callbacks 3 to 3 }, { concurrency = 6 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 5 threads to run 6 callbacks thread 1 running callbacks 1 to 1 @@ -1611,15 +1469,14 @@ main thread running callbacks 6 to 6 --- no_error_log [warn] [error] -[alert] +[crit] === TEST 37: get_bulk() opts.concurrency: 6 (with 7 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1642,9 +1499,7 @@ main thread running callbacks 6 to 6 }, { concurrency = 6 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 5 threads to run 7 callbacks thread 1 running callbacks 1 to 2 @@ -1652,15 +1507,18 @@ thread 2 running callbacks 3 to 4 thread 3 running callbacks 5 to 6 thread 4 running callbacks 7 to 7 --- no_error_log +[warn] [error] +[crit] +[alert] +[emerg] === TEST 38: get_bulk() opts.concurrency: 6 (with 1 op) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm", { @@ -1677,24 +1535,26 @@ thread 4 running callbacks 7 to 7 }, { concurrency = 6 }) } } ---- request -GET /t ---- no_response_body +--- ignore_response_body --- error_log spawning 0 threads to run 1 callbacks main thread running callbacks 1 to 1 --- no_error_log [warn] [error] +[crit] [alert] +[emerg] +stub +stub +stub === TEST 39: get_bulk() opts.concurrency: 6 (with 7 ops) ---- http_config eval: $::HttpConfig --- log_level: debug --- config - location = /t { + location /t { content_by_lua_block { local mlcache = require "resty.mlcache" local cache = assert(mlcache.new("my_mlcache", "cache_shm")) @@ -1721,8 +1581,6 @@ main thread running callbacks 1 to 1 end } } ---- request -GET /t --- response_body 1 nil 3 2 nil 3 @@ -1731,5 +1589,3 @@ GET /t 5 nil 3 6 nil 3 7 nil 3 ---- no_error_log -[error] diff --git a/t/14-bulk-and-res.t b/t/14-bulk-and-res.t index 7b40bd9d0..f54f49038 100644 --- a/t/14-bulk-and-res.t +++ b/t/14-bulk-and-res.t @@ -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 diff --git a/t/TestMLCache.pm b/t/TestMLCache.pm new file mode 100644 index 000000000..010564fae --- /dev/null +++ b/t/TestMLCache.pm @@ -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; diff --git a/t/Util.pm b/t/Util.pm deleted file mode 100644 index f8a14f3b6..000000000 --- a/t/Util.pm +++ /dev/null @@ -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: