scout_apm 2.1.0 → 2.1.1
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.markdown +6 -0
- data/lib/scout_apm.rb +0 -1
- data/lib/scout_apm/agent.rb +1 -1
- data/lib/scout_apm/config.rb +8 -2
- data/lib/scout_apm/instant/middleware.rb +2 -2
- data/lib/scout_apm/instruments/active_record.rb +2 -2
- data/lib/scout_apm/layaway.rb +9 -5
- data/lib/scout_apm/version.rb +1 -1
- data/test/test_helper.rb +38 -0
- data/test/unit/config_test.rb +2 -2
- data/test/unit/layaway_test.rb +11 -16
- data/test/unit/serializers/payload_serializer_test.rb +0 -102
- data/test/unit/slow_job_policy_test.rb +0 -49
- data/test/unit/slow_request_policy_test.rb +4 -5
- metadata +2 -5
- data/lib/scout_apm/slow_item_set.rb +0 -80
- data/test/unit/slow_item_set_test.rb +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18409ba68d80eefddd0a94c5c8f47f7e8061d921
|
4
|
+
data.tar.gz: 7ab7791459924188b16c1551bf3682defd268937
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a586e1f036a81decca9bdda9d15735174e1676157af56c78cd1c8251b44046872811bfd2fcb4b017c7c973bd6e7daf8e608169c6958f33542a5b32cf16b29d3f
|
7
|
+
data.tar.gz: fb8e623a8f354686a3c3a37ba7e3b9a16db3e17cbefe58b2f7fad198ce5c70b187e33b0d487fcb3f7f71860e8e9b76d2648f81728d6923fdd71bc8e4efee2b7e
|
data/CHANGELOG.markdown
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -120,7 +120,6 @@ require 'scout_apm/metric_meta'
|
|
120
120
|
require 'scout_apm/metric_stats'
|
121
121
|
require 'scout_apm/slow_transaction'
|
122
122
|
require 'scout_apm/slow_job_record'
|
123
|
-
require 'scout_apm/slow_item_set'
|
124
123
|
require 'scout_apm/scored_item_set'
|
125
124
|
require 'scout_apm/slow_request_policy'
|
126
125
|
require 'scout_apm/slow_job_policy'
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -55,7 +55,7 @@ module ScoutApm
|
|
55
55
|
@request_histograms_by_time = Hash.new { |h, k| h[k] = ScoutApm::RequestHistograms.new }
|
56
56
|
|
57
57
|
@store = ScoutApm::Store.new
|
58
|
-
@layaway = ScoutApm::Layaway.new
|
58
|
+
@layaway = ScoutApm::Layaway.new(config, environment)
|
59
59
|
@metric_lookup = Hash.new
|
60
60
|
|
61
61
|
@capacity = ScoutApm::Capacity.new
|
data/lib/scout_apm/config.rb
CHANGED
@@ -137,11 +137,17 @@ module ScoutApm
|
|
137
137
|
end
|
138
138
|
|
139
139
|
def initialize(overlays)
|
140
|
-
@overlays = overlays
|
140
|
+
@overlays = Array(overlays)
|
141
141
|
end
|
142
142
|
|
143
143
|
def value(key)
|
144
|
-
|
144
|
+
o = @overlays.detect{ |overlay| overlay.has_key?(key) }
|
145
|
+
raw_value = if o
|
146
|
+
o.value(key)
|
147
|
+
else
|
148
|
+
# No overlay said it could handle this key, bail out with nil.
|
149
|
+
nil
|
150
|
+
end
|
145
151
|
|
146
152
|
coercion = SETTING_COERCIONS[key] || NullCoercion.new
|
147
153
|
coercion.coerce(raw_value)
|
@@ -61,7 +61,7 @@ module ScoutApm
|
|
61
61
|
:platform => "ruby",
|
62
62
|
}
|
63
63
|
hash = ScoutApm::Serializers::PayloadSerializerToJson.rearrange_slow_transaction(trace)
|
64
|
-
hash.merge!(
|
64
|
+
hash.merge!(:metadata => metadata)
|
65
65
|
payload = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
|
66
66
|
|
67
67
|
if env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
|
@@ -105,4 +105,4 @@ module ScoutApm
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
end
|
108
|
-
end
|
108
|
+
end
|
@@ -173,7 +173,7 @@ module ScoutApm
|
|
173
173
|
req.start_layer(layer)
|
174
174
|
req.ignore_children!
|
175
175
|
begin
|
176
|
-
find_by_sql_without_scout_instruments(*args)
|
176
|
+
find_by_sql_without_scout_instruments(*args, &block)
|
177
177
|
ensure
|
178
178
|
req.acknowledge_children!
|
179
179
|
req.stop_layer
|
@@ -199,7 +199,7 @@ module ScoutApm
|
|
199
199
|
req.start_layer(layer)
|
200
200
|
req.ignore_children!
|
201
201
|
begin
|
202
|
-
find_with_associations_without_scout_instruments(*args)
|
202
|
+
find_with_associations_without_scout_instruments(*args, &block)
|
203
203
|
ensure
|
204
204
|
req.acknowledge_children!
|
205
205
|
req.stop_layer
|
data/lib/scout_apm/layaway.rb
CHANGED
@@ -18,8 +18,12 @@ module ScoutApm
|
|
18
18
|
# Must be sortable as an integer
|
19
19
|
TIME_FORMAT = "%Y%m%d%H%M"
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
attr_reader :config
|
22
|
+
attr_reader :environment
|
23
|
+
|
24
|
+
def initialize(config, environment)
|
25
|
+
@config = config
|
26
|
+
@environment = environment
|
23
27
|
end
|
24
28
|
|
25
29
|
# Returns a Pathname object with the fully qualified directory where the layaway files can be placed.
|
@@ -30,12 +34,12 @@ module ScoutApm
|
|
30
34
|
def directory
|
31
35
|
return @directory if @directory
|
32
36
|
|
33
|
-
data_file =
|
34
|
-
data_file = File.dirname(data_file) if data_file && !File.directory?
|
37
|
+
data_file = config.value("data_file")
|
38
|
+
data_file = File.dirname(data_file) if data_file && !File.directory?(data_file)
|
35
39
|
|
36
40
|
candidates = [
|
37
41
|
data_file,
|
38
|
-
"#{
|
42
|
+
"#{environment.root}/tmp",
|
39
43
|
"/tmp"
|
40
44
|
].compact
|
41
45
|
|
data/lib/scout_apm/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -17,6 +17,34 @@ Kernel.module_eval do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
# A test helper class to create a temporary "configuration" we can control entirely purposes
|
21
|
+
class FakeConfigOverlay
|
22
|
+
def initialize(values)
|
23
|
+
@values = values
|
24
|
+
end
|
25
|
+
|
26
|
+
def value(key)
|
27
|
+
@values[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_key?(key)
|
31
|
+
@values.has_key?(key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class FakeEnvironment
|
36
|
+
def initialize(values)
|
37
|
+
@values = values
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(sym)
|
41
|
+
if @values.has_key?(sym)
|
42
|
+
@values[sym]
|
43
|
+
else
|
44
|
+
raise "#{sym} not found in FakeEnvironment"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
20
48
|
|
21
49
|
# Helpers available to all tests
|
22
50
|
class Minitest::Test
|
@@ -42,6 +70,16 @@ class Minitest::Test
|
|
42
70
|
ScoutApm::Agent.instance.instance_variable_set("@logger", @logger)
|
43
71
|
end
|
44
72
|
|
73
|
+
def make_fake_environment(values)
|
74
|
+
FakeEnvironment.new(values)
|
75
|
+
end
|
76
|
+
|
77
|
+
def make_fake_config(values)
|
78
|
+
ScoutApm::Config.new(FakeConfigOverlay.new(values))
|
79
|
+
end
|
80
|
+
|
45
81
|
DATA_FILE_DIR = File.dirname(__FILE__) + '/tmp'
|
46
82
|
DATA_FILE_PATH = "#{DATA_FILE_DIR}/scout_apm.db"
|
47
83
|
end
|
84
|
+
|
85
|
+
|
data/test/unit/config_test.rb
CHANGED
@@ -4,7 +4,7 @@ require 'scout_apm/config'
|
|
4
4
|
|
5
5
|
class ConfigTest < Minitest::Test
|
6
6
|
def test_initalize_without_a_config
|
7
|
-
conf = ScoutApm::Config.
|
7
|
+
conf = ScoutApm::Config.without_file
|
8
8
|
|
9
9
|
# nil for random keys
|
10
10
|
assert_nil conf.value("log_file_path")
|
@@ -21,7 +21,7 @@ class ConfigTest < Minitest::Test
|
|
21
21
|
set_rack_env("production")
|
22
22
|
|
23
23
|
conf_file = File.expand_path("../../data/config_test_1.yml", __FILE__)
|
24
|
-
conf = ScoutApm::Config.
|
24
|
+
conf = ScoutApm::Config.with_file(conf_file)
|
25
25
|
|
26
26
|
assert_equal "debug", conf.value('log_level')
|
27
27
|
assert_equal "APM Test Conf (Production)", conf.value('name')
|
data/test/unit/layaway_test.rb
CHANGED
@@ -5,25 +5,20 @@ require 'scout_apm/metric_stats'
|
|
5
5
|
require 'scout_apm/context'
|
6
6
|
require 'scout_apm/store'
|
7
7
|
|
8
|
+
require 'fileutils'
|
8
9
|
class LayawayTest < Minitest::Test
|
9
|
-
def
|
10
|
-
|
11
|
-
|
10
|
+
def test_directory_uses_DATA_FILE_option
|
11
|
+
FileUtils.mkdir_p '/tmp/scout_apm_test/data_file_option'
|
12
|
+
config = make_fake_config("data_file" => "/tmp/scout_apm_test/data_file_option")
|
12
13
|
|
13
|
-
|
14
|
-
t = ScoutApm::StoreReportingPeriodTimestamp.new
|
15
|
-
data.add_reporting_period(t,ScoutApm::StoreReportingPeriod.new(t))
|
16
|
-
assert_equal [TIMESTAMP,t].sort_by(&:timestamp), Marshal.load(File.read(DATA_FILE_PATH)).keys.sort_by(&:timestamp)
|
14
|
+
assert_equal Pathname.new("/tmp/scout_apm_test/data_file_option"), ScoutApm::Layaway.new(config, ScoutApm::Agent.instance.environment).directory
|
17
15
|
end
|
18
16
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
unmarshalled = Marshal.load(File.read(DATA_FILE_PATH))
|
24
|
-
assert_equal [TIMESTAMP], unmarshalled.keys
|
25
|
-
end
|
17
|
+
def test_directory_looks_for_root_slash_tmp
|
18
|
+
FileUtils.mkdir_p '/tmp/scout_apm_test/tmp'
|
19
|
+
config = make_fake_config({})
|
20
|
+
env = make_fake_environment(:root => "/tmp/scout_apm_test")
|
26
21
|
|
27
|
-
|
28
|
-
|
22
|
+
assert_equal Pathname.new("/tmp/scout_apm_test/tmp"), ScoutApm::Layaway.new(config, env).directory
|
23
|
+
end
|
29
24
|
end
|
@@ -6,26 +6,11 @@ require 'scout_apm/serializers/payload_serializer_to_json'
|
|
6
6
|
require 'scout_apm/slow_transaction'
|
7
7
|
require 'scout_apm/metric_meta'
|
8
8
|
require 'scout_apm/metric_stats'
|
9
|
-
require 'scout_apm/utils/fake_stack_prof'
|
10
9
|
require 'scout_apm/context'
|
11
10
|
require 'ostruct'
|
12
11
|
require 'json' # to deserialize what has been manually serialized by the production code
|
13
12
|
|
14
|
-
# stub the report_format value
|
15
|
-
# class ScoutApm::Agent
|
16
|
-
# module Config
|
17
|
-
# def self.value(key)
|
18
|
-
# 'json'
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
|
22
|
-
# def self.instance
|
23
|
-
# OpenStruct.new(:config => Config)
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
|
27
13
|
class PayloadSerializerTest < Minitest::Test
|
28
|
-
|
29
14
|
def test_serializes_metadata_as_json
|
30
15
|
metadata = {
|
31
16
|
:app_root => "/srv/app/rootz",
|
@@ -112,93 +97,6 @@ class PayloadSerializerTest < Minitest::Test
|
|
112
97
|
assert_equal formatted_metrics, JSON.parse(payload)["metrics"]
|
113
98
|
end
|
114
99
|
|
115
|
-
def test_serializes_slow_transactions_as_json
|
116
|
-
slow_transaction_metrics = {
|
117
|
-
ScoutApm::MetricMeta.new('ActiveRecord/all').tap { |meta|
|
118
|
-
meta.desc = "SELECT *\nfrom users where filter=?"
|
119
|
-
meta.extra = {:user => 'cooluser'}
|
120
|
-
meta.metric_id = nil
|
121
|
-
meta.scope = "Controller/apps/checkin"
|
122
|
-
} => ScoutApm::MetricStats.new.tap { |stats|
|
123
|
-
stats.call_count = 16
|
124
|
-
stats.max_call_time = 0.005338062
|
125
|
-
stats.min_call_time = 0.000613518
|
126
|
-
stats.sum_of_squares = 9.8040860751126e-05
|
127
|
-
stats.total_call_time = 0.033245704
|
128
|
-
stats.total_exclusive_time = 0.033245704
|
129
|
-
},
|
130
|
-
ScoutApm::MetricMeta.new("Controller/apps/checkin").tap { |meta|
|
131
|
-
meta.desc = nil
|
132
|
-
meta.extra = {}
|
133
|
-
meta.metric_id = nil
|
134
|
-
meta.scope = nil
|
135
|
-
} => ScoutApm::MetricStats.new.tap { |stats|
|
136
|
-
stats.call_count = 2
|
137
|
-
stats.max_call_time = 0.078521419
|
138
|
-
stats.min_call_time = 0.034881757
|
139
|
-
stats.sum_of_squares = 0.007382350213180609
|
140
|
-
stats.total_call_time = 0.113403176
|
141
|
-
stats.total_exclusive_time = 0.07813208899999999
|
142
|
-
}
|
143
|
-
}
|
144
|
-
context = ScoutApm::Context.new
|
145
|
-
context.add({"this" => "that"})
|
146
|
-
context.add_user({"hello" => "goodbye"})
|
147
|
-
slow_t = ScoutApm::SlowTransaction.new("http://example.com/blabla", "Buckethead/something/else", 1.23, slow_transaction_metrics, context, Time.at(1448198788), [], 10)
|
148
|
-
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, {}, [slow_t], [], [])
|
149
|
-
formatted_slow_transactions = [
|
150
|
-
{
|
151
|
-
"key" => {
|
152
|
-
"bucket" => "Buckethead",
|
153
|
-
"name" => "something/else"
|
154
|
-
},
|
155
|
-
"time" => "2015-11-22 06:26:28 -0700",
|
156
|
-
"total_call_time" => 1.23,
|
157
|
-
"uri" => "http://example.com/blabla",
|
158
|
-
"context" => {"this"=>"that", "user"=>{"hello"=>"goodbye"}},
|
159
|
-
"prof" => [],
|
160
|
-
"score" => 10,
|
161
|
-
"metrics" => [
|
162
|
-
{
|
163
|
-
"key" => {
|
164
|
-
"bucket" => "ActiveRecord",
|
165
|
-
"name" => "all",
|
166
|
-
"desc" => "SELECT *\nfrom users where filter=?",
|
167
|
-
"extra" => {
|
168
|
-
"user" => "cooluser",
|
169
|
-
},
|
170
|
-
"scope" => {
|
171
|
-
"bucket" => "Controller",
|
172
|
-
"name" => "apps/checkin",
|
173
|
-
},
|
174
|
-
},
|
175
|
-
"call_count" => 16,
|
176
|
-
"max_call_time" => 0.005338062,
|
177
|
-
"min_call_time" => 0.000613518,
|
178
|
-
"total_call_time" => 0.033245704,
|
179
|
-
"total_exclusive_time" => 0.033245704,
|
180
|
-
},
|
181
|
-
{
|
182
|
-
"key" => {
|
183
|
-
"bucket" => "Controller",
|
184
|
-
"name" => "apps/checkin",
|
185
|
-
"desc" => nil,
|
186
|
-
"extra" => {},
|
187
|
-
"scope" => nil,
|
188
|
-
},
|
189
|
-
"call_count" => 2,
|
190
|
-
"max_call_time" => 0.078521419,
|
191
|
-
"min_call_time" => 0.034881757,
|
192
|
-
"total_call_time" => 0.113403176,
|
193
|
-
"total_exclusive_time" => 0.07813208899999999,
|
194
|
-
}
|
195
|
-
]
|
196
|
-
}
|
197
|
-
]
|
198
|
-
|
199
|
-
assert_equal formatted_slow_transactions, JSON.parse(payload)["slow_transactions"]
|
200
|
-
end
|
201
|
-
|
202
100
|
def test_escapes_json_quotes
|
203
101
|
metadata = {
|
204
102
|
:quotie => "here are some \"quotes\"",
|
@@ -3,53 +3,4 @@ require 'test_helper'
|
|
3
3
|
require 'scout_apm/slow_job_policy'
|
4
4
|
|
5
5
|
class SlowJobPolicyTest < Minitest::Test
|
6
|
-
def test_first_call_is_not_slow
|
7
|
-
policy = ScoutApm::SlowJobPolicy.new
|
8
|
-
assert !policy.slow?("TestWorker", 10)
|
9
|
-
end
|
10
|
-
|
11
|
-
# All of these get faster and faster, so none are marked as slow.
|
12
|
-
def test_fast_calls_are_not_slow
|
13
|
-
policy = ScoutApm::SlowJobPolicy.new
|
14
|
-
assert !policy.slow?("TestWorker", 10)
|
15
|
-
assert !policy.slow?("TestWorker", 8)
|
16
|
-
assert !policy.slow?("TestWorker", 6)
|
17
|
-
assert !policy.slow?("TestWorker", 4)
|
18
|
-
assert !policy.slow?("TestWorker", 2)
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_slow_calls_are_marked_as_slow
|
22
|
-
policy = ScoutApm::SlowJobPolicy.new
|
23
|
-
policy.slow?("TestWorker", 10) # Prime it with a not-slow
|
24
|
-
|
25
|
-
assert policy.slow?("TestWorker", 12)
|
26
|
-
assert policy.slow?("TestWorker", 14)
|
27
|
-
assert policy.slow?("TestWorker", 16)
|
28
|
-
assert policy.slow?("TestWorker", 18)
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_mix_of_fast_and_slow
|
32
|
-
policy = ScoutApm::SlowJobPolicy.new
|
33
|
-
policy.slow?("TestWorker", 10) # Prime it with a not-slow
|
34
|
-
|
35
|
-
assert policy.slow?("TestWorker", 12)
|
36
|
-
assert !policy.slow?("TestWorker", 8)
|
37
|
-
assert policy.slow?("TestWorker", 13)
|
38
|
-
assert !policy.slow?("TestWorker", 6)
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_different_workers_dont_interfere
|
42
|
-
policy = ScoutApm::SlowJobPolicy.new
|
43
|
-
policy.slow?("TestWorker", 10) # Prime it with a not-slow
|
44
|
-
policy.slow?("OtherWorker", 1.0) # Prime it with a not-slow
|
45
|
-
|
46
|
-
assert !policy.slow?("TestWorker", 8)
|
47
|
-
assert policy.slow?("OtherWorker", 2)
|
48
|
-
assert !policy.slow?("TestWorker", 1)
|
49
|
-
assert policy.slow?("OtherWorker", 3)
|
50
|
-
assert policy.slow?("TestWorker", 12)
|
51
|
-
assert !policy.slow?("OtherWorker", 1)
|
52
|
-
assert policy.slow?("TestWorker", 12)
|
53
|
-
assert policy.slow?("OtherWorker", 4)
|
54
|
-
end
|
55
6
|
end
|
@@ -32,11 +32,10 @@ class SlowRequestPolicyTest < Minitest::Test
|
|
32
32
|
|
33
33
|
request.set_duration(10) # 10 seconds
|
34
34
|
policy.last_seen[request.unique_name] = Time.now - 120 # 2 minutes since last seen
|
35
|
-
|
36
|
-
|
37
|
-
# Actual value I have in console is 1.599
|
38
|
-
assert policy.score(request) > 1.5
|
39
|
-
assert policy.score(request) < 2.0
|
35
|
+
ScoutApm::Agent.instance.request_histograms.add(request.unique_name, 1)
|
40
36
|
|
37
|
+
# Actual value I have in console is 1.499
|
38
|
+
assert policy.score(request) > 1.45
|
39
|
+
assert policy.score(request) < 1.55
|
41
40
|
end
|
42
41
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-08-
|
12
|
+
date: 2016-08-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rusage
|
@@ -200,7 +200,6 @@ files:
|
|
200
200
|
- lib/scout_apm/server_integrations/thin.rb
|
201
201
|
- lib/scout_apm/server_integrations/unicorn.rb
|
202
202
|
- lib/scout_apm/server_integrations/webrick.rb
|
203
|
-
- lib/scout_apm/slow_item_set.rb
|
204
203
|
- lib/scout_apm/slow_job_policy.rb
|
205
204
|
- lib/scout_apm/slow_job_record.rb
|
206
205
|
- lib/scout_apm/slow_request_policy.rb
|
@@ -233,7 +232,6 @@ files:
|
|
233
232
|
- test/unit/metric_set_test.rb
|
234
233
|
- test/unit/scored_item_set_test.rb
|
235
234
|
- test/unit/serializers/payload_serializer_test.rb
|
236
|
-
- test/unit/slow_item_set_test.rb
|
237
235
|
- test/unit/slow_job_policy_test.rb
|
238
236
|
- test/unit/slow_request_policy_test.rb
|
239
237
|
- test/unit/sql_sanitizer_test.rb
|
@@ -277,7 +275,6 @@ test_files:
|
|
277
275
|
- test/unit/metric_set_test.rb
|
278
276
|
- test/unit/scored_item_set_test.rb
|
279
277
|
- test/unit/serializers/payload_serializer_test.rb
|
280
|
-
- test/unit/slow_item_set_test.rb
|
281
278
|
- test/unit/slow_job_policy_test.rb
|
282
279
|
- test/unit/slow_request_policy_test.rb
|
283
280
|
- test/unit/sql_sanitizer_test.rb
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# In order to keep load down, only record a sample of Slow Items (Transactions
|
2
|
-
# or Jobs). In order to make that sampling as fair as possible, follow a basic
|
3
|
-
# algorithm:
|
4
|
-
#
|
5
|
-
# When adding a new Slow Item:
|
6
|
-
# * Just add it if there is an open spot
|
7
|
-
# * If there isn't an open spot, attempt to remove an over-represented
|
8
|
-
# item instead ("attempt_to_evict"). Overrepresented is simply "has more
|
9
|
-
# than @fair number of Matching Items in the set". The fastest of the
|
10
|
-
# overrepresented items is removed.
|
11
|
-
# * If there isn't an open spot, and no Item is valid to evict, drop the
|
12
|
-
# incoming Item without adding.
|
13
|
-
#
|
14
|
-
# There is no way to remove Items from this set, create a new object
|
15
|
-
# for each reporting period.
|
16
|
-
#
|
17
|
-
# Item must respond to:
|
18
|
-
# #metric_name - string - grouping key to see if one kind of thing is overrepresented
|
19
|
-
# #total_call_time - float - duration of the item
|
20
|
-
|
21
|
-
module ScoutApm
|
22
|
-
class SlowItemSet
|
23
|
-
include Enumerable
|
24
|
-
|
25
|
-
DEFAULT_TOTAL = 10
|
26
|
-
DEFAULT_FAIR = 1
|
27
|
-
|
28
|
-
attr_reader :total
|
29
|
-
attr_reader :fair
|
30
|
-
|
31
|
-
def initialize(total=DEFAULT_TOTAL, fair=DEFAULT_FAIR)
|
32
|
-
@total = total
|
33
|
-
@fair = fair
|
34
|
-
@items = []
|
35
|
-
end
|
36
|
-
|
37
|
-
def each
|
38
|
-
@items.each { |s| yield s }
|
39
|
-
end
|
40
|
-
|
41
|
-
def <<(item)
|
42
|
-
return if attempt_append(item)
|
43
|
-
attempt_to_evict
|
44
|
-
attempt_append(item)
|
45
|
-
end
|
46
|
-
|
47
|
-
def empty_slot?
|
48
|
-
@items.length < total
|
49
|
-
end
|
50
|
-
|
51
|
-
def attempt_append(item)
|
52
|
-
if empty_slot?
|
53
|
-
@items.push(item)
|
54
|
-
true
|
55
|
-
else
|
56
|
-
false
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def attempt_to_evict
|
61
|
-
return if @items.length == 0
|
62
|
-
|
63
|
-
overrepresented = @items.
|
64
|
-
group_by { |item| unique_name_for(item) }.
|
65
|
-
to_a.
|
66
|
-
sort_by { |(_, items)| items.length }.
|
67
|
-
last
|
68
|
-
|
69
|
-
if overrepresented[1].length > fair
|
70
|
-
fastest = overrepresented[1].sort_by { |item| item.total_call_time }.first
|
71
|
-
@items.delete(fastest)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# Determine this items' "hash key"
|
76
|
-
def unique_name_for(item)
|
77
|
-
item.metric_name
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
require 'scout_apm/slow_item_set'
|
4
|
-
require 'scout_apm/slow_transaction'
|
5
|
-
|
6
|
-
class SlowItemSetTest < Minitest::Test
|
7
|
-
def test_adding_to_empty_set
|
8
|
-
set = ScoutApm::SlowItemSet.new(3, 1)
|
9
|
-
set << make_slow("Controller/Foo")
|
10
|
-
assert_equal 1, set.count
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_adding_to_partially_full_set
|
14
|
-
set = ScoutApm::SlowItemSet.new(3, 1)
|
15
|
-
set << make_slow("Controller/Foo")
|
16
|
-
set << make_slow("Controller/Foo")
|
17
|
-
assert_equal 2, set.count
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_overflow_of_one_type
|
21
|
-
max_size = 3
|
22
|
-
set = ScoutApm::SlowItemSet.new(max_size, 1)
|
23
|
-
set << make_slow("Controller/Foo")
|
24
|
-
set << make_slow("Controller/Foo")
|
25
|
-
set << make_slow("Controller/Foo")
|
26
|
-
set << make_slow("Controller/Foo")
|
27
|
-
set << make_slow("Controller/Foo")
|
28
|
-
set << make_slow("Controller/Foo")
|
29
|
-
assert_equal max_size, set.count
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_eviction_of_overrepresented
|
33
|
-
max_size = 3
|
34
|
-
set = ScoutApm::SlowItemSet.new(max_size, 1)
|
35
|
-
set << make_slow("Controller/Foo")
|
36
|
-
set << make_slow("Controller/Foo")
|
37
|
-
set << make_slow("Controller/Foo")
|
38
|
-
set << make_slow("Controller/Foo")
|
39
|
-
set << make_slow("Controller/Foo")
|
40
|
-
set << make_slow("Controller/Bar")
|
41
|
-
|
42
|
-
# 3 total
|
43
|
-
assert_equal max_size, set.count
|
44
|
-
assert_equal 1, set.select{|sl| sl.metric_name == "Controller/Bar"}.length
|
45
|
-
assert_equal 2, set.select{|sl| sl.metric_name == "Controller/Foo"}.length
|
46
|
-
end
|
47
|
-
|
48
|
-
# Fill the set with /Foo records, then add a /Bar to evict. Check that the
|
49
|
-
# evicted one was the fastest of the Foos
|
50
|
-
def test_eviction_of_fastest
|
51
|
-
max_size = 3
|
52
|
-
set = ScoutApm::SlowItemSet.new(max_size, 1)
|
53
|
-
|
54
|
-
[1,2,3].shuffle.each do |seconds| # Shuffle to remove any assumptions on order
|
55
|
-
set << make_slow("Controller/Foo", seconds)
|
56
|
-
end
|
57
|
-
set << make_slow("Controller/Bar", 8)
|
58
|
-
|
59
|
-
# The foo taking 1 second should be evicted
|
60
|
-
assert_equal 2, set.select{|sl| sl.metric_name == "Controller/Foo"}.map{ |sl| sl.total_call_time}.min
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_eviction_when_no_overrepresented
|
64
|
-
max_size = 4
|
65
|
-
fair = 2
|
66
|
-
set = ScoutApm::SlowItemSet.new(max_size, fair)
|
67
|
-
|
68
|
-
# Full, but each is at fair level
|
69
|
-
set << make_slow("Controller/Bar")
|
70
|
-
set << make_slow("Controller/Bar")
|
71
|
-
set << make_slow("Controller/Foo")
|
72
|
-
set << make_slow("Controller/Foo")
|
73
|
-
|
74
|
-
set << make_slow("Controller/Quux")
|
75
|
-
assert_equal max_size, set.count
|
76
|
-
assert_equal 0, set.select{|sl| sl.metric_name == "Controller/Quux" }.length
|
77
|
-
end
|
78
|
-
|
79
|
-
##############
|
80
|
-
#### Helpers
|
81
|
-
##############
|
82
|
-
|
83
|
-
def make_slow(metric, time=5)
|
84
|
-
ScoutApm::SlowTransaction.new(
|
85
|
-
"http://foo.app/#{metric}",
|
86
|
-
metric,
|
87
|
-
time,
|
88
|
-
{}, # metrics
|
89
|
-
{}, # context
|
90
|
-
Time.now, # end time
|
91
|
-
[], # stackprof
|
92
|
-
0)
|
93
|
-
end
|
94
|
-
end
|