appsignal 3.1.3 → 3.1.4
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.md +14 -0
- data/lib/appsignal/config.rb +0 -3
- data/lib/appsignal/garbage_collection.rb +90 -0
- data/lib/appsignal/probes/mri.rb +5 -3
- data/lib/appsignal/transaction.rb +5 -12
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +1 -6
- data/spec/lib/appsignal/config_spec.rb +0 -1
- data/spec/lib/appsignal/{garbage_collection_profiler_spec.rb → garbage_collection_spec.rb} +28 -6
- data/spec/lib/appsignal/probes/mri_spec.rb +35 -0
- data/spec/lib/appsignal/transaction_spec.rb +2 -27
- data/spec/lib/appsignal_spec.rb +2 -22
- metadata +5 -5
- data/lib/appsignal/garbage_collection_profiler.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c21e4e80dd54b8a0f6619a40b7c046e6bc132634ce26666b281fdd4298c72c3f
|
4
|
+
data.tar.gz: 53743646f5cf821ab45559817cf65c2bf9ed483184725fca2413b3606fad6e1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36a34d74cf16e5f274e3485471833f516e34227546017d14f24d70fc75ca824b68261a7048924391135a3c0d815d7ef605b60ac19396bdeb9b9b46f1d4911daa
|
7
|
+
data.tar.gz: b03a309a6c8649397e6f5d9b62a65324e4b4e39a3a1eee77b6cb60280758838212e2de7ca3eb28c8f10b3b59cb5b02ca7d88347cc9f3995be0308bf5249cae70
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# AppSignal for Ruby gem Changelog
|
2
2
|
|
3
|
+
## 3.1.4
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- [ffe49cfe](https://github.com/appsignal/appsignal-ruby/commit/ffe49cfe94f5269e59d6f168a73114f7a3914f79) patch - Support temporarily disabling GC profiling without reporting inaccurate `gc_time` metric durations. The MRI probe's `gc_time` will not report any value when the `GC::Profiler.enabled?` returns `false`.
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
|
11
|
+
- [af7e666c](https://github.com/appsignal/appsignal-ruby/commit/af7e666cf173ec1f42e9cf3fce2ab6c8e658440c) patch - Listen if the Ruby Garbage Collection profiler is enabled and collect how long the GC is running for the Ruby VM magic dashboard. An app will need to call `GC::Profiler.enable` to enable the GC profiler. Do not enable this in production environments, or at least not for long, because this can negatively impact performance of apps.
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
|
15
|
+
- [b3a163be](https://github.com/appsignal/appsignal-ruby/commit/b3a163be154796e1f358c5061eaee99845c960ee) patch - Fix the MRI probe using the Garbage Collection profiler instead of the NilProfiler when garbage collection instrumentation is not enabled for MRI probe. This caused unnecessary overhead.
|
16
|
+
|
3
17
|
## 3.1.3
|
4
18
|
|
5
19
|
### Added
|
data/lib/appsignal/config.rb
CHANGED
@@ -15,7 +15,6 @@ module Appsignal
|
|
15
15
|
:debug => false,
|
16
16
|
:dns_servers => [],
|
17
17
|
:enable_allocation_tracking => true,
|
18
|
-
:enable_gc_instrumentation => false,
|
19
18
|
:enable_host_metrics => true,
|
20
19
|
:enable_minutely_probes => true,
|
21
20
|
:enable_statsd => true,
|
@@ -63,7 +62,6 @@ module Appsignal
|
|
63
62
|
"APPSIGNAL_DEBUG" => :debug,
|
64
63
|
"APPSIGNAL_DNS_SERVERS" => :dns_servers,
|
65
64
|
"APPSIGNAL_ENABLE_ALLOCATION_TRACKING" => :enable_allocation_tracking,
|
66
|
-
"APPSIGNAL_ENABLE_GC_INSTRUMENTATION" => :enable_gc_instrumentation,
|
67
65
|
"APPSIGNAL_ENABLE_HOST_METRICS" => :enable_host_metrics,
|
68
66
|
"APPSIGNAL_ENABLE_MINUTELY_PROBES" => :enable_minutely_probes,
|
69
67
|
"APPSIGNAL_ENABLE_STATSD" => :enable_statsd,
|
@@ -114,7 +112,6 @@ module Appsignal
|
|
114
112
|
APPSIGNAL_ACTIVE
|
115
113
|
APPSIGNAL_DEBUG
|
116
114
|
APPSIGNAL_ENABLE_ALLOCATION_TRACKING
|
117
|
-
APPSIGNAL_ENABLE_GC_INSTRUMENTATION
|
118
115
|
APPSIGNAL_ENABLE_HOST_METRICS
|
119
116
|
APPSIGNAL_ENABLE_MINUTELY_PROBES
|
120
117
|
APPSIGNAL_ENABLE_STATSD
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
# @api private
|
5
|
+
module GarbageCollection
|
6
|
+
# Return the GC profiler wrapper.
|
7
|
+
#
|
8
|
+
# Returns {Profiler} if the Ruby Garbage Collection profiler is enabled.
|
9
|
+
# This is checked by calling `GC::Profiler.enabled?`.
|
10
|
+
#
|
11
|
+
# GC profiling is disabled by default due to the overhead it causes. Do not
|
12
|
+
# enable this in production for long periods of time.
|
13
|
+
def self.profiler
|
14
|
+
# Cached instances so it doesn't create a new object every time this
|
15
|
+
# method is called. Especially necessary for the {Profiler} because a new
|
16
|
+
# instance will have a new internal time counter.
|
17
|
+
@real_profiler ||= Profiler.new
|
18
|
+
@nil_profiler ||= NilProfiler.new
|
19
|
+
|
20
|
+
enabled? ? @real_profiler : @nil_profiler
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check if Garbage Collection is enabled at the moment.
|
24
|
+
#
|
25
|
+
# @return [Boolean]
|
26
|
+
def self.enabled?
|
27
|
+
GC::Profiler.enabled?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Unset the currently cached profilers.
|
31
|
+
#
|
32
|
+
# @return [void]
|
33
|
+
def self.clear_profiler!
|
34
|
+
@real_profiler = nil
|
35
|
+
@nil_profiler = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# A wrapper around Ruby's `GC::Profiler` that tracks garbage collection
|
39
|
+
# time, while clearing `GC::Profiler`'s total_time to make sure it doesn't
|
40
|
+
# leak memory by keeping garbage collection run samples in memory.
|
41
|
+
class Profiler
|
42
|
+
def self.lock
|
43
|
+
@lock ||= Mutex.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@total_time = 0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Whenever {#total_time} is called, the current `GC::Profiler#total_time`
|
51
|
+
# gets added to `@total_time`, after which `GC::Profiler.clear` is called
|
52
|
+
# to prevent it from leaking memory. A class-level lock is used to make
|
53
|
+
# sure garbage collection time is never counted more than once.
|
54
|
+
#
|
55
|
+
# Whenever `@total_time` gets above two billion milliseconds (about 23
|
56
|
+
# days), it's reset to make sure the result fits in a signed 32-bit
|
57
|
+
# integer.
|
58
|
+
#
|
59
|
+
# @return [Integer]
|
60
|
+
def total_time
|
61
|
+
lock.synchronize do
|
62
|
+
@total_time += (internal_profiler.total_time * 1000).round
|
63
|
+
internal_profiler.clear
|
64
|
+
end
|
65
|
+
|
66
|
+
@total_time = 0 if @total_time > 2_000_000_000
|
67
|
+
|
68
|
+
@total_time
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def internal_profiler
|
74
|
+
GC::Profiler
|
75
|
+
end
|
76
|
+
|
77
|
+
def lock
|
78
|
+
self.class.lock
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# A dummy profiler that always returns 0 as the total time. Used when GC
|
83
|
+
# profiler is disabled.
|
84
|
+
class NilProfiler
|
85
|
+
def total_time
|
86
|
+
0
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/appsignal/probes/mri.rb
CHANGED
@@ -8,7 +8,7 @@ module Appsignal
|
|
8
8
|
defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
|
9
9
|
end
|
10
10
|
|
11
|
-
def initialize(appsignal: Appsignal, gc_profiler: Appsignal::
|
11
|
+
def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollection.profiler)
|
12
12
|
Appsignal.logger.debug("Initializing VM probe")
|
13
13
|
@appsignal = appsignal
|
14
14
|
@gc_profiler = gc_profiler
|
@@ -31,8 +31,10 @@ module Appsignal
|
|
31
31
|
)
|
32
32
|
|
33
33
|
set_gauge("thread_count", Thread.list.size)
|
34
|
-
|
35
|
-
|
34
|
+
if Appsignal::GarbageCollection.enabled?
|
35
|
+
gauge_delta(:gc_time, @gc_profiler.total_time) do |gc_time|
|
36
|
+
set_gauge("gc_time", gc_time) if gc_time > 0
|
37
|
+
end
|
36
38
|
end
|
37
39
|
|
38
40
|
gc_stats = GC.stat
|
@@ -66,11 +66,6 @@ module Appsignal
|
|
66
66
|
def clear_current_transaction!
|
67
67
|
Thread.current[:appsignal_transaction] = nil
|
68
68
|
end
|
69
|
-
|
70
|
-
def garbage_collection_profiler
|
71
|
-
@garbage_collection_profiler ||=
|
72
|
-
Appsignal.config[:enable_gc_instrumentation] ? Appsignal::GarbageCollectionProfiler.new : NilGarbageCollectionProfiler.new
|
73
|
-
end
|
74
69
|
end
|
75
70
|
|
76
71
|
attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded, :breadcrumbs
|
@@ -103,7 +98,7 @@ module Appsignal
|
|
103
98
|
@ext = Appsignal::Extension.start_transaction(
|
104
99
|
@transaction_id,
|
105
100
|
@namespace,
|
106
|
-
|
101
|
+
0
|
107
102
|
) || Appsignal::Extension::MockTransaction.new
|
108
103
|
end
|
109
104
|
|
@@ -117,9 +112,7 @@ module Appsignal
|
|
117
112
|
"because it was manually discarded."
|
118
113
|
return
|
119
114
|
end
|
120
|
-
if @ext.finish(
|
121
|
-
sample_data
|
122
|
-
end
|
115
|
+
sample_data if @ext.finish(0)
|
123
116
|
@ext.complete
|
124
117
|
end
|
125
118
|
|
@@ -355,7 +348,7 @@ module Appsignal
|
|
355
348
|
|
356
349
|
def start_event
|
357
350
|
return if paused?
|
358
|
-
@ext.start_event(
|
351
|
+
@ext.start_event(0)
|
359
352
|
end
|
360
353
|
|
361
354
|
def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
|
@@ -365,7 +358,7 @@ module Appsignal
|
|
365
358
|
title || BLANK,
|
366
359
|
body || BLANK,
|
367
360
|
body_format || Appsignal::EventFormatter::DEFAULT,
|
368
|
-
|
361
|
+
0
|
369
362
|
)
|
370
363
|
end
|
371
364
|
|
@@ -377,7 +370,7 @@ module Appsignal
|
|
377
370
|
body || BLANK,
|
378
371
|
body_format || Appsignal::EventFormatter::DEFAULT,
|
379
372
|
duration,
|
380
|
-
|
373
|
+
0
|
381
374
|
)
|
382
375
|
end
|
383
376
|
|
data/lib/appsignal/version.rb
CHANGED
data/lib/appsignal.rb
CHANGED
@@ -115,11 +115,6 @@ module Appsignal
|
|
115
115
|
Appsignal::Environment.report_enabled("allocation_tracking")
|
116
116
|
end
|
117
117
|
|
118
|
-
if config[:enable_gc_instrumentation]
|
119
|
-
GC::Profiler.enable
|
120
|
-
Appsignal::Environment.report_enabled("gc_instrumentation")
|
121
|
-
end
|
122
|
-
|
123
118
|
Appsignal::Minutely.start if config[:enable_minutely_probes]
|
124
119
|
|
125
120
|
collect_environment_metadata
|
@@ -298,7 +293,7 @@ require "appsignal/hooks"
|
|
298
293
|
require "appsignal/probes"
|
299
294
|
require "appsignal/marker"
|
300
295
|
require "appsignal/minutely"
|
301
|
-
require "appsignal/
|
296
|
+
require "appsignal/garbage_collection"
|
302
297
|
require "appsignal/integrations/railtie" if defined?(::Rails)
|
303
298
|
require "appsignal/transaction"
|
304
299
|
require "appsignal/version"
|
@@ -156,7 +156,6 @@ describe Appsignal::Config do
|
|
156
156
|
:debug => false,
|
157
157
|
:dns_servers => [],
|
158
158
|
:enable_allocation_tracking => true,
|
159
|
-
:enable_gc_instrumentation => false,
|
160
159
|
:enable_host_metrics => true,
|
161
160
|
:enable_minutely_probes => true,
|
162
161
|
:enable_statsd => true,
|
@@ -1,11 +1,33 @@
|
|
1
|
-
describe Appsignal::
|
1
|
+
describe Appsignal::GarbageCollection do
|
2
|
+
describe ".profiler" do
|
3
|
+
before do
|
4
|
+
# Unset the internal memoized variable to avoid state leaking
|
5
|
+
described_class.clear_profiler!
|
6
|
+
end
|
7
|
+
|
8
|
+
context "when GC instrumentation is disabled" do
|
9
|
+
it "returns the NilProfiler" do
|
10
|
+
expect(described_class.profiler).to be_a(Appsignal::GarbageCollection::NilProfiler)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when GC profiling is enabled" do
|
15
|
+
before { GC::Profiler.enable }
|
16
|
+
after { GC::Profiler.disable }
|
17
|
+
|
18
|
+
it "returns the Profiler" do
|
19
|
+
expect(described_class.profiler).to be_a(Appsignal::GarbageCollection::Profiler)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe Appsignal::GarbageCollection::Profiler do
|
2
26
|
let(:internal_profiler) { FakeGCProfiler.new }
|
3
27
|
let(:profiler) { described_class.new }
|
4
28
|
|
5
29
|
before do
|
6
|
-
|
7
|
-
.to receive(:internal_profiler)
|
8
|
-
.and_return(internal_profiler)
|
30
|
+
stub_const("GC::Profiler", internal_profiler)
|
9
31
|
end
|
10
32
|
|
11
33
|
context "on initialization" do
|
@@ -54,7 +76,7 @@ describe Appsignal::GarbageCollectionProfiler do
|
|
54
76
|
|
55
77
|
2.times do
|
56
78
|
threads << Thread.new do
|
57
|
-
profiler = Appsignal::
|
79
|
+
profiler = Appsignal::GarbageCollection::Profiler.new
|
58
80
|
results << profiler.total_time
|
59
81
|
end
|
60
82
|
end
|
@@ -65,7 +87,7 @@ describe Appsignal::GarbageCollectionProfiler do
|
|
65
87
|
end
|
66
88
|
end
|
67
89
|
|
68
|
-
describe Appsignal::
|
90
|
+
describe Appsignal::GarbageCollection::NilProfiler do
|
69
91
|
let(:profiler) { described_class.new }
|
70
92
|
|
71
93
|
describe "#total_time" do
|
@@ -39,6 +39,7 @@ describe Appsignal::Probes::MriProbe do
|
|
39
39
|
let(:hostname) { nil }
|
40
40
|
before do
|
41
41
|
allow(gc_profiler_mock).to receive(:total_time)
|
42
|
+
allow(GC::Profiler).to receive(:enabled?).and_return(true)
|
42
43
|
end
|
43
44
|
|
44
45
|
it "should track vm metrics" do
|
@@ -70,6 +71,40 @@ describe Appsignal::Probes::MriProbe do
|
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
74
|
+
context "when GC profiling is disabled" do
|
75
|
+
it "does not report a gc_time metric" do
|
76
|
+
allow(GC::Profiler).to receive(:enabled?).and_return(false)
|
77
|
+
expect(gc_profiler_mock).to_not receive(:total_time)
|
78
|
+
probe.call # Normal call, create a cache
|
79
|
+
probe.call # Report delta value based on cached value
|
80
|
+
metrics = appsignal_mock.gauges.map { |(key)| key }
|
81
|
+
expect(metrics).to_not include("gc_time")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "does not report a gc_time metric while temporarily disabled" do
|
85
|
+
# While enabled
|
86
|
+
allow(GC::Profiler).to receive(:enabled?).and_return(true)
|
87
|
+
expect(gc_profiler_mock).to receive(:total_time).and_return(10, 15)
|
88
|
+
probe.call # Normal call, create a cache
|
89
|
+
probe.call # Report delta value based on cached value
|
90
|
+
expect_gauges([["gc_time", 5]])
|
91
|
+
|
92
|
+
# While disabled
|
93
|
+
allow(GC::Profiler).to receive(:enabled?).and_return(false)
|
94
|
+
probe.call # Call twice to make sure any caches resets wouldn't mess up the assertion
|
95
|
+
probe.call
|
96
|
+
# Does not include any newly reported metrics
|
97
|
+
expect_gauges([["gc_time", 5]])
|
98
|
+
|
99
|
+
# When enabled after being disabled for a while, it only reports the
|
100
|
+
# newly reported time since it was renabled
|
101
|
+
allow(GC::Profiler).to receive(:enabled?).and_return(true)
|
102
|
+
expect(gc_profiler_mock).to receive(:total_time).and_return(25)
|
103
|
+
probe.call
|
104
|
+
expect_gauges([["gc_time", 5], ["gc_time", 10]])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
73
108
|
it "tracks GC run count" do
|
74
109
|
expect(GC).to receive(:count).and_return(10, 15)
|
75
110
|
expect(GC).to receive(:stat).and_return(
|
@@ -792,23 +792,6 @@ describe Appsignal::Transaction do
|
|
792
792
|
end
|
793
793
|
end
|
794
794
|
|
795
|
-
describe "#garbage_collection_profiler" do
|
796
|
-
before { Appsignal::Transaction.instance_variable_set(:@garbage_collection_profiler, nil) }
|
797
|
-
|
798
|
-
it "returns the NilGarbageCollectionProfiler" do
|
799
|
-
expect(Appsignal::Transaction.garbage_collection_profiler).to be_a(Appsignal::NilGarbageCollectionProfiler)
|
800
|
-
end
|
801
|
-
|
802
|
-
context "when gc profiling is enabled" do
|
803
|
-
before { Appsignal.config.config_hash[:enable_gc_instrumentation] = true }
|
804
|
-
after { Appsignal.config.config_hash[:enable_gc_instrumentation] = false }
|
805
|
-
|
806
|
-
it "returns the GarbageCollectionProfiler" do
|
807
|
-
expect(Appsignal::Transaction.garbage_collection_profiler).to be_a(Appsignal::GarbageCollectionProfiler)
|
808
|
-
end
|
809
|
-
end
|
810
|
-
end
|
811
|
-
|
812
795
|
describe "#start_event" do
|
813
796
|
it "starts the event in the extension" do
|
814
797
|
expect(transaction.ext).to receive(:start_event).with(0).and_call_original
|
@@ -827,11 +810,7 @@ describe Appsignal::Transaction do
|
|
827
810
|
end
|
828
811
|
|
829
812
|
describe "#finish_event" do
|
830
|
-
let(:fake_gc_time) {
|
831
|
-
before do
|
832
|
-
expect(described_class.garbage_collection_profiler)
|
833
|
-
.to receive(:total_time).at_least(:once).and_return(fake_gc_time)
|
834
|
-
end
|
813
|
+
let(:fake_gc_time) { 0 }
|
835
814
|
|
836
815
|
it "should finish the event in the extension" do
|
837
816
|
expect(transaction.ext).to receive(:finish_event).with(
|
@@ -878,11 +857,7 @@ describe Appsignal::Transaction do
|
|
878
857
|
end
|
879
858
|
|
880
859
|
describe "#record_event" do
|
881
|
-
let(:fake_gc_time) {
|
882
|
-
before do
|
883
|
-
expect(described_class.garbage_collection_profiler)
|
884
|
-
.to receive(:total_time).at_least(:once).and_return(fake_gc_time)
|
885
|
-
end
|
860
|
+
let(:fake_gc_time) { 0 }
|
886
861
|
|
887
862
|
it "should record the event in the extension" do
|
888
863
|
expect(transaction.ext).to receive(:record_event).with(
|
data/spec/lib/appsignal_spec.rb
CHANGED
@@ -54,20 +54,12 @@ describe Appsignal do
|
|
54
54
|
Appsignal.start
|
55
55
|
end
|
56
56
|
|
57
|
-
context "when allocation tracking
|
57
|
+
context "when allocation tracking has been enabled" do
|
58
58
|
before do
|
59
|
-
allow(GC::Profiler).to receive(:enable)
|
60
59
|
Appsignal.config.config_hash[:enable_allocation_tracking] = true
|
61
|
-
Appsignal.config.config_hash[:enable_gc_instrumentation] = true
|
62
60
|
capture_environment_metadata_report_calls
|
63
61
|
end
|
64
62
|
|
65
|
-
it "should enable Ruby's GC::Profiler" do
|
66
|
-
expect(GC::Profiler).to receive(:enable)
|
67
|
-
Appsignal.start
|
68
|
-
expect_environment_metadata("ruby_gc_instrumentation_enabled", "true")
|
69
|
-
end
|
70
|
-
|
71
63
|
unless DependencyHelper.running_jruby?
|
72
64
|
it "installs the allocation event hook" do
|
73
65
|
expect(Appsignal::Extension).to receive(:install_allocation_event_hook)
|
@@ -78,29 +70,17 @@ describe Appsignal do
|
|
78
70
|
end
|
79
71
|
end
|
80
72
|
|
81
|
-
context "when allocation tracking
|
73
|
+
context "when allocation tracking has been disabled" do
|
82
74
|
before do
|
83
75
|
Appsignal.config.config_hash[:enable_allocation_tracking] = false
|
84
|
-
Appsignal.config.config_hash[:enable_gc_instrumentation] = false
|
85
76
|
capture_environment_metadata_report_calls
|
86
77
|
end
|
87
78
|
|
88
|
-
it "should not enable Ruby's GC::Profiler" do
|
89
|
-
expect(GC::Profiler).not_to receive(:enable)
|
90
|
-
Appsignal.start
|
91
|
-
end
|
92
|
-
|
93
79
|
it "should not install the allocation event hook" do
|
94
80
|
expect(Appsignal::Minutely).not_to receive(:install_allocation_event_hook)
|
95
81
|
Appsignal.start
|
96
82
|
expect_not_environment_metadata("ruby_allocation_tracking_enabled")
|
97
83
|
end
|
98
|
-
|
99
|
-
it "should not add the gc probe to minutely" do
|
100
|
-
expect(Appsignal::Minutely).not_to receive(:register_garbage_collection_probe)
|
101
|
-
Appsignal.start
|
102
|
-
expect_not_environment_metadata("ruby_gc_instrumentation_enabled")
|
103
|
-
end
|
104
84
|
end
|
105
85
|
|
106
86
|
context "when minutely metrics has been enabled" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appsignal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Beekman
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-08-
|
13
|
+
date: 2022-08-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -204,7 +204,7 @@ files:
|
|
204
204
|
- lib/appsignal/event_formatter/sequel/sql_formatter.rb
|
205
205
|
- lib/appsignal/extension.rb
|
206
206
|
- lib/appsignal/extension/jruby.rb
|
207
|
-
- lib/appsignal/
|
207
|
+
- lib/appsignal/garbage_collection.rb
|
208
208
|
- lib/appsignal/helpers/instrumentation.rb
|
209
209
|
- lib/appsignal/helpers/metrics.rb
|
210
210
|
- lib/appsignal/hooks.rb
|
@@ -305,7 +305,7 @@ files:
|
|
305
305
|
- spec/lib/appsignal/extension/jruby_spec.rb
|
306
306
|
- spec/lib/appsignal/extension_install_failure_spec.rb
|
307
307
|
- spec/lib/appsignal/extension_spec.rb
|
308
|
-
- spec/lib/appsignal/
|
308
|
+
- spec/lib/appsignal/garbage_collection_spec.rb
|
309
309
|
- spec/lib/appsignal/hooks/action_cable_spec.rb
|
310
310
|
- spec/lib/appsignal/hooks/action_mailer_spec.rb
|
311
311
|
- spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
|
@@ -457,7 +457,7 @@ test_files:
|
|
457
457
|
- spec/lib/appsignal/extension/jruby_spec.rb
|
458
458
|
- spec/lib/appsignal/extension_install_failure_spec.rb
|
459
459
|
- spec/lib/appsignal/extension_spec.rb
|
460
|
-
- spec/lib/appsignal/
|
460
|
+
- spec/lib/appsignal/garbage_collection_spec.rb
|
461
461
|
- spec/lib/appsignal/hooks/action_cable_spec.rb
|
462
462
|
- spec/lib/appsignal/hooks/action_mailer_spec.rb
|
463
463
|
- spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Appsignal
|
4
|
-
# {Appsignal::GarbageCollectionProfiler} wraps Ruby's `GC::Profiler` to be
|
5
|
-
# able to track garbage collection time for multiple transactions, while
|
6
|
-
# constantly clearing `GC::Profiler`'s total_time to make sure it doesn't
|
7
|
-
# leak memory by keeping garbage collection run samples in memory.
|
8
|
-
#
|
9
|
-
# @api private
|
10
|
-
class GarbageCollectionProfiler
|
11
|
-
def self.lock
|
12
|
-
@lock ||= Mutex.new
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
@total_time = 0
|
17
|
-
end
|
18
|
-
|
19
|
-
# Whenever {#total_time} is called, the current `GC::Profiler#total_time`
|
20
|
-
# gets added to `@total_time`, after which `GC::Profiler.clear` is called
|
21
|
-
# to prevent it from leaking memory. A class-level lock is used to make
|
22
|
-
# sure garbage collection time is never counted more than once.
|
23
|
-
#
|
24
|
-
# Whenever `@total_time` gets above two billion milliseconds (about 23
|
25
|
-
# days), it's reset to make sure the result fits in a signed 32-bit
|
26
|
-
# integer.
|
27
|
-
#
|
28
|
-
# @return [Integer]
|
29
|
-
def total_time
|
30
|
-
lock.synchronize do
|
31
|
-
@total_time += (internal_profiler.total_time * 1000).round
|
32
|
-
internal_profiler.clear
|
33
|
-
end
|
34
|
-
|
35
|
-
@total_time = 0 if @total_time > 2_000_000_000
|
36
|
-
|
37
|
-
@total_time
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def internal_profiler
|
43
|
-
GC::Profiler
|
44
|
-
end
|
45
|
-
|
46
|
-
def lock
|
47
|
-
self.class.lock
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# {Appsignal::NilGarbageCollectionProfiler} is a dummy profiler
|
52
|
-
# that always returns 0 as the total time.
|
53
|
-
# Used when we don't want any profile information
|
54
|
-
#
|
55
|
-
# @api private
|
56
|
-
class NilGarbageCollectionProfiler
|
57
|
-
def total_time
|
58
|
-
0
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|