Skip to content

ngx.shared.DICT.incr optional expire argument #358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 30 commits into from

Conversation

splitice
Copy link

@splitice splitice commented Apr 5, 2014

@splitice
Copy link
Author

Can I bump this? Its an extremely simple patch for a feature I continue to find to be very useful. Primarily we use this in systems with timed states (e.g bans) based on keys stored into shared memory.

Merges cleanly into latest master.

if (n != 3) {
return luaL_error(L, "expecting 3 arguments, but only seen %d", n);
if (n < 3) {
return luaL_error(L, "expecting atleast 3 arguments, but only seen %d", n);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: atleast => at least

@agentzh
Copy link
Member

agentzh commented Mar 31, 2015

@splitice Sorry for the delay on my side. Feel free to bump tickets lacking my replies :)

In addition to the issues commented above, will you please add some unit test cases for this new feature in the existing test suite? Or even better, some some simple docs in the file doc/HttpLuaModule.wiki. Thanks!

@splitice
Copy link
Author

splitice commented Apr 1, 2015

Thank you for the feedback, I have updated the pull request to include the recommendations given (with the exception of a unit test).

I am not confident in my understanding of the unit testing system used, nor do I currently have the testing environment configured. It should not be difficult to add as the semantics are the same as with other methods including the parameter.

return luaL_error(L, "expecting at least 3 arguments, but only seen %d", n);
}

if (n > 4) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: indentation not aligned. Maybe you're using a tab here?

Also, I think we can merge these two tests into a single test (see other functions).

@agentzh
Copy link
Member

agentzh commented Apr 1, 2015

@splitice The documentation for the test scaffold can be found here:

https://github.com/openresty/lua-nginx-module#test-suite

https://github.com/openresty/test-nginx#readme

And I usually use the util/build2.sh script in the ngx_lua repos to build the nginx used by the test scaffold. Let me know if you have any specific questions regarding the test suite :) Thanks!

@splitice
Copy link
Author

splitice commented Apr 2, 2015

Fixed the stray tab.

Added better parameter handling, now accepts nil and does not silently handle values <0

@splitice
Copy link
Author

splitice commented Jan 4, 2016

Related #586

@agentzh
Copy link
Member

agentzh commented Dec 16, 2016

I wonder if we can just add an expire method here? @doujiang24 already has a patch to propose I think?

@doujiang24
Copy link
Member

yeah, I think expire method is more proper, the patch will in the independent lua-shdict-nginx-module, it's coming soon.

@splitice
Copy link
Author

I still feel that the incr method should match the syntax of set and add which both have an expire argument. And at-least in our use-cases for the shared dictionary (primarily as a keyed hit counter, or cache) we use expires for near every call.

Although its going to be difficult to get consistency now that the init parameter has been introduced :(

This PR has conflicts and has got messy (since it was based off our master years ago) at the moment, I'll resolve them if this ever looks likely to merge.

@agentzh
Copy link
Member

agentzh commented Dec 18, 2016

@doujiang24 Separate nginx C module? I don't think we are there yet :) Better be in the lua-nginx-module core now. We can split the subsystems systematically later.

@agentzh
Copy link
Member

agentzh commented Dec 18, 2016

@splitice set and add both insert new keys most of the times (if not all the times) while incr usually works on existing keys. So it's not really common to specify an expiration time on incr. To me, the expire() method is a more natural fit since it is supposed to work on existing keys.

@splitice
Copy link
Author

splitice commented Dec 18, 2016

Sorry, i'm not sure I really see your point regarding existing keys.

At-least in our usages of incr over multiple subsystems I can count on one hand the number of times we have used incr without an expire argument. Large amounts of the time its used as a key'ed counter for certain events, or in reverse to limit certain events.

In order to do this with two methods expire would have to return false if the key was not found, and then incr called with init, then if created call expire again. Three method calls (worst-case) for something we currently do with one parameter (and one rbtree lookup).

I have no objections for an exipre method as well. its definitely a plus. Theres a few places we do set variables just to touch the expire time.

@agentzh
Copy link
Member

agentzh commented Dec 18, 2016

@splitice Your PR's diff contains unrelated changes like cas, which is rather confusing. Regarding the expire argument of incr(), how exactly do you define its semantics? I don't see why you need 3 method calls for your use case. Seems like two should be sufficient.

@splitice
Copy link
Author

Yes, as stated this would require work before merging now. I never expected it to sit around for 2-3 years. I'll make a feature branch and re-order the parameters (we are using key, value, expire, init since for us expire came first, and its more consistent with set & add)

Heres what we currently use almost everywhere:

ngx.shared.test:incr("key_test", 1, 1, 300) -- parameter order key, value, init, expire

Heres what it would become:

local existing_key = ngx.shared.test:expire("key_test", 300) -- bump expire for 5 minutes
ngx.shared.test:incr("key_test", 1, 1) -- increment, or init if new
if not existing_key then
    ngx.shared.test:expire("key_test", 300) -- if is new, we still need to set the expire
end

@agentzh
Copy link
Member

agentzh commented Dec 19, 2016

@splitice Well, we could add an optional exptime argument to incr() to avoid the small race condition between the incr() and expire() call sequence.

@splitice
Copy link
Author

This Pull request adds an expires argument to incr to do just that. 3 method calls and a conditional -> 1 method call.

@agentzh
Copy link
Member

agentzh commented Dec 19, 2016

@splitice Just a reminder: to make your PR merge-able, you'll have to

  1. prepare the corresponding test cases for your new feature or changes.
  2. update both the old CFunction-based implementation in ngx_lua's core and the FFI-based implementation in lua-resty-core.

The last time I checked, this PR lacks test cases at least.

@agentzh
Copy link
Member

agentzh commented Dec 19, 2016

@splitice Thanks for your contribution!

@splitice
Copy link
Author

All APIs (FFI & CFunction) have been built and work (we have used them for years), they just need to be rebased to a newer version and their arguments order switched for backwards compatibility. There is a corresponding lua-resty-core patch.

If this will get merged I'll write a simple set tests, Something like:

=== TEST N: incr expire test new key
--- http_config
    lua_shared_dict dogs 1m;
--- config
    location = /test {
        content_by_lua '
            local dogs = ngx.shared.dogs
            dogs:incr("foo", 32, 1, 0.5)
            ngx.location.capture("/sleep/0.01")
            ngx.say(dogs:get("foo"))
        ';
    }
    location ~ '^/sleep/(.+)' {
        echo_sleep $1;
    }
--- request
GET /test
--- response_body
32
--- no_error_log
[error]


=== TEST N: incr expire test new key expire
--- http_config
    lua_shared_dict dogs 1m;
--- config
    location = /test {
        content_by_lua '
            local dogs = ngx.shared.dogs
            dogs:incr("foo", 32, 1,  0.001)
            ngx.location.capture("/sleep/ 0.002")
            ngx.say(dogs:get("foo"))
        ';
    }
    location ~ '^/sleep/(.+)' {
        echo_sleep $1;
    }
--- request
GET /test
--- response_body
nil
--- no_error_log
[error]

=== TEST N: incr expire test new key expire
--- http_config
    lua_shared_dict dogs 1m;
--- config
    location = /test {
        content_by_lua '
            local dogs = ngx.shared.dogs
            dogs:incr("foo", 32, 1)
            dogs:incr("foo", 32, nil,  0.001)
            ngx.location.capture("/sleep/ 0.002")
            ngx.say(dogs:get("foo"))
        ';
    }
    location ~ '^/sleep/(.+)' {
        echo_sleep $1;
    }
--- request
GET /test
--- response_body
nil
--- no_error_log
[error]


=== TEST N: incr expire set no init
--- http_config
    lua_shared_dict dogs 1m;
--- config
    location = /test {
        content_by_lua '
            local dogs = ngx.shared.dogs
            dogs:incr("foo", 32, nil, 0.5)
            ngx.location.capture("/sleep/0.01")
            ngx.say(dogs:get("foo"))
        ';
    }
    location ~ '^/sleep/(.+)' {
        echo_sleep $1;
    }
--- request
GET /test
--- response_body
nil
--- no_error_log
[error]

If this is sufficient, I'll do the work. Otherwise if there is still debate over the feature, I'll leave it.

@agentzh
Copy link
Member

agentzh commented Dec 20, 2016

@splitice The API design looks good to me from your test draft. @doujiang24 @membphis @moonbingbing ?

BTW, newly added tests should use *_by_lua_block instead of *_by_lua. Also, you'd better use ngx.sleep instead of ngx.location.capture for sleeping.

@doujiang24
Copy link
Member

LGME:)

@moonbingbing
Copy link

LGTM

@membphis
Copy link
Contributor

LGME :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants