[njs] Modules: fixed size() and keys() methods of a shared dictionary.

Dmitry Volyntsev xeioex at nginx.com
Thu Aug 31 04:01:40 UTC 2023


details:   https://hg.nginx.org/njs/rev/3fe16507f80a
branches:  
changeset: 2187:3fe16507f80a
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Wed Aug 30 18:59:28 2023 -0700
description:
Modules: fixed size() and keys() methods of a shared dictionary.

Previously, these methods did not take into the account exprired
entries. The expired entries appear in js_shared_dict_zone when timeout
directive is specified as the expired elements are not removed at the
moment they become stale right away.

This closes #665 issue on Github.

diffstat:

 nginx/ngx_js_shared_dict.c |  16 +++++++++++++
 nginx/t/js_shared_dict.t   |  54 ++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 64 insertions(+), 6 deletions(-)

diffs (149 lines):

diff -r c7d2a7846b0b -r 3fe16507f80a nginx/ngx_js_shared_dict.c
--- a/nginx/ngx_js_shared_dict.c	Wed Aug 30 12:06:12 2023 -0700
+++ b/nginx/ngx_js_shared_dict.c	Wed Aug 30 18:59:28 2023 -0700
@@ -599,6 +599,8 @@ njs_js_ext_shared_dict_keys(njs_vm_t *vm
 {
     njs_int_t            rc;
     ngx_int_t            max_count;
+    ngx_msec_t           now;
+    ngx_time_t          *tp;
     njs_value_t         *value;
     ngx_rbtree_t        *rbtree;
     ngx_js_dict_t       *dict;
@@ -630,6 +632,12 @@ njs_js_ext_shared_dict_keys(njs_vm_t *vm
 
     ngx_rwlock_rlock(&dict->sh->rwlock);
 
+    if (dict->timeout) {
+        tp = ngx_timeofday();
+        now = tp->sec * 1000 + tp->msec;
+        ngx_js_dict_expire(dict, now);
+    }
+
     rbtree = &dict->sh->rbtree;
 
     if (rbtree->root == rbtree->sentinel) {
@@ -835,6 +843,8 @@ njs_js_ext_shared_dict_size(njs_vm_t *vm
     njs_index_t unused, njs_value_t *retval)
 {
     njs_int_t           items;
+    ngx_msec_t          now;
+    ngx_time_t         *tp;
     ngx_rbtree_t       *rbtree;
     ngx_js_dict_t      *dict;
     ngx_shm_zone_t     *shm_zone;
@@ -851,6 +861,12 @@ njs_js_ext_shared_dict_size(njs_vm_t *vm
 
     ngx_rwlock_rlock(&dict->sh->rwlock);
 
+    if (dict->timeout) {
+        tp = ngx_timeofday();
+        now = tp->sec * 1000 + tp->msec;
+        ngx_js_dict_expire(dict, now);
+    }
+
     rbtree = &dict->sh->rbtree;
 
     if (rbtree->root == rbtree->sentinel) {
diff -r c7d2a7846b0b -r 3fe16507f80a nginx/t/js_shared_dict.t
--- a/nginx/t/js_shared_dict.t	Wed Aug 30 12:06:12 2023 -0700
+++ b/nginx/t/js_shared_dict.t	Wed Aug 30 18:59:28 2023 -0700
@@ -46,6 +46,10 @@ http {
         listen       127.0.0.1:8080;
         server_name  localhost;
 
+        location /njs {
+            js_content test.njs;
+        }
+
         location /add {
             js_content test.add;
         }
@@ -115,6 +119,10 @@ http {
 EOF
 
 $t->write_file('test.js', <<'EOF');
+    function test_njs(r) {
+        r.return(200, njs.version);
+    }
+
     function convertToValue(dict, v) {
         if (dict.type == 'number') {
             return parseInt(v);
@@ -197,7 +205,7 @@ EOF
             ks = ngx.shared[r.args.dict].keys();
         }
 
-        r.return(200, ks.toSorted());
+        r.return(200, `[${ks.toSorted()}]`);
     }
 
     function name(r) {
@@ -239,10 +247,11 @@ EOF
     }
 
     export default { add, capacity, chain, clear, del, free_space, get, has,
-                     incr, keys, name, pop, replace, set, size, zones };
+                     incr, keys, name, njs: test_njs, pop, replace, set, size,
+                     zones };
 EOF
 
-$t->try_run('no js_shared_dict_zone')->plan(38);
+$t->try_run('no js_shared_dict_zone')->plan(41);
 
 ###############################################################################
 
@@ -274,8 +283,9 @@ like(http_get('/has?dict=waka&key=FOO'),
 
 $t->reload();
 
-like(http_get('/keys?dict=foo'), qr/FOO\,FOO2\,FOO3/, 'foo keys');
-like(http_get('/keys?dict=foo&max=2'), qr/FOO\,FOO3/, 'foo keys max 2');
+like(http_get('/keys?dict=foo'), qr/\[FOO\,FOO2\,FOO3]/, 'foo keys');
+like(http_get('/keys?dict=foo&max=2'), qr/\[FOO\,FOO3]/, 'foo keys max 2');
+like(http_get('/size?dict=foo'), qr/size: 3/, 'no of items in foo');
 like(http_get('/get?dict=foo&key=FOO2'), qr/yyy/, 'get foo.FOO2');
 like(http_get('/get?dict=bar&key=FOO'), qr/zzz/, 'get bar.FOO');
 like(http_get('/get?dict=foo&key=FOO'), qr/xxx/, 'get foo.FOO');
@@ -292,8 +302,40 @@ select undef, undef, undef, 2.1;
 
 like(http_get('/get?dict=foo&key=FOO'), qr/undefined/, 'get expired foo.FOO');
 like(http_get('/pop?dict=foo&key=FOO'), qr/undefined/, 'pop expired foo.FOO');
-like(http_get('/size?dict=foo'), qr/size: 2/, 'no of items in foo');
+
+TODO: {
+local $TODO = 'not yet' unless has_version('0.8.1');
+
+like(http_get('/keys?dict=foo'), qr/\[]/, 'foo keys after expire');
+like(http_get('/keys?dict=bar'), qr/\[FOO\,FOO2]/, 'bar keys after a delay');
+like(http_get('/size?dict=foo'), qr/size: 0/,
+	'no of items in foo after expire');
+
+}
+
 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');
 like(http_get('/clear?dict=foo'), qr/undefined/, 'clear foo');
 like(http_get('/size?dict=foo'), qr/size: 0/, 'no of items in foo after clear');
+
+###############################################################################
+
+sub has_version {
+	my $need = shift;
+
+	http_get('/njs') =~ /^([.0-9]+)$/m;
+
+	my @v = split(/\./, $1);
+	my ($n, $v);
+
+	for $n (split(/\./, $need)) {
+		$v = shift @v || 0;
+		return 0 if $n > $v;
+		return 1 if $v > $n;
+	}
+
+	return 1;
+}
+
+###############################################################################


More information about the nginx-devel mailing list