Skip to content

Shdict cas #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2903,6 +2903,7 @@ Nginx API for Lua
* [ngx.shared.DICT.add](#ngxshareddictadd)
* [ngx.shared.DICT.safe_add](#ngxshareddictsafe_add)
* [ngx.shared.DICT.replace](#ngxshareddictreplace)
* [ngx.shared.DICT.cas](#ngxshareddictcas)
* [ngx.shared.DICT.delete](#ngxshareddictdelete)
* [ngx.shared.DICT.incr](#ngxshareddictincr)
* [ngx.shared.DICT.flush_all](#ngxshareddictflush_all)
Expand Down Expand Up @@ -5892,6 +5893,7 @@ The resulting object `dict` has the following methods:
* [add](#ngxshareddictadd)
* [safe_add](#ngxshareddictsafe_add)
* [replace](#ngxshareddictreplace)
* [cas](#ngxshareddictcas)
* [delete](#ngxshareddictdelete)
* [incr](#ngxshareddictincr)
* [flush_all](#ngxshareddictflush_all)
Expand Down Expand Up @@ -6110,6 +6112,62 @@ See also [ngx.shared.DICT](#ngxshareddict).

[Back to TOC](#nginx-api-for-lua)

ngx.shared.DICT.cas
-------------------
**syntax:** *success, err, forcible, current_value?, current_flags? = ngx.shared.DICT:cas(key, value, exptime, flags, old_value, old_flags?)*

**context:** *init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, certificate_by_lua**

Just like the [replace](#ngxshareddictreplace) method, but only stores the key-value pair into the dictionary [ngx.shared.DICT](#ngxshareddict) if and only if the `old_value` argument and `old_flags` argument *do* match the value and flags in the dictionary [ngx.shared.DICT](#ngxshareddict).

The `old_value` argument can be `nil` only when `old_flags` argument is specified, in which case only `flags` will be checked.

If `old_flags` argument is not specified, only `value` will be checked.

The optional `old_flags` argument can be `nil`, and it means `0`.

If they do *not* match, the `success` return value will be `false` and the `err` return value will be `"not matched"`. The `current_value` return value and `current_flags` return value will be the current `value` and current `flags` in the dictionary [ngx.shared.DICT](#ngxshareddict), just like [get](#ngxshareddictget) does.

This function is often used to avoid race condition between [get](#ngxshareddictget) and [set](#ngxshareddictset) across multipe nginx worker processes, and below is an example:

```lua

local cats = ngx.shared.cats
cats:set("foo", 1, 1)

local old_value, old_flags = cats:get("foo")

while true do
local newvalue = calculate(old_value) -- some logic
local newflags = (old_flags or 0) + 1

local success, err, forcibly, current_value, current_flags
= cats:cas("foo", newvalue, 0, newflags, old_value, old_flags)

if success then
break

elseif err == "not matched" then
old_value = current_value
old_flags = current_flags

elseif err == "not found" then
-- add or some other handle
cats:add("foo", newvalue, 0, newflags)
break

else
-- "no memory" or some other error
-- just log or some other handle
break
end
end
```

See also [ngx.shared.DICT](#ngxshareddict).

[Back to TOC](#nginx-api-for-lua)

ngx.shared.DICT.delete
----------------------
**syntax:** *ngx.shared.DICT:delete(key)*
Expand Down
53 changes: 53 additions & 0 deletions doc/HttpLuaModule.wiki
Original file line number Diff line number Diff line change
Expand Up @@ -4930,6 +4930,7 @@ The resulting object <code>dict</code> has the following methods:
* [[#ngx.shared.DICT.add|add]]
* [[#ngx.shared.DICT.safe_add|safe_add]]
* [[#ngx.shared.DICT.replace|replace]]
* [[#ngx.shared.DICT.cas|cas]]
* [[#ngx.shared.DICT.delete|delete]]
* [[#ngx.shared.DICT.incr|incr]]
* [[#ngx.shared.DICT.flush_all|flush_all]]
Expand Down Expand Up @@ -5119,6 +5120,58 @@ This feature was first introduced in the <code>v0.3.1rc22</code> release.

See also [[#ngx.shared.DICT|ngx.shared.DICT]].

== ngx.shared.DICT.cas ==
'''syntax:''' ''success, err, forcible, current_value?, current_flags? = ngx.shared.DICT:cas(key, value, exptime, flags, old_value, old_flags?)''

'''context:''' ''init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, certificate_by_lua*''

Just like the [[#ngx.shared.DICT.replace|replace]] method, but only stores the key-value pair into the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]] if and only if the <code>old_value</code> argument and <code>old_flags</code> argument ''do'' match the value and flags in the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]].

The <code>old_value</code> argument can be <code>nil</code> only when <code>old_flags</code> argument is specified, in which case only <code>flags</code> will be checked.

If <code>old_flags</code> argument is not specified, only <code>value</code> will be checked.

The optional <code>old_flags</code> argument can be <code>nil</code>, and it means <code>0</code>.

If they do ''not'' match, the <code>success</code> return value will be <code>false</code> and the <code>err</code> return value will be <code>"not matched"</code>. The <code>current_value</code> return value and <code>current_flags</code> return value will be the current <code>value</code> and current <code>flags</code> in the dictionary [[#ngx.shared.DICT|ngx.shared.DICT]], just like [[#ngx.shared.DICT.get|get]] does.

This function is often used to avoid race condition between [[#ngx.shared.DICT.get|get]] and [[#ngx.shared.DICT.set|set]] across multipe nginx worker processes, and below is an example:

<geshi lang="lua">
local cats = ngx.shared.cats
cats:set("foo", 1, 1)

local old_value, old_flags = cats:get("foo")

while true do
local newvalue = calculate(old_value) -- some logic
local newflags = (old_flags or 0) + 1

local success, err, forcibly, current_value, current_flags
= cats:cas("foo", newvalue, 0, newflags, old_value, old_flags)

if success then
break

elseif err == "not matched" then
old_value = current_value
old_flags = current_flags

elseif err == "not found" then
-- add or some other handle
cats:add("foo", newvalue, 0, newflags)
break

else
-- "no memory" or some other error
-- just log or some other handle
break
end
end
</geshi>

See also [[#ngx.shared.DICT|ngx.shared.DICT]].

== ngx.shared.DICT.delete ==
'''syntax:''' ''ngx.shared.DICT:delete(key)''

Expand Down
171 changes: 167 additions & 4 deletions src/ngx_http_lua_shdict.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ 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_safe_add(lua_State *L);
static int ngx_http_lua_shdict_replace(lua_State *L);
static int ngx_http_lua_shdict_cas(lua_State *L);
static int ngx_http_lua_shdict_incr(lua_State *L);
static int ngx_http_lua_shdict_delete(lua_State *L);
static int ngx_http_lua_shdict_flush_all(lua_State *L);
Expand All @@ -43,6 +44,7 @@ static ngx_inline ngx_shm_zone_t *ngx_http_lua_shdict_get_zone(lua_State *L,
#define NGX_HTTP_LUA_SHDICT_ADD 0x0001
#define NGX_HTTP_LUA_SHDICT_REPLACE 0x0002
#define NGX_HTTP_LUA_SHDICT_SAFE_STORE 0x0004
#define NGX_HTTP_LUA_SHDICT_CHECK 0x0008


enum {
Expand Down Expand Up @@ -339,6 +341,9 @@ ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L)
lua_pushcfunction(L, ngx_http_lua_shdict_replace);
lua_setfield(L, -2, "replace");

lua_pushcfunction(L, ngx_http_lua_shdict_cas);
lua_setfield(L, -2, "cas");

lua_pushcfunction(L, ngx_http_lua_shdict_incr);
lua_setfield(L, -2, "incr");

Expand Down Expand Up @@ -834,6 +839,14 @@ ngx_http_lua_shdict_replace(lua_State *L)
}


static int
ngx_http_lua_shdict_cas(lua_State *L)
{
return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_REPLACE
|NGX_HTTP_LUA_SHDICT_CHECK);
}


static int
ngx_http_lua_shdict_set(lua_State *L)
{
Expand All @@ -852,6 +865,7 @@ static int
ngx_http_lua_shdict_set_helper(lua_State *L, int flags)
{
int i, n;
ngx_str_t name;
ngx_str_t key;
uint32_t hash;
ngx_int_t rc;
Expand All @@ -869,11 +883,28 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags)
int forcible = 0;
/* indicates whether to foricibly override other
* valid entries */
int32_t user_flags = 0;
uint32_t user_flags = 0;
ngx_str_t old_value;
double old_num;
u_char old_c;
int old_value_type;
uint32_t old_user_flags = 0;

n = lua_gettop(L);

if (n != 3 && n != 4 && n != 5) {
if (flags & NGX_HTTP_LUA_SHDICT_CHECK) {
if (n != 6 && n != 7) {
return luaL_error(L, "expecting 6 or 7 arguments, "
"but only seen %d", n);
}

if (n == 6 && lua_type(L, 6) == LUA_TNIL) {
lua_pushnil(L);
lua_pushliteral(L, "old_value is nil and no old_flags");
return 2;
}

} else if (n != 3 && n != 4 && n != 5) {
return luaL_error(L, "expecting 3, 4 or 5 arguments, "
"but only seen %d", n);
}
Expand Down Expand Up @@ -931,7 +962,9 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags)
break;

case LUA_TNIL:
if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) {
if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)
&& ! (flags & NGX_HTTP_LUA_SHDICT_CHECK))
{
lua_pushnil(L);
lua_pushliteral(L, "attempt to add or replace nil values");
return 2;
Expand All @@ -953,7 +986,7 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags)
}
}

if (n == 5) {
if (n >= 5) {
user_flags = (uint32_t) luaL_checkinteger(L, 5);
}

Expand All @@ -980,6 +1013,62 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags)

/* rc == NGX_OK */

if (flags & NGX_HTTP_LUA_SHDICT_CHECK) {
/* check current value flags */
old_value_type = lua_type(L, 6);

if (old_value_type != LUA_TNIL) {

if (sd->value_type != old_value_type) {
dd("value type check failed");
goto check_failed;
}

switch (old_value_type) {
case LUA_TSTRING:
old_value.data = (u_char *) lua_tolstring(L, 6,
&old_value.len);
break;

case LUA_TNUMBER:
old_value.len = sizeof(double);
old_num = lua_tonumber(L, 6);
old_value.data = (u_char *) &old_num;
break;

case LUA_TBOOLEAN:
old_value.len = sizeof(u_char);
old_c = lua_toboolean(L, 6) ? 1 : 0;
old_value.data = &old_c;
break;
}

if (old_value.len != sd->value_len) {
dd("value len check failed");
goto check_failed;
}

if (ngx_memn2cmp(old_value.data, sd->data + sd->key_len,
old_value.len, sd->value_len) != 0)
{
dd("value data check failed");
goto check_failed;
}
}

if (n == 7) {

if (lua_type(L, 7) != LUA_TNIL) {
old_user_flags = (uint32_t) luaL_checkinteger(L, 7);
}

if (sd->user_flags != old_user_flags) {
dd("user_flags check failed");
goto check_failed;
}
}
}

goto replace;
}

Expand Down Expand Up @@ -1166,6 +1255,80 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags)
lua_pushnil(L);
lua_pushboolean(L, forcible);
return 3;

check_failed:

name = ctx->name;

lua_pushboolean(L, 0);
lua_pushliteral(L, "not matched");
lua_pushboolean(L, forcible);

value_type = sd->value_type;

dd("data: %p", sd->data);
dd("key len: %d", (int) sd->key_len);

value.data = sd->data + sd->key_len;
value.len = (size_t) sd->value_len;

switch (value_type) {
case LUA_TSTRING:

lua_pushlstring(L, (char *) value.data, value.len);
break;

case LUA_TNUMBER:

if (value.len != sizeof(double)) {

ngx_shmtx_unlock(&ctx->shpool->mutex);

return luaL_error(L, "bad lua number value size found for key %s "
"in shared_dict %s: %lu", key.data, name.data,
(unsigned long) value.len);
}

ngx_memcpy(&num, value.data, sizeof(double));

lua_pushnumber(L, num);
break;

case LUA_TBOOLEAN:

if (value.len != sizeof(u_char)) {

ngx_shmtx_unlock(&ctx->shpool->mutex);

return luaL_error(L, "bad lua boolean value size found for key %s "
"in shared_dict %s: %lu", key.data, name.data,
(unsigned long) value.len);
}

c = *value.data;

lua_pushboolean(L, c ? 1 : 0);
break;

default:

ngx_shmtx_unlock(&ctx->shpool->mutex);

return luaL_error(L, "bad value type found for key %s in "
"shared_dict %s: %d", key.data, name.data,
value_type);
}

user_flags = sd->user_flags;

ngx_shmtx_unlock(&ctx->shpool->mutex);

if (user_flags) {
lua_pushinteger(L, (lua_Integer) user_flags);
return 5;
}

return 4;
}


Expand Down
Loading