diff --git a/lib/memory/leak/cluster.rb b/lib/memory/leak/cluster.rb index 31d0992..ef85835 100644 --- a/lib/memory/leak/cluster.rb +++ b/lib/memory/leak/cluster.rb @@ -87,7 +87,7 @@ def apply_limit!(total_size_limit = @total_size_limit) def sample! System.memory_usages(@processes.keys) do |process_id, memory_usage| if monitor = @processes[process_id] - monitor.current_size = memory_usage + monitor.sample!(memory_usage) end end end @@ -96,8 +96,6 @@ def sample! # # @yields {|process_id, monitor| ...} each process ID and monitor that is leaking or exceeds the memory limit. def check!(&block) - return to_enum(__method__) unless block_given? - self.sample! leaking = [] @@ -110,10 +108,14 @@ def check!(&block) end end - leaking.each(&block) + if block_given? + leaking.each(&block) + end # Finally, apply any per-cluster memory limits: apply_limit!(@total_size_limit, &block) if @total_size_limit + + return leaking end end end diff --git a/lib/memory/leak/monitor.rb b/lib/memory/leak/monitor.rb index 5686070..cb7d988 100644 --- a/lib/memory/leak/monitor.rb +++ b/lib/memory/leak/monitor.rb @@ -33,9 +33,11 @@ class Monitor def initialize(process_id = Process.pid, maximum_size: nil, maximum_size_limit: nil, threshold_size: DEFAULT_THRESHOLD_SIZE, increase_limit: DEFAULT_INCREASE_LIMIT) @process_id = process_id + @sample_count = 0 @current_size = nil @maximum_size = maximum_size @maximum_size_limit = maximum_size_limit + @maximum_observed_size = nil @threshold_size = threshold_size @increase_count = 0 @@ -46,6 +48,7 @@ def initialize(process_id = Process.pid, maximum_size: nil, maximum_size_limit: def as_json(...) { process_id: @process_id, + sample_count: @sample_count, current_size: @current_size, maximum_size: @maximum_size, maximum_size_limit: @maximum_size_limit, @@ -83,6 +86,9 @@ def memory_usage System.memory_usage(@process_id) end + # @attribute [Integer] The number of samples taken. + attr :sample_count + # @returns [Integer] The last sampled memory usage. def current_size @current_size ||= memory_usage @@ -119,7 +125,9 @@ def leaking? # Capture a memory usage sample and yield if a memory leak is detected. # # @yields {|sample, monitor| ...} If a memory leak is detected. - def sample! + def sample!(memory_usage = self.memory_usage) + @sample_count += 1 + self.current_size = memory_usage if @maximum_observed_size diff --git a/releases.md b/releases.md index e322a82..e8a8e7f 100644 --- a/releases.md +++ b/releases.md @@ -1,5 +1,11 @@ # Releases +## Unreleased + + - Added `sample_count` attribute to monitor to track number of samples taken. + - `check!` method in cluster now returns an array of leaking monitors if no block is given. + - `Cluster#check!` now invokes `Monitor#sample!` to ensure memory usage is updated before checking for leaks. + ## v0.5.0 - Improved variable names. diff --git a/test/memory/leak/cluster.rb b/test/memory/leak/cluster.rb index e8a867b..0cf52a3 100644 --- a/test/memory/leak/cluster.rb +++ b/test/memory/leak/cluster.rb @@ -45,6 +45,18 @@ attr :children + it "samples memory usage" do + cluster.check!.to_a + + children.each do |process_id, child| + monitor = cluster.processes[process_id] + expect(monitor).to have_attributes( + sample_count: be > 0, + current_size: be_a(Integer), + ) + end + end + it "can detect memory leaks" do child = children.values.first monitor = cluster.processes[child.process_id] @@ -58,6 +70,10 @@ monitor.sample! end + expect(monitor).to have_attributes( + sample_count: be > 0, + ) + cluster.check! do |process_id, monitor| expect(process_id).to be == child.process_id expect(monitor.increase_count).to be == monitor.increase_limit diff --git a/test/memory/leak/monitor.rb b/test/memory/leak/monitor.rb index 791e764..9d3771d 100644 --- a/test/memory/leak/monitor.rb +++ b/test/memory/leak/monitor.rb @@ -36,6 +36,7 @@ # It is very unlikely that in the above test, the threshold of the 2nd and 3rd samples will be greater than the threshold of the 1st sample. # Therefore, the count should be 0. expect(monitor.increase_count).to be == 0 + expect(monitor.sample_count).to be == 3 end with "a leaking child process" do