Class: Wassup::Helpers::GitHub::RateLimiter

Inherits:
Object
  • Object
show all
Defined in:
lib/wassup/helpers/github_rate_limiter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRateLimiter

Returns a new instance of RateLimiter.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 12

def initialize
  @mutex = Mutex.new
  @queue = []
  @remaining = nil
  @reset_at = nil
  @limit = nil
  @search_remaining = nil
  @search_reset_at = nil
  @search_limit = nil
  @last_request_time = nil
  @worker_threads = []
  @running = false
  @max_queue_size = 20
  @max_concurrent_requests = 5
  @min_delay_between_requests = 1 # seconds
  @min_delay_between_search_requests = 5 # seconds (12 requests/minute to avoid abuse detection)
  @last_search_request_time = nil
  @current_requests = []
  @last_completed_request = nil
  @last_error = nil
end

Instance Attribute Details

#limitObject (readonly)

Returns the value of attribute limit.



10
11
12
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 10

def limit
  @limit
end

#queue_sizeObject (readonly)

Returns the value of attribute queue_size.



10
11
12
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 10

def queue_size
  @queue_size
end

#remainingObject (readonly)

Returns the value of attribute remaining.



10
11
12
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 10

def remaining
  @remaining
end

#reset_atObject (readonly)

Returns the value of attribute reset_at.



10
11
12
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 10

def reset_at
  @reset_at
end

Instance Method Details

#execute_request(method:, url:, **options) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 51

def execute_request(method:, url:, **options)
  future = RequestFuture.new
  
  @mutex.synchronize do
    # Reject requests if queue is too large
    if @queue.size >= @max_queue_size
      future.set_error(StandardError.new("Rate limiter queue is full (#{@max_queue_size} requests). Please try again later."))
      return future.get
    end
    
    @queue << {
      future: future,
      method: method,
      url: url,
      options: options,
      queued_at: Time.now
    }
  end

  start_worker
  
  # Add timeout to prevent hanging
  begin
    Timeout.timeout(120) do
      future.get
    end
  rescue Timeout::Error
    queue_size = @mutex.synchronize { @queue.size }
    raise StandardError.new("GitHub API request timed out after 120 seconds: #{method} #{url} (queue size: #{queue_size})")
  end
end

#start_workerObject



34
35
36
37
38
39
40
41
42
43
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 34

def start_worker
  return if @running
  
  @running = true
  @max_concurrent_requests.times do |i|
    @worker_threads << Thread.new do
      process_queue
    end
  end
end

#statusObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 87

def status
  @mutex.synchronize do
    next_search_available = nil
    if @last_search_request_time
      next_search_time = @last_search_request_time + @min_delay_between_search_requests
      next_search_available = [(next_search_time - Time.now).to_i, 0].max
    end
    
    {
      remaining: @remaining,
      reset_at: @reset_at,
      limit: @limit,
      search_remaining: @search_remaining,
      search_reset_at: @search_reset_at,
      search_limit: @search_limit,
      next_search_available: next_search_available,
      queue_size: @queue.size,
      running: @running,
      worker_threads: @worker_threads.size,
      current_requests: @current_requests.dup,
      last_completed: @last_completed_request,
      last_error: @last_error
    }
  end
end

#stop_workerObject



45
46
47
48
49
# File 'lib/wassup/helpers/github_rate_limiter.rb', line 45

def stop_worker
  @running = false
  @worker_threads.each(&:join)
  @worker_threads.clear
end