Sincronizzare un metodo diverso fili sidekiq e attendere
-
21-12-2019 - |
Domanda
Problema: Ho diversi fili Sidekiq e una funzione che può essere chiamata solo una volta al momento da qualsiasi file.
Motivo: Stiamo interrogando l'API AdWords per ottenere alcuni dati. Sono piuttosto restrittivi quando si tratta di limiti di frequenza. Solo uno dei thread può chiamare la funzione per ottenere dati alla volta.
Ora alcuni Codice:
# Public: Get estimates for a set of keywords. If there is an error, retry
# several times. If not successful, raise an error
#
# keywords: The keyword objects to get estimates for.
# save: Boolean to indicate whether the keyword objects should be saved to
# the database
#
def repeatedly_try_get_estimates(keywords: [], save: true, sleep_delay: 150)
return keywords if keywords.empty?
func = -> { get_estimates(keywords, !save) }
retry_operation(function: func, max_tries: 15, sleep_delay: sleep_delay)
end
.
- .
- Come puoi vedere, in questo momento ho un enorme
sleep_delay
lavora nel problema. - Il codice chiama la funzione
retry_operation
con ilget_estimates
Function come parametro. Quindi riproverò ilget_estimates
funzionalità più volte fino a quando non ci sia un'API Eccezione.
The retry_function
:
# Private: Retry a function X times and wait X seconds. If it does not work X times,
# raise an error. If successful return the functions results.
#
# - max_tries: The maximum tries to repeat the function
# - sleep_delay: The seconds to wait between each iteration.
# - function: The lambda function to call each iteration
#
def retry_operation(max_tries: 5, sleep_delay: 30, function: nil, current_try: 0, result: nil)
# Can't call, no function
if function.nil?
return
end
# Abort, tried too frequently.
if current_try > max_tries
raise "Failed function too often"
end
# Check if there is an exception
exception = true
begin
result = function.call
exception = false
rescue => e
Rails.logger.info "Received error when repeatedly calling function #{e.message.to_s}"
end
if exception
sleep sleep_delay if sleep_delay > 0
retry_operation(max_tries: max_tries, sleep_delay: sleep_delay, function: function, current_try: current_try + 1)
else
result
end
end
.
Il get_estimates_function
è qui: https://gist.github.com/a14868d939ef0e34ef9f . È troppo lungo, solo nel caso.
Suppongo di dover fare quanto segue:
- .
- Regola il codice nella funzione
repeatedly_try_get_estimates
. - Utilizzare un mutex nella classe.
- Salva l'eccezione se il mutex è in uso.
- Solo se il mutex è gratuito, esegui il
rety_operation
, altrimenti dormi un po 'di tempo
Grazie per il tuo aiuto :)
Soluzione
Qui andiamo, ottenuto per lavorare:
# Public: Get estimates for a set of keywords. If there is an error, retry
# several times. If not successful, raise an error
#
# keywords: The keyword objects to get estimates for.
# save: Boolean to indicate whether the keyword objects should be saved to
# the database
#
def repeatedly_try_get_estimates(keywords: [], save: true, sleep_delay: 40)
return keywords if keywords.empty?
func = -> { get_estimates(keywords, save_keywords: true) }
exception = nil
result = nil
initial_sleep = 0
estimates_mutex.synchronize do
since_last_request = Time.now.to_i - last_adwords_api_request
if since_last_request <= 30
Rails.logger.info "AdWords: Last request was only few seconds ago - sleeping #{since_last_request}."
initial_sleep = since_last_request
end
begin
result = retry_operation(function: func, max_tries: 15, sleep_delay: sleep_delay, initial_sleep: initial_sleep)
rescue => e
exception = e
end
@@last_adwords_api_request = Time.now.to_i
end
if exception
raise exception
end
result
end
. Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow