thrifter 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Makefile +1 -1
- data/lib/thrifter.rb +7 -1
- data/lib/thrifter/extensions/retriable.rb +2 -0
- data/lib/thrifter/instrumented_pool.rb +36 -0
- data/lib/thrifter/middleware/client_metrics.rb +4 -0
- data/lib/thrifter/middleware/rpc_metrics.rb +4 -0
- data/lib/thrifter/version.rb +1 -1
- data/test/acceptance_test.rb +10 -2
- data/test/extensions/retry_test.rb +43 -0
- data/test/instrumented_pool_test.rb +117 -0
- data/test/middleware/client_metrics_test.rb +17 -0
- data/test/middleware/rpc_metrics_test.rb +17 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65d98aacd1ca9934a61d232bda0b67412571c0f6
|
4
|
+
data.tar.gz: a932dd7f6d35c006f4b82fbb1c1dcafa28fddb34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46844d62766a667447c0138d60d07746a3d384f11586ff524eda5743de9d1ed804433de8bd18e00bc790c0e7f1493452facecc3c7bc764a58b1a58d14da819bc
|
7
|
+
data.tar.gz: 75b6e393f98b6c0bb4741db90c0663f9d8bbba43f9075035056fd9c912f7f74ac00bb999aa8ab21469ea041869ed1de6ffb9ab75e26f2f593d965f04a378e851
|
data/Makefile
CHANGED
data/lib/thrifter.rb
CHANGED
@@ -33,6 +33,10 @@ module Thrifter
|
|
33
33
|
def increment(*)
|
34
34
|
|
35
35
|
end
|
36
|
+
|
37
|
+
def gauge(*)
|
38
|
+
|
39
|
+
end
|
36
40
|
end
|
37
41
|
|
38
42
|
RESERVED_METHODS = [
|
@@ -148,7 +152,7 @@ module Thrifter
|
|
148
152
|
fail ArgumentError, 'URI did not contain port' unless @uri.port
|
149
153
|
end
|
150
154
|
|
151
|
-
@pool =
|
155
|
+
@pool = InstrumentedPool.new(statsd: config.statsd, size: config.pool_size.to_i, timeout: config.pool_timeout.to_f) do
|
152
156
|
stack = MiddlewareStack.new
|
153
157
|
|
154
158
|
stack.use config.stack
|
@@ -191,6 +195,8 @@ module Thrifter
|
|
191
195
|
end
|
192
196
|
end
|
193
197
|
|
198
|
+
require_relative 'thrifter/instrumented_pool'
|
199
|
+
|
194
200
|
require_relative 'thrifter/extensions/ping'
|
195
201
|
require_relative 'thrifter/extensions/retriable'
|
196
202
|
|
@@ -41,9 +41,11 @@ module Thrifter
|
|
41
41
|
client.send name, *args
|
42
42
|
rescue *retriable => ex
|
43
43
|
if counter < tries
|
44
|
+
config.statsd.increment("rpc.#{name}.retry")
|
44
45
|
sleep interval
|
45
46
|
retry
|
46
47
|
else
|
48
|
+
config.statsd.increment("rpc.#{name}.retry")
|
47
49
|
raise RetryError.new(tries, name, ex)
|
48
50
|
end
|
49
51
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Thrifter
|
2
|
+
class InstrumentedPool < ConnectionPool
|
3
|
+
attr_reader :statsd
|
4
|
+
|
5
|
+
def initialize(options = { }, &block)
|
6
|
+
super(options, &block)
|
7
|
+
@statsd = options.fetch(:statsd)
|
8
|
+
end
|
9
|
+
|
10
|
+
def checkout(*args)
|
11
|
+
statsd.gauge('thread_pool.size', @size)
|
12
|
+
statsd.time('thread_pool.latency') do
|
13
|
+
super.tap do |conn|
|
14
|
+
statsd.increment('thread_pool.checkout')
|
15
|
+
statsd.gauge('thread_pool.in_use', in_use)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
rescue Timeout::Error => ex
|
19
|
+
statsd.increment('thread_pool.timeout')
|
20
|
+
raise ex
|
21
|
+
end
|
22
|
+
|
23
|
+
def checkin(*args)
|
24
|
+
super.tap do
|
25
|
+
statsd.increment('thread_pool.checkin')
|
26
|
+
statsd.gauge('thread_pool.in_use', in_use)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def in_use
|
33
|
+
(1 - (@available.length / @size.to_f)).round(2)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -28,6 +28,10 @@ module Thrifter
|
|
28
28
|
statsd.increment 'rpc.error'
|
29
29
|
statsd.increment 'rpc.error.timeout'
|
30
30
|
raise ex
|
31
|
+
rescue Thrifter::RetryError => ex
|
32
|
+
statsd.increment 'rpc.error'
|
33
|
+
statsd.increment 'rpc.error.retry'
|
34
|
+
raise ex
|
31
35
|
rescue => ex
|
32
36
|
statsd.increment 'rpc.error'
|
33
37
|
statsd.increment 'rpc.error.other'
|
@@ -77,6 +77,10 @@ module Thrifter
|
|
77
77
|
statsd.increment "rpc.#{rpc.name}.error"
|
78
78
|
statsd.increment "rpc.#{rpc.name}.error.timeout"
|
79
79
|
raise ex
|
80
|
+
rescue Thrifter::RetryError => ex
|
81
|
+
statsd.increment "rpc.#{rpc.name}.error"
|
82
|
+
statsd.increment "rpc.#{rpc.name}.error.retry"
|
83
|
+
raise ex
|
80
84
|
rescue => ex
|
81
85
|
statsd.increment "rpc.#{rpc.name}.error"
|
82
86
|
statsd.increment "rpc.#{rpc.name}.error.other"
|
data/lib/thrifter/version.rb
CHANGED
data/test/acceptance_test.rb
CHANGED
@@ -99,7 +99,11 @@ class AcceptanceTest < MiniTest::Unit::TestCase
|
|
99
99
|
config.pool_timeout = 75
|
100
100
|
end
|
101
101
|
|
102
|
-
|
102
|
+
Thrifter::InstrumentedPool.expects(:new).with({
|
103
|
+
statsd: client.config.statsd,
|
104
|
+
size: 50,
|
105
|
+
timeout: 75
|
106
|
+
})
|
103
107
|
|
104
108
|
client.new
|
105
109
|
end
|
@@ -111,7 +115,11 @@ class AcceptanceTest < MiniTest::Unit::TestCase
|
|
111
115
|
config.pool_timeout = '75.5'
|
112
116
|
end
|
113
117
|
|
114
|
-
|
118
|
+
Thrifter::InstrumentedPool.expects(:new).with({
|
119
|
+
statsd: client.config.statsd,
|
120
|
+
size: 50,
|
121
|
+
timeout: 75.5
|
122
|
+
})
|
115
123
|
|
116
124
|
client.new
|
117
125
|
end
|
@@ -14,10 +14,41 @@ class RetryTest < MiniTest::Unit::TestCase
|
|
14
14
|
config.transport = FakeTransport
|
15
15
|
end
|
16
16
|
|
17
|
+
class TestStatsd
|
18
|
+
attr_reader :counters
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@counters = [ ]
|
22
|
+
end
|
23
|
+
|
24
|
+
def increment(counter)
|
25
|
+
@counters << counter
|
26
|
+
end
|
27
|
+
|
28
|
+
def time(*args)
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
|
32
|
+
def gauge(*)
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :statsd
|
38
|
+
|
17
39
|
def known_errors
|
18
40
|
Thrifter::Retry::DEFAULT_RETRIABLE_ERRORS
|
19
41
|
end
|
20
42
|
|
43
|
+
def setup
|
44
|
+
super
|
45
|
+
@statsd = TestStatsd.new
|
46
|
+
|
47
|
+
RetryClient.configure do |config|
|
48
|
+
config.statsd = statsd
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
21
52
|
def test_does_not_retry_on_unexpected_errors
|
22
53
|
thrift_client = mock
|
23
54
|
thrift_client.expects(:echo).with(:request).raises(JunkError)
|
@@ -42,6 +73,12 @@ class RetryTest < MiniTest::Unit::TestCase
|
|
42
73
|
result = client.with_retry({ tries: 2, interval: 0.01 }).echo(:request)
|
43
74
|
|
44
75
|
assert :response == result, 'return value incorrect'
|
76
|
+
|
77
|
+
counters = statsd.counters.count do |item|
|
78
|
+
item == 'rpc.echo.retry'
|
79
|
+
end
|
80
|
+
|
81
|
+
assert_equal 1, counters, 'Retry not counted'
|
45
82
|
end
|
46
83
|
|
47
84
|
def test_retries_on_exceptions_specified_explicitly
|
@@ -88,6 +125,12 @@ class RetryTest < MiniTest::Unit::TestCase
|
|
88
125
|
assert_match /5/, error.message, 'Error not descriptive'
|
89
126
|
assert_match /echo/, error.message, 'Error not descriptive'
|
90
127
|
assert_match /#{err.to_s}/, error.message, 'Missing error details'
|
128
|
+
|
129
|
+
counters = statsd.counters.count do |item|
|
130
|
+
item == 'rpc.echo.retry'
|
131
|
+
end
|
132
|
+
|
133
|
+
assert_equal 5, counters, 'Retry not counted'
|
91
134
|
end
|
92
135
|
|
93
136
|
def test_retries_on_application_exception
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class InstrumentedPoolTest < MiniTest::Unit::TestCase
|
4
|
+
class TestStatsd
|
5
|
+
attr_reader :timers, :gauges, :counters
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@timers = [ ]
|
9
|
+
@gauges = [ ]
|
10
|
+
@counters = [ ]
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
timers.clear
|
15
|
+
gauges.clear
|
16
|
+
counters.clear
|
17
|
+
end
|
18
|
+
|
19
|
+
def time(key)
|
20
|
+
timers << key
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
|
24
|
+
def increment(key, value = 1)
|
25
|
+
counters << [ key, value ]
|
26
|
+
end
|
27
|
+
|
28
|
+
def gauge(key, value)
|
29
|
+
gauges << [ key, value ]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class TimeoutPool < Thrifter::InstrumentedPool
|
34
|
+
class FakeStack
|
35
|
+
def pop(*args)
|
36
|
+
raise Timeout::Error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(*args, &block)
|
41
|
+
super(*args) { :placeholder }
|
42
|
+
@available = FakeStack.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :pool, :statsd
|
47
|
+
|
48
|
+
def setup
|
49
|
+
super
|
50
|
+
|
51
|
+
@statsd = TestStatsd.new
|
52
|
+
@pool = Thrifter::InstrumentedPool.new(size: 5, timeout: 5, statsd: statsd) do
|
53
|
+
:foo
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_checkout_instrumentation
|
58
|
+
assert_equal :foo, pool.checkout, 'Incorrect connection'
|
59
|
+
|
60
|
+
latency = statsd.timers.first
|
61
|
+
assert latency
|
62
|
+
assert_equal 'thread_pool.latency', latency
|
63
|
+
|
64
|
+
counter = statsd.counters[0]
|
65
|
+
assert counter
|
66
|
+
assert_equal 'thread_pool.checkout', counter[0]
|
67
|
+
assert_equal 1, counter[1]
|
68
|
+
|
69
|
+
size = statsd.gauges[0]
|
70
|
+
assert size
|
71
|
+
assert_equal 'thread_pool.size', size[0]
|
72
|
+
assert_equal 5, size[1]
|
73
|
+
|
74
|
+
in_use = statsd.gauges[1]
|
75
|
+
assert in_use
|
76
|
+
assert_equal 'thread_pool.in_use', in_use[0]
|
77
|
+
assert_equal 0.2, in_use[1]
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_checkin_instrumentation
|
81
|
+
assert pool.checkout
|
82
|
+
statsd.clear
|
83
|
+
|
84
|
+
pool.checkin
|
85
|
+
|
86
|
+
counter = statsd.counters[0]
|
87
|
+
assert counter
|
88
|
+
assert_equal 'thread_pool.checkin', counter[0]
|
89
|
+
assert_equal 1, counter[1]
|
90
|
+
|
91
|
+
in_use = statsd.gauges.first
|
92
|
+
assert in_use
|
93
|
+
assert_equal 'thread_pool.in_use', in_use[0]
|
94
|
+
assert_equal 0.0, in_use[1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_checkout_timeout_instrumentation
|
98
|
+
@pool = TimeoutPool.new({
|
99
|
+
statsd: statsd,
|
100
|
+
size: 5,
|
101
|
+
timeout: 5
|
102
|
+
})
|
103
|
+
|
104
|
+
assert_raises Timeout::Error do
|
105
|
+
pool.checkout
|
106
|
+
end
|
107
|
+
|
108
|
+
latency = statsd.timers.first
|
109
|
+
assert latency
|
110
|
+
assert_equal 'thread_pool.latency', latency
|
111
|
+
|
112
|
+
counter = statsd.counters.first
|
113
|
+
assert counter
|
114
|
+
assert_equal 'thread_pool.timeout', counter[0]
|
115
|
+
assert_equal 1, counter[1]
|
116
|
+
end
|
117
|
+
end
|
@@ -92,6 +92,23 @@ class ClientMetricsTest < MiniTest::Unit::TestCase
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
def test_counts_retry_failures
|
96
|
+
app = stub
|
97
|
+
app.stubs(:call).with(rpc).raises(Thrifter::RetryError.new(1, 'echo', StandardError.new))
|
98
|
+
|
99
|
+
statsd = mock
|
100
|
+
statsd.expects(:time).yields
|
101
|
+
statsd.expects(:increment).with("rpc.error")
|
102
|
+
statsd.expects(:increment).with("rpc.outgoing")
|
103
|
+
statsd.expects(:increment).with("rpc.error.retry")
|
104
|
+
|
105
|
+
middleware = Thrifter::ClientMetrics.new app, statsd
|
106
|
+
|
107
|
+
assert_raises Thrifter::RetryError do
|
108
|
+
middleware.call rpc
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
95
112
|
def test_counts_other_errors
|
96
113
|
app = stub
|
97
114
|
app.stubs(:call).with(rpc).raises(StandardError)
|
@@ -478,6 +478,23 @@ class RpcMetricsTest < MiniTest::Unit::TestCase
|
|
478
478
|
end
|
479
479
|
end
|
480
480
|
|
481
|
+
def test_counts_retry_failures
|
482
|
+
app = stub
|
483
|
+
app.stubs(:call).with(rpc).raises(Thrifter::RetryError.new(1, 'echo', StandardError.new))
|
484
|
+
|
485
|
+
statsd = mock
|
486
|
+
statsd.expects(:time).yields
|
487
|
+
statsd.expects(:increment).with("rpc.#{rpc.name}.error.retry")
|
488
|
+
statsd.expects(:increment).with("rpc.#{rpc.name}.outgoing")
|
489
|
+
statsd.expects(:increment).with("rpc.#{rpc.name}.error")
|
490
|
+
|
491
|
+
middleware = Thrifter::RpcMetrics.new app, statsd
|
492
|
+
|
493
|
+
assert_raises Thrifter::RetryError do
|
494
|
+
middleware.call rpc
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
481
498
|
def test_counts_other_errors
|
482
499
|
app = stub
|
483
500
|
app.stubs(:call).with(rpc).raises(StandardError)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thrifter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ahawkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thrift
|
@@ -227,6 +227,7 @@ files:
|
|
227
227
|
- lib/thrifter/extensions/ping.rb
|
228
228
|
- lib/thrifter/extensions/queueing.rb
|
229
229
|
- lib/thrifter/extensions/retriable.rb
|
230
|
+
- lib/thrifter/instrumented_pool.rb
|
230
231
|
- lib/thrifter/middleware/client_metrics.rb
|
231
232
|
- lib/thrifter/middleware/error_wrapping.rb
|
232
233
|
- lib/thrifter/middleware/rpc_metrics.rb
|
@@ -239,6 +240,7 @@ files:
|
|
239
240
|
- test/extensions/ping_test.rb
|
240
241
|
- test/extensions/queueing_test.rb
|
241
242
|
- test/extensions/retry_test.rb
|
243
|
+
- test/instrumented_pool_test.rb
|
242
244
|
- test/middleware/client_metrics_test.rb
|
243
245
|
- test/middleware/error_wrapping_test.rb
|
244
246
|
- test/middleware/rpc_metrics_test.rb
|
@@ -277,6 +279,7 @@ test_files:
|
|
277
279
|
- test/extensions/ping_test.rb
|
278
280
|
- test/extensions/queueing_test.rb
|
279
281
|
- test/extensions/retry_test.rb
|
282
|
+
- test/instrumented_pool_test.rb
|
280
283
|
- test/middleware/client_metrics_test.rb
|
281
284
|
- test/middleware/error_wrapping_test.rb
|
282
285
|
- test/middleware/rpc_metrics_test.rb
|