stripe 5.3.0 → 5.4.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/CHANGELOG.md +3 -0
- data/VERSION +1 -1
- data/lib/stripe/connection_manager.rb +6 -5
- data/lib/stripe/stripe_client.rb +11 -10
- data/lib/stripe/util.rb +13 -0
- data/lib/stripe/version.rb +1 -1
- data/test/stripe/connection_manager_test.rb +12 -15
- data/test/stripe/stripe_client_test.rb +42 -31
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3a4f568f2aa887474af9b072e52b0b97de8ec04f72ee52595682192f3c75998
|
4
|
+
data.tar.gz: 7791a3d16f6b79d8e100821d241981532326527bbd805be585f20f2bb70338d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31a50d23fc166cf97222b0e51b307d5356693d92596d56ab1bdefb885f8c9de941567d2c38102dca0ce1ae8f92a38a6ccb36f52b98293ed16b1c944c5d298a45
|
7
|
+
data.tar.gz: bf4c8207a3f9c6a4ec955309fd54f235002edb9e82a7a313d3c5dff0f7ff83f0b57bfb683c77ac693a3132977dd6c7ddf48f58176c985c1a34b6b9bfc072f806
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 5.4.0 - 2019-10-01
|
4
|
+
* [#857](https://github.com/stripe/stripe-ruby/pull/857) Move to monotonic time for duration calculations
|
5
|
+
|
3
6
|
## 5.3.0 - 2019-10-01
|
4
7
|
* [#853](https://github.com/stripe/stripe-ruby/pull/853) Support `Stripe-Should-Retry` header
|
5
8
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.
|
1
|
+
5.4.0
|
@@ -10,14 +10,15 @@ module Stripe
|
|
10
10
|
# Note that this class in itself is *not* thread safe. We expect it to be
|
11
11
|
# instantiated once per thread.
|
12
12
|
class ConnectionManager
|
13
|
-
# Timestamp
|
14
|
-
#
|
15
|
-
#
|
13
|
+
# Timestamp (in seconds procured from the system's monotonic clock)
|
14
|
+
# indicating when the connection manager last made a request. This is used
|
15
|
+
# by `StripeClient` to determine whether a connection manager should be
|
16
|
+
# garbage collected or not.
|
16
17
|
attr_reader :last_used
|
17
18
|
|
18
19
|
def initialize
|
19
20
|
@active_connections = {}
|
20
|
-
@last_used =
|
21
|
+
@last_used = Util.monotonic_time
|
21
22
|
|
22
23
|
# A connection manager may be accessed across threads as one thread makes
|
23
24
|
# requests on it while another is trying to clear it (either because it's
|
@@ -78,7 +79,7 @@ module Stripe
|
|
78
79
|
raise ArgumentError, "query should be a string" \
|
79
80
|
if query && !query.is_a?(String)
|
80
81
|
|
81
|
-
@last_used =
|
82
|
+
@last_used = Util.monotonic_time
|
82
83
|
|
83
84
|
connection = connection_for(uri)
|
84
85
|
|
data/lib/stripe/stripe_client.rb
CHANGED
@@ -9,7 +9,7 @@ module Stripe
|
|
9
9
|
# synchronize global access to them.
|
10
10
|
@thread_contexts_with_connection_managers = []
|
11
11
|
@thread_contexts_with_connection_managers_mutex = Mutex.new
|
12
|
-
@last_connection_manager_gc =
|
12
|
+
@last_connection_manager_gc = Util.monotonic_time
|
13
13
|
|
14
14
|
# Initializes a new `StripeClient`.
|
15
15
|
#
|
@@ -368,11 +368,11 @@ module Stripe
|
|
368
368
|
# For internal use only. Does not provide a stable API and may be broken
|
369
369
|
# with future non-major changes.
|
370
370
|
def self.maybe_gc_connection_managers
|
371
|
-
|
372
|
-
|
373
|
-
end
|
371
|
+
next_gc_time = @last_connection_manager_gc + CONNECTION_MANAGER_GC_PERIOD
|
372
|
+
return nil if next_gc_time > Util.monotonic_time
|
374
373
|
|
375
|
-
last_used_threshold =
|
374
|
+
last_used_threshold =
|
375
|
+
Util.monotonic_time - CONNECTION_MANAGER_GC_LAST_USED_EXPIRY
|
376
376
|
|
377
377
|
pruned_thread_contexts = []
|
378
378
|
@thread_contexts_with_connection_managers.each do |thread_context|
|
@@ -385,7 +385,7 @@ module Stripe
|
|
385
385
|
end
|
386
386
|
|
387
387
|
@thread_contexts_with_connection_managers -= pruned_thread_contexts
|
388
|
-
@last_connection_manager_gc =
|
388
|
+
@last_connection_manager_gc = Util.monotonic_time
|
389
389
|
|
390
390
|
pruned_thread_contexts.count
|
391
391
|
end
|
@@ -445,7 +445,7 @@ module Stripe
|
|
445
445
|
private def execute_request_with_rescues(method, api_base, context)
|
446
446
|
num_retries = 0
|
447
447
|
begin
|
448
|
-
request_start =
|
448
|
+
request_start = Util.monotonic_time
|
449
449
|
log_request(context, num_retries)
|
450
450
|
resp = yield
|
451
451
|
context = context.dup_from_response_headers(resp)
|
@@ -455,7 +455,8 @@ module Stripe
|
|
455
455
|
log_response(context, request_start, resp.code.to_i, resp.body)
|
456
456
|
|
457
457
|
if Stripe.enable_telemetry? && context.request_id
|
458
|
-
request_duration_ms =
|
458
|
+
request_duration_ms =
|
459
|
+
((Util.monotonic_time - request_start) * 1000).to_int
|
459
460
|
@last_request_metrics =
|
460
461
|
StripeRequestMetrics.new(context.request_id, request_duration_ms)
|
461
462
|
end
|
@@ -726,7 +727,7 @@ module Stripe
|
|
726
727
|
Util.log_info("Response from Stripe API",
|
727
728
|
account: context.account,
|
728
729
|
api_version: context.api_version,
|
729
|
-
elapsed:
|
730
|
+
elapsed: Util.monotonic_time - request_start,
|
730
731
|
idempotency_key: context.idempotency_key,
|
731
732
|
method: context.method,
|
732
733
|
path: context.path,
|
@@ -748,7 +749,7 @@ module Stripe
|
|
748
749
|
|
749
750
|
private def log_response_error(context, request_start, error)
|
750
751
|
Util.log_error("Request error",
|
751
|
-
elapsed:
|
752
|
+
elapsed: Util.monotonic_time - request_start,
|
752
753
|
error_message: error.message,
|
753
754
|
idempotency_key: context.idempotency_key,
|
754
755
|
method: context.method,
|
data/lib/stripe/util.rb
CHANGED
@@ -172,6 +172,19 @@ module Stripe
|
|
172
172
|
result
|
173
173
|
end
|
174
174
|
|
175
|
+
# `Time.now` can be unstable in cases like an administrator manually
|
176
|
+
# updating its value or a reconcilation via NTP. For this reason, prefer
|
177
|
+
# the use of the system's monotonic clock especially where comparing times
|
178
|
+
# to calculate an elapsed duration.
|
179
|
+
#
|
180
|
+
# Shortcut for getting monotonic time, mostly for purposes of line length
|
181
|
+
# and stubbing (Timecop doesn't freeze the monotonic clock). Returns time
|
182
|
+
# in seconds since the event used for monotonic reference purposes by the
|
183
|
+
# platform (e.g. system boot time).
|
184
|
+
def self.monotonic_time
|
185
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
186
|
+
end
|
187
|
+
|
175
188
|
def self.normalize_id(id)
|
176
189
|
if id.is_a?(Hash) # overloaded id
|
177
190
|
params_hash = id.dup
|
data/lib/stripe/version.rb
CHANGED
@@ -10,10 +10,9 @@ module Stripe
|
|
10
10
|
|
11
11
|
context "#initialize" do
|
12
12
|
should "set #last_used to current time" do
|
13
|
-
t =
|
14
|
-
|
15
|
-
|
16
|
-
end
|
13
|
+
t = 123.0
|
14
|
+
Util.stubs(:monotonic_time).returns(t)
|
15
|
+
assert_equal t, Stripe::ConnectionManager.new.last_used
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
@@ -147,19 +146,17 @@ module Stripe
|
|
147
146
|
stub_request(:post, "#{Stripe.api_base}/path")
|
148
147
|
.to_return(body: JSON.generate(object: "account"))
|
149
148
|
|
150
|
-
t =
|
149
|
+
t = 123.0
|
150
|
+
Util.stubs(:monotonic_time).returns(t)
|
151
151
|
|
152
|
-
|
153
|
-
# than the one we're going to measure at because `#last_used` is also
|
154
|
-
# set by the constructor.
|
155
|
-
manager = Timecop.freeze(t) do
|
156
|
-
Stripe::ConnectionManager.new
|
157
|
-
end
|
152
|
+
manager = Stripe::ConnectionManager.new
|
158
153
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
154
|
+
# `#last_used` is also set by the constructor, so make sure we get a
|
155
|
+
# new value for it.
|
156
|
+
Util.stubs(:monotonic_time).returns(t + 1.0)
|
157
|
+
|
158
|
+
manager.execute_request(:post, "#{Stripe.api_base}/path")
|
159
|
+
assert_equal t + 1.0, manager.last_used
|
163
160
|
end
|
164
161
|
end
|
165
162
|
end
|
@@ -27,47 +27,63 @@ module Stripe
|
|
27
27
|
StripeClient.clear_all_connection_managers
|
28
28
|
|
29
29
|
# Establish a base time.
|
30
|
-
t =
|
30
|
+
t = 0.0
|
31
31
|
|
32
32
|
# And pretend that `StripeClient` was just initialized for the first
|
33
33
|
# time. (Don't access instance variables like this, but it's tricky to
|
34
34
|
# test properly otherwise.)
|
35
35
|
StripeClient.instance_variable_set(:@last_connection_manager_gc, t)
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
client.execute_request(:post, "/v1/path")
|
37
|
+
#
|
38
|
+
# t
|
39
|
+
#
|
40
|
+
Util.stubs(:monotonic_time).returns(t)
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
# Execute an initial request to ensure that a connection manager was
|
43
|
+
# created.
|
44
|
+
client = StripeClient.new
|
45
|
+
client.execute_request(:post, "/v1/path")
|
46
46
|
|
47
|
+
# The GC shouldn't run yet (a `nil` return indicates that GC didn't run).
|
48
|
+
assert_equal nil, StripeClient.maybe_gc_connection_managers
|
49
|
+
|
50
|
+
#
|
51
|
+
# t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1
|
52
|
+
#
|
47
53
|
# Move time to just *before* garbage collection is eligible to run.
|
48
54
|
# Nothing should happen.
|
49
|
-
|
50
|
-
|
51
|
-
|
55
|
+
#
|
56
|
+
Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD - 1)
|
57
|
+
|
58
|
+
assert_equal nil, StripeClient.maybe_gc_connection_managers
|
52
59
|
|
60
|
+
#
|
61
|
+
# t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1
|
62
|
+
#
|
53
63
|
# Move time to just *after* garbage collection is eligible to run.
|
54
64
|
# Garbage collection will run, but because the connection manager is
|
55
65
|
# not passed its expiry age, it will not be collected. Zero is returned
|
56
66
|
# to indicate so.
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
#
|
68
|
+
Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_PERIOD + 1)
|
69
|
+
|
70
|
+
assert_equal 0, StripeClient.maybe_gc_connection_managers
|
60
71
|
|
72
|
+
#
|
73
|
+
# t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1
|
74
|
+
#
|
61
75
|
# Move us far enough into the future that we're passed the horizons for
|
62
76
|
# both a GC run as well as well as the expiry age of a connection
|
63
77
|
# manager. That means the GC will run and collect the connection
|
64
78
|
# manager that we created above.
|
65
|
-
|
66
|
-
|
79
|
+
#
|
80
|
+
Util.stubs(:monotonic_time).returns(t + StripeClient::CONNECTION_MANAGER_GC_LAST_USED_EXPIRY + 1)
|
67
81
|
|
68
|
-
|
69
|
-
|
70
|
-
|
82
|
+
assert_equal 1, StripeClient.maybe_gc_connection_managers
|
83
|
+
|
84
|
+
# And as an additional check, the connection manager of the current
|
85
|
+
# thread context should have been set to `nil` as it was GCed.
|
86
|
+
assert_nil StripeClient.current_thread_context.default_connection_manager
|
71
87
|
end
|
72
88
|
end
|
73
89
|
|
@@ -300,17 +316,12 @@ module Stripe
|
|
300
316
|
|
301
317
|
context "logging" do
|
302
318
|
setup do
|
303
|
-
# Freeze time for the purposes of the `elapsed` parameter that we
|
304
|
-
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
|
309
|
-
Timecop.freeze(Time.local(1990))
|
310
|
-
end
|
311
|
-
|
312
|
-
teardown do
|
313
|
-
Timecop.return
|
319
|
+
# Freeze time for the purposes of the `elapsed` parameter that we emit
|
320
|
+
# for responses. Mocha's `anything` parameter can't match inside of a
|
321
|
+
# hash and is therefore not useful for this purpose, and Timecop
|
322
|
+
# doesn't freeze monotonic time. If we switch over to rspec-mocks at
|
323
|
+
# some point, we can probably remove Timecop from the project.
|
324
|
+
Util.stubs(:monotonic_time).returns(0.0)
|
314
325
|
end
|
315
326
|
|
316
327
|
should "produce appropriate logging" do
|