right_support 2.8.26 → 2.8.27
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/right_support/stats/activity.rb +31 -12
- data/lib/right_support/stats/helpers.rb +3 -3
- data/right_support.gemspec +3 -3
- data/spec/stats/activity_spec.rb +53 -16
- data/spec/stats/helpers_spec.rb +3 -3
- metadata +13 -8
- checksums.yaml +0 -7
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.8.
|
1
|
+
2.8.27
|
@@ -56,7 +56,7 @@ module RightSupport::Stats
|
|
56
56
|
def reset
|
57
57
|
@interval = 0.0
|
58
58
|
@last_start_time = Time.now
|
59
|
-
@
|
59
|
+
@avg_latency = nil
|
60
60
|
@total = 0
|
61
61
|
@count_per_type = {}
|
62
62
|
@last_type = nil
|
@@ -89,22 +89,22 @@ module RightSupport::Stats
|
|
89
89
|
now
|
90
90
|
end
|
91
91
|
|
92
|
-
# Mark the finish of an activity and update the average
|
92
|
+
# Mark the finish of an activity and update the average latency
|
93
93
|
#
|
94
94
|
# @param start_time [Time] Time when activity started, defaults to last time update was called
|
95
95
|
# @param id [String] Unique identifier associated with this activity
|
96
96
|
#
|
97
|
-
# @return [Float] Activity
|
97
|
+
# @return [Float] Activity latency in seconds
|
98
98
|
def finish(start_time = nil, id = nil)
|
99
99
|
now = Time.now
|
100
100
|
start_time ||= @last_start_time
|
101
|
-
|
102
|
-
@
|
101
|
+
latency = now - start_time
|
102
|
+
@avg_latency = average(@avg_latency || 0.0, latency)
|
103
103
|
@last_id = 0 if id && id == @last_id
|
104
|
-
|
104
|
+
latency
|
105
105
|
end
|
106
106
|
|
107
|
-
# Update activity and measure its
|
107
|
+
# Update activity and measure its latency
|
108
108
|
#
|
109
109
|
# @param type [String, Symbol] Type of activity, with anything that is not a symbol, true, or false
|
110
110
|
# automatically converted to a String and truncated to MAX_TYPE_SIZE characters; defaults to nil
|
@@ -134,14 +134,17 @@ module RightSupport::Stats
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
-
# Get average
|
137
|
+
# Get average latency for performing an activity
|
138
138
|
#
|
139
|
-
# @return [Float, NilClass] Average
|
139
|
+
# @return [Float, NilClass] Average latency in seconds of activity weighted
|
140
140
|
# toward past activity, or nil if total is 0
|
141
|
-
def
|
142
|
-
@
|
141
|
+
def avg_latency
|
142
|
+
@avg_latency
|
143
143
|
end
|
144
144
|
|
145
|
+
# avg_duration is deprecated but still supported
|
146
|
+
alias :avg_duration :avg_latency
|
147
|
+
|
145
148
|
# Get stats about last activity
|
146
149
|
#
|
147
150
|
# @return [Hash, NilClass] Information about last activity, or nil if the total is 0
|
@@ -170,7 +173,7 @@ module RightSupport::Stats
|
|
170
173
|
end
|
171
174
|
end
|
172
175
|
|
173
|
-
# Get stat summary including all aspects of activity that were measured except
|
176
|
+
# Get stat summary including all aspects of activity that were measured except latency
|
174
177
|
#
|
175
178
|
# @return [Hash, NilClass] Information about activity, or nil if the total is 0
|
176
179
|
# "total" [Integer] Total activity count
|
@@ -180,6 +183,7 @@ module RightSupport::Stats
|
|
180
183
|
# "type" [String] Type of activity if tracking type, otherwise omitted
|
181
184
|
# "active" [Boolean] Whether activity still active if tracking whether active, otherwise omitted
|
182
185
|
# "rate" [Float] Recent average rate if measuring rate, otherwise omitted
|
186
|
+
# "latency" [Float] Recent average latency if measuring latency, otherwise omitted
|
183
187
|
def all
|
184
188
|
if @total > 0
|
185
189
|
result = if @count_per_type.empty?
|
@@ -189,6 +193,7 @@ module RightSupport::Stats
|
|
189
193
|
end
|
190
194
|
result.merge!("last" => last)
|
191
195
|
result.merge!("rate" => avg_rate) if @measure_rate
|
196
|
+
result.merge!("latency" => avg_latency) if @avg_latency
|
192
197
|
result
|
193
198
|
end
|
194
199
|
end
|
@@ -219,12 +224,15 @@ module RightSupport::Stats
|
|
219
224
|
# "type" [String] Type of activity if tracking type, otherwise omitted
|
220
225
|
# "active" [Boolean] Whether activity still active if tracking whether active, otherwise omitted
|
221
226
|
# "rate" [Float] Recent average rate if measuring rate, otherwise omitted
|
227
|
+
# "latency" [Float] Recent average latency if measuring latency, otherwise omitted
|
222
228
|
def self.all(stats)
|
223
229
|
if (total = stats.inject(0) { |t, s| t += s["total"] if s && s["total"]; t }) > 0
|
224
230
|
all = percentage(stats, total)
|
225
231
|
all["last"] = last(stats.map { |s| s["last"] if s })
|
226
232
|
rate = avg_rate(stats.map { |s| {"rate" => s["rate"], "total" => s["total"]} if s }, total)
|
227
233
|
all["rate"] = rate if rate
|
234
|
+
latency = avg_latency(stats.map { |s| {"latency" => s["latency"], "total" => s["total"]} if s }, total)
|
235
|
+
all["latency"] = latency if latency
|
228
236
|
all
|
229
237
|
end
|
230
238
|
end
|
@@ -267,6 +275,17 @@ module RightSupport::Stats
|
|
267
275
|
sum / total if sum
|
268
276
|
end
|
269
277
|
|
278
|
+
# Compute average latency from multiple average latency
|
279
|
+
#
|
280
|
+
# @param stats [Array] List of stats containing hash of "latency" and "total"
|
281
|
+
# @param total [Integer] Overall total
|
282
|
+
#
|
283
|
+
# @return [Fixnum, NilClass] Average latency or nil if no average latency data
|
284
|
+
def self.avg_latency(stats, total)
|
285
|
+
sum = stats.inject(nil) { |sum, stat| sum = (sum || 0.0) + (stat["latency"] * stat["total"]) if stat && stat["latency"]; sum }
|
286
|
+
sum / total if sum
|
287
|
+
end
|
288
|
+
|
270
289
|
# Determine last activity from multiple last activity stats
|
271
290
|
#
|
272
291
|
# @param stats [Array] Multiple last activity stats
|
@@ -420,7 +420,7 @@ module RightSupport
|
|
420
420
|
# "type"(String):: Type of activity if tracking type, otherwise omitted
|
421
421
|
# "active"(Boolean):: Whether activity still active if tracking whether active, otherwise omitted
|
422
422
|
# "rate" [Float] Recent average rate if measuring rate, otherwise omitted
|
423
|
-
# "
|
423
|
+
# "latency" [Float] Recent average latency of activity if tracking latency, otherwise omitted
|
424
424
|
#
|
425
425
|
# @return [String] Activity stats in displayable format without any line separators
|
426
426
|
def self.activity_str(value)
|
@@ -430,9 +430,9 @@ module RightSupport
|
|
430
430
|
str += "#{value['total']}"
|
431
431
|
str += ", last: #{last_activity_str(value['last'], single_item = true)}" if value["last"]
|
432
432
|
str += ", rate: #{enough_precision(value['rate'])}/sec" if value["rate"]
|
433
|
-
str += ",
|
433
|
+
str += ", latency: #{enough_precision(value['latency'])} sec" if value["latency"]
|
434
434
|
value.each do |name, data|
|
435
|
-
unless ["total", "percent", "last", "rate", "
|
435
|
+
unless ["total", "percent", "last", "rate", "latency"].include?(name)
|
436
436
|
str += ", #{name}: #{data.is_a?(String) ? data : data.inspect}"
|
437
437
|
end
|
438
438
|
end
|
data/right_support.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: right_support 2.8.
|
5
|
+
# stub: right_support 2.8.27 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "right_support"
|
9
|
-
s.version = "2.8.
|
9
|
+
s.version = "2.8.27"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Tony Spataro", "Sergey Sergyenko", "Ryan Williamson", "Lee Kirchhoff", "Alexey Karpik", "Scott Messier"]
|
14
|
-
s.date = "2014-08-
|
14
|
+
s.date = "2014-08-08"
|
15
15
|
s.description = "A toolkit of useful, reusable foundation code created by RightScale."
|
16
16
|
s.email = "support@rightscale.com"
|
17
17
|
s.extra_rdoc_files = [
|
data/spec/stats/activity_spec.rb
CHANGED
@@ -47,7 +47,7 @@ describe RightSupport::Stats::Activity do
|
|
47
47
|
it "initializes stats data" do
|
48
48
|
@stats.instance_variable_get(:@interval).should == 0.0
|
49
49
|
@stats.instance_variable_get(:@last_start_time).should == @now
|
50
|
-
@stats.instance_variable_get(:@
|
50
|
+
@stats.instance_variable_get(:@avg_latency).should be_nil
|
51
51
|
@stats.instance_variable_get(:@total).should == 0
|
52
52
|
@stats.instance_variable_get(:@count_per_type).should == {}
|
53
53
|
end
|
@@ -59,7 +59,7 @@ describe RightSupport::Stats::Activity do
|
|
59
59
|
@stats.update
|
60
60
|
@stats.instance_variable_get(:@interval).should == 1.0
|
61
61
|
@stats.instance_variable_get(:@last_start_time).should == @now + 10
|
62
|
-
@stats.instance_variable_get(:@
|
62
|
+
@stats.instance_variable_get(:@avg_latency).should be_nil
|
63
63
|
@stats.instance_variable_get(:@total).should == 1
|
64
64
|
@stats.instance_variable_get(:@count_per_type).should == {}
|
65
65
|
end
|
@@ -69,7 +69,7 @@ describe RightSupport::Stats::Activity do
|
|
69
69
|
@stats.update("test")
|
70
70
|
@stats.instance_variable_get(:@interval).should == 1.0
|
71
71
|
@stats.instance_variable_get(:@last_start_time).should == @now + 10
|
72
|
-
@stats.instance_variable_get(:@
|
72
|
+
@stats.instance_variable_get(:@avg_latency).should be_nil
|
73
73
|
@stats.instance_variable_get(:@total).should == 1
|
74
74
|
@stats.instance_variable_get(:@count_per_type).should == {"test" => 1}
|
75
75
|
end
|
@@ -106,7 +106,7 @@ describe RightSupport::Stats::Activity do
|
|
106
106
|
@stats.update
|
107
107
|
@stats.instance_variable_get(:@interval).should == 0.0
|
108
108
|
@stats.instance_variable_get(:@last_start_time).should == @now + 10
|
109
|
-
@stats.instance_variable_get(:@
|
109
|
+
@stats.instance_variable_get(:@avg_latency).should be_nil
|
110
110
|
@stats.instance_variable_get(:@total).should == 1
|
111
111
|
@stats.instance_variable_get(:@count_per_type).should == {}
|
112
112
|
end
|
@@ -118,24 +118,24 @@ describe RightSupport::Stats::Activity do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
context :finish do
|
121
|
-
it "updates
|
121
|
+
it "updates latency when finish using internal start time by default" do
|
122
122
|
flexmock(Time).should_receive(:now).and_return(1000010)
|
123
|
-
@stats.
|
123
|
+
@stats.avg_latency.should be_nil
|
124
124
|
@stats.finish
|
125
125
|
@stats.instance_variable_get(:@interval).should == 0.0
|
126
126
|
@stats.instance_variable_get(:@last_start_time).should == @now
|
127
|
-
@stats.instance_variable_get(:@
|
127
|
+
@stats.instance_variable_get(:@avg_latency).should == 1.0
|
128
128
|
@stats.instance_variable_get(:@total).should == 0
|
129
129
|
@stats.instance_variable_get(:@count_per_type).should == {}
|
130
130
|
end
|
131
131
|
|
132
|
-
it "updates
|
132
|
+
it "updates latency when finish using specified start time" do
|
133
133
|
flexmock(Time).should_receive(:now).and_return(1000030)
|
134
|
-
@stats.
|
134
|
+
@stats.avg_latency.should be_nil
|
135
135
|
@stats.finish(1000010)
|
136
136
|
@stats.instance_variable_get(:@interval).should == 0.0
|
137
137
|
@stats.instance_variable_get(:@last_start_time).should == @now
|
138
|
-
@stats.instance_variable_get(:@
|
138
|
+
@stats.instance_variable_get(:@avg_latency).should == 2.0
|
139
139
|
@stats.instance_variable_get(:@total).should == 0
|
140
140
|
@stats.instance_variable_get(:@count_per_type).should == {}
|
141
141
|
end
|
@@ -150,7 +150,7 @@ describe RightSupport::Stats::Activity do
|
|
150
150
|
@stats.instance_variable_get(:@total).should == 1
|
151
151
|
@stats.instance_variable_get(:@count_per_type).should == {"test" => 1}
|
152
152
|
@stats.instance_variable_get(:@last_id).should == 0
|
153
|
-
@stats.instance_variable_get(:@
|
153
|
+
@stats.instance_variable_get(:@avg_latency).should == 1.0
|
154
154
|
end
|
155
155
|
|
156
156
|
it "yields to the block" do
|
@@ -227,7 +227,7 @@ describe RightSupport::Stats::Activity do
|
|
227
227
|
@stats.last.should == {"elapsed" => 10, "type" => "test", "active" => true}
|
228
228
|
@stats.finish(@now - 10, "token")
|
229
229
|
@stats.last.should == {"elapsed" => 10, "type" => "test", "active" => false}
|
230
|
-
@stats.instance_variable_get(:@
|
230
|
+
@stats.instance_variable_get(:@avg_latency).should == 2.0
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
@@ -251,17 +251,25 @@ describe RightSupport::Stats::Activity do
|
|
251
251
|
end
|
252
252
|
|
253
253
|
context :all do
|
254
|
-
it "returns all activity aspects that were measured
|
254
|
+
it "returns all activity aspects that were measured" do
|
255
255
|
flexmock(Time).should_receive(:now).and_return(1000020, 1000042)
|
256
256
|
@stats.update
|
257
|
-
@stats.
|
257
|
+
@stats.finish
|
258
|
+
@stats.all.should == {"last" => {"elapsed" => 22}, "total" => 1, "rate" => 0.25, "latency" => 2.2}
|
258
259
|
end
|
259
260
|
|
260
261
|
it "excludes rate if it is not being measured" do
|
261
262
|
@stats = RightSupport::Stats::Activity.new(false)
|
262
|
-
flexmock(Time).should_receive(:now).and_return(
|
263
|
+
flexmock(Time).should_receive(:now).and_return(1000020, 1000042)
|
264
|
+
@stats.update
|
265
|
+
@stats.finish
|
266
|
+
@stats.all.should == {"last" => {"elapsed" => 22}, "total" => 1, "latency" => 2.2}
|
267
|
+
end
|
268
|
+
|
269
|
+
it "excludes latency if it is not being measured" do
|
270
|
+
flexmock(Time).should_receive(:now).and_return(1000020, 1000042)
|
263
271
|
@stats.update
|
264
|
-
@stats.all.should == {"last" => {"elapsed" =>
|
272
|
+
@stats.all.should == {"last" => {"elapsed" => 22}, "total" => 1, "rate" => 0.25}
|
265
273
|
end
|
266
274
|
|
267
275
|
it "includes percentage breakdown when update recorded per type" do
|
@@ -310,6 +318,13 @@ describe RightSupport::Stats::Activity do
|
|
310
318
|
{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 5, "percent" => {"foo" => 20.0, "bar" => 80.0}, "rate" => 0.18}
|
311
319
|
end
|
312
320
|
|
321
|
+
it "includes latency if provided" do
|
322
|
+
stats = [{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}, "rate" => 0.5, "latency" => 0.5},
|
323
|
+
{"last" => {"elapsed" => 20, "type" => "bar"}, "total" => 4, "percent" => {"bar" => 100.0}, "rate" => 0.1, "latency" => 0.1}]
|
324
|
+
RightSupport::Stats::Activity.all(stats).should ==
|
325
|
+
{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 5, "percent" => {"foo" => 20.0, "bar" => 80.0}, "rate" => 0.18, "latency" => 0.18}
|
326
|
+
end
|
327
|
+
|
313
328
|
it "handles nil stat" do
|
314
329
|
stats = [{"last" => {"elapsed" => 10, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}},
|
315
330
|
nil,
|
@@ -367,6 +382,28 @@ describe RightSupport::Stats::Activity do
|
|
367
382
|
end
|
368
383
|
end
|
369
384
|
|
385
|
+
context :avg_latency do
|
386
|
+
it "computes average latency from multiple average latencies" do
|
387
|
+
stats = [{"total" => 1, "latency" => 0.5},
|
388
|
+
{"total" => 5, "latency" => 0.1},
|
389
|
+
{"total" => 4, "latency" => 0.2}]
|
390
|
+
RightSupport::Stats::Activity.avg_latency(stats, 10).should == 0.18
|
391
|
+
end
|
392
|
+
|
393
|
+
it "handles nil stat" do
|
394
|
+
stats = [{"total" => 1, "latency" => 0.5},
|
395
|
+
nil,
|
396
|
+
{"total" => 5, "latency" => 0.1},
|
397
|
+
nil,
|
398
|
+
{"total" => 4, "latency" => 0.2}]
|
399
|
+
RightSupport::Stats::Activity.avg_latency(stats, 10).should == 0.18
|
400
|
+
end
|
401
|
+
|
402
|
+
it "returns nil if there is no data" do
|
403
|
+
RightSupport::Stats::Activity.avg_latency([], 0).should be_nil
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
370
407
|
context :last do
|
371
408
|
it "determines last activity from multiple last activity stats" do
|
372
409
|
stats = [{"last" => {"elapsed" => 0, "type" => "foo"}, "total" => 1, "percent" => {"foo" => 100.0}},
|
data/spec/stats/helpers_spec.rb
CHANGED
@@ -338,7 +338,7 @@ describe RightSupport::Stats do
|
|
338
338
|
" (1) Mon Jan 12 #{@hr}:46:40 Exception: Test error\n" +
|
339
339
|
" heartbeat : 60 sec\n" +
|
340
340
|
" returns : no queue consumers: 67%, no queue: 33%, total: 3, \n" +
|
341
|
-
" last: no queue consumers (10 sec ago), rate: 1.0/sec\n"
|
341
|
+
" last: no queue consumers (10 sec ago), rate: 1.0/sec, latency: 1.0 sec\n"
|
342
342
|
end
|
343
343
|
|
344
344
|
it "omits exceptions from brokers if exceptions key omitted" do
|
@@ -357,7 +357,7 @@ describe RightSupport::Stats do
|
|
357
357
|
" b2: rs-broker-localhost-5674 failed, disconnects: none, failures: 3 (16 min 40 sec ago w/ 2 retries)\n" +
|
358
358
|
" heartbeat : 60 sec\n" +
|
359
359
|
" returns : no queue consumers: 67%, no queue: 33%, total: 3, \n" +
|
360
|
-
" last: no queue consumers (10 sec ago), rate: 1.0/sec\n"
|
360
|
+
" last: no queue consumers (10 sec ago), rate: 1.0/sec, latency: 1.0 sec\n"
|
361
361
|
end
|
362
362
|
end
|
363
363
|
|
@@ -371,7 +371,7 @@ describe RightSupport::Stats do
|
|
371
371
|
activity.update("more testing")
|
372
372
|
flexmock(Time).should_receive(:now).and_return(1000010)
|
373
373
|
@helpers.activity_str(activity.all).should == "more testing: 75%, testing: 25%, total: 4, last: more testing (10 sec ago), " +
|
374
|
-
"rate: 1.0/sec"
|
374
|
+
"rate: 1.0/sec, latency: 1.0 sec"
|
375
375
|
end
|
376
376
|
|
377
377
|
it 'converts last activity stats to string' do
|
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.8.
|
4
|
+
version: 2.8.27
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Tony Spataro
|
@@ -13,7 +14,7 @@ authors:
|
|
13
14
|
autorequire:
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
|
-
date: 2014-08-
|
17
|
+
date: 2014-08-08 00:00:00.000000000 Z
|
17
18
|
dependencies: []
|
18
19
|
description: A toolkit of useful, reusable foundation code created by RightScale.
|
19
20
|
email: support@rightscale.com
|
@@ -23,7 +24,7 @@ extra_rdoc_files:
|
|
23
24
|
- LICENSE
|
24
25
|
- README.rdoc
|
25
26
|
files:
|
26
|
-
-
|
27
|
+
- .rspec
|
27
28
|
- CHANGELOG.rdoc
|
28
29
|
- Gemfile
|
29
30
|
- Gemfile.lock
|
@@ -145,25 +146,29 @@ files:
|
|
145
146
|
homepage: https://github.com/rightscale/right_support
|
146
147
|
licenses:
|
147
148
|
- MIT
|
148
|
-
metadata: {}
|
149
149
|
post_install_message:
|
150
150
|
rdoc_options: []
|
151
151
|
require_paths:
|
152
152
|
- lib
|
153
153
|
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
154
155
|
requirements:
|
155
|
-
- -
|
156
|
+
- - ! '>='
|
156
157
|
- !ruby/object:Gem::Version
|
157
158
|
version: '0'
|
159
|
+
segments:
|
160
|
+
- 0
|
161
|
+
hash: 3750525095222830475
|
158
162
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
159
164
|
requirements:
|
160
|
-
- -
|
165
|
+
- - ! '>='
|
161
166
|
- !ruby/object:Gem::Version
|
162
167
|
version: '0'
|
163
168
|
requirements: []
|
164
169
|
rubyforge_project:
|
165
|
-
rubygems_version:
|
170
|
+
rubygems_version: 1.8.26
|
166
171
|
signing_key:
|
167
|
-
specification_version:
|
172
|
+
specification_version: 3
|
168
173
|
summary: Reusable foundation code.
|
169
174
|
test_files: []
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: 6010fe3e9ebbaa222eacaf224b0576a36e350f7c
|
4
|
-
data.tar.gz: a4a49a381d7a250a19230b0a3cd848436a51f221
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: a74303a4224cd7bc1ffa4bb3d65df38b4664baa9c55ba31f63327696ff598756c7898d6de68ac625caa8f0e453e5811c73adf8b725a9dc709ebdf200d5f09189
|
7
|
-
data.tar.gz: 9f1d6b77e03702c6e8f83e04633f5fe8708d79f4a790dfbae77a4c860b5da1772576405db4d30d00f8eb19cb15b21d9591b3a91b575806702bd212b68f88cd91
|