22local typeof = require (" typeof" )
33local cjson = require (" cjson.safe" )
44local setmetatable = setmetatable
5+ local random = math.random
56local clear_tab = require (" table.clear" )
67local utils = require (" resty.etcd.utils" )
78local tab_nkeys = require (" table.nkeys" )
@@ -19,6 +20,7 @@ local decode_json = cjson.decode
1920local encode_json = cjson .encode
2021local encode_base64 = ngx .encode_base64
2122local decode_base64 = ngx .decode_base64
23+ local semaphore = require (" ngx.semaphore" )
2224local INIT_COUNT_RESIZE = 2e8
2325
2426local _M = {}
@@ -29,6 +31,7 @@ local mt = { __index = _M }
2931local refresh_jwt_token
3032
3133local function _request_uri (self , method , uri , opts , timeout , ignore_auth )
34+ utils .log_info (" v3 request uri: " , uri , " , timeout: " , timeout )
3235
3336 local body
3437 if opts and opts .body and tab_nkeys (opts .body ) > 0 then
@@ -44,7 +47,7 @@ local function _request_uri(self, method, uri, opts, timeout, ignore_auth)
4447 if self .is_auth then
4548 if not ignore_auth then
4649 -- authentication reqeust not need auth request
47- local _ , err = refresh_jwt_token (self )
50+ local _ , err = refresh_jwt_token (self , timeout )
4851 if err then
4952 return nil , err
5053 end
@@ -162,8 +165,15 @@ function _M.new(opts)
162165 })
163166 end
164167
168+ local sema , err = semaphore .new ()
169+ if not sema then
170+ return nil , err
171+ end
172+
165173 return setmetatable ({
166174 last_auth_time = now (), -- save last Authentication time
175+ last_refresh_jwt_err = nil ,
176+ sema = sema ,
167177 jwt_token = nil , -- last Authentication token
168178 is_auth = not not (user and password ),
169179 user = user ,
@@ -195,34 +205,69 @@ local function choose_endpoint(self)
195205 return endpoints [pos ]
196206end
197207
208+
209+ local function wake_up_everyone (self )
210+ local count = - self .sema :count ()
211+ if count > 0 then
212+ self .sema :post (count )
213+ end
214+ end
215+
216+
198217-- return refresh_is_ok, error
199- function refresh_jwt_token (self )
218+ function refresh_jwt_token (self , timeout )
200219 -- token exist and not expire
201- -- default is 5min, we use 3min
220+ -- default is 5min, we use 3min plus random seconds to smooth the refresh across workers
202221 -- https://github.com/etcd-io/etcd/issues/8287
203- if self .jwt_token and now () - self .last_auth_time < 60 * 3 then
222+ if self .jwt_token and now () - self .last_auth_time < 60 * 3 + random ( 0 , 60 ) then
204223 return true , nil
205224 end
206225
226+ if self .requesting_token then
227+ self .sema :wait (timeout )
228+ if self .jwt_token and now () - self .last_auth_time < 60 * 3 + random (0 , 60 ) then
229+ return true , nil
230+ end
231+
232+ if self .last_refresh_jwt_err then
233+ utils .log_info (" v3 refresh jwt last err: " , self .last_refresh_jwt_err )
234+ return nil , self .last_refresh_jwt_err
235+ end
236+
237+ -- something unexpected happened, try again
238+ utils .log_info (" v3 try auth after waiting, timeout: " , timeout )
239+ end
240+
241+ self .last_refresh_jwt_err = nil
242+ self .requesting_token = true
243+
207244 local opts = {
208245 body = {
209246 name = self .user ,
210247 password = self .password ,
211248 }
212249 }
213250 local res , err = _request_uri (self , ' POST' ,
214- choose_endpoint (self ).full_prefix .. " /auth/authenticate" ,
215- opts , 5 , true ) -- default authenticate timeout 5 second
251+ choose_endpoint (self ).full_prefix .. " /auth/authenticate" ,
252+ opts , timeout , true )
253+ self .requesting_token = false
254+
216255 if err then
256+ self .last_refresh_jwt_err = err
257+ wake_up_everyone (self )
217258 return nil , err
218259 end
219260
220261 if not res or not res .body or not res .body .token then
221- return nil , ' authenticate refresh token fail'
262+ err = ' authenticate refresh token fail'
263+ self .last_refresh_jwt_err = err
264+ wake_up_everyone (self )
265+ return nil , err
222266 end
223267
224268 self .jwt_token = res .body .token
225269 self .last_auth_time = now ()
270+ wake_up_everyone (self )
226271
227272 return true , nil
228273end
@@ -469,7 +514,7 @@ local function request_chunk(self, method, scheme, host, port, path, opts, timeo
469514 local headers = {}
470515 if self .is_auth then
471516 -- authentication reqeust not need auth request
472- _ , err = refresh_jwt_token (self )
517+ _ , err = refresh_jwt_token (self , timeout )
473518 if err then
474519 return nil , err
475520 end
0 commit comments