Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/ruby/find-inactive-members/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ cd platform-samples/api/ruby/find-inactive-members
### Install dependencies

```shell
gem install octokit
gem install octokit faraday-retry
```

### Configure Octokit
Expand Down
72 changes: 70 additions & 2 deletions api/ruby/find-inactive-members/find_inactive_members.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,69 @@
require 'optparse'
require 'optparse/date'

# Custom Faraday middleware for API request throttling
class ThrottleMiddleware < Faraday::Middleware
# Throttle to 5000 requests per hour (approximately 1.39 requests per second)
MAX_REQUESTS_PER_HOUR = 5000
MIN_DELAY_SECONDS = 3600.0 / MAX_REQUESTS_PER_HOUR # 0.72 seconds

def initialize(app, options = {})
super(app)
@request_count = 0
@hour_start_time = Time.now
@last_request_time = Time.now
@mutex = Mutex.new
end

def call(env)
@mutex.synchronize do
throttle_request
log_throttle_status
end

@app.call(env)
end

private

def throttle_request
current_time = Time.now

# Reset counter if we've moved to a new hour (sliding window)
if current_time - @hour_start_time >= 3600
@request_count = 0
@hour_start_time = current_time
@last_request_time = current_time
end

# Ensure minimum delay between requests to maintain steady rate under 5000/hour
time_since_last = current_time - @last_request_time
if time_since_last < MIN_DELAY_SECONDS
sleep_time = MIN_DELAY_SECONDS - time_since_last
if sleep_time > 0
sleep(sleep_time)
end
end

@request_count += 1
@last_request_time = Time.now

# Log warning if we're approaching the limit
if @request_count % 1000 == 0
elapsed_hour = @last_request_time - @hour_start_time
current_rate = elapsed_hour > 0 ? (@request_count / elapsed_hour * 3600).round(1) : 0
$stderr.print "Throttling status: #{@request_count} requests in #{elapsed_hour.round(1)}s (#{current_rate}/hour rate)\n"
end
end

def log_throttle_status
# This method can be called for detailed debugging if needed
elapsed_hour = Time.now - @hour_start_time
rate_per_hour = elapsed_hour > 0 ? (@request_count / elapsed_hour * 3600).round(1) : 0
$stderr.print "Throttle debug: #{@request_count} requests in last #{elapsed_hour.round(1)}s (#{rate_per_hour}/hour rate)\n" if ENV['THROTTLE_DEBUG']
end
end

class InactiveMemberSearch
attr_accessor :organization, :members, :repositories, :date, :unrecognized_authors

Expand Down Expand Up @@ -42,7 +105,10 @@ def check_scopes
end

def check_rate_limit
info "Rate limit: #{@client.rate_limit.remaining}/#{@client.rate_limit.limit}\n"
rate_limit = @client.rate_limit
info "Rate limit: #{rate_limit.remaining}/#{rate_limit.limit}\n"
info "Rate limit resets at: #{rate_limit.resets_at}\n"
info "Throttling: Limited to #{ThrottleMiddleware::MAX_REQUESTS_PER_HOUR} requests/hour (#{ThrottleMiddleware::MIN_DELAY_SECONDS.round(2)}s min delay)\n"
end

def env_help
Expand Down Expand Up @@ -195,7 +261,8 @@ def member_activity

# for each repo
@repositories.each do |repo|
info "rate limit remaining: #{@client.rate_limit.remaining} "
rate_limit = @client.rate_limit
info "rate limit remaining: #{rate_limit.remaining}/#{rate_limit.limit} "
info "analyzing #{repo}"

commit_activity(repo)
Expand Down Expand Up @@ -268,6 +335,7 @@ def member_activity
end.parse!

stack = Faraday::RackBuilder.new do |builder|
builder.use ThrottleMiddleware
builder.use Octokit::Middleware::FollowRedirects
builder.use Octokit::Response::RaiseError
builder.use Octokit::Response::FeedParser
Expand Down
Loading