Skip to content
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

fix(plugin) reuse redis conn for different db & auth optimize #3293

Closed
wants to merge 8 commits into from

Conversation

mengskysama
Copy link
Contributor

Summary

If user configure response-ratelimiting、response-ratelimiting use same redis instance with db0 and other will get unexpected results.

Full changelog

  • red:select each time.
  • red:auth only once.

Issues resolved

Fix #3292

@hishamhm
Copy link
Contributor

@mengskysama Hello, thank you for sending a PR! Could you include in it a regression test for the issue you are meaning to fix?

Copy link
Member

@thibaultcha thibaultcha left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Nice catch

if conf.redis_database ~= nil then
db = conf.redis_database
end
local ok, err = red:select(db)
Copy link
Member

Choose a reason for hiding this comment

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

The PR makes sense. A comment here to explain why we need to select another database (even after the previous get_reused_times() would help. The fact that there is no way to retrieve which database the socket is connected to (from what I can see) is slightly annoying, we are making unnecessary round-trips, so I suspect this to raise eyebrows in the future.

local db = 0
if conf.redis_database ~= nil then
db = conf.redis_database
end
Copy link
Member

Choose a reason for hiding this comment

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

Simpler to do:

local db = conf.redis_database or 0

Or even

local ok, err = red:select(conf.redis_database or 0)

ngx_log(ngx.ERR, "failed to get connect reused times: ", err)
return nil, err
end
if conf.redis_password and conf.redis_password ~= "" and times == 0 then
Copy link
Member

Choose a reason for hiding this comment

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

Let's move the check for times == 0 at the beginning of this condition, like so:

if times == 0 and conf.redis_password and conf.redis_password ~= "" then

end

Copy link
Member

Choose a reason for hiding this comment

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

btw, this is slightly unrelated to the fix you are trying to write (not send the authentication frame for already established connections vs. always switching to the configured database) - this could be part of another PR (or at least another commit in the same PR, for the sake of atomicity).

@thibaultcha thibaultcha added the pr/changes requested Changes were requested to this PR by a maintainer. Please address them and ping back once done. label Mar 13, 2018
@thibaultcha
Copy link
Member

I agree with @hishamhm - setting up the test case for this will be non-trivial though...

Copy link
Contributor Author

@mengskysama mengskysama left a comment

Choose a reason for hiding this comment

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

Hi! I already commit test case for this fix commit.

ngx_log(ngx.ERR, "failed to change Redis database: ", err)
return nil, err
end
local ok, err = red:select(conf.redis_database or 0)
Copy link
Member

Choose a reason for hiding this comment

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

Could we add a comment above this line to explain why we need this? As we like to say, we do not trust our future selves to remember that this is because the redis connection pool conflicts with that of other instances of this plugin, or that of the response-ratelimiting plugin :)

Thanks!

Copy link
Member

@thibaultcha thibaultcha left a comment

Choose a reason for hiding this comment

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

Thanks a lot for working on this! Hopefully you have some more time to address a few more concern/minor issues before we can merge this? Thanks

local client

setup(function()
helpers.dao:drop_schema()
Copy link
Member

Choose a reason for hiding this comment

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

This will immensely slow down the tests and should ultimately not be needed. Instead, I suggest:

  1. helpers.run_migrations() (make sure we are up-to-date or run the migrations if not)
  2. helpers.dao:truncate_tables()

local function flush_redis(db)
local red = redis:new()
red:set_timeout(2000)
local ok, err = red:connect(REDIS_HOST, REDIS_PORT)
Copy link
Member

Choose a reason for hiding this comment

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

Let's use the proper Lua idioms here and save us a lot of boilerplate:

assert(red:connect())
assert(red:auth())
assert(red:select())


teardown(function()
flush_redis(REDIS_DATABASE)
flush_redis(REDIS_DATABASE_SECOND)
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure how fast those operations are, but if we can avoid it, we should. There is no point in cleaning up behind ourselves in our tests, since they always run in a test instance and because it is actually beneficial to preserve the tests results, for a potential investigation around a failing test. We do this a lot. Plus the setup() hooks performs the same operations as well for the next run of this test suite.

red:set_timeout(2000)
local ok, err = red:connect(REDIS_HOST, REDIS_PORT)
if not ok then
error("failed to connect to Redis: " .. err)
Copy link
Member

Choose a reason for hiding this comment

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

ditto: let's follow the Lua idioms and also make this a bit more readable (a few line jumps would be nice)

assert.are.same(1, tonumber(val1))
assert.are.same(0, tonumber(val2))

-- rate-limiting plugin reuse api1 redis connection
Copy link
Member

Choose a reason for hiding this comment

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

Curious: did you verify that this test fails with consistency without your patch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I verified this test cash get fail if not patched this commit.

spec/03-plugins/24-rate-limiting/05-integration_spec.lua:164: Expected objects to be the same.
Passed in:
(number) 2
Expected:
(number) 1

Copy link
Member

Choose a reason for hiding this comment

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

Ok, thanks for confirming :)

@mengskysama
Copy link
Contributor Author

I already commit my change. Thank for your patience.

thibaultcha pushed a commit that referenced this pull request Mar 20, 2018
Needed for rate-limiting plugins integration tests with the Redis
strategy.

From #3293

Signed-off-by: Thibault Charbonnier <thibaultcha@me.com>
thibaultcha pushed a commit that referenced this pull request Mar 20, 2018
Since every instance of both rate-limiting plugin can share Redis
connection pools, and because each plugin can be configured with a
different Redis database, we must ensure that as each plugin gets
processes in every request, it uses the Redis database it was configured
for. We thus force database selection (see comment in code).

Fix #3292
From #3293

Signed-off-by: Thibault Charbonnier <thibaultcha@me.com>
@thibaultcha
Copy link
Member

Merged to master with some modifications. Thank you for the patch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr/changes requested Changes were requested to this PR by a maintainer. Please address them and ping back once done.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

response-ratelimiting response-ratelimiting redis will get unexpected results
3 participants