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