lucid_shopify 0.19.0 → 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/lucid_shopify.rb +6 -0
- data/lib/lucid_shopify/container.rb +7 -1
- data/lib/lucid_shopify/redis_throttled_strategy.rb +79 -0
- data/lib/lucid_shopify/send_request.rb +4 -3
- data/lib/lucid_shopify/throttled_strategy.rb +5 -4
- data/lib/lucid_shopify/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2949f5da34bd486fe5ac4a76104577c730c5c086e36bde82c2bccc2925df3ade
|
4
|
+
data.tar.gz: c402dbe292c0b59a04e8b57d87b09017e666681e72dd99af180f7041a51c99d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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)
|
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
|
-
#
|
24
|
-
#
|
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|
|
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.
|
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-
|
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
|