Skip to content

Commit cff4176

Browse files
committed
Merge branch 'feature/config_as_table' into develop
2 parents 962e878 + 8d96d48 commit cff4176

File tree

5 files changed

+424
-151
lines changed

5 files changed

+424
-151
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ check_ports:
141141
@$(foreach port,$(REDIS_PORTS),! lsof -i :$(port) &&) true 2>&1 > /dev/null
142142

143143
test_redis: flush_db
144-
$(TEST_REDIS_VARS) $(PROVE) $(TEST_FILE)
145144
util/lua-releng
145+
$(TEST_REDIS_VARS) $(PROVE) $(TEST_FILE)
146146

147147
test_leak: flush_db
148148
$(TEST_REDIS_VARS) TEST_NGINX_CHECK_LEAK=1 $(PROVE) $(TEST_FILE)

lib/resty/redis/connector.lua

Lines changed: 146 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -19,100 +19,175 @@ if not ok then
1919
end
2020

2121

22-
local _M = {
23-
_VERSION = '0.03',
22+
-- A metatable which prevents undefined fields from being created / accessed
23+
local fixed_field_metatable = {
24+
__index =
25+
function(t, k)
26+
error("field " .. tostring(k) .. " does not exist", 2)
27+
end,
28+
__newindex =
29+
function(t, k, v)
30+
error("attempt to create new field " .. tostring(k), 2)
31+
end,
2432
}
2533

26-
local mt = { __index = _M }
2734

35+
-- Returns a new table, recursively copied from the one given, retaining
36+
-- metatable assignment.
37+
--
38+
-- @param table table to be copied
39+
-- @return table
40+
local function tbl_copy(orig)
41+
local orig_type = type(orig)
42+
local copy
43+
if orig_type == "table" then
44+
copy = {}
45+
for orig_key, orig_value in next, orig, nil do
46+
copy[tbl_copy(orig_key)] = tbl_copy(orig_value)
47+
end
48+
setmetatable(copy, tbl_copy(getmetatable(orig)))
49+
else -- number, string, boolean, etc
50+
copy = orig
51+
end
52+
return copy
53+
end
54+
55+
56+
-- Returns a new table, recursively copied from the combination of the given
57+
-- table `t1`, with any missing fields copied from `defaults`.
58+
--
59+
-- If `defaults` is of type "fixed field" and `t1` contains a field name not
60+
-- present in the defults, an error will be thrown.
61+
--
62+
-- @param table t1
63+
-- @param table defaults
64+
-- @return table a new table, recursively copied and merged
65+
local function tbl_copy_merge_defaults(t1, defaults)
66+
if t1 == nil then t1 = {} end
67+
if defaults == nil then defaults = {} end
68+
if type(t1) == "table" and type(defaults) == "table" then
69+
local mt = getmetatable(defaults)
70+
local copy = {}
71+
for t1_key, t1_value in next, t1, nil do
72+
copy[tbl_copy(t1_key)] = tbl_copy_merge_defaults(
73+
t1_value, tbl_copy(defaults[t1_key])
74+
)
75+
end
76+
for defaults_key, defaults_value in next, defaults, nil do
77+
if t1[defaults_key] == nil then
78+
copy[tbl_copy(defaults_key)] = tbl_copy(defaults_value)
79+
end
80+
end
81+
return copy
82+
else
83+
return t1 -- not a table
84+
end
85+
end
86+
87+
88+
local DEFAULTS = setmetatable({
89+
connect_timeout = 100,
90+
read_timeout = 1000,
91+
connection_options = {}, -- pool, etc
92+
93+
keepalive_timeout = 60000,
94+
keepalive_poolsize = 30,
2895

29-
local DEFAULTS = {
3096
host = "127.0.0.1",
3197
port = 6379,
32-
path = nil, -- /tmp/redis.sock
33-
password = nil,
98+
path = "", -- /tmp/redis.sock
99+
password = "",
34100
db = 0,
101+
102+
url = "", -- DSN url
103+
35104
master_name = "mymaster",
36-
role = "master", -- master | slave | any (tries master first, failover to a slave)
37-
sentinels = nil,
38-
cluster_startup_nodes = {},
105+
role = "master", -- master | slave | any
106+
sentinels = {},
107+
108+
cluster_startup_nodes = {}, -- TODO remove this until implemented?
109+
}, fixed_field_metatable)
110+
111+
112+
local _M = {
113+
_VERSION = '0.03',
39114
}
40115

116+
local mt = { __index = _M }
117+
41118

42-
function _M.new()
43-
return setmetatable({
44-
connect_timeout = 100,
45-
read_timeout = 1000,
46-
connection_options = nil, -- pool, etc
47-
}, mt)
119+
function _M.new(config)
120+
local ok, config = pcall(tbl_copy_merge_defaults, config, DEFAULTS)
121+
if not ok then
122+
return nil, config -- err
123+
else
124+
return setmetatable({
125+
config = config
126+
}, mt)
127+
end
48128
end
49129

50130

51131
function _M.set_connect_timeout(self, timeout)
52-
self.connect_timeout = timeout
132+
self.config.connect_timeout = timeout
53133
end
54134

55135

56136
function _M.set_read_timeout(self, timeout)
57-
self.read_timeout = timeout
137+
self.config.read_timeout = timeout
58138
end
59139

60140

61141
function _M.set_connection_options(self, options)
62-
self.connection_options = options
142+
self.config.connection_options = options
63143
end
64144

65145

66146
local function parse_dsn(params)
67147
local url = params.url
68-
if url then
148+
if url and url ~= "" then
69149
local url_pattern = [[^(?:(redis|sentinel)://)(?:([^@]*)@)?([^:/]+)(?::(\d+|[msa]+))/?(.*)$]]
70-
local m, err = ngx_re_match(url, url_pattern, "")
150+
151+
local m, err = ngx_re_match(url, url_pattern, "oj")
71152
if not m then
72-
ngx_log(ngx_ERR, "could not parse DSN: ", err)
73-
else
74-
local fields
75-
if m[1] == "redis" then
76-
fields = { "password", "host", "port", "db" }
77-
elseif m[1] == "sentinel" then
78-
fields = { "password", "master_name", "role", "db" }
79-
end
153+
return nil, "could not parse DSN: " .. err
154+
end
80155

81-
-- password may not be present
82-
if #m < 5 then tbl_remove(fields, 1) end
156+
local fields
157+
if m[1] == "redis" then
158+
fields = { "password", "host", "port", "db" }
159+
elseif m[1] == "sentinel" then
160+
fields = { "password", "master_name", "role", "db" }
161+
end
83162

84-
local roles = { m = "master", s = "slave", a = "any" }
163+
-- password may not be present
164+
if #m < 5 then tbl_remove(fields, 1) end
85165

86-
for i,v in ipairs(fields) do
87-
params[v] = m[i + 1]
88-
if v == "role" then
89-
params[v] = roles[params[v]]
90-
end
166+
local roles = { m = "master", s = "slave", a = "any" }
167+
168+
for i,v in ipairs(fields) do
169+
params[v] = m[i + 1]
170+
if v == "role" then
171+
params[v] = roles[params[v]]
91172
end
92173
end
93174
end
175+
176+
return true, nil
94177
end
95178

96179

97180
function _M.connect(self, params)
98-
-- If we have nothing, assume default host connection options apply
99-
if not params or type(params) ~= "table" then
100-
params = {}
101-
end
181+
local params = tbl_copy_merge_defaults(params, self.config)
102182

103183
if params.url then
104-
parse_dsn(params)
184+
local ok, err = parse_dsn(params)
185+
if not ok then ngx_log(ngx_ERR, err) end
105186
end
106187

107-
if params.sentinels then
108-
setmetatable(params, { __index = DEFAULTS } )
188+
if #params.sentinels > 0 then
109189
return self:connect_via_sentinel(params)
110-
elseif params.startup_cluster_nodes then
111-
setmetatable(params, { __index = DEFAULTS } )
112-
-- TODO: Implement cluster
113-
return nil, "Redis Cluster not yet implemented"
114190
else
115-
setmetatable(params, { __index = DEFAULTS } )
116191
return self:connect_to_host(params)
117192
end
118193
end
@@ -202,19 +277,21 @@ end
202277

203278
function _M.connect_to_host(self, host)
204279
local r = redis.new()
205-
r:set_timeout(self.connect_timeout)
280+
local config = self.config
281+
r:set_timeout(config.connect_timeout)
206282

207283
local ok, err
208284
local socket = host.socket
285+
local opts = config.connection_options
209286
if socket then
210-
if self.connection_options then
211-
ok, err = r:connect(socket, self.connection_options)
287+
if opts then
288+
ok, err = r:connect(socket, config.connection_options)
212289
else
213290
ok, err = r:connect(socket)
214291
end
215292
else
216-
if self.connection_options then
217-
ok, err = r:connect(host.host, host.port, self.connection_options)
293+
if opts then
294+
ok, err = r:connect(host.host, host.port, config.connection_options)
218295
else
219296
ok, err = r:connect(host.host, host.port)
220297
end
@@ -224,10 +301,10 @@ function _M.connect_to_host(self, host)
224301
ngx_log(ngx_ERR, err, " for ", host.host, ":", host.port)
225302
return nil, err
226303
else
227-
r:set_timeout(self, self.read_timeout)
304+
r:set_timeout(self, config.read_timeout)
228305

229306
local password = host.password
230-
if password then
307+
if password and password ~= "" then
231308
local res, err = r:auth(password)
232309
if err then
233310
ngx_log(ngx_ERR, err)
@@ -241,4 +318,17 @@ function _M.connect_to_host(self, host)
241318
end
242319

243320

244-
return _M
321+
local function set_keepalive(self, redis)
322+
-- Restore connection to "NORMAL" before putting into keepalive pool,
323+
-- ignoring any errors.
324+
redis:discard()
325+
326+
local config = self.config
327+
return redis:set_keepalive(
328+
config.keepalive_timeout, config.keepalive_poolsize
329+
)
330+
end
331+
_M.set_keepalive = set_keepalive
332+
333+
334+
return setmetatable(_M, fixed_field_metatable)

0 commit comments

Comments
 (0)