Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions lib/memory/leak/cluster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = []
Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion lib/memory/leak/monitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
16 changes: 16 additions & 0 deletions test/memory/leak/cluster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions test/memory/leak/monitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading