@@ -175,50 +175,72 @@ local function fetch_target_list(self)
175
175
end
176
176
177
177
178
- --- Run the given function holding a lock on the target list.
179
- -- WARNING: the callback will run unprotected, so it should never
180
- -- throw an error, but always return `nil + error` instead.
181
- -- @param self The checker object
182
- -- @param fn The function to execute
183
- -- @return The results of the function; or nil and an error message
184
- -- in case it fails locking.
185
- local function locking_target_list (self , fn )
178
+ --- Helper function to run the function holding a lock on the target list.
179
+ -- @see locking_target_list
180
+ local function run_fn_locked_target_list (premature , self , fn )
181
+
182
+ if premature then
183
+ return
184
+ end
186
185
187
186
local lock , lock_err = resty_lock :new (self .shm_name , {
188
- exptime = 10 , -- timeout after which lock is released anyway
189
- timeout = 5 , -- max wait time to acquire lock
190
- })
187
+ exptime = 10 , -- timeout after which lock is released anyway
188
+ timeout = 5 , -- max wait time to acquire lock
189
+ })
190
+
191
191
if not lock then
192
192
return nil , " failed to create lock:" .. lock_err
193
193
end
194
194
195
- local ok , err = lock :lock (self .TARGET_LIST_LOCK )
196
- if not ok then
197
- return nil , " failed to acquire lock: " .. err
195
+ local pok , perr = pcall (resty_lock .lock , lock , self .TARGET_LIST_LOCK )
196
+ if not pok then
197
+ self :log (DEBUG , " failed to acquire lock: " , perr )
198
+ return nil , " failed to acquire lock"
198
199
end
199
200
200
- local target_list
201
- target_list , err = fetch_target_list (self )
201
+ local target_list , err = fetch_target_list (self )
202
202
203
203
local final_ok , final_err
204
204
205
205
if target_list then
206
- final_ok , final_err = fn ( target_list )
206
+ final_ok , final_err = pcall ( fn , target_list )
207
207
else
208
208
final_ok , final_err = nil , err
209
209
end
210
210
211
+ local ok
211
212
ok , err = lock :unlock ()
212
213
if not ok then
213
214
-- recoverable: not returning this error, only logging it
214
215
self :log (ERR , " failed to release lock '" , self .TARGET_LIST_LOCK ,
215
- " ': " , err )
216
+ " ': " , err )
216
217
end
217
218
218
219
return final_ok , final_err
219
220
end
220
221
221
222
223
+ --- Run the given function holding a lock on the target list.
224
+ -- @param self The checker object
225
+ -- @param fn The function to execute
226
+ -- @return The results of the function; or nil and an error message
227
+ -- in case it fails locking.
228
+ local function locking_target_list (self , fn )
229
+
230
+ local ok , err = run_fn_locked_target_list (false , self , fn )
231
+ if err == " failed to acquire lock" then
232
+ local _ , terr = ngx .timer .at (0 , run_fn_locked_target_list , self , fn )
233
+ if terr ~= nil then
234
+ return nil , terr
235
+ end
236
+
237
+ return true
238
+ end
239
+
240
+ return ok , err
241
+ end
242
+
243
+
222
244
--- Get a target
223
245
local function get_target (self , ip , port , hostname )
224
246
hostname = hostname or ip
@@ -416,17 +438,13 @@ end
416
438
---- --------------------------------------------------------------------------
417
439
418
440
419
- -- Run the given function holding a lock on the target.
420
- -- WARNING: the callback will run unprotected, so it should never
421
- -- throw an error, but always return `nil + error` instead.
422
- -- @param self The checker object
423
- -- @param ip Target IP
424
- -- @param port Target port
425
- -- @param hostname Target hostname
426
- -- @param fn The function to execute
427
- -- @return The results of the function; or nil and an error message
428
- -- in case it fails locking.
429
- local function locking_target (self , ip , port , hostname , fn )
441
+ --- Helper function to actually run the function holding a lock on the target.
442
+ -- @see locking_target
443
+ local function run_mutexed_fn (premature , self , ip , port , hostname , fn )
444
+ if premature then
445
+ return
446
+ end
447
+
430
448
local lock , lock_err = resty_lock :new (self .shm_name , {
431
449
exptime = 10 , -- timeout after which lock is released anyway
432
450
timeout = 5 , -- max wait time to acquire lock
@@ -436,20 +454,45 @@ local function locking_target(self, ip, port, hostname, fn)
436
454
end
437
455
local lock_key = key_for (self .TARGET_LOCK , ip , port , hostname )
438
456
439
- local ok , err = lock :lock (lock_key )
440
- if not ok then
441
- return nil , " failed to acquire lock: " .. err
457
+ local pok , perr = pcall (resty_lock .lock , lock , lock_key )
458
+ if not pok then
459
+ self :log (DEBUG , " failed to acquire lock: " , perr )
460
+ return nil , " failed to acquire lock"
442
461
end
443
462
444
- local final_ok , final_err = fn ( )
463
+ local final_ok , final_err = pcall ( fn )
445
464
446
- ok , err = lock :unlock ()
465
+ local ok , err = lock :unlock ()
447
466
if not ok then
448
467
-- recoverable: not returning this error, only logging it
449
468
self :log (ERR , " failed to release lock '" , lock_key , " ': " , err )
450
469
end
451
470
452
471
return final_ok , final_err
472
+
473
+ end
474
+
475
+
476
+ -- Run the given function holding a lock on the target.
477
+ -- @param self The checker object
478
+ -- @param ip Target IP
479
+ -- @param port Target port
480
+ -- @param hostname Target hostname
481
+ -- @param fn The function to execute
482
+ -- @return The results of the function; or true in case it fails locking and
483
+ -- will retry asynchronously; or nil+err in case it fails to retry.
484
+ local function locking_target (self , ip , port , hostname , fn )
485
+ local ok , err = run_mutexed_fn (false , self , ip , port , hostname , fn )
486
+ if err == " failed to acquire lock" then
487
+ local _ , terr = ngx .timer .at (0 , run_mutexed_fn , self , ip , port , hostname , fn )
488
+ if terr ~= nil then
489
+ return nil , terr
490
+ end
491
+
492
+ return true
493
+ end
494
+
495
+ return ok , err
453
496
end
454
497
455
498
0 commit comments