diff --git a/.travis.yml b/.travis.yml index ace02f2b..a31a47b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,12 +59,12 @@ install: - git clone https://github.com/openresty/mockeagain.git - git clone https://github.com/openresty/test-nginx.git - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git - - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module + - git clone -b atomic_shm https://github.com/webcore-no/lua-nginx-module.git ../lua-nginx-module - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module - git clone https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module - git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core + - git clone -b atomic_shm https://github.com/webcore-no/lua-resty-core.git ../lua-resty-core script: - sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT diff --git a/src/ngx_stream_lua_shdict.c b/src/ngx_stream_lua_shdict.c index 4779ce8d..4f222dd3 100644 --- a/src/ngx_stream_lua_shdict.c +++ b/src/ngx_stream_lua_shdict.c @@ -319,6 +319,37 @@ ngx_stream_lua_shdict_expire(ngx_stream_lua_shdict_ctx_t *ctx, ngx_uint_t n) } +static inline ngx_rbtree_node_t * +ngx_stream_lua_shdict_force_alloc(ngx_stream_lua_shdict_ctx_t *ctx, size_t size, + int *forcible) +{ + int i; + ngx_rbtree_node_t *node; + + node = ngx_slab_alloc_locked(ctx->shpool, size); + *forcible = 0; + + if (node) { + return node; + } + + for (i = 0; i < 30; i++) { + if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) { + break; + } + + *forcible = 1; + + node = ngx_slab_alloc_locked(ctx->shpool, size); + if (node) { + return node; + } + } + + return NULL; +} + + void ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, lua_State *L) { @@ -1227,13 +1258,463 @@ ngx_stream_lua_ffi_shdict_udata_to_zone(void *zone_udata) } +int +ngx_stream_lua_ffi_shdict_cas(ngx_shm_zone_t *zone, u_char *key, + size_t key_len, int old_value_type, u_char *old_str_value_buf, + size_t old_str_value_len, double old_num_value, int old_user_flags, + int value_type, u_char *str_value_buf, size_t str_value_len, + double num_value, int user_flags, int set_user_flags, + long exptime, int match_flags, int *match, char **errmsg, int *forcible) +{ + int n, match_value; + u_char c, oc, *p; + uint32_t hash; + ngx_int_t rc; + ngx_time_t *tp; + ngx_queue_t *queue, *q; + ngx_rbtree_node_t *node; + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + + dd("exptime: %ld", exptime); + + ctx = zone->data; + + *match = 0; + *forcible = 0; + *errmsg = NULL; + + hash = ngx_crc32_short(key, key_len); + + switch (value_type) { + + case SHDICT_TSTRING: + /* do nothing */ + break; + + case SHDICT_TNUMBER: + dd("num value: %lf", num_value); + str_value_buf = (u_char *) &num_value; + str_value_len = sizeof(double); + break; + + case SHDICT_TBOOLEAN: + c = num_value ? 1 : 0; + str_value_buf = &c; + str_value_len = sizeof(u_char); + break; + + case LUA_TNIL: + str_value_buf = NULL; + str_value_len = 0; + break; + + default: + *errmsg = "unsupported value type"; + return NGX_ERROR; + } + + switch (old_value_type) { + + case SHDICT_TSTRING: + /* do nothing */ + match_value = 1; + break; + + case SHDICT_TNUMBER: + dd("num value: %lf", old_num_value); + old_str_value_buf = (u_char *) &old_num_value; + old_str_value_len = sizeof(double); + match_value = 1; + break; + + case SHDICT_TBOOLEAN: + oc = old_num_value ? 1 : 0; + old_str_value_buf = &oc; + old_str_value_len = sizeof(u_char); + match_value = 1; + break; + + case LUA_TNIL: + match_value = 0; + break; + + default: + *errmsg = "unsupported old_value type"; + return NGX_ERROR; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd); + + dd("lookup returns %d", (int) rc); + + + if (match_flags + && (rc != NGX_OK || old_user_flags != (int) sd->user_flags)) + { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *match = 1; + return NGX_DECLINED; + } + + if (match_value + && (rc != NGX_OK + || old_value_type != sd->value_type + || old_str_value_len != sd->value_len + || ngx_memcmp(old_str_value_buf, sd->data + key_len, + (size_t) old_str_value_len) + )) + { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *match = 1; + return NGX_DECLINED; + } + + if (rc == NGX_OK || rc == NGX_DONE) { + if (value_type == LUA_TNIL && !set_user_flags) { + goto remove; + } + + if (set_user_flags && value_type == LUA_TNIL) { + /* Only set flag, leave value unchanged */ + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) exptime; + + } else { + sd->expires = 0; + } + + sd->user_flags = user_flags; + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; + } + + if (str_value_buf + && str_value_len == (size_t) sd->value_len + && sd->value_type != SHDICT_TLIST) + { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict set: found old entry and value " + "size matched, reusing it"); + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) exptime; + + } else { + sd->expires = 0; + } + + if (set_user_flags) { + sd->user_flags = user_flags; + } + + dd("setting value type to %d", value_type); + + sd->value_type = (uint8_t) value_type; + + ngx_memcpy(sd->data + key_len, str_value_buf, str_value_len); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict set: found old entry but value size " + "NOT matched, removing it first"); + if (!set_user_flags) { + user_flags = sd->user_flags; + } + +remove: + + if (sd->value_type == SHDICT_TLIST) { + queue = ngx_stream_lua_shdict_get_list_head(sd, key_len); + + for (q = ngx_queue_head(queue); + q != ngx_queue_sentinel(queue); + q = ngx_queue_next(q)) + { + p = (u_char *) ngx_queue_data(q, + ngx_stream_lua_shdict_list_node_t, + queue); + + ngx_slab_free_locked(ctx->shpool, p); + } + } + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + } + + /* rc == NGX_DECLINED or value size unmatch */ + + if (str_value_buf == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict cas: creating a new entry"); + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_stream_lua_shdict_node_t, data) + + key_len + + str_value_len; + + node = ngx_stream_lua_shdict_force_alloc(ctx, n, forcible); + if (*forcible == 1) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict cas: overwrote non-expired items " + "due to memory shortage for entry \"%*s\"", key_len, + key); + } + + if (!node) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *errmsg = "no memory"; + return NGX_ERROR; + } + + sd = (ngx_stream_lua_shdict_node_t *) &node->color; + + node->key = hash; + sd->key_len = (u_short) key_len; + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) exptime; + + } else { + sd->expires = 0; + } + + sd->user_flags = user_flags; + sd->value_len = (uint32_t) str_value_len; + dd("setting value type to %d", value_type); + sd->value_type = (uint8_t) value_type; + + p = ngx_copy(sd->data, key, key_len); + ngx_memcpy(p, str_value_buf, str_value_len); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_shdict_get_if_not_eq(ngx_shm_zone_t *zone, + u_char *key, size_t key_len, int old_value_type, + u_char *old_str_value_buf, size_t old_str_value_len, + double old_num_value, int old_user_flags, int *value_type, + u_char **str_value_buf, size_t *str_value_len, double *num_value, + int *user_flags, int match_flags, int *unmatch, char **errmsg) +{ + u_char oc; + ngx_str_t name; + uint32_t hash; + ngx_int_t rc; + ngx_stream_lua_shdict_ctx_t *ctx; + ngx_stream_lua_shdict_node_t *sd; + ngx_str_t value; + int match_value; + + *errmsg = NULL; + *unmatch = 0; + + ctx = zone->data; + name = ctx->name; + + hash = ngx_crc32_short(key, key_len); + + switch (old_value_type) { + case SHDICT_TSTRING: + /* do nothing */ + match_value = 1; + break; + + case SHDICT_TNUMBER: + dd("num value: %lf", old_num_value); + old_str_value_buf = (u_char *) &old_num_value; + old_str_value_len = sizeof(double); + match_value = 1; + break; + + case SHDICT_TBOOLEAN: + oc = old_num_value ? 1 : 0; + old_str_value_buf = &oc; + old_str_value_len = sizeof(u_char); + match_value = 1; + break; + + case LUA_TNIL: + match_value = 0; + break; + + default: + *errmsg = "unsupported old_value type"; + return NGX_ERROR; + } + +#if (NGX_DEBUG) + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "fetching key \"%*s\" in shared dict \"%V\"", key_len, + key, &name); +#endif /* NGX_DEBUG */ + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_stream_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd); + + dd("shdict lookup returns %d", (int) rc); + + if (rc == NGX_DECLINED || (rc == NGX_DONE)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *value_type = LUA_TNIL; + return NGX_OK; + } + + if (match_value || match_flags) { + if (!match_flags || old_user_flags == (int) sd->user_flags) + { + if (!match_value || (old_value_type == sd->value_type && + old_str_value_len == sd->value_len && + !ngx_memcmp(old_str_value_buf, sd->data + key_len, + (size_t) old_str_value_len))) + { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *unmatch = 1; + return NGX_DECLINED; + } + } + } + + *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; + + if (*str_value_len < (size_t) value.len) { + if (*value_type == SHDICT_TBOOLEAN) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + + if (*value_type == SHDICT_TSTRING) { + *str_value_buf = malloc(value.len); + if (*str_value_buf == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + } + } + + switch (*value_type) { + + case SHDICT_TSTRING: + *str_value_len = value.len; + ngx_memcpy(*str_value_buf, value.data, value.len); + break; + + case SHDICT_TNUMBER: + + if (value.len != sizeof(double)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "bad lua number value size found for key %*s " + "in shared_dict %V: %z", key_len, key, + &name, value.len); + return NGX_ERROR; + } + + *str_value_len = value.len; + ngx_memcpy(num_value, value.data, sizeof(double)); + break; + + case SHDICT_TBOOLEAN: + + if (value.len != sizeof(u_char)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "bad lua boolean value size found for key %*s " + "in shared_dict %V: %z", key_len, key, &name, + value.len); + return NGX_ERROR; + } + + ngx_memcpy(*str_value_buf, value.data, value.len); + break; + + case SHDICT_TLIST: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *errmsg = "value is a list"; + return NGX_ERROR; + + default: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "bad value type found for key %*s in " + "shared_dict %V: %d", key_len, key, &name, + *value_type); + return NGX_ERROR; + } + + *user_flags = sd->user_flags; + dd("user flags: %d", *user_flags); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_OK; +} + + int ngx_stream_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, size_t key_len, int value_type, u_char *str_value_buf, size_t str_value_len, double num_value, long exptime, int user_flags, char **errmsg, int *forcible) { - int i, n; + int n; u_char c, *p; uint32_t hash; ngx_int_t rc; @@ -1425,43 +1906,26 @@ ngx_stream_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, + key_len + str_value_len; - node = ngx_slab_alloc_locked(ctx->shpool, n); + if (op & NGX_STREAM_LUA_SHDICT_SAFE_STORE) { + node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node == NULL) { - - if (op & NGX_STREAM_LUA_SHDICT_SAFE_STORE) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - *errmsg = "no memory"; - return NGX_ERROR; - } - - ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "lua shared dict set: overriding non-expired items " - "due to memory shortage for entry \"%*s\"", key_len, - key); - - for (i = 0; i < 30; i++) { - if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) { - break; - } - - *forcible = 1; - - node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node != NULL) { - goto allocated; - } + } else { + node = ngx_stream_lua_shdict_force_alloc(ctx, n, forcible); + if (*forcible) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "lua shared dict store: overwrote non-expired items " + "due to memory shortage for entry \"%*s\"", key_len, + key); } + } + if (!node) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } -allocated: - sd = (ngx_stream_lua_shdict_node_t *) &node->color; node->key = hash; diff --git a/t/062-count.t b/t/062-count.t index d90a1484..444f7319 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -123,7 +123,7 @@ n = 9 ngx.say("n = ", n) } --- stream_response -n = 22 +n = 24 --- no_error_log [error] diff --git a/t/163-atomic-shdict.t b/t/163-atomic-shdict.t new file mode 100644 index 00000000..5cb17177 --- /dev/null +++ b/t/163-atomic-shdict.t @@ -0,0 +1,545 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua::Stream; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +#repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3); + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +run_tests(); + +__DATA__ + +=== TEST 1: CAS int value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local ok, err = dogs:cas("foo", 32, nil, 42) + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok) + } +--- stream_response +42 number true +--- no_error_log +[error] + + + +=== TEST 2: CAS int value failed +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local ok, err = dogs:cas("foo", 31, nil, 42) + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok, " ", err) + } +--- stream_response +32 number false false +--- no_error_log +[error] + + + +=== TEST 3: CAS string value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local value = "Turboencabulator" + dogs:set("foo", value) + dogs:cas("foo", value, nil, "bar") + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + } +--- stream_response +bar string +--- no_error_log +[error] + + + +=== TEST 4: CAS string value invalid +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local value = "Turboencabulator" + dogs:set("foo", value) + local ok, err = dogs:cas("foo", "c", nil, "bar") + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok, " ", err) + } +--- stream_response +Turboencabulator string false false +--- no_error_log +[error] + + + +=== TEST 5: CAS boolean value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", true) + dogs:cas("foo", true, nil, false) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)) + } +--- stream_response +false boolean +--- no_error_log +[error] + + + +=== TEST 6: CAS flags +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "bar", nil, 42) + local ok, err = dogs:cas("foo", nil, 42, "baz") + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok, " ", err) + } +--- stream_response +baz string true nil +--- no_error_log +[error] + + + +=== TEST 7: CAS flags invalid +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "bar", nil, 42) + local ok, err = dogs:cas("foo", nil, 41, "baz") + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok, " ", err) + } +--- stream_response +bar string false false +--- no_error_log +[error] + + + +=== TEST 8: CAS invalid flags error message +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "bar", nil, 42) + local ok, err = dogs:cas("foo", nil, 41, "baz") + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok, " ", err ) + + } +--- stream_response +bar string false false +--- no_error_log +[error] + + + +=== TEST 9: CAS invalid value error message +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "bar", nil, 42) + local ok, err = dogs:cas("foo", "baz", nil, "bas") + + local val = dogs:get("foo") + ngx.say(val, " ", type(val), " ", ok, " ", err) + } +--- stream_response +bar string false false +--- no_error_log +[error] + + + +=== TEST 10: COG number value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local val = dogs:get_if_not_eq("foo", 31) + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +--- no_error_log +[error] + + + +=== TEST 11: COG number value match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local val, err = dogs:get_if_not_eq("foo", 32) + ngx.say(val, " ", type(val)," ", err) + } +--- stream_response +nil nil false +--- no_error_log +[error] + + + +=== TEST 12: COG string value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "bar") + local val, err = dogs:get_if_not_eq("foo", "baz") + ngx.say(val, " ", type(val)) + } +--- stream_response +bar string +--- no_error_log +[error] + + + +=== TEST 13: COG string value match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", "bar") + local val, err = dogs:get_if_not_eq("foo", "bar") + ngx.say(val, " ", type(val)," ", err) + } +--- stream_response +nil nil false +--- no_error_log +[error] + + + +=== TEST 14: COG boolean value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", true) + local val, err = dogs:get_if_not_eq("foo", false) + ngx.say(val, " ", type(val)) + } +--- stream_response +true boolean +--- no_error_log +[error] + + + +=== TEST 15: COG boolean value match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", true) + local val, err = dogs:get_if_not_eq("foo", true) + ngx.say(val, " ", type(val)," ", err) + } +--- stream_response +nil nil false +--- no_error_log +[error] + + + +=== TEST 16: COG flags +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 42) + local val, err = dogs:get_if_not_eq("foo", nil, 41) + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +--- no_error_log +[error] + + + +=== TEST 17: COG flags match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, nil, 42) + local val, err = dogs:get_if_not_eq("foo", nil, 42) + ngx.say(val, " ", type(val)," ", err) + } +--- stream_response +nil nil false +--- no_error_log +[error] + + + +=== TEST 18: COG flags 0 +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, nil, 1) + local val = dogs:get_if_not_eq("foo", nil, 0) + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +--- no_error_log +[error] + + + +=== TEST 19: COG flags 0 match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local val, err = dogs:get_if_not_eq("foo", nil, 0) + ngx.say(val, " ", type(val)," ", err) + } +--- stream_response +nil nil false +--- no_error_log +[error] + + + +=== TEST 20: COG flags match but not value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32) + local val, err = dogs:get_if_not_eq("foo", 31, 0) + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +--- no_error_log +[error] + + + +=== TEST 21: COG value match but not flags +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local val = dogs:get_if_not_eq("foo", 32, 11) + ngx.say(val, " ", type(val)) + } +--- stream_response +32 number +--- no_error_log +[error] + + + +=== TEST 22: COG flags and value match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local val, err = dogs:get_if_not_eq("foo", 32, 10) + ngx.say(val, " ", type(val)," ", err) + } +--- stream_response +nil nil false +--- no_error_log +[error] + + + +=== TEST 23: CAS only value match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local ok, err = dogs:cas("foo", 32, 11, 1, 1) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)," ", ok, " ", err) + } +--- stream_response +32 number false false +--- no_error_log +[error] + + + +=== TEST 24: CAS only flags match +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local ok, err = dogs:cas("foo", 31, 10, 1, 1) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)," ", ok, " ", err) + } +--- stream_response +32 number false false +--- no_error_log +[error] + + + +=== TEST 25: CAS set nil +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local ok, err = dogs:cas("foo", 32, 10) + local val = dogs:get("foo") + ngx.say(val, " ", type(val)," ", ok) + } +--- stream_response +nil nil true +--- no_error_log +[error] + + + +=== TEST 26: CAS as remove +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local ok, err = dogs:cas("foo") + local val = dogs:get("foo") + ngx.say(val, " ", type(val)," ", ok) + } +--- stream_response +nil nil true +--- no_error_log +[error] + + + +=== TEST 27: CAS as set value +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local ok, err = dogs:cas("foo", nil, nil, "foo") + local val, flags = dogs:get("foo") + ngx.say(val, " ", type(val)," ", flags, " ", ok) + } +--- stream_response +foo string 10 true +--- no_error_log +[error] + + + +=== TEST 28: CAS as set flag +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local ok, err = dogs:cas("foo", nil, nil, nil, 13) + local val, flags = dogs:get("foo") + ngx.say(val, " ", type(val)," ", flags, " ", ok) + } +--- stream_response +32 number 13 true +--- no_error_log +[error] + + + +=== TEST 29: COG as get +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + dogs:set("foo", 32, 0, 10) + local val, flags = dogs:get_if_not_eq("foo") + ngx.say(val, " ", type(val), " ", flags ) + } +--- stream_response +32 number 10 +--- no_error_log +[error] + + + +=== TEST 30: COG get nothing +--- stream_config + lua_shared_dict dogs 1m; +--- stream_server_config + content_by_lua_block { + local dogs = ngx.shared.dogs + local val, flags = dogs:get_if_not_eq("foo") + ngx.say(val, " ", type(val), " ", flags ) + } +--- stream_response +nil nil nil +--- no_error_log +[error]