faraday_throttler 0.0.1 → 0.0.2

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 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