Codebase list lua-nginx-redis-connector / 37dc6d5
New upstream version 0.04 ChangZhuo Chen (陳昌倬) 6 years ago
14 changed file(s) with 1007 addition(s) and 523 deletion(s). Raw diff Collapse all Expand all
0 *.t linguist-language=lua
00 t/servroot/
11 t/error.log
2 luacov.*
0 modules = {
1 ["resty.redis.connector"] = "lib/resty/redis/connector.lua",
2 ["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua",
3 }
00 SHELL := /bin/bash # Cheat by using bash :)
11
2 OPENRESTY_PREFIX = /usr/local/openresty-debug
2 OPENRESTY_PREFIX = /usr/local/openresty
33
44 TEST_FILE ?= t
55 SENTINEL_TEST_FILE ?= $(TEST_FILE)/sentinel
2020 REDIS_SLAVE_ARG := --slaveof 127.0.0.1 $(REDIS_FIRST_PORT)
2121 REDIS_CLI := redis-cli -p $(REDIS_FIRST_PORT) -n $(TEST_REDIS_DATABASE)
2222
23 # Override socket for running make test on its own
23 # Override socket for running make test on its own
2424 # (make test TEST_REDIS_SOCKET=/path/to/sock.sock)
2525 TEST_REDIS_SOCKET ?= $(REDIS_PREFIX)$(REDIS_FIRST_PORT)$(REDIS_SOCK)
2626
9494 ) true
9595
9696
97 stop_redis_instances: delete_sentinel_config
97 stop_redis_instances: delete_sentinel_config
9898 -@$(foreach port,$(TEST_REDIS_PORTS) $(TEST_SENTINEL_PORTS), \
9999 $(MAKE) stop_redis_instance cleanup_redis_instance port=$(port) \
100100 prefix=$(REDIS_PREFIX)$(port) && \
140140 @$(foreach port,$(REDIS_PORTS),! lsof -i :$(port) &&) true 2>&1 > /dev/null
141141
142142 test_redis: flush_db
143 util/lua-releng
144 @rm -f luacov.stats.out
143145 $(TEST_REDIS_VARS) $(PROVE) $(TEST_FILE)
144 util/lua-releng
146 @luacov
147 @tail -7 luacov.report.out
145148
146149 test_leak: flush_db
147150 $(TEST_REDIS_VARS) TEST_NGINX_CHECK_LEAK=1 $(PROVE) $(TEST_FILE)
00 # lua-resty-redis-connector
11
2 Connection utilities for [lua-resty-redis](https://github.com/openresty/lua-resty-redis), making
3 it easy and reliable to connect to Redis hosts, either directly or via
4 [Redis Sentinel](http://redis.io/topics/sentinel).
2 Connection utilities for [lua-resty-redis](https://github.com/openresty/lua-resty-redis), making it easy and reliable to connect to Redis hosts, either directly or via [Redis Sentinel](http://redis.io/topics/sentinel).
53
64
75 ## Synopsis
86
9 ```lua
10 local redis_connector = require "resty.redis.connector"
11 local rc = redis_connector.new()
12
13 local redis, err = rc:connect{ url = "redis://PASSWORD@127.0.0.1:6379/2" }
14
15 -- or...
16
17 local redis, err = rc:connect{
7 Quick and simple authenticated connection on localhost to DB 2:
8
9 ```lua
10 local redis, err = require("resty.redis.connector").new({
11 url = "redis://PASSWORD@127.0.0.1:6379/2",
12 }):connect()
13 ```
14
15 More verbose configuration, with timeouts and a default password:
16
17 ```lua
18 local rc = require("resty.redis.connector").new({
19 connect_timeout = 50,
20 read_timeout = 5000,
21 keepalive_timeout = 30000,
22 password = "mypass",
23 })
24
25 local redis, err = rc:connect({
26 url = "redis://127.0.0.1:6379/2",
27 })
28
29 -- ...
30
31 local ok, err = rc:set_keepalive(redis) -- uses keepalive params
32 ```
33
34 Keep all config in a table, to easily create / close connections as needed:
35
36 ```lua
37 local rc = require("resty.redis.connector").new({
38 connect_timeout = 50,
39 read_timeout = 5000,
40 keepalive_timeout = 30000,
41
1842 host = "127.0.0.1",
1943 port = 6379,
2044 db = 2,
21 password = "PASSWORD",
22 }
23
24 if not redis then
25 ngx.log(ngx.ERR, err)
26 end
27 ```
45 password = "mypass",
46 })
47
48 local redis, err = rc:connect()
49
50 -- ...
51
52 local ok, err = rc:set_keepalive(redis)
53 ```
54
55 `connect` can be used to override defaults given in `new`
56
57
58 ```lua
59 local rc = require("resty.redis.connector").new({
60 host = "127.0.0.1",
61 port = 6379,
62 db = 2,
63 })
64
65 local redis, err = rc:connect({
66 db = 5,
67 })
68 ```
69
2870
2971 ## DSN format
3072
31 The [connect](#connect) method accepts a single table of named arguments. If the `url` field is
32 present then it will be parsed, overriding values supplied in the parameters table.
73 If the `params.url` field is present then it will be parsed, overriding values supplied in the parameters table.
74
75 ### Direct Redis connections
3376
3477 The format for connecting directly to Redis is:
3578
3679 `redis://PASSWORD@HOST:PORT/DB`
3780
3881 The `PASSWORD` and `DB` fields are optional, all other components are required.
82
83 ### Connections via Redis Sentinel
3984
4085 When connecting via Redis Sentinel, the format is as follows:
4186
4792 * `s`: slave
4893 * `a`: any (first tries the master, but will failover to a slave if required)
4994
50
51 ## Parameters
52
53 The [connect](#connect) method expects the following field values, either by falling back to
54 defaults, populating the fields by parsing the DSN, or being specified directly.
55
56 The defaults are as follows:
57
58
59 ```lua
60 {
61 host = "127.0.0.1",
62 port = "6379",
63 path = nil, -- unix socket path, e.g. /tmp/redis.sock
64 password = "",
65 db = 0,
66 master_name = "mymaster",
67 role = "master", -- master | slave | any
68 sentinels = nil,
69 }
70 ```
71
72 Note that if `sentinel://` is supplied as the `url` parameter, a table of `sentinels` must also
73 be supplied. e.g.
95 A table of `sentinels` must also be supplied. e.g.
7496
7597 ```lua
7698 local redis, err = rc:connect{
82104 ```
83105
84106
107 ## Default Parameters
108
109
110 ```lua
111 {
112 connect_timeout = 100,
113 read_timeout = 1000,
114 connection_options = {}, -- pool, etc
115 keepalive_timeout = 60000,
116 keepalive_poolsize = 30,
117
118 host = "127.0.0.1",
119 port = "6379",
120 path = "", -- unix socket path, e.g. /tmp/redis.sock
121 password = "",
122 db = 0,
123
124 master_name = "mymaster",
125 role = "master", -- master | slave | any
126 sentinels = {},
127 }
128 ```
129
130
85131 ## API
86132
87133 * [new](#new)
88 * [set_connect_timeout](#set_connect_timeout)
89 * [set_read_timeout](#set_read_timeout)
90 * [set_connection_options](#set_connection_options)
91134 * [connect](#connect)
92135 * [Utilities](#utilities)
93136 * [connect_via_sentinel](#connect_via_sentinel)
94137 * [try_hosts](#try_hosts)
95138 * [connect_to_host](#connect_to_host)
96 * [Sentinel Utilities](#sentinel-utilities)
97139 * [sentinel.get_master](#sentinelget_master)
98140 * [sentinel.get_slaves](#sentinelget_slaves)
99141
105147 Creates the Redis Connector object. In case of failures, returns `nil` and a string describing the error.
106148
107149
108 ### set_connect_timeout
109
110 `syntax: rc:set_connect_timeout(100)`
111
112 Sets the cosocket connection timeout, in ms.
113
114
115
116 ### set_read_timeout
117
118 `syntax: rc:set_read_timeout(500)`
119
120 Sets the cosocket read timeout, in ms.
121
122
123 ### set_connection_options
124
125 `syntax: rc:set_connection_options({ pool = params.host .. ":" .. params.port .. "/" .. params.db })`
126
127 Sets the connection options table, as supplied to [tcpsock:connect](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
128 method.
129
130
131150 ### connect
132151
133152 `syntax: redis, err = rc:connect(params)`
134153
135 Attempts to create a connection, according to the [params](#parameters) supplied.
154 Attempts to create a connection, according to the [params](#parameters) supplied. If a connection cannot be made, returns `nil` and a string describing the reason.
136155
137156
138157 ## Utilities
158
159 The following methods are not typically needed, but may be useful if a custom interface is required.
160
139161
140162 ### connect_via_sentinel
141163
159181 Attempts to connect to the supplied `host`.
160182
161183
162 ## Sentinel Utilities
163
164184 ### sentinel.get_master
165185
166186 `syntax: master, err = sentinel.get_master(sentinel, master_name)`
175195 Given a connected Sentinel instance and a master name, will return a list of registered slave Redis instances.
176196
177197
178 ## TODO
179
180 * Redis Cluster support.
181
182
183198 # Author
184199
185200 James Hurst <james@pintsized.co.uk>
189204
190205 This module is licensed under the 2-clause BSD license.
191206
192 Copyright (c) 2015, James Hurst <james@pintsized.co.uk>
207 Copyright (c) James Hurst <james@pintsized.co.uk>
193208
194209 All rights reserved.
195210
0 name=lua-resty-redis-connector
1 abstract=Connection utilities for lua-resty-redis, making it easy and reliable to connect to Redis hosts, either directly or via Redis Sentinel.
2 author=James Hurst
3 is_original=yes
4 license=2bsd
5 lib_dir=lib
6 doc_dir=lib
7 repo_link=https://github.com/pintsized/lua-resty-redis-connector
8 main_module=lib/resty/redis/connector.lua
0 local redis = require "resty.redis"
1 redis.add_commands("sentinel")
2 local sentinel = require "resty.redis.sentinel"
3
4
5 local ipairs, setmetatable, pcall = ipairs, setmetatable, pcall
6 local ngx_null = ngx.null
0 local ipairs, pcall, error, tostring, type, next, setmetatable, getmetatable =
1 ipairs, pcall, error, tostring, type, next, setmetatable, getmetatable
2
73 local ngx_log = ngx.log
8 local ngx_DEBUG = ngx.DEBUG
94 local ngx_ERR = ngx.ERR
105 local ngx_re_match = ngx.re.match
11 local tbl_insert = table.insert
6
127 local tbl_remove = table.remove
138 local tbl_sort = table.sort
14
159 local ok, tbl_new = pcall(require, "table.new")
1610 if not ok then
1711 tbl_new = function (narr, nrec) return {} end
1812 end
1913
20
21 local _M = {
22 _VERSION = '0.03',
14 local redis = require("resty.redis")
15 redis.add_commands("sentinel")
16
17 local get_master = require("resty.redis.sentinel").get_master
18 local get_slaves = require("resty.redis.sentinel").get_slaves
19
20
21 -- A metatable which prevents undefined fields from being created / accessed
22 local fixed_field_metatable = {
23 __index =
24 function(t, k)
25 error("field " .. tostring(k) .. " does not exist", 3)
26 end,
27 __newindex =
28 function(t, k, v)
29 error("attempt to create new field " .. tostring(k), 3)
30 end,
2331 }
2432
25 local mt = { __index = _M }
26
27
28 local DEFAULTS = {
33
34 -- Returns a new table, recursively copied from the one given, retaining
35 -- metatable assignment.
36 --
37 -- @param table table to be copied
38 -- @return table
39 local function tbl_copy(orig)
40 local orig_type = type(orig)
41 local copy
42 if orig_type == "table" then
43 copy = {}
44 for orig_key, orig_value in next, orig, nil do
45 copy[tbl_copy(orig_key)] = tbl_copy(orig_value)
46 end
47 setmetatable(copy, tbl_copy(getmetatable(orig)))
48 else -- number, string, boolean, etc
49 copy = orig
50 end
51 return copy
52 end
53
54
55 -- Returns a new table, recursively copied from the combination of the given
56 -- table `t1`, with any missing fields copied from `defaults`.
57 --
58 -- If `defaults` is of type "fixed field" and `t1` contains a field name not
59 -- present in the defults, an error will be thrown.
60 --
61 -- @param table t1
62 -- @param table defaults
63 -- @return table a new table, recursively copied and merged
64 local function tbl_copy_merge_defaults(t1, defaults)
65 if t1 == nil then t1 = {} end
66 if defaults == nil then defaults = {} end
67 if type(t1) == "table" and type(defaults) == "table" then
68 local mt = getmetatable(defaults)
69 local copy = {}
70 for t1_key, t1_value in next, t1, nil do
71 copy[tbl_copy(t1_key)] = tbl_copy_merge_defaults(
72 t1_value, tbl_copy(defaults[t1_key])
73 )
74 end
75 for defaults_key, defaults_value in next, defaults, nil do
76 if t1[defaults_key] == nil then
77 copy[tbl_copy(defaults_key)] = tbl_copy(defaults_value)
78 end
79 end
80 return copy
81 else
82 return t1 -- not a table
83 end
84 end
85
86
87 local DEFAULTS = setmetatable({
88 connect_timeout = 100,
89 read_timeout = 1000,
90 connection_options = {}, -- pool, etc
91 keepalive_timeout = 60000,
92 keepalive_poolsize = 30,
93
2994 host = "127.0.0.1",
3095 port = 6379,
31 path = nil, -- /tmp/redis.sock
32 password = nil,
96 path = "", -- /tmp/redis.sock
97 password = "",
3398 db = 0,
99 url = "", -- DSN url
100
34101 master_name = "mymaster",
35 role = "master", -- master | slave | any (tries master first, failover to a slave)
36 sentinels = nil,
37 cluster_startup_nodes = {},
102 role = "master", -- master | slave | any
103 sentinels = {},
104
105 }, fixed_field_metatable)
106
107
108 local _M = {
109 _VERSION = '0.04',
38110 }
39111
40
41 function _M.new()
42 return setmetatable({
43 connect_timeout = 100,
44 read_timeout = 1000,
45 connection_options = nil, -- pool, etc
46 }, mt)
47 end
48
49
50 function _M.set_connect_timeout(self, timeout)
51 self.connect_timeout = timeout
52 end
53
54
55 function _M.set_read_timeout(self, timeout)
56 self.read_timeout = timeout
57 end
58
59
60 function _M.set_connection_options(self, options)
61 self.connection_options = options
112 local mt = { __index = _M }
113
114
115 function _M.new(config)
116 local ok, config = pcall(tbl_copy_merge_defaults, config, DEFAULTS)
117 if not ok then
118 return nil, config -- err
119 else
120 return setmetatable({
121 config = setmetatable(config, fixed_field_metatable)
122 }, mt)
123 end
62124 end
63125
64126
65127 local function parse_dsn(params)
66128 local url = params.url
67 if url then
129 if url and url ~= "" then
68130 local url_pattern = [[^(?:(redis|sentinel)://)(?:([^@]*)@)?([^:/]+)(?::(\d+|[msa]+))/?(.*)$]]
69 local m, err = ngx_re_match(url, url_pattern, "")
131
132 local m, err = ngx_re_match(url, url_pattern, "oj")
70133 if not m then
71 ngx_log(ngx_ERR, "could not parse DSN: ", err)
72 else
73 local fields
74 if m[1] == "redis" then
75 fields = { "password", "host", "port", "db" }
76 elseif m[1] == "sentinel" then
77 fields = { "password", "master_name", "role", "db" }
134 return nil, "could not parse DSN: " .. tostring(err)
135 end
136
137 local fields
138 if m[1] == "redis" then
139 fields = { "password", "host", "port", "db" }
140 elseif m[1] == "sentinel" then
141 fields = { "password", "master_name", "role", "db" }
142 end
143
144 -- password may not be present
145 if #m < 5 then tbl_remove(fields, 1) end
146
147 local roles = { m = "master", s = "slave", a = "any" }
148
149 for i,v in ipairs(fields) do
150 params[v] = m[i + 1]
151 if v == "role" then
152 params[v] = roles[params[v]]
78153 end
79
80 -- password may not be present
81 if #m < 5 then tbl_remove(fields, 1) end
82
83 local roles = { m = "master", s = "slave", a = "any" }
84
85 for i,v in ipairs(fields) do
86 params[v] = m[i + 1]
87 if v == "role" then
88 params[v] = roles[params[v]]
89 end
90 end
91 end
92 end
93 end
154 end
155 end
156
157 return true, nil
158 end
159 _M.parse_dsn = parse_dsn
94160
95161
96162 function _M.connect(self, params)
97 -- If we have nothing, assume default host connection options apply
98 if not params or type(params) ~= "table" then
99 params = {}
100 end
101
102 if params.url then
103 parse_dsn(params)
104 end
105
106 if params.sentinels then
107 setmetatable(params, { __index = DEFAULTS } )
163 local params = tbl_copy_merge_defaults(params, self.config)
164
165 if params.url then
166 local ok, err = parse_dsn(params)
167 if not ok then ngx_log(ngx_ERR, err) end
168 end
169
170 if #params.sentinels > 0 then
108171 return self:connect_via_sentinel(params)
109 elseif params.startup_cluster_nodes then
110 setmetatable(params, { __index = DEFAULTS } )
111 -- TODO: Implement cluster
112 return nil, "Redis Cluster not yet implemented"
113 else
114 setmetatable(params, { __index = DEFAULTS } )
172 else
115173 return self:connect_to_host(params)
116174 end
117175 end
118176
119177
120178 local function sort_by_localhost(a, b)
121 if a.host == "127.0.0.1" then
179 if a.host == "127.0.0.1" and b.host ~= "127.0.0.1" then
122180 return true
123181 else
124182 return false
139197 end
140198
141199 if role == "master" or role == "any" then
142 local master, err = sentinel.get_master(sentnl, master_name)
200 local master, err = get_master(sentnl, master_name)
143201 if master then
144202 master.db = db
145203 master.password = password
157215 end
158216
159217 -- We either wanted a slave, or are failing over to a slave "any"
160 local slaves, err = sentinel.get_slaves(sentnl, master_name)
218 local slaves, err = get_slaves(sentnl, master_name)
161219 sentnl:set_keepalive()
162220
163221 if not slaves then
187245 -- the last error received, and previous_errors is a table of the previous errors.
188246 function _M.try_hosts(self, hosts)
189247 local errors = tbl_new(#hosts, 0)
190
248
191249 for i, host in ipairs(hosts) do
192 local r
193 r, errors[i] = self:connect_to_host(host)
194 if r then
195 return r, tbl_remove(errors), errors
196 end
197 end
198 return nil, tbl_remove(errors), errors
250 local r, err = self:connect_to_host(host)
251 if r and not err then
252 return r, nil, errors
253 else
254 errors[i] = err
255 end
256 end
257
258 return nil, "no hosts available", errors
199259 end
200260
201261
202262 function _M.connect_to_host(self, host)
203263 local r = redis.new()
204 r:set_timeout(self.connect_timeout)
264 local config = self.config
265 r:set_timeout(config.connect_timeout)
205266
206267 local ok, err
207 local socket = host.socket
208 if socket then
209 if self.connection_options then
210 ok, err = r:connect(socket, self.connection_options)
268 local path = host.path
269 local opts = config.connection_options
270 if path and path ~= "" then
271 if opts then
272 ok, err = r:connect(path, config.connection_options)
211273 else
212 ok, err = r:connect(socket)
213 end
214 else
215 if self.connection_options then
216 ok, err = r:connect(host.host, host.port, self.connection_options)
274 ok, err = r:connect(path)
275 end
276 else
277 if opts then
278 ok, err = r:connect(host.host, host.port, config.connection_options)
217279 else
218280 ok, err = r:connect(host.host, host.port)
219281 end
220282 end
221283
222284 if not ok then
223 ngx_log(ngx_ERR, err, " for ", host.host, ":", host.port)
224285 return nil, err
225286 else
226 r:set_timeout(self, self.read_timeout)
287 r:set_timeout(self, config.read_timeout)
227288
228289 local password = host.password
229 if password then
290 if password and password ~= "" then
230291 local res, err = r:auth(password)
231292 if err then
232293 ngx_log(ngx_ERR, err)
234295 end
235296 end
236297
237 r:select(host.db)
298 if host.db ~= nil then
299 r:select(host.db)
300 end
238301 return r, nil
239302 end
240303 end
241304
242305
243 return _M
306 local function set_keepalive(self, redis)
307 -- Restore connection to "NORMAL" before putting into keepalive pool,
308 -- ignoring any errors.
309 redis:discard()
310
311 local config = self.config
312 return redis:set_keepalive(
313 config.keepalive_timeout, config.keepalive_poolsize
314 )
315 end
316 _M.set_keepalive = set_keepalive
317
318
319
320 -- Deprecated: use config table in new() or connect() instead.
321 function _M.set_connect_timeout(self, timeout)
322 self.config.connect_timeout = timeout
323 end
324
325
326 -- Deprecated: use config table in new() or connect() instead.
327 function _M.set_read_timeout(self, timeout)
328 self.config.read_timeout = timeout
329 end
330
331
332 -- Deprecated: use config table in new() or connect() instead.
333 function _M.set_connection_options(self, options)
334 self.config.connection_options = options
335 end
336
337
338 return setmetatable(_M, fixed_field_metatable)
00 local ipairs, type = ipairs, type
1
12 local ngx_null = ngx.null
3
24 local tbl_insert = table.insert
3
45 local ok, tbl_new = pcall(require, "table.new")
56 if not ok then
67 tbl_new = function (narr, nrec) return {} end
78 end
89
910
10 local _M = {}
11 _M._VERSION = 0.03
11 local _M = {
12 _VERSION = '0.04'
13 }
1214
1315
1416 function _M.get_master(sentinel, master_name)
+0
-27
lua-resty-redis-connector-0.02-0.rockspec less more
0 package = "lua-resty-redis-connector"
1 version = "0.02-0"
2 source = {
3 url = "git://github.com/pintsized/lua-resty-redis-connector",
4 tag = "v0.02"
5 }
6 description = {
7 summary = "Connection utilities for lua-resty-redis.",
8 detailed = [[
9 Connection utilities for lua-resty-redis, making it easy and
10 reliable to connect to Redis hosts, either directly or via Redis
11 Sentinel.
12 ]],
13 homepage = "https://github.com/pintsized/lua-resty-redis-connector",
14 license = "2-clause BSD",
15 maintainer = "James Hurst <james@pintsized.co.uk>"
16 }
17 dependencies = {
18 "lua >= 5.1",
19 }
20 build = {
21 type = "builtin",
22 modules = {
23 ["resty.redis.connector"] = "lib/resty/redis/connector.lua",
24 ["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua"
25 }
26 }
0 package = "lua-resty-redis-connector"
1 version = "0.04-0"
2 source = {
3 url = "git://github.com/pintsized/lua-resty-redis-connector",
4 tag = "v0.04"
5 }
6 description = {
7 summary = "Connection utilities for lua-resty-redis.",
8 detailed = [[
9 Connection utilities for lua-resty-redis, making it easy and
10 reliable to connect to Redis hosts, either directly or via Redis
11 Sentinel.
12 ]],
13 homepage = "https://github.com/pintsized/lua-resty-redis-connector",
14 license = "2-clause BSD",
15 maintainer = "James Hurst <james@pintsized.co.uk>"
16 }
17 dependencies = {
18 "lua >= 5.1",
19 }
20 build = {
21 type = "builtin",
22 modules = {
23 ["resty.redis.connector"] = "lib/resty/redis/connector.lua",
24 ["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua"
25 }
26 }
0 use Test::Nginx::Socket::Lua;
1 use Cwd qw(cwd);
2
3 repeat_each(2);
4
5 plan tests => repeat_each() * blocks() * 2;
6
7 my $pwd = cwd();
8
9 our $HttpConfig = qq{
10 lua_package_path "$pwd/lib/?.lua;;";
11
12 init_by_lua_block {
13 require("luacov.runner").init()
14 }
15 };
16
17 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
18
19 no_long_string();
20 run_tests();
21
22 __DATA__
23
24 === TEST 1: Default config
25 --- http_config eval: $::HttpConfig
26 --- config
27 location /t {
28 content_by_lua_block {
29 local rc = require("resty.redis.connector").new()
30
31 local redis = assert(rc:connect(), "rc:connect should return postively")
32 assert(redis:set("foo", "bar"), "redis:set should return positvely")
33 assert(redis:get("foo") == "bar", "get(foo) should return bar")
34 redis:close()
35 }
36 }
37 --- request
38 GET /t
39 --- no_error_log
40 [error]
41
42
43 === TEST 2: Defaults via new
44 --- http_config eval: $::HttpConfig
45 --- config
46 location /t {
47 content_by_lua_block {
48 local config = {
49 connect_timeout = 500,
50 port = 6380,
51 db = 6,
52 }
53 local rc = require("resty.redis.connector").new(config)
54
55 assert(config ~= rc.config, "config should not equal rc.config")
56 assert(rc.config.connect_timeout == 500, "connect_timeout should be 500")
57 assert(rc.config.db == 6, "db should be 6")
58 assert(rc.config.role == "master", "role should be master")
59 }
60 }
61 --- request
62 GET /t
63 --- no_error_log
64 [error]
65
66
67 === TEST 3: Config via connect still overrides
68 --- http_config eval: $::HttpConfig
69 --- config
70 location /t {
71 content_by_lua_block {
72 local rc = require("resty.redis.connector").new({
73 connect_timeout = 500,
74 port = 6380,
75 db = 6,
76 keepalive_poolsize = 10,
77 })
78
79 assert(config ~= rc.config, "config should not equal rc.config")
80 assert(rc.config.connect_timeout == 500, "connect_timeout should be 500")
81 assert(rc.config.db == 6, "db should be 6")
82 assert(rc.config.role == "master", "role should be master")
83 assert(rc.config.keepalive_poolsize == 10,
84 "keepalive_poolsize should be 10")
85
86 local redis = assert(rc:connect({ port = 6379 }),
87 "rc:connect should return positively")
88 }
89 }
90 --- request
91 GET /t
92 --- no_error_log
93 [error]
94
95
96 === TEST 4: Unknown config errors, all config does not error
97 --- http_config eval: $::HttpConfig
98 --- config
99 location /t {
100 content_by_lua_block {
101 local rc, err = require("resty.redis.connector").new({
102 connect_timeout = 500,
103 port = 6380,
104 db = 6,
105 foo = "bar",
106 })
107
108 assert(rc == nil, "rc should be nil")
109 assert(string.find(err, "field foo does not exist"),
110 "err should contain error")
111
112 -- Provide all options, without errors
113
114 assert(require("resty.redis.connector").new({
115 connect_timeout = 100,
116 read_timeout = 1000,
117 connection_options = { pool = "<host>::<port>" },
118 keepalive_timeout = 60000,
119 keepalive_poolsize = 30,
120
121 host = "127.0.0.1",
122 port = 6379,
123 path = "",
124 password = "",
125 db = 0,
126
127 url = "",
128
129 master_name = "mymaster",
130 role = "master",
131 sentinels = {},
132 }), "new should return positively")
133
134 -- Provide all options via connect, without errors
135
136 local rc = require("resty.redis.connector").new()
137
138 assert(rc:connect({
139 connect_timeout = 100,
140 read_timeout = 1000,
141 connection_options = { pool = "<host>::<port>" },
142 keepalive_timeout = 60000,
143 keepalive_poolsize = 30,
144
145 host = "127.0.0.1",
146 port = 6379,
147 path = "",
148 password = "",
149 db = 0,
150
151 url = "",
152
153 master_name = "mymaster",
154 role = "master",
155 sentinels = {},
156 }), "rc:connect should return positively")
157 }
158 }
159 --- request
160 GET /t
161 --- no_error_log
162 [error]
0 # vim:set ft= ts=4 sw=4 et:
1
2 use Test::Nginx::Socket::Lua;
0 use Test::Nginx::Socket 'no_plan';
31 use Cwd qw(cwd);
42
5 repeat_each(2);
6
7 plan tests => repeat_each() * (3 * blocks());
8
93 my $pwd = cwd();
104
115 our $HttpConfig = qq{
12 lua_package_path "$pwd/lib/?.lua;;";
6 lua_package_path "$pwd/lib/?.lua;;";
7
8 init_by_lua_block {
9 require("luacov.runner").init()
10 }
1311 };
1412
1513 $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
1614 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
1715
1816 no_long_string();
19 #no_diff();
20
2117 run_tests();
2218
2319 __DATA__
2420
25 === TEST 1: basic
26 --- http_config eval: $::HttpConfig
27 --- config
28 location /t {
29 content_by_lua '
30 local redis_connector = require "resty.redis.connector"
31 local rc = redis_connector.new()
32
33 local params = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT }
34
35 local redis, err = rc:connect(params)
36 if not redis then
37 ngx.say("failed to connect: ", err)
38 return
39 end
40
41 local res, err = redis:set("dog", "an animal")
42 if not res then
43 ngx.say("failed to set dog: ", err)
44 return
45 end
46
47 ngx.say("set dog: ", res)
48
49 redis:close()
50 ';
51 }
52 --- request
53 GET /t
54 --- response_body
55 set dog: OK
56 --- no_error_log
57 [error]
58
59
60 === TEST 2: test we can try a list of hosts, and connect to the first working one
61 --- http_config eval: $::HttpConfig
62 --- config
63 location /t {
64 content_by_lua '
65 local redis_connector = require "resty.redis.connector"
66 local rc = redis_connector.new()
67 rc:set_connect_timeout(100)
68
69 local hosts = {
70 { host = "127.0.0.1", port = 1 },
71 { host = "127.0.0.1", port = 2 },
72 { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT },
73 }
74
75 local redis, err, previous_errors = rc:try_hosts(hosts)
76 if not redis then
77 ngx.say("failed to connect: ", err)
78 return
79 end
80
81 -- Print the failed connection errors
82 ngx.say("connection 1 error: ", err)
83
84 ngx.say("connection 2 error: ", previous_errors[1])
85
86 local res, err = redis:set("dog", "an animal")
87 if not res then
88 ngx.say("failed to set dog: ", err)
89 return
90 end
91
92 ngx.say("set dog: ", res)
93
94
95 redis:close()
96 ';
97 }
98 --- request
99 GET /t
100 --- response_body
101 connection 1 error: connection refused
102 connection 2 error: connection refused
103 set dog: OK
21 === TEST 1: basic connect
22 --- http_config eval: $::HttpConfig
23 --- config
24 location /t {
25 content_by_lua_block {
26 local rc = require("resty.redis.connector").new({
27 port = $TEST_NGINX_REDIS_PORT
28 })
29
30 local redis, err = assert(rc:connect(params),
31 "connect should return positively")
32
33 assert(redis:set("dog", "an animal"),
34 "redis:set should return positively")
35
36 redis:close()
37 }
38 }
39 --- request
40 GET /t
41 --- no_error_log
42 [error]
43
44
45 === TEST 2: try_hosts
46 --- http_config eval: $::HttpConfig
47 --- config
48 location /t {
49 lua_socket_log_errors off;
50 content_by_lua_block {
51 local rc = require("resty.redis.connector").new({
52 connect_timeout = 100,
53 })
54
55 local hosts = {
56 { host = "127.0.0.1", port = 1 },
57 { host = "127.0.0.1", port = 2 },
58 { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT },
59 }
60
61 local redis, err, previous_errors = rc:try_hosts(hosts)
62 assert(redis and not err,
63 "try_hosts should return a connection and no error")
64
65 assert(previous_errors[1] == "connection refused",
66 "previous_errors[1] should be 'connection refused'")
67 assert(previous_errors[2] == "connection refused",
68 "previous_errors[2] should be 'connection refused'")
69
70 assert(redis:set("dog", "an animal"),
71 "redis connection should be working")
72
73 redis:close()
74
75 local hosts = {
76 { host = "127.0.0.1", port = 1 },
77 { host = "127.0.0.1", port = 2 },
78 }
79
80 local redis, err, previous_errors = rc:try_hosts(hosts)
81 assert(not redis and err == "no hosts available",
82 "no available hosts should return an error")
83
84 assert(previous_errors[1] == "connection refused",
85 "previous_errors[1] should be 'connection refused'")
86 assert(previous_errors[2] == "connection refused",
87 "previous_errors[2] should be 'connection refused'")
88 }
89 }
90 --- request
91 GET /t
92 --- no_error_log
93 [error]
94
95
96 === TEST 3: connect_to_host
97 --- http_config eval: $::HttpConfig
98 --- config
99 location /t {
100 content_by_lua_block {
101 local rc = require("resty.redis.connector").new()
102
103 local host = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT }
104
105 local redis, err = rc:connect_to_host(host)
106 assert(redis and not err,
107 "connect_to_host should return positively")
108
109 assert(redis:set("dog", "an animal"),
110 "redis connection should be working")
111
112 redis:close()
113 }
114 }
115 --- request
116 GET /t
117 --- no_error_log
118 [error]
119
120
121 === TEST 4: connect_to_host options ignore defaults
122 --- http_config eval: $::HttpConfig
123 --- config
124 location /t {
125 content_by_lua_block {
126 local rc = require("resty.redis.connector").new({
127 port = $TEST_NGINX_REDIS_PORT,
128 db = 2,
129 })
130
131 local redis, err = assert(rc:connect_to_host({
132 host = "127.0.0.1",
133 db = 1,
134 port = $TEST_NGINX_REDIS_PORT
135 }), "connect_to_host should return positively")
136
137 assert(redis:set("dog", "an animal") == "OK",
138 "set should return 'OK'")
139
140 redis:select(2)
141 assert(redis:get("dog") == ngx.null,
142 "dog should not exist in db 2")
143
144 redis:select(1)
145 assert(redis:get("dog") == "an animal",
146 "dog should be 'an animal' in db 1")
147
148 redis:close()
149 }
150 }
151 --- request
152 GET /t
153 --- no_error_log
154 [error]
155
156
157 === TEST 5: Test set_keepalive method
158 --- http_config eval: $::HttpConfig
159 --- config
160 location /t {
161 lua_socket_log_errors Off;
162 content_by_lua_block {
163 local rc = require("resty.redis.connector").new({
164 port = $TEST_NGINX_REDIS_PORT,
165 })
166
167 local redis = assert(rc:connect(),
168 "rc:connect should return positively")
169 local ok, err = rc:set_keepalive(redis)
170 assert(not err, "set_keepalive error should be nil")
171
172 local ok, err = redis:set("foo", "bar")
173 assert(not ok, "ok should be nil")
174 assert(string.find(err, "closed"), "error should contain 'closed'")
175
176 local redis = assert(rc:connect(), "connect should return positively")
177 assert(redis:subscribe("channel"), "subscribe should return positively")
178
179 local ok, err = rc:set_keepalive(redis)
180 assert(not ok, "ok should be nil")
181 assert(string.find(err, "subscribed state"),
182 "error should contain 'subscribed state'")
183
184 }
185 }
186 --- request
187 GET /t
188 --- no_error_log
189 [error]
190
191
192 === TEST 6: password
193 --- http_config eval: $::HttpConfig
194 --- config
195 location /t {
196 lua_socket_log_errors Off;
197 content_by_lua_block {
198 local rc = require("resty.redis.connector").new({
199 port = $TEST_NGINX_REDIS_PORT,
200 password = "foo",
201 })
202
203 local redis, err = rc:connect()
204 assert(not redis and string.find(err, "ERR Client sent AUTH, but no password is set"),
205 "connect should fail with password error")
206
207 }
208 }
209 --- request
210 GET /t
104211 --- error_log
105 111: Connection refused
106
107
108 === TEST 3: Test connect_to_host directly
109 --- http_config eval: $::HttpConfig
110 --- config
111 location /t {
112 content_by_lua '
113 local redis_connector = require "resty.redis.connector"
114 local rc = redis_connector.new()
115
116 local host = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT }
117
118 local redis, err = rc:connect_to_host(host)
119 if not redis then
120 ngx.say("failed to connect: ", err)
121 return
122 end
123
124 local res, err = redis:set("dog", "an animal")
125 if not res then
126 ngx.say("failed to set dog: ", err)
127 return
128 end
129
130 ngx.say("set dog: ", res)
131
132 redis:close()
133 ';
134 }
135 --- request
136 GET /t
137 --- response_body
138 set dog: OK
139 --- no_error_log
140 [error]
141
142
143 === TEST 4: Test connect options override
144 --- http_config eval: $::HttpConfig
145 --- config
146 location /t {
147 content_by_lua '
148 local redis_connector = require "resty.redis.connector"
149 local rc = redis_connector.new()
150
151 local host = {
152 host = "127.0.0.1",
153 port = $TEST_NGINX_REDIS_PORT,
154 db = 1,
155 }
156
157 local redis, err = rc:connect_to_host(host)
158 if not redis then
159 ngx.say("failed to connect: ", err)
160 return
161 end
162
163 local res, err = redis:set("dog", "an animal")
164 if not res then
165 ngx.say("failed to set dog: ", err)
166 return
167 end
168
169 ngx.say("set dog: ", res)
170
171 redis:select(2)
172 ngx.say(redis:get("dog"))
173
174 redis:select(1)
175 ngx.say(redis:get("dog"))
176
177 redis:close()
178 ';
179 }
180 --- request
181 GET /t
182 --- response_body
183 set dog: OK
184 null
185 an animal
186 --- no_error_log
187 [error]
212 ERR Client sent AUTH, but no password is set
213
214
215 === TEST 7: unix domain socket
216 --- http_config eval: $::HttpConfig
217 --- config
218 location /t {
219 lua_socket_log_errors Off;
220 content_by_lua_block {
221 local redis, err = require("resty.redis.connector").new({
222 path = "unix://tmp/redis.sock",
223 }):connect()
224
225 assert(not redis and err == "no such file or directory",
226 "bad domain socket should fail")
227 }
228 }
229 --- request
230 GET /t
231 --- no_error_log
232 [error]
233
234
235 === TEST 8: parse_dsn
236 --- http_config eval: $::HttpConfig
237 --- config
238 location /t {
239 lua_socket_log_errors Off;
240 content_by_lua_block {
241 local rc = require("resty.redis.connector")
242
243 local params = {
244 url = "redis://foo@127.0.0.1:$TEST_NGINX_REDIS_PORT/4"
245 }
246
247 local ok, err = rc.parse_dsn(params)
248 assert(ok and not err,
249 "url should parse without error: " .. tostring(err))
250
251 assert(params.host == "127.0.0.1", "host should be localhost")
252 assert(tonumber(params.port) == $TEST_NGINX_REDIS_PORT,
253 "port should be $TEST_NGINX_REDIS_PORT")
254 assert(tonumber(params.db) == 4, "db should be 4")
255 assert(params.password == "foo", "password should be foo")
256
257
258 local params = {
259 url = "sentinel://foo@foomaster:s/2"
260 }
261
262 local ok, err = rc.parse_dsn(params)
263 assert(ok and not err,
264 "url should parse without error: " .. tostring(err))
265
266 assert(params.master_name == "foomaster", "master_name should be foomaster")
267 assert(params.role == "slave", "role should be slave")
268 assert(tonumber(params.db) == 2, "db should be 2")
269
270
271 local params = {
272 url = "sentinels:/wrongformat",
273 }
274
275 local ok, err = rc.parse_dsn(params)
276 assert(not ok and err == "could not parse DSN: nil",
277 "url should fail to parse")
278 }
279 }
280 --- request
281 GET /t
282 --- no_error_log
283 [error]
0 # vim:set ft= ts=4 sw=4 et:
1
2 use Test::Nginx::Socket::Lua;
0 use Test::Nginx::Socket 'no_plan';
31 use Cwd qw(cwd);
42
5 #repeat_each(2);
6
7 plan tests => repeat_each() * (3 * blocks());
8
93 my $pwd = cwd();
104
115 our $HttpConfig = qq{
12 lua_package_path "$pwd/lib/?.lua;;";
6 lua_package_path "$pwd/lib/?.lua;;";
7
8 init_by_lua_block {
9 require("luacov.runner").init()
10 }
1311 };
1412
1513 $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
1614 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
1715
1816 no_long_string();
19 #no_diff();
20
2117 run_tests();
2218
2319 __DATA__
2521 === TEST 1: Get the master
2622 --- http_config eval: $::HttpConfig
2723 --- config
28 location /t {
29 content_by_lua '
30 local redis_connector = require "resty.redis.connector"
31 local rc = redis_connector.new()
32
33 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
34 if not sentinel then
35 ngx.say("failed to connect: ", err)
36 return
37 end
38
39 local redis_sentinel = require "resty.redis.sentinel"
40
41 local master, err = redis_sentinel.get_master(sentinel, "mymaster")
42 if not master then
43 ngx.say(err)
44 else
45 ngx.say("host: ", master.host)
46 ngx.say("port: ", master.port)
47 end
48
49 sentinel:close()
50 ';
51 }
24 location /t {
25 content_by_lua_block {
26 local rc = require("resty.redis.connector").new()
27
28 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
29 assert(sentinel and not err, "sentinel should connect without errors")
30
31 local master, err = require("resty.redis.sentinel").get_master(
32 sentinel,
33 "mymaster"
34 )
35
36 assert(master and not err, "get_master should return the master")
37
38 assert(master.host == "127.0.0.1" and tonumber(master.port) == 6379,
39 "host should be 127.0.0.1 and port should be 6379")
40
41 sentinel:close()
42 }
43 }
44 --- request
45 GET /t
46 --- no_error_log
47 [error]
48
49
50 === TEST 1b: Get the master directly
51 --- http_config eval: $::HttpConfig
52 --- config
53 location /t {
54 content_by_lua_block {
55 local rc = require("resty.redis.connector").new()
56
57 local master, err = rc:connect({
58 url = "sentinel://mymaster:m/3",
59 sentinels = {
60 { host = "127.0.0.1", port = 6381 }
61 }
62 })
63
64 assert(master and not err, "get_master should return the master")
65 assert(master:set("foo", "bar"), "set should run without error")
66 assert(master:get("foo") == "bar", "get(foo) should return bar")
67
68 master:close()
69 }
70 }
71 --- request
72 GET /t
73 --- no_error_log
74 [error]
75
76
77 === TEST 2: Get slaves
78 --- http_config eval: $::HttpConfig
79 --- config
80 location /t {
81 content_by_lua_block {
82 local rc = require("resty.redis.connector").new()
83
84 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
85 assert(sentinel and not err, "sentinel should connect without error")
86
87 local slaves, err = require("resty.redis.sentinel").get_slaves(
88 sentinel,
89 "mymaster"
90 )
91
92 assert(slaves and not err, "slaves should be returned without error")
93
94 local slaveports = { ["6378"] = false, ["6380"] = false }
95
96 for _,slave in ipairs(slaves) do
97 slaveports[tostring(slave.port)] = true
98 end
99
100 assert(slaveports["6378"] == true and slaveports["6380"] == true,
101 "slaves should both be found")
102
103 sentinel:close()
104 }
105 }
52106 --- request
53107 GET /t
54 --- response_body
55 host: 127.0.0.1
56 port: 6379
57 --- no_error_log
58 [error]
59
60
61 === TEST 2: Get slaves
62 --- http_config eval: $::HttpConfig
63 --- config
64 location /t {
65 content_by_lua '
66 local redis_connector = require "resty.redis.connector"
67 local rc = redis_connector.new()
68
69 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
70 if not sentinel then
71 ngx.say("failed to connect: ", err)
72 return
73 end
74
75 local redis_sentinel = require "resty.redis.sentinel"
76
77 local slaves, err = redis_sentinel.get_slaves(sentinel, "mymaster")
78 if not slaves then
79 ngx.say(err)
80 else
81 -- order is undefined
82 local all = {}
83 for i,slave in ipairs(slaves) do
84 all[i] = tonumber(slave.port)
85 end
86 table.sort(all)
87 for _,p in ipairs(all) do
88 ngx.say(p)
89 end
90 end
91
92 sentinel:close()
93 ';
94 }
95 --- request
96 GET /t
97 --- response_body
98 6378
99 6380
100 --- no_error_log
101 [error]
108 --- no_error_log
109 [error]
110
102111
103112 === TEST 3: Get only healthy slaves
104113 --- http_config eval: $::HttpConfig
105114 --- config
106 location /t {
107 content_by_lua '
108
109 local redis = require "resty.redis"
110 local r = redis.new()
111 r:connect("127.0.0.1", 6378)
112 r:slaveof("127.0.0.1", 7000)
113
114 ngx.sleep(9)
115
116 local redis_connector = require "resty.redis.connector"
117 local rc = redis_connector.new()
118
119 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
120 if not sentinel then
121 ngx.say("failed to connect: ", err)
122 return
123 end
124
125 local redis_sentinel = require "resty.redis.sentinel"
126
127 local slaves, err = redis_sentinel.get_slaves(sentinel, "mymaster")
128 if not slaves then
129 ngx.say(err)
130 else
131 for _,slave in ipairs(slaves) do
132 ngx.say("host: ", slave.host)
133 ngx.say("port: ", slave.port)
134 end
135 end
136
137 sentinel:close()
138 ';
139 }
140 --- request
141 GET /t
115 location /t {
116 content_by_lua_block {
117 local rc = require("resty.redis.connector").new()
118
119 local sentinel, err = rc:connect({ url = "redis://127.0.0.1:6381" })
120 assert(sentinel and not err, "sentinel should connect without error")
121
122 local slaves, err = require("resty.redis.sentinel").get_slaves(
123 sentinel,
124 "mymaster"
125 )
126
127 assert(slaves and not err, "slaves should be returned without error")
128
129 local slaveports = { ["6378"] = false, ["6380"] = false }
130
131 for _,slave in ipairs(slaves) do
132 slaveports[tostring(slave.port)] = true
133 end
134
135 assert(slaveports["6378"] == true and slaveports["6380"] == true,
136 "slaves should both be found")
137
138 -- connect to one and remove it
139 local r = require("resty.redis.connector").new():connect({
140 port = 6378,
141 })
142 r:slaveof("127.0.0.1", 7000)
143
144 ngx.sleep(9)
145
146 local slaves, err = require("resty.redis.sentinel").get_slaves(
147 sentinel,
148 "mymaster"
149 )
150
151 assert(slaves and not err, "slaves should be returned without error")
152
153 local slaveports = { ["6378"] = false, ["6380"] = false }
154
155 for _,slave in ipairs(slaves) do
156 slaveports[tostring(slave.port)] = true
157 end
158
159 assert(slaveports["6378"] == false and slaveports["6380"] == true,
160 "only 6380 should be found")
161
162 r:slaveof("127.0.0.1", 6379)
163
164 sentinel:close()
165 }
166 }
167 --- request
168 GET /t
142169 --- timeout: 10
143 --- response_body
144 host: 127.0.0.1
145 port: 6380
146 --- no_error_log
147 [error]
170 --- no_error_log
171 [error]
172
173
174 === TEST 4: connector.connect_via_sentinel
175 --- http_config eval: $::HttpConfig
176 --- config
177 location /t {
178 content_by_lua_block {
179 local rc = require("resty.redis.connector").new()
180
181 local params = {
182 sentinels = {
183 { host = "127.0.0.1", port = 6381 },
184 { host = "127.0.0.1", port = 6382 },
185 { host = "127.0.0.1", port = 6383 },
186 },
187 master_name = "mymaster",
188 role = "master",
189 }
190
191 local redis, err = rc:connect_via_sentinel(params)
192 assert(redis and not err, "redis should connect without error")
193
194 params.role = "slave"
195
196 local redis, err = rc:connect_via_sentinel(params)
197 assert(redis and not err, "redis should connect without error")
198 }
199 }
200 --- request
201 GET /t
202 --- no_error_log
203 [error]
204
205
206 === TEST 5: regression for slave sorting (iss12)
207 --- http_config eval: $::HttpConfig
208 --- config
209 location /t {
210 lua_socket_log_errors Off;
211 content_by_lua_block {
212 local rc = require("resty.redis.connector").new()
213
214 local params = {
215 sentinels = {
216 { host = "127.0.0.1", port = 6381 },
217 { host = "127.0.0.1", port = 6382 },
218 { host = "127.0.0.1", port = 6383 },
219 },
220 master_name = "mymaster",
221 role = "slave",
222 }
223
224 -- hotwire get_slaves to expose sorting issue
225 local sentinel = require("resty.redis.sentinel")
226 sentinel.get_slaves = function()
227 return {
228 { host = "127.0.0.1", port = 6380 },
229 { host = "127.0.0.1", port = 6378 },
230 { host = "127.0.0.1", port = 6377 },
231 { host = "134.123.51.2", port = 6380 },
232 }
233 end
234
235 local redis, err = rc:connect_via_sentinel(params)
236 assert(redis and not err, "redis should connect without error")
237 }
238 }
239 --- request
240 GET /t
241 --- no_error_log
242 [error]
3838 }
3939 close $in;
4040
41 print "Checking use of Lua global variables in file $file ...\n";
42 system("luac -p -l $file | grep ETGLOBAL | grep -vE 'require|type|tostring|error|ngx|ndk|jit|setmetatable|getmetatable|string|table|io|os|print|tonumber|math|pcall|xpcall|unpack|pairs|ipairs|assert|module|package|coroutine|[gs]etfenv|next|select|rawset|rawget|debug'");
41 #print "Checking use of Lua global variables in file $file ...\n";
42 system("luac5.1 -p -l $file | grep ETGLOBAL | grep -vE '(require|type|tostring|error|ngx|ndk|jit|setmetatable|getmetatable|string|table|io|os|print|tonumber|math|pcall|xpcall|unpack|pairs|ipairs|assert|module|package|coroutine|[gs]etfenv|next|select|rawset|rawget|debug)\$'");
4343 #file_contains($file, "attempt to write to undeclared variable");
4444 system("grep -H -n -E --color '.{120}' $file");
4545 }