[njs] Modules: shared dictionary add, set, incr methods timeout argument.

noreply at nginx.com noreply at nginx.com
Mon Jun 10 23:05:04 UTC 2024


details:   https://hg.nginx.org/njs/rev/a5da85a3d309
branches:  
changeset: 2356:a5da85a3d309
user:      jo-carter <104033676+jo-carter at users.noreply.github.com>
date:      Sun Jun 02 09:11:13 2024 +0100
description:
Modules: shared dictionary add, set, incr methods timeout argument.

The optional timeout argument overrides the timeout specified with
the shared_dict_zone directive for the effected key and operation
only. The timeout is specified in milliseconds.

This is useful when the majority of keys are expected to require
unique timeouts.

diffstat:

 nginx/ngx_js_shared_dict.c |  85 ++++++++++++++++++++++++++++++++++++---------
 nginx/t/js_shared_dict.t   |  55 ++++++++++++++++++++++++++---
 2 files changed, 116 insertions(+), 24 deletions(-)

diffs (298 lines):

diff -r ae4f50f7b7b3 -r a5da85a3d309 nginx/ngx_js_shared_dict.c
--- a/nginx/ngx_js_shared_dict.c	Fri Jun 07 22:58:53 2024 -0700
+++ b/nginx/ngx_js_shared_dict.c	Sun Jun 02 09:11:13 2024 +0100
@@ -85,15 +85,16 @@ static ngx_js_dict_node_t *ngx_js_dict_l
 #define NGX_JS_DICT_FLAG_MUST_NOT_EXIST   2
 
 static ngx_int_t ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict,
-    njs_str_t *key, njs_value_t *value, unsigned flags);
+    njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, unsigned flags);
 static ngx_int_t ngx_js_dict_add(ngx_js_dict_t *dict, njs_str_t *key,
-    njs_value_t *value, ngx_msec_t now);
+    njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now);
 static ngx_int_t ngx_js_dict_update(ngx_js_dict_t *dict,
-    ngx_js_dict_node_t *node, njs_value_t *value, ngx_msec_t now);
+    ngx_js_dict_node_t *node, njs_value_t *value, ngx_msec_t timeout,
+    ngx_msec_t now);
 static ngx_int_t ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict,
     njs_str_t *key, njs_value_t *retval);
 static ngx_int_t ngx_js_dict_incr(ngx_js_dict_t *dict, njs_str_t *key,
-    njs_value_t *delta, njs_value_t *init, double *value);
+    njs_value_t *delta, njs_value_t *init, double *value, ngx_msec_t timeout);
 static ngx_int_t ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict,
     njs_str_t *key, njs_value_t *retval);
 static ngx_int_t ngx_js_dict_copy_value_locked(njs_vm_t *vm,
@@ -726,7 +727,8 @@ njs_js_ext_shared_dict_incr(njs_vm_t *vm
     double               value;
     ngx_int_t            rc;
     njs_str_t            key;
-    njs_value_t         *delta, *init;
+    ngx_msec_t           timeout;
+    njs_value_t         *delta, *init, *timeo;
     ngx_js_dict_t       *dict;
     ngx_shm_zone_t      *shm_zone;
     njs_opaque_value_t   lvalue;
@@ -765,7 +767,30 @@ njs_js_ext_shared_dict_incr(njs_vm_t *vm
         njs_value_number_set(init, 0);
     }
 
-    rc = ngx_js_dict_incr(shm_zone->data, &key, delta, init, &value);
+    timeo = njs_arg(args, nargs, 4);
+    if (!njs_value_is_undefined(timeo)) {
+        if (!njs_value_is_number(timeo)) {
+            njs_vm_type_error(vm, "timeout is not a number");
+            return NJS_ERROR;
+        }
+
+        if (!dict->timeout) {
+            njs_vm_type_error(vm, "shared dict must be declared with timeout");
+            return NJS_ERROR;
+        }
+
+        timeout = (ngx_msec_t) njs_value_number(timeo);
+
+        if (timeout < 1) {
+            njs_vm_type_error(vm, "timeout must be greater than or equal to 1");
+            return NJS_ERROR;
+        }
+
+    } else {
+        timeout = dict->timeout;
+    }
+
+    rc = ngx_js_dict_incr(shm_zone->data, &key, delta, init, &value, timeout);
     if (rc == NGX_ERROR) {
         njs_vm_error(vm, "failed to increment value in shared dict");
         return NJS_ERROR;
@@ -936,7 +961,8 @@ njs_js_ext_shared_dict_set(njs_vm_t *vm,
 {
     njs_str_t        key;
     ngx_int_t        rc;
-    njs_value_t     *value;
+    ngx_msec_t       timeout;
+    njs_value_t     *value, *timeo;
     ngx_js_dict_t   *dict;
     ngx_shm_zone_t  *shm_zone;
 
@@ -967,7 +993,30 @@ njs_js_ext_shared_dict_set(njs_vm_t *vm,
         }
     }
 
-    rc = ngx_js_dict_set(vm, shm_zone->data, &key, value, flags);
+    timeo = njs_arg(args, nargs, 3);
+    if (!njs_value_is_undefined(timeo)) {
+        if (!njs_value_is_number(timeo)) {
+            njs_vm_type_error(vm, "timeout is not a number");
+            return NJS_ERROR;
+        }
+
+        if (!dict->timeout) {
+            njs_vm_type_error(vm, "shared dict must be declared with timeout");
+            return NJS_ERROR;
+        }
+
+        timeout = (ngx_msec_t) njs_value_number(timeo);
+
+        if (timeout < 1) {
+            njs_vm_type_error(vm, "timeout must be greater than or equal to 1");
+            return NJS_ERROR;
+        }
+
+    } else {
+        timeout = dict->timeout;
+    }
+
+    rc = ngx_js_dict_set(vm, shm_zone->data, &key, value, timeout, flags);
     if (rc == NGX_ERROR) {
         return NJS_ERROR;
     }
@@ -1118,7 +1167,7 @@ ngx_js_dict_node_free(ngx_js_dict_t *dic
 
 static ngx_int_t
 ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key,
-    njs_value_t *value, unsigned flags)
+    njs_value_t *value, ngx_msec_t timeout, unsigned flags)
 {
     ngx_msec_t           now;
     ngx_time_t          *tp;
@@ -1137,7 +1186,7 @@ ngx_js_dict_set(njs_vm_t *vm, ngx_js_dic
             return NGX_DECLINED;
         }
 
-        if (ngx_js_dict_add(dict, key, value, now) != NGX_OK) {
+        if (ngx_js_dict_add(dict, key, value, timeout, now) != NGX_OK) {
             goto memory_error;
         }
 
@@ -1149,7 +1198,7 @@ ngx_js_dict_set(njs_vm_t *vm, ngx_js_dic
             }
         }
 
-        if (ngx_js_dict_update(dict, node, value, now) != NGX_OK) {
+        if (ngx_js_dict_update(dict, node, value, timeout, now) != NGX_OK) {
             goto memory_error;
         }
     }
@@ -1170,7 +1219,7 @@ memory_error:
 
 static ngx_int_t
 ngx_js_dict_add(ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *value,
-    ngx_msec_t now)
+    ngx_msec_t timeout, ngx_msec_t now)
 {
     size_t               n;
     uint32_t             hash;
@@ -1214,7 +1263,7 @@ ngx_js_dict_add(ngx_js_dict_t *dict, njs
     ngx_rbtree_insert(&dict->sh->rbtree, &node->sn.node);
 
     if (dict->timeout) {
-        node->expire.key = now + dict->timeout;
+        node->expire.key = now + timeout;
         ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire);
     }
 
@@ -1224,7 +1273,7 @@ ngx_js_dict_add(ngx_js_dict_t *dict, njs
 
 static ngx_int_t
 ngx_js_dict_update(ngx_js_dict_t *dict, ngx_js_dict_node_t *node,
-    njs_value_t *value, ngx_msec_t now)
+    njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now)
 {
     u_char     *p;
     njs_str_t   string;
@@ -1249,7 +1298,7 @@ ngx_js_dict_update(ngx_js_dict_t *dict, 
 
     if (dict->timeout) {
         ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire);
-        node->expire.key = now + dict->timeout;
+        node->expire.key = now + timeout;
         ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire);
     }
 
@@ -1306,7 +1355,7 @@ ngx_js_dict_delete(njs_vm_t *vm, ngx_js_
 
 static ngx_int_t
 ngx_js_dict_incr(ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *delta,
-    njs_value_t *init, double *value)
+    njs_value_t *init, double *value, ngx_msec_t timeout)
 {
     ngx_msec_t           now;
     ngx_time_t          *tp;
@@ -1322,7 +1371,7 @@ ngx_js_dict_incr(ngx_js_dict_t *dict, nj
     if (node == NULL) {
         njs_value_number_set(init, njs_value_number(init)
                                    + njs_value_number(delta));
-        if (ngx_js_dict_add(dict, key, init, now) != NGX_OK) {
+        if (ngx_js_dict_add(dict, key, init, timeout, now) != NGX_OK) {
             ngx_rwlock_unlock(&dict->sh->rwlock);
             return NGX_ERROR;
         }
@@ -1335,7 +1384,7 @@ ngx_js_dict_incr(ngx_js_dict_t *dict, nj
 
         if (dict->timeout) {
             ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire);
-            node->expire.key = now + dict->timeout;
+            node->expire.key = now + timeout;
             ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire);
         }
     }
diff -r ae4f50f7b7b3 -r a5da85a3d309 nginx/t/js_shared_dict.t
--- a/nginx/t/js_shared_dict.t	Fri Jun 07 22:58:53 2024 -0700
+++ b/nginx/t/js_shared_dict.t	Sun Jun 02 09:11:13 2024 +0100
@@ -40,7 +40,7 @@ http {
 
     js_shared_dict_zone zone=foo:32k timeout=2s evict;
     js_shared_dict_zone zone=bar:64k type=string;
-    js_shared_dict_zone zone=waka:32k type=number;
+    js_shared_dict_zone zone=waka:32k timeout=1000s type=number;
     js_shared_dict_zone zone=no_timeout:32k;
 
     server {
@@ -146,7 +146,14 @@ EOF
     function add(r) {
         var dict = ngx.shared[r.args.dict];
         var value = convertToValue(dict, r.args.value);
-        r.return(200, dict.add(r.args.key, value));
+
+        if (r.args.timeout) {
+            var timeout = Number(r.args.timeout);
+            r.return(200, dict.add(r.args.key, value, timeout));
+
+        } else {
+            r.return(200, dict.add(r.args.key, value));
+        }
     }
 
     function capacity(r) {
@@ -200,8 +207,16 @@ EOF
     function incr(r) {
         var dict = ngx.shared[r.args.dict];
         var def = r.args.def ? parseInt(r.args.def) : 0;
-        var val = dict.incr(r.args.key, parseInt(r.args.by), def);
-        r.return(200, val);
+
+        if (r.args.timeout) {
+            var timeout = Number(r.args.timeout);
+            var val = dict.incr(r.args.key, parseInt(r.args.by), def, timeout);
+            r.return(200, val);
+
+        } else {
+            var val = dict.incr(r.args.key, parseInt(r.args.by), def);
+            r.return(200, val);
+        }
     }
 
     function keys(r) {
@@ -256,7 +271,14 @@ EOF
     function set(r) {
         var dict = ngx.shared[r.args.dict];
         var value = convertToValue(dict, r.args.value);
-        r.return(200, dict.set(r.args.key, value) === dict);
+
+        if (r.args.timeout) {
+            var timeout = Number(r.args.timeout);
+            r.return(200, dict.set(r.args.key, value, timeout) === dict);
+
+        } else {
+            r.return(200, dict.set(r.args.key, value) === dict);
+        }
     }
 
     function size(r) {
@@ -283,7 +305,7 @@ EOF
                      set_clear, size, zones };
 EOF
 
-$t->try_run('no js_shared_dict_zone')->plan(44);
+$t->try_run('no js_shared_dict_zone')->plan(51);
 
 ###############################################################################
 
@@ -350,6 +372,27 @@ like(http_get('/items?dict=waka'),
 
 }
 
+TODO: {
+local $TODO = 'not yet' unless has_version('0.8.5');
+
+http_get('/clear?dict=waka');
+like(http_get('/set?dict=waka&key=BAR&value=1&timeout=1'), qr/true/,
+	'set waka.BAR');
+like(http_get('/add?dict=waka&key=BAR2&value=1&timeout=1'), qr/true/,
+	'add waka.BAR2');
+like(http_get('/incr?dict=waka&key=BAR3&by=42&timeout=1'), qr/42/,
+	'incr waka.BAR3');
+like(http_get('/set?dict=waka&key=FOO&value=42&timeout=1000'), qr/true/,
+	'set waka.FOO');
+like(http_get('/add?dict=waka&key=FOO2&value=42&timeout=1000'), qr/true/,
+	'add waka.FOO2');
+like(http_get('/incr?dict=waka&key=FOO3&by=42&timeout=1000'), qr/42/,
+	'incr waka.FOO3');
+
+like(http_get('/keys?dict=waka'), qr/\[FOO\,FOO2\,FOO3]/, 'waka keys');
+
+}
+
 like(http_get('/pop?dict=bar&key=FOO'), qr/zzz/, 'pop bar.FOO');
 like(http_get('/pop?dict=bar&key=FOO'), qr/undefined/, 'pop deleted bar.FOO');
 http_get('/set?dict=foo&key=BAR&value=xxx');


More information about the nginx-devel mailing list