faraday_throttler 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ecd40afc7d5acf7024059f4fcd820accc8da1bd1
4
- data.tar.gz: 67e615a50d4067f00175991e00b1161b624bbb7a
3
+ metadata.gz: 8f579e07afd3e9eaef5e05bfad056a6334cf5f32
4
+ data.tar.gz: 0d714c032c901eb44c75974a6fdfe7ab69946e4c
5
5
  SHA512:
6
- metadata.gz: 4c3cc18b494510df0da3d56210bc1fee14518b1d9f9ff012d601af0dfcdfbccbee8c3d3a5bee0e4bf4c57a189dbd7b5f6dbc9939aa525072df4c28fcd73a6988
7
- data.tar.gz: 026e803e53c5097b8c8623555932eb87826d7908ebdc209c3b81f1b96682fd9a9ebc642096a7c692999f489c15e3891a6e431cf4732aae3f7d92a086e668597c
6
+ metadata.gz: a401855bca195c3b855b31160df610490395238ad6f626b5561d93db05ed616c5bf922f70cd994a83043b7c10bf690c7f676e15fc5d1ad9065912028eb10c43e
7
+ data.tar.gz: 274a82b0fed4d985b230298725b0e3ef10aa8c0634a986294d7c5b92228e594074d816d1b8a4bc0f757ba359a10da02731d0384290df09b333f383ed655a5dc2
data/README.md CHANGED
@@ -8,6 +8,10 @@ Configurable Faraday middleware for Ruby HTTP clients that:
8
8
  * does its best to return cached or placeholder responses to clients while backend service is unavailable or slow.
9
9
  * optionally uses Redis to rate-limit outgoing requests across processes and servers.
10
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
+
11
15
  ## Installation
12
16
 
13
17
  Add this line to your application's Gemfile:
@@ -28,7 +32,7 @@ Or install it yourself as:
28
32
 
29
33
  ### Defaults
30
34
 
31
- The defaul configuration use an in-memory lock and in-memory cache. Not suitable for multi-server deployments.
35
+ The defaul configuration uses an in-memory lock and in-memory cache. Not suitable for multi-server deployments.
32
36
 
33
37
  ```ruby
34
38
  require 'faraday'
@@ -55,11 +59,11 @@ resp = client.get('/foobar')
55
59
  resp.body
56
60
  ```
57
61
 
58
- 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.
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.
59
63
 
60
- If an in-flight request finishes within that period, queued requests will respond with the same data.
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.
61
65
 
62
- If the in-flight request doesn't finish within 2 seconds, queued requests will attempt to serve a previous response from the same resource from cache.
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.
63
67
 
64
68
  If no matching response found in cache, a default fallback response will be used (status 204 No Content). Fallback responses can be cofigured.
65
69
 
@@ -80,7 +80,16 @@ module FaradayThrottler
80
80
  #
81
81
  # `request_id` is the result of cache_key_resolver#call, normally an MD5 hash of the request full URL.
82
82
  # `state` can be one of :fresh, :cached, :timeout, :fallback
83
- gauge: nil
83
+ gauge: nil,
84
+
85
+ # If async is TRUE, sending the request and populating the cache from the response
86
+ # will happen asynchronously in a thread, while the main thread will
87
+ # poll the cache for the duration of the :wait period.
88
+ # If the cache is populated within that period, the newly cached response will be returned
89
+ # Otherwise the fallback response will be returned.
90
+ # The main difference is that, when async: false, a fresh request will block until it gets data from the server.
91
+ # When async: true, a fresh request will try to respond with (possibly stale) cached data ASAP while the new response is cached in the background.
92
+ async: false
84
93
  )
85
94
 
86
95
  validate_dep! lock, :lock, :set
@@ -98,7 +107,7 @@ module FaradayThrottler
98
107
  @timeout = timeout.to_i
99
108
  @fallbacks = fallbacks
100
109
  @gauge = gauge || Gauge.new(rate: @rate, wait: @wait)
101
-
110
+ @async = async
102
111
  validate_dep! @gauge, :gauge, :start, :update, :finish
103
112
 
104
113
  super app
@@ -115,17 +124,10 @@ module FaradayThrottler
115
124
  gauge.start cache_key, start
116
125
 
117
126
  if lock.set(lock_key, gauge.rate(cache_key))
118
- begin
119
- with_timeout(timeout) {
120
- app.call(request_env).on_complete do |response_env|
121
- cache.set cache_key, response_env
122
- gauge.finish cache_key, :fresh
123
- debug_headers response_env, :fresh, start
124
- end
125
- }
126
- rescue ::Timeout::Error => e
127
- gauge.update cache_key, :timeout
128
- serve_from_cache_or_fallback request_env, cache_key, start
127
+ if async?
128
+ handle_async(request_env, cache_key, start)
129
+ else
130
+ handle_sync(request_env, cache_key, start)
129
131
  end
130
132
  else
131
133
  serve_from_cache_or_fallback request_env, cache_key, start
@@ -135,6 +137,34 @@ module FaradayThrottler
135
137
  private
136
138
  attr_reader :app, :lock, :cache, :lock_key_resolver, :cache_key_resolver, :rate, :wait, :timeout, :fallbacks, :gauge
137
139
 
140
+ def async?
141
+ @async
142
+ end
143
+
144
+ def handle_sync(request_env, cache_key, start)
145
+ with_timeout(timeout) {
146
+ fetch_and_cache(request_env, cache_key, start)
147
+ }
148
+ rescue ::Timeout::Error => e
149
+ gauge.update cache_key, :timeout
150
+ serve_from_cache_or_fallback request_env, cache_key, start
151
+ end
152
+
153
+ def handle_async(request_env, cache_key, start)
154
+ Thread.new do
155
+ fetch_and_cache(request_env, cache_key, start)
156
+ end
157
+ serve_from_cache_or_fallback request_env, cache_key, start
158
+ end
159
+
160
+ def fetch_and_cache(request_env, cache_key, start)
161
+ app.call(request_env).on_complete do |response_env|
162
+ cache.set cache_key, response_env
163
+ gauge.finish cache_key, :fresh
164
+ debug_headers response_env, :fresh, start
165
+ end
166
+ end
167
+
138
168
  def serve_from_cache_or_fallback(request_env, cache_key, start)
139
169
  if cached_response = cache.get(cache_key, gauge.wait(cache_key))
140
170
  gauge.finish cache_key, :cached
@@ -1,3 +1,3 @@
1
1
  module FaradayThrottler
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday_throttler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-12 00:00:00.000000000 Z
11
+ date: 2016-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  version: '0'
121
121
  requirements: []
122
122
  rubyforge_project:
123
- rubygems_version: 2.4.8
123
+ rubygems_version: 2.5.1
124
124
  signing_key:
125
125
  specification_version: 4
126
126
  summary: Redis-backed request throttler requests to protect backend APIs against request