diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index 4eb638eb..6762317c 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -14,6 +14,15 @@ static int ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, static ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp); +static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags); +static int ngx_http_lua_shdict_add(lua_State *L); +static int ngx_http_lua_shdict_replace(lua_State *L); +static int ngx_http_lua_shdict_incr(lua_State *L); +static int ngx_http_lua_shdict_delete(lua_State *L); + + +#define NGX_HTTP_LUA_SHDICT_ADD 0x0001 +#define NGX_HTTP_LUA_SHDICT_REPLACE 0x0002 ngx_int_t @@ -199,7 +208,7 @@ ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n) * and one or two zero rate entries */ - while (n < 5) { + while (n < 3) { if (ngx_queue_empty(&ctx->sh->queue)) { return freed; @@ -252,8 +261,22 @@ ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L) lua_pushcfunction(L, ngx_http_lua_shdict_get); lua_setfield(L, -2, "get"); + lua_pushcfunction(L, ngx_http_lua_shdict_set); lua_setfield(L, -2, "set"); + + lua_pushcfunction(L, ngx_http_lua_shdict_add); + lua_setfield(L, -2, "add"); + + lua_pushcfunction(L, ngx_http_lua_shdict_replace); + lua_setfield(L, -2, "replace"); + + lua_pushcfunction(L, ngx_http_lua_shdict_incr); + lua_setfield(L, -2, "incr"); + + lua_pushcfunction(L, ngx_http_lua_shdict_delete); + lua_setfield(L, -2, "delete"); + lua_pushvalue(L, -1); /* shared mt mt */ lua_setfield(L, -2, "__index"); /* shared mt */ @@ -341,6 +364,8 @@ ngx_http_lua_shdict_get(lua_State *L) rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + dd("shdict lookup returns %d", (int) rc); + if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); @@ -410,8 +435,47 @@ ngx_http_lua_shdict_get(lua_State *L) } +static int +ngx_http_lua_shdict_delete(lua_State *L) +{ + int n; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, " + "but only seen %d", n); + } + + lua_pushnil(L); + + return ngx_http_lua_shdict_set_helper(L, 0); +} + + +static int +ngx_http_lua_shdict_add(lua_State *L) +{ + return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD); +} + + +static int +ngx_http_lua_shdict_replace(lua_State *L) +{ + return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_REPLACE); +} + + static int ngx_http_lua_shdict_set(lua_State *L) +{ + return ngx_http_lua_shdict_set_helper(L, 0); +} + + +static int +ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { ngx_http_request_t *r; int i, n; @@ -430,8 +494,9 @@ ngx_http_lua_shdict_set(lua_State *L) ngx_rbtree_node_t *node; ngx_time_t *tp; ngx_shm_zone_t *zone; - int override = 0; - /* indicates whether to override other valid entries */ + int forcible = 0; + /* indicates whether to foricibly override other + * valid entries */ n = lua_gettop(L); @@ -458,8 +523,7 @@ ngx_http_lua_shdict_set(lua_State *L) key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { - lua_pushnil(L); - return 1; + return luaL_error(L, "attemp to use empty keys"); } if (key.len > 65535) { @@ -490,6 +554,10 @@ ngx_http_lua_shdict_set(lua_State *L) break; case LUA_TNIL: + if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { + return luaL_error(L, "attempt to add or replace nil values"); + } + value.len = 0; value.data = NULL; break; @@ -518,12 +586,55 @@ ngx_http_lua_shdict_set(lua_State *L) rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + dd("shdict lookup returned %d", (int) rc); + + if (flags & NGX_HTTP_LUA_SHDICT_REPLACE) { + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "not found"); + lua_pushboolean(L, forcible); + return 3; + } + + /* rc == NGX_OK */ + + goto replace; + } + + if (flags & NGX_HTTP_LUA_SHDICT_ADD) { + + if (rc == NGX_OK) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "exists"); + lua_pushboolean(L, forcible); + return 3; + } + + if (rc == NGX_DONE) { + /* exists but expired */ + + dd("go to replace"); + goto replace; + } + + /* rc == NGX_DECLINED */ + + dd("go to insert"); + goto insert; + } + if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } +replace: if (value.len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -555,8 +666,10 @@ ngx_http_lua_shdict_set(lua_State *L) ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_pushboolean(L, override); - return 1; + lua_pushboolean(L, 1); + lua_pushnil(L); + lua_pushboolean(L, forcible); + return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -575,12 +688,16 @@ remove: } +insert: /* rc == NGX_DECLINED or value size unmatch */ if (value_type == LUA_TNIL) { ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 1); + lua_pushnil(L); lua_pushboolean(L, 0); - return 1; + return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -598,13 +715,13 @@ remove: "lua shared dict set: overriding non-expired items due to memory " "shortage for entry \"%V\"", &name); - override = 1; - for (i = 0; i < 30; i++) { if (ngx_http_lua_shdict_expire(ctx, 0) == 0) { break; } + forcible = 1; + node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; @@ -613,9 +730,10 @@ remove: ngx_shmtx_unlock(&ctx->shpool->mutex); - return luaL_error(L, "failed to allocate memory for " - "shared_dict %s (maybe the total capacity is too " - "small?)", name.data); + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + lua_pushboolean(L, forcible); + return 3; } allocated: @@ -648,7 +766,121 @@ allocated: ngx_shmtx_unlock(&ctx->shpool->mutex); - lua_pushboolean(L, override); - return 1; + lua_pushboolean(L, 1); + lua_pushnil(L); + lua_pushboolean(L, forcible); + return 3; +} + + +static int +ngx_http_lua_shdict_incr(lua_State *L) +{ + ngx_http_request_t *r; + int n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + lua_Number num; + u_char *p; + ngx_shm_zone_t *zone; + lua_Number value; + + n = lua_gettop(L); + + if (n != 3) { + return luaL_error(L, "expecting 3 arguments, " + "but only seen %d", n); + } + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + + zone = lua_touserdata(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + ctx = zone->data; + + name = ctx->name; + + lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + return luaL_error(L, "attemp to use empty keys"); + } + + if (key.len > 65535) { + return luaL_error(L, + "the key argument is more than 65535 bytes: %d", + (int) key.len); + } + + hash = ngx_crc32_short(key.data, key.len); + + value = luaL_checknumber(L, 3); + + if (value == 0) { + lua_pushboolean(L, 1); + lua_pushnil(L); + return 2; + } + + dd("looking up key %s in shared dict %s", key.data, name.data); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, + &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "not found"); + return 2; + } + + /* rc == NGX_OK */ + + if (sd->value_type != LUA_TNUMBER || sd->value_len != sizeof(lua_Number)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "not a number"); + return 2; + } + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); + + dd("setting value type to %d", value_type); + + p = sd->data + key.len; + + num = *(lua_Number *) p; + + num += value; + + ngx_memcpy(p, (lua_Number *) &num, sizeof(lua_Number)); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 1); + lua_pushnil(L); + return 2; } diff --git a/t/043-shdict.t b/t/043-shdict.t index 2af3ce95..8bd4a335 100644 --- a/t/043-shdict.t +++ b/t/043-shdict.t @@ -107,9 +107,9 @@ false boolean --- request GET /test --- response_body -false -false -false +truenilfalse +truenilfalse +truenilfalse dog dog bird string @@ -140,7 +140,7 @@ hello -=== TEST 6: expired entries +=== TEST 6: expired entries (can be auto-removed by get) --- http_config lua_shared_dict dogs 1m; --- config @@ -162,7 +162,31 @@ nil -=== TEST 7: not yet expired entries +=== TEST 7: expired entries (can NOT be auto-removed by get) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bar", 56, 0.001) + dogs:set("baz", 78, 0.001) + dogs:set("foo", 32, 0.01) + ngx.location.capture("/sleep/0.01") + ngx.say(dogs:get("foo")) + '; + } + location ~ '^/sleep/(.+)' { + echo_sleep $1; + } +--- request +GET /test +--- response_body +nil + + + +=== TEST 8: not yet expired entries --- http_config lua_shared_dict dogs 1m; --- config @@ -184,7 +208,7 @@ GET /test -=== TEST 8: forcibly override other valid entries +=== TEST 9: forcibly override other valid entries --- http_config lua_shared_dict dogs 100k; --- config @@ -195,8 +219,9 @@ GET /test while i < 1000 do i = i + 1 local val = string.rep(" hello", 10) .. i - local override = dogs:set("key_" .. i, val) - if override then + local res, err, forcible = dogs:set("key_" .. i, val) + if not res or forcible then + ngx.say(res, " ", err, " ", forcible) break end end @@ -213,14 +238,14 @@ GET /test --- pipelined_requests eval ["GET /test", "GET /test"] --- response_body eval -my $a = "abort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: nil\n2nd value: " . (" hello" x 10) . "2\n"; +my $a = "true nil true\nabort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: nil\n2nd value: " . (" hello" x 10) . "2\n"; [qr/$a/, -"abort at 1\ncur value: " . (" hello" x 10) . "1\n" +"true nil true\nabort at 1\ncur value: " . (" hello" x 10) . "1\n" ] -=== TEST 9: forcibly override other valid entries and test LRU +=== TEST 10: forcibly override other valid entries and test LRU --- http_config lua_shared_dict dogs 100k; --- config @@ -234,8 +259,9 @@ my $a = "abort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: n if i == 10 then dogs:get("key_1") end - local override = dogs:set("key_" .. i, val) - if override then + local res, err, forcible = dogs:set("key_" .. i, val) + if not res or forcible then + ngx.say(res, " ", err, " ", forcible) break end end @@ -252,14 +278,14 @@ my $a = "abort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: n --- pipelined_requests eval ["GET /test", "GET /test"] --- response_body eval -my $a = "abort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: " . (" hello" x 10) . "1\n2nd value: nil\n"; +my $a = "true nil true\nabort at (353|705)\ncur value: " . (" hello" x 10) . "\\1\n1st value: " . (" hello" x 10) . "1\n2nd value: nil\n"; [qr/$a/, -"abort at 2\ncur value: " . (" hello" x 10) . "2\n1st value: " . (" hello" x 10) . "1\n" +"true nil true\nabort at 2\ncur value: " . (" hello" x 10) . "2\n1st value: " . (" hello" x 10) . "1\n" ] -=== TEST 10: dogs and cats dicts +=== TEST 11: dogs and cats dicts --- http_config lua_shared_dict dogs 1m; lua_shared_dict cats 1m; @@ -287,7 +313,7 @@ hello, world -=== TEST 11: get non-existent keys +=== TEST 12: get non-existent keys --- http_config lua_shared_dict dogs 1m; --- config @@ -306,7 +332,7 @@ nil -=== TEST 12: not feed the object into the call +=== TEST 13: not feed the object into the call --- http_config lua_shared_dict dogs 1m; --- config @@ -330,25 +356,25 @@ false expecting exactly two arguments, but only seen 1 -=== TEST 13: too big value +=== TEST 14: too big value --- http_config lua_shared_dict dogs 50k; --- config location = /test { content_by_lua ' local dogs = ngx.shared.dogs - local rc, err = pcall(dogs.set, dogs, "foo", string.rep("helloworld", 10000)) - ngx.say(rc, " ", err) + local res, err, forcible = dogs:set("foo", string.rep("helloworld", 10000)) + ngx.say(res, " ", err, " ", forcible) '; } --- request GET /test --- response_body -false failed to allocate memory for shared_dict dogs (maybe the total capacity is too small?) +false no memory false -=== TEST 14: too big key +=== TEST 15: too big key --- http_config lua_shared_dict dogs 1m; --- config @@ -356,7 +382,7 @@ false failed to allocate memory for shared_dict dogs (maybe the total capacity i content_by_lua ' local dogs = ngx.shared.dogs local key = string.rep("a", 65535) - local rc, err = pcall(dogs.set, dogs, key, "hello") + local rc, err = dogs:set(key, "hello") ngx.say(rc, " ", err) ngx.say(dogs:get(key)) @@ -369,13 +395,13 @@ false failed to allocate memory for shared_dict dogs (maybe the total capacity i --- request GET /test --- response_body -true false +true nil hello false the key argument is more than 65535 bytes: 65536 -=== TEST 15: bad value type +=== TEST 16: bad value type --- http_config lua_shared_dict dogs 1m; --- config @@ -393,7 +419,52 @@ false unsupported value type for key "foo" in shared_dict "dogs": userdata -=== TEST 16: set nil after setting values +=== TEST 17: delete after setting values +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + ngx.say(dogs:get("foo")) + dogs:delete("foo") + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +32 +nil +hello, world + + + +=== TEST 18: delete at first +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:delete("foo") + ngx.say(dogs:get("foo")) + dogs:set("foo", "hello, world") + ngx.say(dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +nil +hello, world + + + +=== TEST 19: set nil after setting values --- http_config lua_shared_dict dogs 1m; --- config @@ -417,7 +488,7 @@ hello, world -=== TEST 17: set nil at first +=== TEST 20: set nil at first --- http_config lua_shared_dict dogs 1m; --- config @@ -438,7 +509,7 @@ hello, world -=== TEST 18: set nil at first +=== TEST 21: fail to allocate memory --- http_config lua_shared_dict dogs 100k; --- config @@ -449,8 +520,9 @@ hello, world while i < 1000 do i = i + 1 local val = string.rep("hello", i ) - local override = dogs:set("key_" .. i, val) - if override then + local res, err, forcible = dogs:set("key_" .. i, val) + if not res or forcible then + ngx.say(res, " ", err, " ", forcible) break end end @@ -460,11 +532,11 @@ hello, world --- request GET /test --- response_body_like -^abort at (?:139|142)$ +^true nil true\nabort at (?:139|142)$ -=== TEST 19: string key, int value (write_by_lua) +=== TEST 22: string key, int value (write_by_lua) --- http_config lua_shared_dict dogs 1m; --- config @@ -488,7 +560,7 @@ GET /test -=== TEST 20: string key, int value (access_by_lua) +=== TEST 23: string key, int value (access_by_lua) --- http_config lua_shared_dict dogs 1m; --- config @@ -512,7 +584,7 @@ GET /test -=== TEST 21: string key, int value (set_by_lua) +=== TEST 24: string key, int value (set_by_lua) --- http_config lua_shared_dict dogs 1m; --- config @@ -531,7 +603,7 @@ GET /test -=== TEST 21: string key, int value (header_by_lua) +=== TEST 25: string key, int value (header_by_lua) --- http_config lua_shared_dict dogs 1m; --- config @@ -550,3 +622,374 @@ X-Foo: 32 --- response_body hello + + +=== TEST 26: too big value (forcible) +--- http_config + lua_shared_dict dogs 50k; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bah", "hello") + local res, err, forcible = dogs:set("foo", string.rep("helloworld", 10000)) + ngx.say(res, " ", err, " ", forcible) + '; + } +--- request +GET /test +--- response_body +false no memory true + + + +=== TEST 27: add key (key exists) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err, forcible = dogs:add("foo", 10502) + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +add: false exists false +foo = 32 + + + +=== TEST 28: add key (key not exists) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bah", 32) + local res, err, forcible = dogs:add("foo", 10502) + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +add: true nil false +foo = 10502 + + + +=== TEST 29: add key (key expired) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bar", 32, 0.001) + dogs:set("baz", 32, 0.001) + dogs:set("foo", 32, 0.001) + ngx.location.capture("/sleep/0.002") + local res, err, forcible = dogs:add("foo", 10502) + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } + location ~ ^/sleep/(.+) { + echo_sleep $1; + } +--- request +GET /test +--- response_body +add: true nil false +foo = 10502 + + + +=== TEST 30: add key (key expired and value size unmatched) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bar", 32, 0.001) + dogs:set("baz", 32, 0.001) + dogs:set("foo", "hi", 0.001) + ngx.location.capture("/sleep/0.002") + local res, err, forcible = dogs:add("foo", "hello") + ngx.say("add: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } + location ~ ^/sleep/(.+) { + echo_sleep $1; + } +--- request +GET /test +--- response_body +add: true nil false +foo = hello + + + +=== TEST 31: incr key (key exists) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + + local res, err, forcible = dogs:replace("foo", "hello") + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + + '; + } +--- request +GET /test +--- response_body +replace: true nil false +foo = 10502 +replace: true nil false +foo = hello + + + +=== TEST 32: replace key (key not exists) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bah", 32) + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: false not found false +foo = nil + + + +=== TEST 33: replace key (key expired) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bar", 3, 0.001) + dogs:set("baz", 2, 0.001) + dogs:set("foo", 32, 0.001) + ngx.location.capture("/sleep/0.002") + local res, err, forcible = dogs:replace("foo", 10502) + ngx.say("replace: ", res, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } + location ~ ^/sleep/(.+) { + echo_sleep $1; + } +--- request +GET /test +--- response_body +replace: false not found false +foo = nil + + + +=== TEST 34: replace key (key expired and value size unmatched) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bar", 32, 0.001) + dogs:set("baz", 32, 0.001) + dogs:set("foo", "hi", 0.001) + ngx.location.capture("/sleep/0.002") + local rc, err, forcible = dogs:replace("foo", "hello") + ngx.say("replace: ", rc, " ", err, " ", forcible) + ngx.say("foo = ", dogs:get("foo")) + '; + } + location ~ ^/sleep/(.+) { + echo_sleep $1; + } +--- request +GET /test +--- response_body +replace: false not found false +foo = nil + + + +=== TEST 35: incr key (key exists) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", 10502) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: true nil +foo = 10534 + + + +=== TEST 36: replace key (key not exists) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bah", 32) + local res, err = dogs:incr("foo", 2) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: false not found +foo = nil + + + +=== TEST 37: replace key (key expired) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("bar", 3, 0.001) + dogs:set("baz", 2, 0.001) + dogs:set("foo", 32, 0.001) + ngx.location.capture("/sleep/0.002") + local res, err = dogs:incr("foo", 10502) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } + location ~ ^/sleep/(.+) { + echo_sleep $1; + } +--- request +GET /test +--- response_body +replace: false not found +foo = nil + + + +=== TEST 38: incr key (incr by 0) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", 0) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: true nil +foo = 32 + + + +=== TEST 39: incr key (incr by floating point number) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", 0.14) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: true nil +foo = 32.14 + + + +=== TEST 40: incr key (incr by negative numbers) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local res, err = dogs:incr("foo", -0.14) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: true nil +foo = 31.86 + + + +=== TEST 41: incr key (original value is not number) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua ' + local dogs = ngx.shared.dogs + dogs:set("foo", true) + local res, err = dogs:incr("foo", -0.14) + ngx.say("replace: ", res, " ", err) + ngx.say("foo = ", dogs:get("foo")) + '; + } +--- request +GET /test +--- response_body +replace: false not a number +foo = true +