Skip to content

Commit fc6c14a

Browse files
committed
Merge branch 'release/0.05'
2 parents a1e2d10 + a6c6a4c commit fc6c14a

File tree

5 files changed

+220
-8
lines changed

5 files changed

+220
-8
lines changed

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@ local redis, err = rc:connect{
104104
}
105105
```
106106

107+
## Proxy Mode
108+
109+
Enable the `connection_is_proxied` parameter if connecting to Redis through a proxy service (e.g. Twemproxy).
110+
These proxies generally only support a limited sub-set of Redis commands, those which do not require state and do not affect multiple keys.
111+
Databases and transactions are also not supported.
112+
113+
Proxy mode will disable switching to a DB on connect.
114+
Unsupported commands (defaults to those not supported by Twemproxy) will return `nil, err` immediately rather than being sent to the proxy, which can result in dropped connections.
115+
116+
`discard` will not be sent when adding connections to the keepalive pool
117+
118+
119+
## Disabled commands
120+
121+
If configured as a table of commands, the command methods will be replaced by a function which immediately returns `nil, err` without forwarding the command to the server
107122

108123
## Default Parameters
109124

@@ -115,16 +130,20 @@ local redis, err = rc:connect{
115130
connection_options = {}, -- pool, etc
116131
keepalive_timeout = 60000,
117132
keepalive_poolsize = 30,
118-
133+
119134
host = "127.0.0.1",
120135
port = "6379",
121136
path = "", -- unix socket path, e.g. /tmp/redis.sock
122137
password = "",
123138
db = 0,
124-
139+
125140
master_name = "mymaster",
126141
role = "master", -- master | slave | any
127142
sentinels = {},
143+
144+
connection_is_proxied = false,
145+
146+
disabled_commands = {},
128147
}
129148
```
130149

lib/resty/redis/connector.lua

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,28 @@ local DEFAULTS = setmetatable({
103103
role = "master", -- master | slave | any
104104
sentinels = {},
105105

106+
-- Redis proxies typically don't support full Redis capabilities
107+
connection_is_proxied = false,
108+
109+
disabled_commands = {},
110+
106111
}, fixed_field_metatable)
107112

108113

114+
-- This is the set of commands unsupported by Twemproxy
115+
local default_disabled_commands = {
116+
"migrate", "move", "object", "randomkey", "rename", "renamenx", "scan",
117+
"bitop", "msetnx", "blpop", "brpop", "brpoplpush", "psubscribe", "publish",
118+
"punsubscribe", "subscribe", "unsubscribe", "discard", "exec", "multi",
119+
"unwatch", "watch", "script", "auth", "echo", "select", "bgrewriteaof",
120+
"bgsave", "client", "config", "dbsize", "debug", "flushall", "flushdb",
121+
"info", "lastsave", "monitor", "save", "shutdown", "slaveof", "slowlog",
122+
"sync", "time"
123+
}
124+
125+
109126
local _M = {
110-
_VERSION = '0.04',
127+
_VERSION = '0.05',
111128
}
112129

113130
local mt = { __index = _M }
@@ -118,6 +135,13 @@ function _M.new(config)
118135
if not ok then
119136
return nil, config -- err
120137
else
138+
-- In proxied Redis mode disable default commands
139+
if config.connection_is_proxied == true and
140+
not next(config.disabled_commands) then
141+
142+
config.disabled_commands = default_disabled_commands
143+
end
144+
121145
return setmetatable({
122146
config = setmetatable(config, fixed_field_metatable)
123147
}, mt)
@@ -135,6 +159,7 @@ local function parse_dsn(params)
135159
return nil, "could not parse DSN: " .. tostring(err)
136160
end
137161

162+
-- TODO: Support a 'protocol' for proxied Redis?
138163
local fields
139164
if m[1] == "redis" then
140165
fields = { "password", "host", "port", "db" }
@@ -265,6 +290,15 @@ function _M.connect_to_host(self, host)
265290
local config = self.config
266291
r:set_timeout(config.connect_timeout)
267292

293+
-- Stub out methods for disabled commands
294+
if next(config.disabled_commands) then
295+
for _, cmd in ipairs(config.disabled_commands) do
296+
r[cmd] = function(...)
297+
return nil, ("Command "..cmd.." is disabled")
298+
end
299+
end
300+
end
301+
268302
local ok, err
269303
local path = host.path
270304
local opts = config.connection_options
@@ -296,7 +330,8 @@ function _M.connect_to_host(self, host)
296330
end
297331
end
298332

299-
if host.db ~= nil then
333+
-- No support for DBs in proxied Redis.
334+
if config.connection_is_proxied ~= true and host.db ~= nil then
300335
r:select(host.db)
301336
end
302337
return r, nil
@@ -307,7 +342,10 @@ end
307342
local function set_keepalive(self, redis)
308343
-- Restore connection to "NORMAL" before putting into keepalive pool,
309344
-- ignoring any errors.
310-
redis:discard()
345+
-- Proxied Redis does not support transactions.
346+
if self.config.connection_is_proxied ~= true then
347+
redis:discard()
348+
end
311349

312350
local config = self.config
313351
return redis:set_keepalive(

lib/resty/redis/sentinel.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ end
1010

1111

1212
local _M = {
13-
_VERSION = '0.04'
13+
_VERSION = '0.05'
1414
}
1515

1616

lua-resty-redis-connector-0.04-0.rockspec renamed to lua-resty-redis-connector-0.05-0.rockspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package = "lua-resty-redis-connector"
2-
version = "0.04-0"
2+
version = "0.05-0"
33
source = {
44
url = "git://github.com/pintsized/lua-resty-redis-connector",
5-
tag = "v0.04"
5+
tag = "v0.05"
66
}
77
description = {
88
summary = "Connection utilities for lua-resty-redis.",

t/proxy.t

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use Test::Nginx::Socket 'no_plan';
2+
use Cwd qw(cwd);
3+
4+
my $pwd = cwd();
5+
6+
our $HttpConfig = qq{
7+
lua_package_path "$pwd/lib/?.lua;;";
8+
9+
init_by_lua_block {
10+
require("luacov.runner").init()
11+
}
12+
};
13+
14+
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
15+
$ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
16+
17+
no_long_string();
18+
run_tests();
19+
20+
__DATA__
21+
22+
=== TEST 1: Proxy mode disables commands
23+
--- http_config eval: $::HttpConfig
24+
--- config
25+
location /t {
26+
content_by_lua_block {
27+
local rc = require("resty.redis.connector").new({
28+
port = $TEST_NGINX_REDIS_PORT,
29+
connection_is_proxied = true
30+
})
31+
32+
local redis, err = assert(rc:connect(params),
33+
"connect should return positively")
34+
35+
assert(redis:set("dog", "an animal"),
36+
"redis:set should return positively")
37+
38+
local ok, err = redis:multi()
39+
assert(ok == nil, "redis:multi should return nil")
40+
assert(err == "Command multi is disabled")
41+
42+
redis:close()
43+
}
44+
}
45+
--- request
46+
GET /t
47+
--- no_error_log
48+
[error]
49+
50+
51+
=== TEST 2: Proxy mode disables custom commands
52+
--- http_config eval: $::HttpConfig
53+
--- config
54+
location /t {
55+
content_by_lua_block {
56+
local rc = require("resty.redis.connector").new({
57+
port = $TEST_NGINX_REDIS_PORT,
58+
connection_is_proxied = true,
59+
disabled_commands = { "foobar", "hget"}
60+
})
61+
62+
local redis, err = assert(rc:connect(params),
63+
"connect should return positively")
64+
65+
assert(redis:set("dog", "an animal"),
66+
"redis:set should return positively")
67+
68+
assert(redis:multi(),
69+
"redis:multi should return positively")
70+
71+
local ok, err = redis:hget()
72+
assert(ok == nil, "redis:hget should return nil")
73+
assert(err == "Command hget is disabled")
74+
75+
local ok, err = redis:foobar()
76+
assert(ok == nil, "redis:foobar should return nil")
77+
assert(err == "Command foobar is disabled")
78+
79+
redis:close()
80+
}
81+
}
82+
--- request
83+
GET /t
84+
--- no_error_log
85+
[error]
86+
87+
=== TEST 3: Proxy mode does not switch DB
88+
--- http_config eval: $::HttpConfig
89+
--- config
90+
location /t {
91+
content_by_lua_block {
92+
local redis = require("resty.redis.connector").new({
93+
port = $TEST_NGINX_REDIS_PORT,
94+
db = 2
95+
}):connect()
96+
97+
local proxy = require("resty.redis.connector").new({
98+
port = $TEST_NGINX_REDIS_PORT,
99+
connection_is_proxied = true,
100+
db = 2
101+
}):connect()
102+
103+
assert(redis:set("proxy", "test"),
104+
"redis:set should return positively")
105+
106+
assert(proxy:get("proxy") == ngx.null,
107+
"proxy key should not exist in proxy")
108+
109+
redis:seelct(2)
110+
assert(redis:get("proxy") == "test",
111+
"proxy key should be 'test' in db 1")
112+
113+
redis:close()
114+
}
115+
}
116+
--- request
117+
GET /t
118+
--- no_error_log
119+
[error]
120+
121+
122+
=== TEST 4: Commands are disabled without proxy mode
123+
--- http_config eval: $::HttpConfig
124+
--- config
125+
location /t {
126+
content_by_lua_block {
127+
local rc = require("resty.redis.connector").new({
128+
port = $TEST_NGINX_REDIS_PORT,
129+
disabled_commands = { "foobar", "hget"}
130+
})
131+
132+
local redis, err = assert(rc:connect(params),
133+
"connect should return positively")
134+
135+
assert(redis:set("dog", "an animal"),
136+
"redis:set should return positively")
137+
138+
assert(redis:multi(),
139+
"redis:multi should return positively")
140+
141+
local ok, err = redis:hget()
142+
assert(ok == nil, "redis:hget should return nil")
143+
assert(err == "Command hget is disabled")
144+
145+
local ok, err = redis:foobar()
146+
assert(ok == nil, "redis:foobar should return nil")
147+
assert(err == "Command foobar is disabled")
148+
149+
redis:close()
150+
}
151+
}
152+
--- request
153+
GET /t
154+
--- no_error_log
155+
[error]

0 commit comments

Comments
 (0)