lucid_shopify 0.19.0 → 0.20.0

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
  SHA256:
3
- metadata.gz: 959629883e5fa5237962423d070d6553aec6455b06854ec2385cc99e07d069b7
4
- data.tar.gz: 741fd2391fd995b4ddf48c59dd1cc428e81a6fd1c706980912de140172c548c3
3
+ metadata.gz: 2949f5da34bd486fe5ac4a76104577c730c5c086e36bde82c2bccc2925df3ade
4
+ data.tar.gz: c402dbe292c0b59a04e8b57d87b09017e666681e72dd99af180f7041a51c99d0
5
5
  SHA512:
6
- metadata.gz: 234a167e82f1a58d4f3f494350c6b77e186ca9b75bad804a736f609650137a6861f39dcde9d0d80dd3441f426d51a5c2604e30b33723825c65f215fb91ee7b73
7
- data.tar.gz: 02d491cea4b1b39c2ddd8a41963cf4ba3967d6c312b88da7eb333da2b52d41dc050974a250ebcf43418eb7139424692181fddaecad01a6bed608d64efa82c75f
6
+ metadata.gz: 1dc26e15a811b752753df1aeda9b1763de6edb8eef58e8277c691c9cf428578b7d0dd3742f03ff64d26d31eb04fd15f428ecfdec49b02f36763a23c2769969e4
7
+ data.tar.gz: efbdeeff6b99542cbe79ab72b0bcfdbd5b9f11c64ee19fc160fe80a5bc37c1aae6920b1b853463775a9b0d5b582fe9f5465c1ad1eb618b5b84935bec5df4888a
data/lib/lucid_shopify.rb CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  require 'dry/initializer'
4
4
 
5
+ begin
6
+ require 'redis'
7
+ rescue LoadError
8
+ end
9
+
5
10
  module LucidShopify
6
11
  autoload :ActivateCharge, 'lucid_shopify/activate_charge'
7
12
  autoload :Authorize, 'lucid_shopify/authorize'
@@ -18,6 +23,7 @@ module LucidShopify
18
23
  autoload :GetRequest, 'lucid_shopify/get_request'
19
24
  autoload :PostRequest, 'lucid_shopify/post_request'
20
25
  autoload :PutRequest, 'lucid_shopify/put_request'
26
+ autoload :RedisThrottledStrategy, 'lucid_shopify/redis_throttled_strategy'
21
27
  autoload :Request, 'lucid_shopify/request'
22
28
  autoload :Response, 'lucid_shopify/response'
23
29
  autoload :Result, 'lucid_shopify/result'
@@ -20,7 +20,13 @@ module LucidShopify
20
20
  Container.register(:delete_webhook) { DeleteWebhook.new }
21
21
  Container.register(:http) { ::HTTP::Client.new }
22
22
  Container.register(:send_request) { SendRequest.new }
23
- Container.register(:send_throttled_request) { SendRequest.new(strategy: ThrottledStrategy.new) }
23
+ Container.register(:send_throttled_request) do
24
+ if defined?(Redis)
25
+ SendRequest.new(strategy: RedisThrottledStrategy.new)
26
+ else
27
+ SendRequest.new(strategy: ThrottledStrategy.new)
28
+ end
29
+ end
24
30
  Container.register(:verify_callback) { VerifyCallback.new }
25
31
  Container.register(:verify_webhook) { VerifyWebhook.new }
26
32
  Container.register(:webhook_handler_list) { LucidShopify.handlers }
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lucid_shopify'
4
+
5
+ if defined?(Redis)
6
+ module LucidShopify
7
+ #
8
+ # Use Redis to maintain API call limit throttling across threads/processes.
9
+ #
10
+ # No delay for requests up to half of the call limit.
11
+ #
12
+ class RedisThrottledStrategy < ThrottledStrategy
13
+ LEAK_RATE = 500
14
+
15
+ #
16
+ # @param redis_client [Redis]
17
+ #
18
+ def initialize(redis_client: Redis.current)
19
+ @redis_client = redis_client
20
+ end
21
+
22
+ #
23
+ # @param request [Request]
24
+ #
25
+ # @yieldreturn [Response]
26
+ #
27
+ # @return [Response]
28
+ #
29
+ def call(request, &send_request)
30
+ interval_key = build_interval_key(request)
31
+
32
+ interval(interval_key)
33
+
34
+ send_request.().tap do |res|
35
+ cur, max = res.headers['X-Shopify-Shop-Api-Call-Limit'].split('/')
36
+
37
+ @redis_client.mapped_hmset(interval_key,
38
+ cur: cur,
39
+ max: max,
40
+ at: timestamp
41
+ )
42
+ end
43
+ end
44
+
45
+ #
46
+ # If over half the call limit, sleep until requests leak back to the
47
+ # threshold.
48
+ #
49
+ # @param interval_key [String]
50
+ #
51
+ private def interval(interval_key)
52
+ cur, max, at = @redis_client.hmget(interval_key, :cur, :max, :at).map(&:to_i)
53
+
54
+ cur = leak(cur, at)
55
+
56
+ delay_threshold = max / 2 # no delay
57
+
58
+ if cur > delay_threshold
59
+ sleep(Rational((cur - delay_threshold) * LEAK_RATE, 1000))
60
+ end
61
+ end
62
+
63
+ #
64
+ # Find the actual value of {cur}, by subtracting requests leaked by the
65
+ # leaky bucket algorithm since the value was set.
66
+ #
67
+ # @param cur [Integer]
68
+ # @param at [Integer]
69
+ #
70
+ # @return [Integer]
71
+ #
72
+ private def leak(cur, at)
73
+ n = Rational(timestamp - at, LEAK_RATE).floor
74
+
75
+ n > cur ? 0 : cur - n
76
+ end
77
+ end
78
+ end
79
+ end
@@ -75,7 +75,7 @@ module LucidShopify
75
75
  req.object_id,
76
76
  req.http_method.to_s.upcase,
77
77
  req.url,
78
- req.options[:params]&.to_json || '{}'
78
+ req.options[:params]&.to_json || '{}',
79
79
  ])
80
80
  end
81
81
 
@@ -87,10 +87,11 @@ module LucidShopify
87
87
  req = request
88
88
  res = response
89
89
 
90
- LucidShopify.config.logger.info('<%s> [%i] %i' % [
90
+ LucidShopify.config.logger.info('<%s> [%i] %i (%s)' % [
91
91
  self.class.to_s,
92
92
  req.object_id,
93
- res.status_code
93
+ res.status_code,
94
+ res.headers['X-Shopify-Shop-Api-Call-Limit'],
94
95
  ])
95
96
  end
96
97
 
@@ -3,6 +3,9 @@
3
3
  require 'lucid_shopify'
4
4
 
5
5
  module LucidShopify
6
+ #
7
+ # Maintain API call limit throttling across a single thread.
8
+ #
6
9
  class ThrottledStrategy
7
10
  MINIMUM_INTERVAL = 500 # ms
8
11
 
@@ -20,13 +23,11 @@ module LucidShopify
20
23
  end
21
24
 
22
25
  #
23
- # Sleep for the difference if time since the last request is less than the
24
- # MINIMUM_INTERVAL.
26
+ # If time since the last request < {MINIMUM_INTERVAL}, then sleep for the
27
+ # difference.
25
28
  #
26
29
  # @param interval_key [String]
27
30
  #
28
- # @note Throttling is only maintained across a single thread.
29
- #
30
31
  private def interval(interval_key)
31
32
  if Thread.current[interval_key]
32
33
  (timestamp - Thread.current[interval_key]).tap do |n|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucidShopify
4
- VERSION = '0.19.0'
4
+ VERSION = '0.20.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucid_shopify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kelsey Judson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-01 00:00:00.000000000 Z
11
+ date: 2018-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -132,6 +132,7 @@ files:
132
132
  - lib/lucid_shopify/get_request.rb
133
133
  - lib/lucid_shopify/post_request.rb
134
134
  - lib/lucid_shopify/put_request.rb
135
+ - lib/lucid_shopify/redis_throttled_strategy.rb
135
136
  - lib/lucid_shopify/request.rb
136
137
  - lib/lucid_shopify/response.rb
137
138
  - lib/lucid_shopify/send_request.rb