├── .gitignore ├── Makefile ├── lib └── resty │ └── shdict │ └── simple.lua ├── .travis.yml ├── README.md └── t └── shdict-simple.t /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | *.bak 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENRESTY_PREFIX=/usr/local/openresty 2 | 3 | #LUA_VERSION := 5.1 4 | PREFIX ?= /usr/local 5 | LUA_INCLUDE_DIR ?= $(PREFIX)/include 6 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) 7 | INSTALL ?= install 8 | 9 | .PHONY: all test install 10 | 11 | all: ; 12 | 13 | install: all 14 | $(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/resty/shdict/ 15 | $(INSTALL) lib/resty/shdict/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/shdict/ 16 | 17 | test: all 18 | PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t 19 | -------------------------------------------------------------------------------- /lib/resty/shdict/simple.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) CloudFlare Inc. 2 | -- 3 | -- Generate shdict cache methods, based on shared memory dictionary 4 | 5 | 6 | local _M = { 7 | version = '0.01', 8 | } 9 | 10 | 11 | local DEBUG = ngx.config.debug 12 | local ngx = ngx 13 | local error = error 14 | local tostring = tostring 15 | 16 | 17 | function _M.gen_shdict_methods (opts) 18 | local dlog = opts.debug_logger 19 | local error_log = opts.error_logger 20 | local pos_ttl = opts.positive_ttl 21 | local neg_ttl = opts.negative_ttl 22 | local disable_shdict = opts.disable_shdict 23 | local dict_name = opts.dict_name 24 | local max_tries = opts.max_tries or 1 25 | local shdict = ngx.shared[dict_name] 26 | if not shdict then 27 | error("failed to find lua_shared_dict \"" 28 | .. tostring(dict_name) .. "\" in your nginx.conf") 29 | end 30 | 31 | local shdict_set, shdict_get 32 | 33 | pos_ttl = pos_ttl / 1000 -- convert to sec 34 | neg_ttl = neg_ttl / 1000 -- convert to sec 35 | 36 | local function shdict_set(ctx, key, value, ttl) 37 | if disable_shdict then 38 | return true -- stub 39 | end 40 | 41 | if value == "" then 42 | ttl = ttl or neg_ttl 43 | 44 | else 45 | ttl = ttl or pos_ttl 46 | end 47 | 48 | local tries = 0 49 | local ok, err 50 | while tries < max_tries do 51 | ok, err = shdict:set(key, value, ttl) 52 | tries = tries + 1 53 | if DEBUG then 54 | dlog(ctx, 'try to set key: ' .. key .. ', the '.. tostring(tries) .. 'th time') 55 | end 56 | if ok or err ~= "no memory" then 57 | break 58 | end 59 | end 60 | 61 | if not ok then 62 | error_log(ctx, 'failed to set key "', key, '" to shdict "', 63 | dict_name, '": ', err) 64 | return false 65 | end 66 | 67 | return true 68 | end 69 | 70 | local function shdict_get(ctx, key) 71 | if disable_shdict then 72 | return nil -- stub 73 | end 74 | 75 | local res, flags, stale = shdict:get_stale(key) 76 | 77 | if res and not stale then 78 | if DEBUG then 79 | dlog(ctx, res == "" and "negative" or "positive", 80 | ' cache hit on key "', key, '"') 81 | end 82 | end 83 | 84 | return res, stale 85 | end 86 | 87 | return shdict_set, shdict_get 88 | end 89 | 90 | 91 | return _M 92 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: focal 3 | 4 | os: linux 5 | 6 | language: c 7 | 8 | compiler: 9 | - gcc 10 | - clang 11 | 12 | cache: 13 | directories: 14 | - download-cache 15 | 16 | env: 17 | global: 18 | - JOBS=3 19 | - NGX_BUILD_JOBS=$JOBS 20 | - LUAJIT_PREFIX=/opt/luajit21 21 | - LUAJIT_LIB=$LUAJIT_PREFIX/lib 22 | - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 23 | - LUA_INCLUDE_DIR=$LUAJIT_INC 24 | - OPENSSL_PREFIX=/opt/ssl 25 | - OPENSSL_LIB=$OPENSSL_PREFIX/lib 26 | - OPENSSL_INC=$OPENSSL_PREFIX/include 27 | - OPENSSL_VER=1.1.1w 28 | - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH 29 | - TEST_NGINX_SLEEP=0.006 30 | matrix: 31 | - NGINX_VERSION=1.27.1 32 | 33 | install: 34 | - if [ ! -d download-cache ]; then mkdir download-cache; fi 35 | - if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -O download-cache/openssl-$OPENSSL_VER.tar.gz https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz; fi 36 | - sudo apt-get install -qq -y cpanminus axel 37 | - sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1) 38 | - wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz 39 | - git clone https://github.com/openresty/test-nginx.git 40 | - git clone https://github.com/openresty/openresty.git ../openresty 41 | - git clone https://github.com/openresty/nginx-devel-utils.git 42 | - git clone https://github.com/openresty/lua-cjson.git 43 | - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core 44 | - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache 45 | - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module 46 | - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module 47 | - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx 48 | - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git 49 | 50 | script: 51 | - cd luajit2/ 52 | - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1) 53 | - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1) 54 | - cd ../lua-cjson && make && sudo PATH=$PATH make install && cd .. 55 | - tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz 56 | - cd openssl-$OPENSSL_VER/ 57 | - ./config shared --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1) 58 | - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1) 59 | - sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1) 60 | - cd .. 61 | - export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH 62 | - export NGX_BUILD_CC=$CC 63 | - ngx-build $NGINX_VERSION --without-pcre2 --with-ipv6 --with-http_realip_module --with-http_ssl_module --add-module=../echo-nginx-module --add-module=../lua-nginx-module --with-debug 64 | - nginx -V 65 | - ldd `which nginx`|grep -E 'luajit|ssl|pcre' 66 | - prove -r -I./test-nginx/inc -I./test-nginx/ t 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | lua-resty-shdict-simple - Simple application-oriented interface to OpenResty's shared dictionary API 5 | 6 | Table of Contents 7 | ================= 8 | 9 | * [Name](#name) 10 | * [Synopsis](#synopsis) 11 | * [TODO](#todo) 12 | * [Author](#author) 13 | * [Copyright and License](#copyright-and-license) 14 | * [See Also](#see-also) 15 | 16 | Synopsis 17 | ======== 18 | 19 | ```lua 20 | local shdict_simple = require "resty.shdict.simple" 21 | 22 | local function dlog(ctx, ...) 23 | ngx.log(ngx.DEBUG, "my app: ", ...) 24 | end 25 | 26 | local function error_log(ctx, ...) 27 | ngx.log(ngx.ERR, "my app: ", ...) 28 | end 29 | 30 | local pos_ttl = 20000 -- in ms 31 | local neg_ttl = 10000 -- in ms 32 | 33 | local my_shdict_set, my_shdict_get 34 | = shdict_simple.gen_shdict_methods{ 35 | dict_name = "my_lua_shared_dict_name", 36 | debug_logger = dlog, 37 | error_logger = error_log, 38 | positive_ttl = pos_ttl, 39 | negative_ttl = neg_ttl, 40 | max_tries = 10, -- default is 1 41 | } 42 | 43 | -- on hot code paths: 44 | 45 | local ctx = ngx.ctx 46 | 47 | local key = "name" 48 | local value = "John Green" 49 | 50 | -- when value == "", negative ttl is used; otherwise positive ttl is used. 51 | my_shdict_set(ctx, key, value) 52 | 53 | -- it is also possible to override the default ttl for a single method call: 54 | my_shdict_set(ctx, key, value, my_temp_ttl) 55 | 56 | local res, stale = my_shdict_get(ctx, key) 57 | if not res then 58 | if stale then 59 | -- use the stale data in the cache... 60 | end 61 | end 62 | ``` 63 | 64 | TODO 65 | ==== 66 | 67 | 68 | Author 69 | ====== 70 | 71 | Yichun "agentzh" Zhang (章亦春) , CloudFlare Inc. 72 | 73 | [Back to TOC](#table-of-contents) 74 | 75 | Copyright and License 76 | ===================== 77 | 78 | This module is licensed under the BSD license. 79 | 80 | Copyright (C) 2016, by CloudFlare Inc. 81 | 82 | All rights reserved. 83 | 84 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 85 | 86 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 87 | 88 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 89 | 90 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 91 | 92 | [Back to TOC](#table-of-contents) 93 | 94 | See Also 95 | ======== 96 | 97 | * [lua-resty-memcached-shdict](https://github.com/openresty/lua-resty-memcached-shdict) 98 | * [lua-resty-lrucache](https://github.com/openresty/lua-resty-lrucache) 99 | * [lua-resty-memcached](https://github.com/openresty/lua-resty-memcached) 100 | * [lua_shared_dict](https://github.com/openresty/lua-nginx-module#lua_shared_dict) 101 | 102 | [Back to TOC](#table-of-contents) 103 | 104 | -------------------------------------------------------------------------------- /t/shdict-simple.t: -------------------------------------------------------------------------------- 1 | # vi:ft= 2 | 3 | use Test::Nginx::Socket::Lua; 4 | 5 | repeat_each(2); 6 | no_long_string(); 7 | 8 | plan tests => repeat_each() * (3 * blocks()); 9 | 10 | our $HttpConfig = <<'_EOC_'; 11 | lua_shared_dict shared 1m; 12 | lua_package_path 'lib/?.lua;;'; 13 | _EOC_ 14 | 15 | log_level 'debug'; 16 | 17 | run_tests(); 18 | 19 | __DATA__ 20 | 21 | === TEST 1: sanity 22 | --- http_config eval: $::HttpConfig 23 | --- config 24 | location /t { 25 | content_by_lua_block { 26 | local shdict_simple = require "resty.shdict.simple" 27 | 28 | local function dlog(ctx, ...) 29 | ngx.log(ngx.DEBUG, "my app: ", ...) 30 | end 31 | 32 | local function error_log(ctx, ...) 33 | ngx.log(ngx.ERR, "my app: ", ...) 34 | end 35 | 36 | local function warn(ctx, ...) 37 | ngx.log(ngx.WARN, "my app: ", ...) 38 | end 39 | 40 | local meta_shdict_set, meta_shdict_get = 41 | shdict_simple.gen_shdict_methods{ 42 | dict_name = "shared", 43 | debug_logger = dlog, 44 | error_logger = error_log, 45 | positive_ttl = 24 * 60 * 60 * 1000, -- in ms 46 | negative_ttl = 60 * 60 * 1000, -- in ms 47 | } 48 | 49 | local ctx = ngx.ctx 50 | local key = "c57a769495f14df8985d773eaa410ba2411aa9d588798b3aea60eae29a383ab0" 51 | local value = string.rep("a", 129) 52 | ok = meta_shdict_set(ctx, key, value) 53 | ngx.say(ok) 54 | local data, err = meta_shdict_get(ctx, key) 55 | ngx.say(data == value) 56 | } 57 | } 58 | --- request 59 | GET /t 60 | --- response_body 61 | true 62 | true 63 | --- grep_error_log eval: qr/try to set key: \w+, the \d+th time/ 64 | --- grep_error_log_out 65 | try to set key: c57a769495f14df8985d773eaa410ba2411aa9d588798b3aea60eae29a383ab0, the 1th time 66 | 67 | 68 | 69 | === TEST 2: max_tries 70 | --- http_config eval: $::HttpConfig 71 | --- config 72 | location /t { 73 | content_by_lua_block { 74 | local shdict_simple = require "resty.shdict.simple" 75 | 76 | local function dlog(ctx, ...) 77 | ngx.log(ngx.DEBUG, "my app: ", ...) 78 | end 79 | 80 | local function error_log(ctx, ...) 81 | ngx.log(ngx.ERR, "my app: ", ...) 82 | end 83 | 84 | local function warn(ctx, ...) 85 | ngx.log(ngx.WARN, "my app: ", ...) 86 | end 87 | 88 | local meta_shdict_set, meta_shdict_get = 89 | shdict_simple.gen_shdict_methods{ 90 | dict_name = "shared", 91 | debug_logger = dlog, 92 | error_logger = error_log, 93 | positive_ttl = 24 * 60 * 60 * 1000, -- in ms 94 | negative_ttl = 60 * 60 * 1000, -- in ms 95 | max_tries = 10, 96 | } 97 | 98 | local ctx = ngx.ctx 99 | local key = "a" 100 | local value = string.rep("a", 1000 * 1000) 101 | ok = meta_shdict_set(ctx, key, value) 102 | ngx.say(ok) 103 | local data, err = meta_shdict_get(ctx, key) 104 | ngx.say(data == value) 105 | 106 | key = "b" 107 | value = string.rep("b", 1000 * 1000) 108 | ok = meta_shdict_set(ctx, key, value) 109 | ngx.say(ok) 110 | local data, err = meta_shdict_get(ctx, key) 111 | ngx.say(data == value) 112 | } 113 | } 114 | --- request 115 | GET /t 116 | --- response_body 117 | true 118 | true 119 | true 120 | true 121 | --- grep_error_log eval: qr/try to set key: \w+, the \d+th time/ 122 | --- grep_error_log_out 123 | try to set key: a, the 1th time 124 | try to set key: b, the 1th time 125 | 126 | 127 | 128 | === TEST 3: use the default value of max_tries 129 | --- http_config eval: $::HttpConfig 130 | --- config 131 | location /t { 132 | content_by_lua_block { 133 | local shdict_simple = require "resty.shdict.simple" 134 | 135 | local function dlog(ctx, ...) 136 | ngx.log(ngx.DEBUG, "my app: ", ...) 137 | end 138 | 139 | local function error_log(ctx, ...) 140 | ngx.log(ngx.ERR, "my app: ", ...) 141 | end 142 | 143 | local function warn(ctx, ...) 144 | ngx.log(ngx.WARN, "my app: ", ...) 145 | end 146 | 147 | local meta_shdict_set, meta_shdict_get = 148 | shdict_simple.gen_shdict_methods{ 149 | dict_name = "shared", 150 | debug_logger = dlog, 151 | error_logger = error_log, 152 | positive_ttl = 24 * 60 * 60 * 1000, -- in ms 153 | negative_ttl = 60 * 60 * 1000, -- in ms 154 | } 155 | 156 | local ctx = ngx.ctx 157 | local key = "a" 158 | local value = string.rep("a", 1000 * 1000) 159 | ok = meta_shdict_set(ctx, key, value) 160 | ngx.say(ok) 161 | local data, err = meta_shdict_get(ctx, key) 162 | ngx.say(data == value) 163 | 164 | key = "b" 165 | value = string.rep("b", 1000 * 1000) 166 | ok = meta_shdict_set(ctx, key, value) 167 | ngx.say(ok) 168 | local data, err = meta_shdict_get(ctx, key) 169 | ngx.say(data == value) 170 | } 171 | } 172 | --- request 173 | GET /t 174 | --- response_body 175 | true 176 | true 177 | true 178 | true 179 | --- grep_error_log eval: qr/try to set key: \w+, the \d+th time/ 180 | --- grep_error_log_out 181 | try to set key: a, the 1th time 182 | try to set key: b, the 1th time 183 | 184 | 185 | 186 | === TEST 4: no memory error 187 | --- http_config eval: $::HttpConfig 188 | --- config 189 | location /t { 190 | content_by_lua_block { 191 | local shdict_simple = require "resty.shdict.simple" 192 | 193 | local function dlog(ctx, ...) 194 | ngx.log(ngx.DEBUG, "my app: ", ...) 195 | end 196 | 197 | local function error_log(ctx, ...) 198 | ngx.log(ngx.ERR, "my app: ", ...) 199 | end 200 | 201 | local function warn(ctx, ...) 202 | ngx.log(ngx.WARN, "my app: ", ...) 203 | end 204 | 205 | local meta_shdict_set, meta_shdict_get = 206 | shdict_simple.gen_shdict_methods{ 207 | dict_name = "shared", 208 | debug_logger = dlog, 209 | error_logger = error_log, 210 | positive_ttl = 24 * 60 * 60 * 1000, -- in ms 211 | negative_ttl = 60 * 60 * 1000, -- in ms 212 | max_tries = 10, 213 | } 214 | 215 | local ctx = ngx.ctx 216 | local key = "a" 217 | local value = string.rep("a", 1000 * 1000) 218 | ok = meta_shdict_set(ctx, key, value) 219 | ngx.say(ok) 220 | local data, err = meta_shdict_get(ctx, key) 221 | ngx.say(data == value) 222 | 223 | key = "b" 224 | value = string.rep("b", 1000 * 2000) 225 | ok = meta_shdict_set(ctx, key, value) 226 | ngx.say(ok) 227 | local data, err = meta_shdict_get(ctx, key) 228 | ngx.say(data == value) 229 | } 230 | } 231 | --- request 232 | GET /t 233 | --- response_body 234 | true 235 | true 236 | false 237 | false 238 | --- grep_error_log eval: qr/try to set key: \w+, the \d+th time/ 239 | --- grep_error_log_out 240 | try to set key: a, the 1th time 241 | try to set key: b, the 1th time 242 | try to set key: b, the 2th time 243 | try to set key: b, the 3th time 244 | try to set key: b, the 4th time 245 | try to set key: b, the 5th time 246 | try to set key: b, the 6th time 247 | try to set key: b, the 7th time 248 | try to set key: b, the 8th time 249 | try to set key: b, the 9th time 250 | try to set key: b, the 10th time 251 | --------------------------------------------------------------------------------