Pregunta

This question not about ruby only.

I have many workers running to creates many connections to external API. This API has a limit.
At now I use sidekiq and redis for limiting access.
i.e. at every access to API that rated I run worker.
Then worker is started it check when was last time to access to API, if is earlier than that API allows it, the worker is rescheduled, else it touch time in redis and run request.

ex:

def run_or_schedule
  limiter = RedisLimiter.new(account_token)
  if limiter.can_i_run?
    limiter.like
    run
  else
    ApiWorker.perform_at(limiter.next_like, *params)
  end
end

Problem is that I create many request and they many times rescheduled.

Maybe can someone can recommend a better solution?

Maybe any design patterns exist for this?

¿Fue útil?

Solución

One alternative to the polling approach you are using would be to have a supervisor.

So instead of having each worker handle the question itself, you have another object/worker/process which decides when is time for the next worker to run.

If the API imposes a time-limit between requests you could have this supervisor execute as often as the time limit allows.

If the limit is more complex (e.g. total number of requests per X interval) you could have the supervisor running constantly and upon hitting a limit block (sleep) for that amount of interval. Upon resuming it could continue with the next worker from the queue.

One apparent advantage of this approach is that you could skip the overhead related to each individual worker being instantiated, checking itself whether it should run, and being rescheduled if it shouldn't.

Otros consejos

You can use a queue, and a dedicated thread that sends events (when there any waiting) at the maximum rate allowable.

Say you can send one API call every second, you can do the following:

class APIProxy
  def like(data)
    @like_queue << data
  end

  def run
    Thread.new do
      @like_queue = Queue.new
      loop do
        actual_send_like @like_queue.pop
        sleep 1
      end
    end
  end

  private
  def actual_send_like(data)
    # use the API you need
  end
end
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top