faraday-throttler-rx 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a90bfffb43e9cfb65219fe3325686b7c3d8e4cf410bcd91802e9a84222306de0
4
+ data.tar.gz: bfd4da59b12e9b86fdcc3706eb669817dab02b1054aba2c08749c6997eda8d95
5
+ SHA512:
6
+ metadata.gz: 703f490b28de0dba22faa60e0de17aaf2a513b610272f8f939973950f01e2e95a5a21abafa9f9421c95e1555a3e2f18d3d74b1448790f9e67bae4ff324620946
7
+ data.tar.gz: 3b09c9b0fdaef98b68bf28451d626dd6903673cc863e327410067fbfa3702e8e4559f479af5b347092daa28f5be7550b469828e87b7423a9509cbf409eb88aca
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in faraday_throttler.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'byebug'
8
+ end
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ [ ![Codeship Status for ismasan/faraday_throttler](https://codeship.com/projects/40d401a0-5c01-0133-561a-22b0ee77d2e6/status?branch=master)](https://codeship.com/projects/110895)
2
+
3
+ # FaradayThrottler
4
+
5
+ Configurable Faraday middleware for Ruby HTTP clients that:
6
+
7
+ * limits request rate to backend services.
8
+ * does its best to return cached or placeholder responses to clients while backend service is unavailable or slow.
9
+ * optionally uses Redis to rate-limit outgoing requests across processes and servers.
10
+
11
+ ## Use case
12
+
13
+ Use this gem if you want to help avoid request [stampedes](https://en.wikipedia.org/wiki/Cache_stampede) to your backend APIs. For example after front-end cache expiration. This middleware can help you limit the number and rate of concurrent requests to the backend, while serving useful cached or hard-coded responses back to the client when the backend is not inmediatly available.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'faraday_throttler'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install faraday_throttler
30
+
31
+ ## Usage
32
+
33
+ ### Defaults
34
+
35
+ The defaul configuration uses an in-memory lock and in-memory cache. Not suitable for multi-server deployments.
36
+
37
+ ```ruby
38
+ require 'faraday'
39
+ require 'faraday_throttler'
40
+
41
+ client = Faraday.new(:url => 'https://my.api.com') do |c|
42
+ c.use(
43
+ :throttler,
44
+ # Allow up to 1 request every 3 seconds, per path, to backend
45
+ rate: 3,
46
+ # Queued requests will wait for up to 2 seconds for current in-flight request
47
+ # to the same path.
48
+ # If in-flight request hasn't finished after that time, return a default placeholder response.
49
+ wait: 2
50
+ )
51
+ c.adapter Faraday.default_adapter
52
+ end
53
+ ```
54
+
55
+ Make some requests:
56
+
57
+ ```ruby
58
+ resp = client.get('/foobar')
59
+ resp.body
60
+ ```
61
+
62
+ The configuration above will only issue 1 request every 3 seconds to `my.api.com/foobar`. Requests to the same path will wait for up to 2 seconds for current _in-flight_ request to finish.
63
+
64
+ If an in-flight request finishes within the wait period, queued requests will respond with the same data, and the data will be cached as a fallback.
65
+
66
+ If the in-flight request doesn't finish within 2 seconds (wait period), queued requests will attempt to serve a previous response from the same resource from cache.
67
+
68
+ If no matching response found in cache, a default fallback response will be used (status 204 No Content). Fallback responses can be cofigured.
69
+
70
+ Tweaking the `rate` and `wait` arguments allows you to control the rate of cached, fresh and fallback reponses.
71
+
72
+ ### Distributed Redis lock and cache
73
+
74
+ The defaults use in-memory lock and cache store implementations. To make the most efficient use of this gem across processes and servers, you can use [Redis](http://redis.io/) as a distributed lock and cache store.
75
+
76
+ ```ruby
77
+ require 'redis'
78
+ require 'faraday_throttler/redis_lock'
79
+ require 'faraday_throttler/redis_cache'
80
+
81
+ redis = Redis.new(uri: 'redis://my-redis-server.com:1234')
82
+
83
+ redis_lock = FaradayThrottler::RedisLock.new(redis)
84
+
85
+ # Cache entries will be available for 1 hour
86
+ redis_cache = FaradayThrottler::RedisCache.new(redis: redis, ttl: 3600)
87
+
88
+ client = Faraday.new(:url => 'https://my.api.com') do |c|
89
+ c.use(
90
+ :throttler,
91
+ rate: 3,
92
+ wait: 2,
93
+ # Use Redis-backed lock
94
+ lock: redis_lock,
95
+ # Use Redis-backed cache with set expiration
96
+ cache: redis_cache
97
+ )
98
+ c.adapter Faraday.default_adapter
99
+ end
100
+ ```
101
+
102
+ ## Advanced usage
103
+
104
+ Most internal behaviours are split into delegate objects that you can pass as middleware arguments to override the defaults. See the details [in the code](https://github.com/ismasan/faraday_throttler/blob/master/lib/faraday_throttler/middleware.rb#L16).
105
+
106
+ ## Development
107
+
108
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
109
+
110
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
111
+
112
+ ## Contributing
113
+
114
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ismasan/faraday_throttler.
115
+
116
+ To contribute with code:
117
+
118
+ 1. Fork it ( http://github.com/ismasan/faraday_throttler/fork )
119
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
120
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
121
+ 4. Push to the branch (`git push origin my-new-feature`)
122
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "faraday_throttler"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/examples/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gem 'redis'
5
+ gem 'rack'
6
+ gem 'faraday', '1.10.0'
@@ -0,0 +1,41 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ faraday (1.10.0)
5
+ faraday-em_http (~> 1.0)
6
+ faraday-em_synchrony (~> 1.0)
7
+ faraday-excon (~> 1.1)
8
+ faraday-httpclient (~> 1.0)
9
+ faraday-multipart (~> 1.0)
10
+ faraday-net_http (~> 1.0)
11
+ faraday-net_http_persistent (~> 1.0)
12
+ faraday-patron (~> 1.0)
13
+ faraday-rack (~> 1.0)
14
+ faraday-retry (~> 1.0)
15
+ ruby2_keywords (>= 0.0.4)
16
+ faraday-em_http (1.0.0)
17
+ faraday-em_synchrony (1.0.0)
18
+ faraday-excon (1.1.0)
19
+ faraday-httpclient (1.0.1)
20
+ faraday-multipart (1.0.4)
21
+ multipart-post (~> 2)
22
+ faraday-net_http (1.0.1)
23
+ faraday-net_http_persistent (1.2.0)
24
+ faraday-patron (1.0.0)
25
+ faraday-rack (1.0.0)
26
+ faraday-retry (1.0.3)
27
+ multipart-post (2.2.3)
28
+ rack (1.6.4)
29
+ redis (3.2.1)
30
+ ruby2_keywords (0.0.5)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ faraday (= 1.10.0)
37
+ rack
38
+ redis
39
+
40
+ BUNDLED WITH
41
+ 1.17.3
@@ -0,0 +1,25 @@
1
+ ## Examples
2
+
3
+ ```
4
+ bundle install
5
+ ```
6
+
7
+ Run test rack server in one terminal window.
8
+
9
+ ```
10
+ rackup config.ru
11
+ ```
12
+
13
+ Run Redis in another:
14
+
15
+ ```
16
+ redis-server
17
+ ```
18
+
19
+ Run test client in another:
20
+
21
+ ```
22
+ ruby client.rb
23
+ ```
24
+
25
+ Tweak middleware option in `client.rb`
@@ -0,0 +1,32 @@
1
+ require 'bundler/setup'
2
+ require 'redis'
3
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
+ require 'faraday_throttler/middleware'
5
+ require 'faraday_throttler/redis_cache'
6
+
7
+
8
+ redis = Redis.new
9
+ cache = FaradayThrottler::RedisCache.new(redis: redis, ttl: 60)
10
+
11
+ conn = Faraday.new(:url => 'http://localhost:9800') do |faraday|
12
+ # faraday.response :logger # log requests to STDOUT
13
+ faraday.use :throttler, rate: 10, wait: 1, cache: cache
14
+ faraday.adapter Faraday.default_adapter
15
+ end
16
+
17
+ start = Time.now
18
+ success = 0
19
+ tr = (1..100).map do |i|
20
+ Thread.new do
21
+ sleep (rand * 10)
22
+ n = Time.now
23
+ r = conn.get('/foo/bar')
24
+ success += 1 if r.status == 200
25
+ # puts %([#{n}] #{r.headers['X-Throttler']} took: #{r.headers['X-ThrottlerTime']} - #{r.body})
26
+ end
27
+ end
28
+
29
+ tr.map{|t| t.join }
30
+
31
+
32
+ p "Success ------- #{success} --- Time = #{Time.now - start}"
@@ -0,0 +1,7 @@
1
+ require 'bundler/setup'
2
+
3
+ run ->(env) {
4
+ puts 'req'
5
+ sleep (rand * 5).ceil
6
+ [200, {'Content-Type' => 'application/json'}, [%({"date": "#{Time.now.to_s}"})]]
7
+ }
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'faraday_throttler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "faraday-throttler-rx"
8
+ spec.version = FaradayThrottler::VERSION
9
+ spec.authors = ["Boris Koumondji"]
10
+ spec.email = ["brexis2009@yahoo.fr"]
11
+
12
+ spec.summary = %q{Redis-backed request throttler requests to protect backend APIs against request stampedes}
13
+ spec.description = %q{Configure how often you want to hit backend APIs, and fallback responses to keep clients happy}
14
+ spec.homepage = "https://github.com/fedapay/faraday_throttler"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "faraday", ">= 0.9.1"
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,19 @@
1
+ module FaradayThrottler
2
+ class Cache
3
+ def initialize(store = {})
4
+ @mutex = Mutex.new
5
+ @store = store
6
+ end
7
+
8
+ def set(key, resp)
9
+ mutex.synchronize { store[key] = resp }
10
+ end
11
+
12
+ def get(key)
13
+ mutex.synchronize { store[key] }
14
+ end
15
+
16
+ private
17
+ attr_reader :store, :mutex
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ module FaradayThrottler
2
+ module Errors
3
+ class ThrottlerError < StandardError; end
4
+ class SerializerError < ThrottlerError; end
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ module FaradayThrottler
2
+ class Fallbacks
3
+ DEFAULT_CONTENT_TYPE = 'application/json'.freeze
4
+
5
+ def call(req)
6
+ {
7
+ url: req[:url],
8
+ status: 204,
9
+ body: '',
10
+ response_headers: {
11
+ 'Content-Type' => req.fetch(:request_headers, {}).fetch('Content-Type', DEFAULT_CONTENT_TYPE)
12
+ }
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require 'openssl/digest'
2
+
3
+ module FaradayThrottler
4
+ class KeyResolver
5
+ def call(request_env)
6
+ hash request_env[:url].to_s
7
+ end
8
+
9
+ private
10
+ def hash(str)
11
+ OpenSSL::Digest::MD5.hexdigest(str)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,155 @@
1
+ require 'timeout'
2
+ require 'faraday'
3
+ require 'logger'
4
+ require 'faraday_throttler/key_resolver'
5
+ require 'faraday_throttler/cache'
6
+ require 'faraday_throttler/rate_limit_response_checker'
7
+
8
+ module FaradayThrottler
9
+ class Middleware < Faraday::Middleware
10
+ def initialize(
11
+ # The base Faraday adapter.
12
+ app,
13
+
14
+ # Sticks cache.
15
+ cache: Cache.new,
16
+
17
+ # Resolves response unique key to use as cache key
18
+ # Interface:
19
+ # #call(response_env Hash) String
20
+ cache_key_resolver: KeyResolver.new,
21
+
22
+ # Maximum requests to sent to the backend api simultanous
23
+ rate: 10,
24
+
25
+ # Queued requests will wait for up to 1 seconds for current in-flight request
26
+ # to the same path.
27
+ # If in-flight request hasn't finished after that time, return a default placeholder response.
28
+ wait: 1,
29
+
30
+ # Wraps requests to backend service in a timeout block, in seconds.
31
+ # timeout: 0 disables this behaviour.
32
+ timeout: 0,
33
+
34
+ # Use to check if backend api response is a rate limit
35
+ rate_limit_response_checker: RateLimitResponseChecker.new,
36
+
37
+ # Pass your own Logger instance (for example Rails.logger in a Rails app).
38
+ # Defaults to STDOUT.
39
+ # http://ruby-doc.org/stdlib-2.1.0/libdoc/logger/rdoc/Logger.html
40
+ # Interface:
41
+ # #debug(msg String, &block)
42
+ # #warn(msg String, &block)
43
+ # #error(msg String, &block)
44
+ # #info(msg String, &block)
45
+ # #fatal(msg String, &block)
46
+ logger: Logger.new(STDOUT)
47
+ )
48
+
49
+ validate_dep! cache, :cache, :get, :set
50
+ validate_dep! cache_key_resolver, :cache_key_resolver, :call
51
+ validate_dep! rate_limit_response_checker, :rate_limit_response_checker, :call
52
+ validate_dep! logger, :info, :error, :warn, :debug
53
+
54
+ @cache = cache
55
+ @cache_key_resolver = cache_key_resolver
56
+ @rate = rate.to_i
57
+ @wait = wait.to_i
58
+ @timeout = timeout.to_i
59
+ @rate_limit_response_checker = rate_limit_response_checker
60
+ @logger = logger
61
+
62
+ super app
63
+ end
64
+
65
+ def call(request_env)
66
+ return app.call(request_env) if request_env[:method] != :get
67
+
68
+ start = Time.now
69
+
70
+ cache_key = cache_key_resolver.call(request_env)
71
+
72
+ # Wait stick to be available
73
+ until request_stick?(cache_key)
74
+ logger.debug logline(cache_key, "A.1. No stick available. Wait for new one.")
75
+ sleep(wait)
76
+ end
77
+
78
+ logger.debug logline(cache_key, "A.2. start backend request.")
79
+ handle_request(request_env, cache_key, start)
80
+ end
81
+
82
+ private
83
+ attr_reader :app, :cache, :cache_key_resolver, :rate, :wait, :timeout,
84
+ :rate_limit_response_checker, :logger
85
+
86
+ def handle_request(request_env, cache_key, start)
87
+ logger.debug logline(cache_key, "B.1.1. handle sync. Timeout: #{timeout}")
88
+ with_timeout(timeout) {
89
+ fetch_and_check_rate_limit(request_env, cache_key, start)
90
+ }
91
+ rescue ::Timeout::Error => e
92
+ release_request_stick(cache_key)
93
+ logger.error logline(cache_key, "B.1.2. timeout error. Timeout: #{timeout}. Message: #{e.message}")
94
+ raise Faraday::TimeoutError
95
+ end
96
+
97
+ def fetch_and_check_rate_limit(request_env, cache_key, start)
98
+ app.call(request_env).on_complete do |response_env|
99
+ if rate_limit_response_checker.call(response_env)
100
+ sleep wait
101
+ logger.debug logline(cache_key, "C.1.1. Rate limited on backend. Took #{Time.now - start}")
102
+ fetch_and_check_rate_limit(request_env, cache_key, start)
103
+ else
104
+ # Everything alright
105
+ logger.debug logline(cache_key, "C.1.2. Everything alright, request finished. Took #{Time.now - start}")
106
+ debug_headers response_env, :fresh, start
107
+ release_request_stick(cache_key)
108
+ end
109
+ end
110
+ end
111
+
112
+ def validate_dep!(dep, dep_name, *methods)
113
+ methods.each do |m|
114
+ raise ArgumentError, %(#{dep_name} must implement :#{m}) unless dep.respond_to?(m)
115
+ end
116
+ end
117
+
118
+ def debug_headers(resp_env, status, start)
119
+ resp_env[:response_headers].merge!(
120
+ 'X-Throttler' => status.to_s,
121
+ 'X-ThrottlerTime' => (Time.now - start)
122
+ )
123
+ end
124
+
125
+ def with_timeout(seconds, &block)
126
+ if seconds == 0
127
+ yield
128
+ else
129
+ ::Timeout.timeout(seconds, &block)
130
+ end
131
+ end
132
+
133
+ def request_stick?(cache_key)
134
+ counter = cache.get(cache_key, 0).to_i
135
+ p "#{counter}, #{cache_key}"
136
+ if counter < rate
137
+ cache.set(cache_key, counter + 1)
138
+ true
139
+ else
140
+ false
141
+ end
142
+ end
143
+
144
+ def release_request_stick(cache_key)
145
+ counter = cache.get(cache_key, 0).to_i
146
+ cache.set(cache_key, counter - 1) if counter > 0
147
+ end
148
+
149
+ def logline(cache_key, line)
150
+ "[Throttler:#{cache_key}] #{line}"
151
+ end
152
+ end
153
+
154
+ Faraday::Middleware.register_middleware throttler: ->{ Middleware }
155
+ end
@@ -0,0 +1,7 @@
1
+ module FaradayThrottler
2
+ class RateLimitResponseChecker
3
+ def call(response)
4
+ response.has_key?(:status) && response[:status] == 429
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ module FaradayThrottler
2
+ class RedisCache
3
+ NAMESPACE = 'throttler:cache:'.freeze
4
+
5
+ def initialize(redis: Redis.new, ttl: 0)
6
+ @redis = redis
7
+ @ttl = ttl
8
+ end
9
+
10
+ def set(key, value)
11
+ opts = {}
12
+ opts[:ex] = ttl if ttl > 0
13
+ redis.set [NAMESPACE, key].join, value, opts
14
+ end
15
+
16
+ def get(key, default = nil)
17
+ redis.get([NAMESPACE, key].join) || default
18
+ end
19
+
20
+ private
21
+ attr_reader :redis, :ttl
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module FaradayThrottler
2
+ VERSION = "0.0.4"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "faraday_throttler/version"
2
+
3
+ module FaradayThrottler
4
+ # Your code goes here...
5
+ end
6
+
7
+ require 'faraday_throttler/middleware'
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faraday-throttler-rx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Boris Koumondji
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Configure how often you want to hit backend APIs, and fallback responses
70
+ to keep clients happy
71
+ email:
72
+ - brexis2009@yahoo.fr
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - examples/Gemfile
86
+ - examples/Gemfile.lock
87
+ - examples/README.md
88
+ - examples/client.rb
89
+ - examples/config.ru
90
+ - faraday_throttler.gemspec
91
+ - lib/faraday_throttler.rb
92
+ - lib/faraday_throttler/cache.rb
93
+ - lib/faraday_throttler/errors.rb
94
+ - lib/faraday_throttler/fallbacks.rb
95
+ - lib/faraday_throttler/key_resolver.rb
96
+ - lib/faraday_throttler/middleware.rb
97
+ - lib/faraday_throttler/rate_limit_response_checker.rb
98
+ - lib/faraday_throttler/redis_cache.rb
99
+ - lib/faraday_throttler/version.rb
100
+ homepage: https://github.com/fedapay/faraday_throttler
101
+ licenses: []
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.1.2
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Redis-backed request throttler requests to protect backend APIs against request
122
+ stampedes
123
+ test_files: []