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 il get_estimates Function come parametro. Quindi riproverò il get_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:

    .
  1. Regola il codice nella funzione repeatedly_try_get_estimates.
  2. Utilizzare un mutex nella classe.
  3. Salva l'eccezione se il mutex è in uso.
  4. Solo se il mutex è gratuito, esegui il rety_operation, altrimenti dormi un po 'di tempo
  5. Grazie per il tuo aiuto :)

È stato utile?

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
scroll top