Skip to content

Commit cebbd81

Browse files
authored
Merge pull request #7 from Revolyssup/revolyssup/sync-1.05
sync with resty-redis-cluster version 1.05
2 parents 087721b + 4607f40 commit cebbd81

File tree

1 file changed

+59
-15
lines changed

1 file changed

+59
-15
lines changed

lib/resty/rediscluster.lua

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ local string_find = string.find
2020
local redis_crc = xmodem.redis_crc
2121

2222
local DEFAULT_SHARED_DICT_NAME = "redis_cluster_slot_locks"
23+
local DEFAULT_REFRESH_DICT_NAME = "refresh_lock"
2324
local DEFAULT_MAX_REDIRECTION = 5
2425
local DEFAULT_MAX_CONNECTION_ATTEMPTS = 3
2526
local DEFAULT_KEEPALIVE_TIMEOUT = 55000
@@ -97,22 +98,32 @@ local function split(s, delimiter)
9798
end
9899

99100
local function try_hosts_slots(self, serv_list)
101+
local start_time = ngx.now()
100102
local errors = {}
101103
local config = self.config
102104
if #serv_list < 1 then
103105
return nil, "failed to fetch slots, serv_list config is empty"
104106
end
107+
105108
for i = 1, #serv_list do
106109
local ip = serv_list[i].ip
107110
local port = serv_list[i].port
108111
local redis_client = redis:new()
109-
local ok, err
112+
local ok, err, max_connection_timeout_err
110113
redis_client:set_timeouts(config.connect_timeout or DEFAULT_CONNECTION_TIMEOUT,
111114
config.send_timeout or DEFAULT_SEND_TIMEOUT,
112115
config.read_timeout or DEFAULT_READ_TIMEOUT)
113116

114117
--attempt to connect DEFAULT_MAX_CONNECTION_ATTEMPTS times to redis
115118
for k = 1, config.max_connection_attempts or DEFAULT_MAX_CONNECTION_ATTEMPTS do
119+
local total_connection_time_ms = (ngx.now() - start_time) * 1000
120+
if (config.max_connection_timeout and total_connection_time_ms > config.max_connection_timeout) then
121+
max_connection_timeout_err = "max_connection_timeout of " .. config.max_connection_timeout .. "ms reached."
122+
ngx.log(ngx.ERR, max_connection_timeout_err)
123+
table_insert(errors, max_connection_timeout_err)
124+
break
125+
end
126+
116127
ok, err = redis_client:connect(ip, port, self.config.connect_opts)
117128
if ok then break end
118129
if err then
@@ -180,12 +191,14 @@ local function try_hosts_slots(self, serv_list)
180191
table_insert(errors, nerr)
181192
end
182193
release_connection(redis_client, config)
183-
194+
184195
-- refresh of slots and master nodes successful
185196
-- not required to connect/iterate over additional hosts
186197
if nodes_res and slots_info then
187198
return true, nil
188199
end
200+
elseif max_connection_timeout_err then
201+
break
189202
else
190203
table_insert(errors, err)
191204
end
@@ -201,12 +214,19 @@ function _M.fetch_slots(self)
201214
local serv_list = self.config.serv_list
202215
local serv_list_cached = slot_cache[self.config.name .. "serv_list"]
203216

204-
local serv_list_combined = {}
217+
local serv_list_combined
205218

206-
-- if a cached serv_list is present, use it
219+
-- if a cached serv_list is present, start with that
207220
if serv_list_cached then
208221
serv_list_combined = serv_list_cached.serv_list
222+
223+
-- then append the serv_list from config, in the event that the entire
224+
-- cached serv_list no longer points to anything usable
225+
for _, s in ipairs(serv_list) do
226+
table_insert(serv_list_combined, s)
227+
end
209228
else
229+
-- otherwise we bootstrap with our serv_list from config
210230
serv_list_combined = serv_list
211231
end
212232

@@ -221,6 +241,30 @@ function _M.fetch_slots(self)
221241
end
222242

223243

244+
function _M.refresh_slots(self)
245+
local worker_id = ngx.worker.id()
246+
local lock, err, elapsed, ok
247+
lock, err = resty_lock:new(self.config.dict_name or DEFAULT_SHARED_DICT_NAME, {time_out = 0})
248+
if not lock then
249+
ngx.log(ngx.ERR, "failed to create lock in refresh slot cache: ", err)
250+
return nil, err
251+
end
252+
253+
local refresh_lock_key = (self.config.refresh_lock_key or DEFAULT_REFRESH_DICT_NAME) .. worker_id
254+
elapsed, err = lock:lock(refresh_lock_key)
255+
if not elapsed then
256+
return nil, 'race refresh lock fail, ' .. err
257+
end
258+
259+
self:fetch_slots()
260+
ok, err = lock:unlock()
261+
if not ok then
262+
ngx.log(ngx.ERR, "failed to unlock in refresh slot cache:", err)
263+
return nil, err
264+
end
265+
end
266+
267+
224268
function _M.init_slots(self)
225269
if slot_cache[self.config.name] then
226270
-- already initialized
@@ -396,7 +440,7 @@ local function handle_command_with_retry(self, target_ip, target_port, asking, c
396440
ip, port, slave, err = pick_node(self, serv_list, slot)
397441
if err then
398442
ngx.log(ngx.ERR, "pickup node failed, will return failed for this request, meanwhile refereshing slotcache " .. err)
399-
self:fetch_slots()
443+
self:refresh_slots()
400444
return nil, err
401445
end
402446
end
@@ -416,7 +460,7 @@ local function handle_command_with_retry(self, target_ip, target_port, asking, c
416460
--set readonly
417461
ok, err = redis_client:readonly()
418462
if not ok then
419-
self:fetch_slots()
463+
self:refresh_slots()
420464
return nil, err
421465
end
422466
end
@@ -425,7 +469,7 @@ local function handle_command_with_retry(self, target_ip, target_port, asking, c
425469
--executing asking
426470
ok, err = redis_client:asking()
427471
if not ok then
428-
self:fetch_slots()
472+
self:refresh_slots()
429473
return nil, err
430474
end
431475
end
@@ -445,7 +489,7 @@ local function handle_command_with_retry(self, target_ip, target_port, asking, c
445489
release_connection(redis_client, config)
446490
target_ip = nil
447491
target_port = nil
448-
self:fetch_slots()
492+
self:refresh_slots()
449493
need_to_retry = true
450494

451495
elseif string.sub(err, 1, 3) == "ASK" then
@@ -468,7 +512,7 @@ local function handle_command_with_retry(self, target_ip, target_port, asking, c
468512
return nil, "Cannot executing command, cluster status is failed!"
469513
else
470514
--There might be node fail, we should also refresh slot cache
471-
self:fetch_slots()
515+
self:refresh_slots()
472516
return nil, err
473517
end
474518
end
@@ -478,7 +522,7 @@ local function handle_command_with_retry(self, target_ip, target_port, asking, c
478522
end
479523
else
480524
--There might be node fail, we should also refresh slot cache
481-
self:fetch_slots()
525+
self:refresh_slots()
482526
if k == config.max_redirection or k == DEFAULT_MAX_REDIRECTION then
483527
-- only return after allowing for `k` attempts
484528
return nil, connerr
@@ -561,7 +605,7 @@ local function construct_final_pipeline_resp(self, node_res_map, node_req_map)
561605
--ngx.log(ngx.NOTICE, "handle moved signal for cmd:" .. reqs[i]["cmd"] .. " key:" .. reqs[i]["key"])
562606
if need_to_fetch_slots then
563607
-- if there is multiple signal for moved, we just need to fetch slot cache once, and do retry.
564-
self:fetch_slots()
608+
self:refresh_slots()
565609
need_to_fetch_slots = false
566610
end
567611
local movedres, err = handle_command_with_retry(self, nil, nil, false, reqs[i]["cmd"], reqs[i]["key"], unpack(reqs[i]["args"]))
@@ -634,7 +678,7 @@ function _M.commit_pipeline(self)
634678
-- We must empty local reference to slots cache, otherwise there will be memory issue while
635679
-- coroutine swich happens(eg. ngx.sleep, cosocket), very important!
636680
slots = nil
637-
self:fetch_slots()
681+
self:refresh_slots()
638682
return nil, err
639683
end
640684

@@ -671,7 +715,7 @@ function _M.commit_pipeline(self)
671715
--set readonly
672716
local ok, err = redis_client:readonly()
673717
if not ok then
674-
self:fetch_slots()
718+
self:refresh_slots()
675719
return nil, err
676720
end
677721
end
@@ -692,7 +736,7 @@ function _M.commit_pipeline(self)
692736
local res, err = redis_client:commit_pipeline()
693737
if err then
694738
--There might be node fail, we should also refresh slot cache
695-
self:fetch_slots()
739+
self:refresh_slots()
696740
return nil, err .. " return from " .. tostring(ip) .. ":" .. tostring(port)
697741
end
698742

@@ -703,7 +747,7 @@ function _M.commit_pipeline(self)
703747
node_res_map[k] = res
704748
else
705749
--There might be node fail, we should also refresh slot cache
706-
self:fetch_slots()
750+
self:refresh_slots()
707751
return nil, err .. "pipeline commit failed while connecting to " .. tostring(ip) .. ":" .. tostring(port)
708752
end
709753
end

0 commit comments

Comments
 (0)