From d93cb1c487610f03c11500076695bcce679e2bf0 Mon Sep 17 00:00:00 2001 From: Odin Hultgren Van Der Horst Date: Tue, 26 Oct 2021 10:55:05 +0200 Subject: [PATCH 1/2] Added atomic type opperations to shmdict - Added cas(compare and swap) to shdict - Added get_if_not_eq to shdict --- README.markdown | 46 +++ doc/HttpLuaModule.wiki | 38 +++ src/ngx_http_lua_shdict.c | 522 ++++++++++++++++++++++++++++-- t/062-count.t | 2 +- t/167-atomic-shdict.t | 665 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1243 insertions(+), 30 deletions(-) create mode 100644 t/167-atomic-shdict.t diff --git a/README.markdown b/README.markdown index a3a631cb3e..97c84a7865 100644 --- a/README.markdown +++ b/README.markdown @@ -3581,10 +3581,12 @@ Nginx API for Lua * [ngx.shared.DICT](#ngxshareddict) * [ngx.shared.DICT.get](#ngxshareddictget) * [ngx.shared.DICT.get_stale](#ngxshareddictget_stale) +* [ngx.shared.DICT](#ngxshareddict) * [ngx.shared.DICT.set](#ngxshareddictset) * [ngx.shared.DICT.safe_set](#ngxshareddictsafe_set) * [ngx.shared.DICT.add](#ngxshareddictadd) * [ngx.shared.DICT.safe_add](#ngxshareddictsafe_add) +* [ngx.shared.DICT.cas](#ngxshareddictcas) * [ngx.shared.DICT.replace](#ngxshareddictreplace) * [ngx.shared.DICT.delete](#ngxshareddictdelete) * [ngx.shared.DICT.incr](#ngxshareddictincr) @@ -6784,10 +6786,12 @@ The resulting object `dict` has the following methods: * [get](#ngxshareddictget) * [get_stale](#ngxshareddictget_stale) +* [get_if_not_eq](#ngxshareddictget_if_not_eq) * [set](#ngxshareddictset) * [safe_set](#ngxshareddictsafe_set) * [add](#ngxshareddictadd) * [safe_add](#ngxshareddictsafe_add) +* [cas](#ngxshareddictcas) * [replace](#ngxshareddictreplace) * [delete](#ngxshareddictdelete) * [incr](#ngxshareddictincr) @@ -6912,6 +6916,26 @@ See also [ngx.shared.DICT](#ngxshareddict). [Back to TOC](#nginx-api-for-lua) +ngx.shared.DICT +--------------- +**syntax:** *value, flags = ngx.shared.DICT:get_if_not_eq(key, old_value?, old_flags?)* + +**context:** *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*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** + +Similar to the [get](#ngxshareddictget) method, but only returns if +`old_value` or `old_flags` do not match. + +If `old_value` or `old_flags` is `nil` +it will be ignored when comparing. + +In case of match, `nil, false` will be returned. + +This method was first introduced in the `0.10.21` release. + +See also [ngx.shared.DICT](#ngxshareddict). + +[Back to TOC](#nginx-api-for-lua) + ngx.shared.DICT.set ------------------- @@ -7012,6 +7036,28 @@ See also [ngx.shared.DICT](#ngxshareddict). [Back to TOC](#nginx-api-for-lua) +ngx.shared.DICT.cas +------------------- +**syntax:** *success, err, forcible = ngx.shared.DICT:cas(key, old_value?, old_flags?, value?, flags?, exptime?)* + +**context:** *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*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua** + +Conditionally sets key-value pair in shm. + +If `old_value` or `old_flags` is `nil` it will +be ignored. + +If either `value` or `flags` is `nil` it will +remain unchanged. If both are `nil`, key-value pair will be deleted. + +In case of mismatch, `false, false` will be returned. + +This method was first introduced in the `0.10.21` release. + +See also [ngx.shared.DICT](#ngxshareddict). + +[Back to TOC](#nginx-api-for-lua) + ngx.shared.DICT.replace ----------------------- diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 0d7a6f73b1..569df82447 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -5739,10 +5739,12 @@ The resulting object dict has the following methods: * [[#ngx.shared.DICT.get|get]] * [[#ngx.shared.DICT.get_stale|get_stale]] +* [[#ngx.shared.DICT.get_if_not_eq|get_if_not_eq]] * [[#ngx.shared.DICT.set|set]] * [[#ngx.shared.DICT.safe_set|safe_set]] * [[#ngx.shared.DICT.add|add]] * [[#ngx.shared.DICT.safe_add|safe_add]] +* [[#ngx.shared.DICT.cas|cas]] * [[#ngx.shared.DICT.replace|replace]] * [[#ngx.shared.DICT.delete|delete]] * [[#ngx.shared.DICT.incr|incr]] @@ -5855,6 +5857,23 @@ This method was first introduced in the 0.8.6 release. See also [[#ngx.shared.DICT|ngx.shared.DICT]]. +== ngx.shared.DICT == +'''syntax:''' ''value, flags = ngx.shared.DICT:get_if_not_eq(key, old_value?, old_flags?)'' + +'''context:''' ''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*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' + +Similar to the [[#ngx.shared.DICT.get|get]] method, but only returns if +old_value or old_flags do not match. + +If old_value or old_flags is nil +it will be ignored when comparing. + +In case of match, nil, false will be returned. + +This method was first introduced in the 0.10.21 release. + +See also [[#ngx.shared.DICT|ngx.shared.DICT]]. + == ngx.shared.DICT.set == '''syntax:''' ''success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)'' @@ -5941,6 +5960,25 @@ This feature was first introduced in the v0.7.18 release. See also [[#ngx.shared.DICT|ngx.shared.DICT]]. +== ngx.shared.DICT.cas == +'''syntax:''' ''success, err, forcible = ngx.shared.DICT:cas(key, old_value?, old_flags?, value?, flags?, exptime?)'' + +'''context:''' ''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*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*'' + +Conditionally sets key-value pair in shm. + +If old_value or old_flags is nil it will +be ignored. + +If either value or flags is nil it will +remain unchanged. If both are nil, key-value pair will be deleted. + +In case of mismatch, false, false will be returned. + +This method was first introduced in the 0.10.21 release. + +See also [[#ngx.shared.DICT|ngx.shared.DICT]]. + == ngx.shared.DICT.replace == '''syntax:''' ''success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)'' diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index 26622d29f0..fc04ce54c9 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -305,6 +305,37 @@ ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n) } +static inline ngx_rbtree_node_t * +ngx_http_lua_shdict_force_alloc(ngx_http_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_http_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_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L) { @@ -1305,13 +1336,463 @@ ngx_http_lua_ffi_shdict_udata_to_zone(void *zone_udata) } +int +ngx_http_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_http_lua_shdict_ctx_t *ctx; + ngx_http_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_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_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_HTTP, 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_HTTP, 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_http_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_http_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_HTTP, ctx->log, 0, + "lua shared dict cas: creating a new entry"); + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_lua_shdict_node_t, data) + + key_len + + str_value_len; + + node = ngx_http_lua_shdict_force_alloc(ctx, n, forcible); + if (*forcible == 1) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, 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_http_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_http_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_http_lua_shdict_ctx_t *ctx; + ngx_http_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_HTTP, 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_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_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_http_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; @@ -1497,43 +1978,26 @@ ngx_http_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 (node == NULL) { - - if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - - *errmsg = "no memory"; - return NGX_ERROR; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, 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_http_lua_shdict_expire(ctx, 0) == 0) { - break; - } - - *forcible = 1; + if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { + node = ngx_slab_alloc_locked(ctx->shpool, n); - node = ngx_slab_alloc_locked(ctx->shpool, n); - if (node != NULL) { - goto allocated; - } + } else { + node = ngx_http_lua_shdict_force_alloc(ctx, n, forcible); + if (*forcible) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, 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_http_lua_shdict_node_t *) &node->color; node->key = hash; diff --git a/t/062-count.t b/t/062-count.t index f5ba12c91d..c33a73abb5 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -283,7 +283,7 @@ n = 6 --- request GET /test --- response_body -n = 22 +n = 24 --- no_error_log [error] diff --git a/t/167-atomic-shdict.t b/t/167-atomic-shdict.t new file mode 100644 index 0000000000..b91eb2b0d8 --- /dev/null +++ b/t/167-atomic-shdict.t @@ -0,0 +1,665 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua; + +#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 +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +42 number true +--- no_error_log +[error] + + + +=== TEST 2: CAS int value failed +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +32 number false false +--- no_error_log +[error] + + + +=== TEST 3: CAS string value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +bar string +--- no_error_log +[error] + + + +=== TEST 4: CAS string value invalid +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +Turboencabulator string false false +--- no_error_log +[error] + + + +=== TEST 5: CAS boolean value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +false boolean +--- no_error_log +[error] + + + +=== TEST 6: CAS flags +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +baz string true nil +--- no_error_log +[error] + + + +=== TEST 7: CAS flags invalid +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +bar string false false +--- no_error_log +[error] + + + +=== TEST 8: CAS invalid flags error message +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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 ) + + } + } +--- request +GET /test +--- response_body +bar string false false +--- no_error_log +[error] + + + +=== TEST 9: CAS invalid value error message +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +bar string false false +--- no_error_log +[error] + + + +=== TEST 10: COG number value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +32 number +--- no_error_log +[error] + + + +=== TEST 11: COG number value match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil false +--- no_error_log +[error] + + + +=== TEST 12: COG string value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +bar string +--- no_error_log +[error] + + + +=== TEST 13: COG string value match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil false +--- no_error_log +[error] + + + +=== TEST 14: COG boolean value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +true boolean +--- no_error_log +[error] + + + +=== TEST 15: COG boolean value match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil false +--- no_error_log +[error] + + + +=== TEST 16: COG flags +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +32 number +--- no_error_log +[error] + + + +=== TEST 17: COG flags match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil false +--- no_error_log +[error] + + + +=== TEST 18: COG flags 0 +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +32 number +--- no_error_log +[error] + + + +=== TEST 19: COG flags 0 match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil false +--- no_error_log +[error] + + + +=== TEST 20: COG flags match but not value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +32 number +--- no_error_log +[error] + + + +=== TEST 21: COG value match but not flags +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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)) + } + } +--- request +GET /test +--- response_body +32 number +--- no_error_log +[error] + + + +=== TEST 22: COG flags and value match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil false +--- no_error_log +[error] + + + +=== TEST 23: CAS only value match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +32 number false false +--- no_error_log +[error] + + + +=== TEST 24: CAS only flags match +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +32 number false false +--- no_error_log +[error] + + + +=== TEST 25: CAS set nil +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil true +--- no_error_log +[error] + + + +=== TEST 26: CAS as remove +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +nil nil true +--- no_error_log +[error] + + + +=== TEST 27: CAS as set value +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +foo string 10 true +--- no_error_log +[error] + + + +=== TEST 28: CAS as set flag +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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) + } + } +--- request +GET /test +--- response_body +32 number 13 true +--- no_error_log +[error] + + + +=== TEST 29: COG as get +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + 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 ) + } + } +--- request +GET /test +--- response_body +32 number 10 +--- no_error_log +[error] + + + +=== TEST 30: COG get nothing +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + local val, flags = dogs:get_if_not_eq("foo") + ngx.say(val, " ", type(val), " ", flags ) + } + } +--- request +GET /test +--- response_body +nil nil nil +--- no_error_log +[error] From 83b71ebab7686bef1b07ac90b5bfb8b4eb05860d Mon Sep 17 00:00:00 2001 From: Odin Hultgren Van Der Horst Date: Wed, 27 Oct 2021 10:08:58 +0200 Subject: [PATCH 2/2] Modified travis to use atomic_shm branchs --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index daae0704f7..ff4b40bcef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,11 +88,11 @@ install: - git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module - git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module - git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module - - 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 - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql - git clone https://github.com/openresty/lua-resty-string.git ../lua-resty-string - - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module + - git clone -b atomic_shm https://github.com/webcore-no/stream-lua-nginx-module.git ../stream-lua-nginx-module - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2 before_script: