class LucidShopify::RedisThrottledStrategy

Use Redis to maintain API call limit throttling across threads/processes.

No delay for requests up to half of the call limit.

Constants

LEAK_RATE

Public Class Methods

new(redis_client: Redis.current) click to toggle source

@param redis_client [Redis]

# File lib/lucid_shopify/redis_throttled_strategy.rb, line 18
def initialize(redis_client: Redis.current)
  @redis_client = redis_client
end

Public Instance Methods

call(request, &send_request) click to toggle source

@param request [Request]

@yieldreturn [Response]

@return [Response]

# File lib/lucid_shopify/redis_throttled_strategy.rb, line 29
def call(request, &send_request)
  interval_key = build_interval_key(request)

  interval(interval_key)

  send_request.().tap do |res|
    header = res.headers['X-Shopify-Shop-Api-Call-Limit']

    next if header.nil?

    cur, max = header.split('/')

    @redis_client.mapped_hmset(interval_key,
      cur: cur,
      max: max,
      at: timestamp
    )
  end
end

Private Instance Methods

interval(interval_key) click to toggle source

If over half the call limit, sleep until requests leak back to the threshold.

@param interval_key [String]

# File lib/lucid_shopify/redis_throttled_strategy.rb, line 55
        def interval(interval_key)
  cur, max, at = @redis_client.hmget(interval_key, :cur, :max, :at).map(&:to_i)

  cur = leak(cur, at)

  delay_threshold = max / 2 # no delay

  if cur > delay_threshold
    sleep(Rational((cur - delay_threshold) * LEAK_RATE, 1000))
  end
end
leak(cur, at) click to toggle source

Find the actual value of {cur}, by subtracting requests leaked by the leaky bucket algorithm since the value was set.

@param cur [Integer] @param at [Integer]

@return [Integer]

# File lib/lucid_shopify/redis_throttled_strategy.rb, line 76
        def leak(cur, at)
  n = Rational(timestamp - at, LEAK_RATE).floor

  n > cur ? 0 : cur - n
end