stripe 5.3.0 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|