details: https://hg.nginx.org/njs/rev/3fe16507f80a branches: changeset: 2187:3fe16507f80a user: Dmitry Volyntsev <xei...@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; +} + +############################################################################### _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel