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