shopify_unlimited 0.0.13.threadsafe → 0.0.14.threadsafe

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MWZmMDAyMjRlNjFkMDAxZTg1MDE1OGYzNWVhYmI1NmRlYjQ5OTBiYw==
4
+ MGJhYTI2MWQ4NjQxNjNlODZmN2I3ZTY3MjEzZGQ0MjIxZGZhZDI2ZQ==
5
5
  data.tar.gz: !binary |-
6
- MDQyNTNhMTc0ZGI0NDNhZWUyYWM1ZDQ4ZTFlYTdlNDE0ZTY3MGI0YQ==
6
+ ZTQzMDliNDE3MGZmY2RiY2E5OTRjYjdkZDRmZjYwNjZhNDQ2ZmE4Yw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- N2M0NGQ4ODk5YTU5ZDcxYTU4ZGVlNTFjNmE2ZTdhMmU0YzEyMGE4OTU3ZDUy
10
- ZjEwZmJkMzQ4ZjgxOTMyMjJlNjFlMmI3MTIxYzM3ZTA0ZWQzNzkzODhhN2Rh
11
- NWY1OWE1NjZiNTFjNDU4NjMyNmNlYjJkMmQzYjM5MWRjYTBjMzg=
9
+ NjgyNzMxMTE5NWIyOGQxODAyNWI4YTdkZGUwZjBiNTQwOWQzMWJhZDRiYjNk
10
+ MzA3MDI3YTJiOGI4YjNiODRjN2FlNDAxMjI5NTRhNzE5ODVlYTU3NjhmNWRj
11
+ MDRjN2RkNmFiNzc1YjRiMWJlN2UwY2QyZTQyMzdlM2U2OWZiNDI=
12
12
  data.tar.gz: !binary |-
13
- NWE3NTc1YzFlMGQxYjM5ZWZiNDAyZDU2Y2EzMDhiNzdjYzYzNDA1MjQ2NWE0
14
- ZmU0YTg2YTI4NWZhNzE4ZDJkYTVjN2JkZDhkYzVlMzdmYjNlYmY1NWY5Zjgz
15
- N2ZkYjYwMTE1YjI3MmU2OWRjNmFkY2UxMjVlMzEwMjlmMTA5N2I=
13
+ ZWE3MjM3ODk3NmY3ZGYzNDZjYzI4ZjU4OGZhNGNmNzU1ZGFkNDk1ZDIwYWFj
14
+ MjE5OGE2ZjBiNjM1YzFkMjk4NmJhMjQ4YTRhNjVlNWQ3YzU0NTg1MzQzMDY4
15
+ ZWQxZDEwNzlkMDRjMjc0ZDhiMGMzMjdlYzZlZGM0MzhhNDM5MWU=
@@ -38,15 +38,18 @@ module ActiveResource
38
38
  page = 0
39
39
  # as long as the number of results we got back is not less than the limit we (probably) have more to fetch
40
40
  while( (results.count - last_count) >= limit) do
41
- raise ShopifyAPI::Limits::Error.new if ShopifyAPI.credit_maxed?
42
41
  page +=1
43
42
  last_count = results.count
44
43
  options[:params][:page] = page
45
- results.concat find_every.bind(self).call(options)
44
+ ShopifyAPI::Shop.current.throttle.run do
45
+ results.concat find_every.bind(self).call(options)
46
+ end
46
47
  results.requests_made += 1
47
48
  end
48
49
  else
49
- results.concat find_every.bind(self).call(options)
50
+ ShopifyAPI::Shop.current.throttle.run do
51
+ results.concat find_every.bind(self).call(options)
52
+ end
50
53
  results.requests_made += 1
51
54
  end
52
55
 
@@ -0,0 +1,75 @@
1
+ # this allows multiple concurrent workers to behave well, almost
2
+ # never triggering a 429, and distributing requests fairly evenly,
3
+ # rather than 1 worker hogging the bulk
4
+ # of requests until finished, which will typically happen with
5
+ # any naive, non-stochastic implementation.
6
+
7
+
8
+ module ShopifyAPI
9
+ class Shop
10
+ def throttle
11
+ @throttle ||= Throttle.new
12
+ end
13
+ end
14
+
15
+ class Throttle
16
+ attr_accessor :throttle, :throttle_increment, :requests_threshold
17
+ def initialize
18
+ @throttle = 0.6
19
+ @throttle_increment = @throttle
20
+ @requests_threshold = 10
21
+ end
22
+
23
+ def run(&block)
24
+ value = nil
25
+ retries ||= 0
26
+ begin
27
+ left = ShopifyAPI.credit_left
28
+ over = @requests_threshold - left
29
+ if over > 0
30
+ @throttle += (over * rand/20) + rand/10
31
+ sleep @throttle + rand/10
32
+ else
33
+ @throttle = (0.94 + rand/20) * @throttle
34
+ end
35
+ t = Time.now
36
+ value = yield
37
+ rescue ActiveResource::ClientError => e
38
+ case e.response.code
39
+ when '404'
40
+ logger.fatal "Shopify returned not found"
41
+ sleep 5 + retries + (rand * rand * 5)
42
+ retries += 1
43
+ if retries < 4
44
+ ActiveResource::Base.logger = Logger.new(STDOUT)
45
+ retry
46
+ else
47
+ ActiveResource::Base.logger = nil
48
+ raise
49
+ end
50
+ when '429'
51
+ logger.warn "Shopify hit api limit"
52
+ @throttle += rand/5
53
+ retries += 1
54
+ sleep (@throttle * 4 * retries) + rand/10
55
+ if retries < 10
56
+ retry
57
+ else
58
+ raise
59
+ end
60
+ else
61
+ raise
62
+ end
63
+ end
64
+ requests_made = left - ShopifyAPI.credit_left
65
+ if requests_made > 1
66
+ @throttle += (rand/20) * requests_made
67
+ sleep [0, (t + (requests_made * @throttle) - Time.now)].max + rand/10
68
+ else
69
+ @throttle = (0.94 + rand/20) * @throttle
70
+ sleep rand/20
71
+ end
72
+ value
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,3 @@
1
1
  module ShopifyUnlimited
2
- VERSION = "0.0.13.threadsafe"
2
+ VERSION = "0.0.14.threadsafe"
3
3
  end
@@ -1,46 +1,3 @@
1
1
  require "shopify_unlimited/version"
2
2
  require 'shopify_unlimited/active_resource/base_extensions'
3
- require 'shopify_unlimited/active_resource/connection_extensions'
4
-
5
- module ShopifyUnlimited
6
- SHOPIFY_CREDIT_LIMIT_PERIOD = 5.minutes
7
-
8
- class << self
9
- attr_accessor :use_memcached
10
- def memcached
11
- return nil unless use_memcached
12
- @memcached ||= Dalli::Client.new()
13
- end
14
-
15
- def cache_key
16
- URI.parse(ShopifyAPI::Base.site.to_s).host + "_api_reset_time"
17
- end
18
-
19
- def cached_time(max)
20
- now = Time.now
21
- result = now
22
- return result unless memcached
23
- unless memcached.add(cache_key, now, 500)
24
- memcached.cas(cache_key, 500) do |cached_time|
25
- result = ((now - cached_time) > max) ? now : cached_time
26
- result
27
- end
28
- end
29
- result
30
- end
31
-
32
- def set_cached_time(time)
33
- return unless memcached
34
- memcached.set(cache_key, time, 500)
35
- end
36
-
37
-
38
- def estimated_time_until_reset
39
- credit_record = ShopifyAPI::Base.connection.shopify_credit
40
- time = credit_record.time unless credit_record.nil?
41
- time ||= cached_time(ShopifyUnlimited::SHOPIFY_CREDIT_LIMIT_PERIOD)
42
- est = ShopifyUnlimited::SHOPIFY_CREDIT_LIMIT_PERIOD - (Time.now - time)
43
- [est, 0].max
44
- end
45
- end
46
- end
3
+ require 'shopify_unlimited/shopify_api/throttle'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_unlimited
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13.threadsafe
4
+ version: 0.0.14.threadsafe
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Johnston
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-01 00:00:00.000000000 Z
11
+ date: 2014-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeresource
@@ -109,7 +109,7 @@ files:
109
109
  - Rakefile
110
110
  - lib/shopify_unlimited.rb
111
111
  - lib/shopify_unlimited/active_resource/base_extensions.rb
112
- - lib/shopify_unlimited/active_resource/connection_extensions.rb
112
+ - lib/shopify_unlimited/shopify_api/throttle.rb
113
113
  - lib/shopify_unlimited/version.rb
114
114
  - shopify_unlimited.gemspec
115
115
  - spec/spec_helper.rb
@@ -1,49 +0,0 @@
1
- module ::ShopifyUnlimited
2
-
3
- class CreditUsed
4
-
5
- attr_accessor :time, :used
6
- def initialize(used)
7
- @time = Time.now
8
- @used = used
9
- end
10
-
11
- def stale?(used)
12
- (@used > used) || ((Time.now - @time) > ShopifyUnlimited::SHOPIFY_CREDIT_LIMIT_PERIOD)
13
- end
14
-
15
- def estimated_time_until_reset
16
- est = ShopifyUnlimited::SHOPIFY_CREDIT_LIMIT_PERIOD - (Time.now - @time)
17
- [est, 0].max
18
- end
19
-
20
- end
21
- end
22
-
23
- module ::ActiveResource
24
- class Connection
25
- SHOPIFY_CREDIT_LIMIT_HEADER_PARAM = 'http_x_shopify_shop_api_call_limit'
26
-
27
- attr_reader :shopify_credit
28
- def handle_response_with_response_time_capture(response)
29
- handle_response_without_response_time_capture(response)
30
-
31
- if(shopify_credit_header = response[SHOPIFY_CREDIT_LIMIT_HEADER_PARAM])
32
- used = shopify_credit_header.split('/').shift.to_i
33
-
34
- if @shopify_credit.nil?
35
- cached_time = ::ShopifyUnlimited.cached_time(ShopifyUnlimited::SHOPIFY_CREDIT_LIMIT_PERIOD)
36
- @shopify_credit = ::ShopifyUnlimited::CreditUsed.new(used)
37
- @shopify_credit.time = cached_time
38
- elsif @shopify_credit.stale?(used)
39
- @shopify_credit = ::ShopifyUnlimited::CreditUsed.new(used)
40
- ::ShopifyUnlimited.set_cached_time(Time.now)
41
- else
42
- @shopify_credit.used = used
43
- end
44
- end
45
- response
46
- end
47
- alias_method_chain :handle_response, :response_time_capture
48
- end
49
- end