Created
February 3, 2013 08:41
-
-
Save brainopia/4700962 to your computer and use it in GitHub Desktop.
Octokit pool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Octokit::Mirror | |
attr_reader :remaining, :reserved | |
def initialize(options={}) | |
@client = Octokit::Client.new options | |
@remaining = Atomic.new Float::INFINITY | |
@reserved = Atomic.new 0 | |
@monitor = Atomic.new 0 | |
end | |
# выполнить блок кода, если зеркало доступно | |
# то есть осталось больше запросов, чем зарезервировано | |
# | |
# перед выполнением блока увеличиваем зарезервированное кол-во | |
# после выполнения – уменьшаем | |
# | |
# если лимит внезапно закончился, то возвращаем ошибку | |
# и запускаем мониторилку клиента в фоне | |
def reserve! | |
return unless available? | |
begin | |
@reserved.update {|x| x + 1 } | |
yield @client | |
rescue Octokit::Forbidden => error | |
@remaining.value = 0 | |
monitor_rate! | |
error | |
ensure | |
@reserved.update {|x| x - 1 } | |
end | |
end | |
# обновить кол-во доступных запросов | |
# основываясь на респонсе от гитхаба | |
# | |
# респонс может не содержать кол-во доступных запросов, если | |
# - респонс nil (когда успешный conditional request) | |
# - респонс число (например когда мы запрашиваем rate_limit) | |
# | |
# обновляем кол-во доступных запросов только по убывающей | |
# чтобы не зависеть от порядка выполнения запросов | |
# | |
# если лимит закончился, запускаем мониторилку клиента в фоне | |
def update_stats!(response) | |
return unless response.respond_to? :rate_remaining | |
@remaining.update do |x| | |
x > response.rate_remaining ? response.rate_remaining : x | |
end | |
monitor_rate! if @remaining.value.zero? | |
end | |
private | |
def available? | |
@remaining.value > @reserved.value | |
end | |
def monitor_rate! | |
if @monitor.compare_and_swap 0, 1 | |
Thread.new do | |
loop do | |
@remaining.value = @client.rate_limit_remaining | |
@remaining.value > 0 ? break : sleep(120) | |
end | |
@monitor.value = 0 | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Gem::Specification.new do |s| | |
s.name = 'octokit_pool' | |
s.version = '0.5' | |
s.author = 'brainopia' | |
s.summary = 'Octokit pool' | |
s.files = ['octokit_mirror.rb', 'octokit_pool.rb'] | |
s.require_path = '.' | |
s.add_dependency 'octokit' | |
s.add_dependency 'atomic' | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'atomic' | |
require 'octokit' | |
require 'octokit_mirror' | |
module Octokit | |
@mirrors = [ Octokit::Mirror.new ] | |
class << self | |
attr_reader :mirrors | |
# по циклу пытаемся зарезервировать зеркало | |
# и выполнить через него требуемый метод | |
# | |
# - если метод возвращает ошибку (лимит закончился) | |
# переходим к следующему зеркалу | |
# - если ни одно зеркало не подошло | |
# ждем 10 секунд и повторяем заново | |
def method_missing(method, *args) | |
loop do | |
mirrors.each do |mirror| | |
mirror.reserve! do |client| | |
response = client.send method, *args | |
next if response.is_a? Exception | |
mirror.update_stats! response | |
return response | |
end | |
end or sleep 10 | |
end | |
end | |
def mirrors | |
Thread.current[:mirrors] ||= @mirrors.shuffle | |
end | |
def create_mirror(options={}) | |
@mirrors << Octokit::Mirror.new(options) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment