Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions app/controllers/1/syncs_controller.lua
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,36 @@ function SyncsController:healthcheck()
return 200, { state = 'OK' }
end

-- gin dispatches straight to the action and turns raise_error into a response
-- with pcall, so an action has no return point every path passes through. The
-- connection goes back to the pool here, inside the content phase: nginx
-- finalizes cosockets before log_by_lua runs, where set_keepalive fails with
-- "closed".
local function releasing(action)
return function(self, ...)
local ok, status, body, headers = pcall(action, self, ...)
if self.redis then
Redis.release(self.redis)
self.redis = nil
end
if not ok then
error(status, 0)
end
return status, body, headers
end
end

for _, action in ipairs({
"auth_user",
"create_user",
"create_user_disabled",
"delete_user",
"update_password",
"get_progress",
"update_progress",
"healthcheck"
}) do
SyncsController[action] = releasing(SyncsController[action])
end

return SyncsController
31 changes: 29 additions & 2 deletions db/redis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,43 @@ local Redis = {
options = {},
}

-- How long an idle connection is held. config/redis.conf sets `timeout 0`, so
-- the server never closes one first.
local keepalive_timeout = 60000

-- One pool per database. A reused connection is the same socket, so it is
-- already on the database it selected, and connections that selected another
-- are in another pool. port is formatted, not concatenated: a unix socket
-- configuration leaves it unset.
local function pool_name(option)
return string.format("%s:%s/%s", option.host, tostring(option.port), option.database)
end

function Redis:new()
local redis = require("resty.redis")
local option = DbSettings[Gin.env]
local red = redis:new()
red:set_timeout(1000) -- 1 sec
local ok, err = red:connect(option.host, option.port)
local ok, err = red:connect(option.host, option.port, {
pool = pool_name(option),
pool_size = option.pool
})
if ok then
red:select(option.database)
-- Only a new socket needs it; see pool_name above.
if red:get_reused_times() == 0 then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't select still supposed to happen? The reuse thing is just about the TCP connection afaik.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

https://github.com/ledgetech/lua-resty-redis-connector/issues/41#issuecomment-863246307 says that too:

You cannot share connections across requests in a safe way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh right, the Claude commit message was rather wordy (I'd prefer if you wrote your own really) but I think it's saying it's safe in the very specific way it's used here? But if so it should say so in a (non-Claude verbose style) comment.

red:select(option.database)
end
return red
end
end

-- Returns the connection to the pool. set_keepalive refuses a socket with an
-- unread reply or a read or write error, and close is the fallback.
function Redis.release(red)
local option = DbSettings[Gin.env]
if not red:set_keepalive(keepalive_timeout, option.pool) then
red:close()
end
end

return Redis
Loading