@@ -10,25 +10,46 @@ module Async
10
10
module Container
11
11
module Supervisor
12
12
class MemoryMonitor
13
- def initialize ( interval : 10 , limit : nil , &block )
13
+ # Create a new memory monitor.
14
+ #
15
+ # @parameter interval [Integer] The interval at which to check for memory leaks.
16
+ # @parameter limit [Integer] The limit of memory that a process can consume before being killed.
17
+ def initialize ( interval : 10 )
14
18
@interval = interval
15
19
@cluster = Memory ::Leak ::Cluster . new ( limit : limit )
16
20
@processes = Hash . new { |hash , key | hash [ key ] = Set . new . compare_by_identity }
17
21
end
18
22
23
+ # Add a process to the memory monitor. You may override this to control how processes are added to the cluster.
24
+ #
25
+ # @parameter process_id [Integer] The process ID to add.
26
+ def add ( process_id )
27
+ @cluster . add ( process_id )
28
+ end
29
+
30
+ # Remove a process from the memory monitor.
31
+ #
32
+ # @parameter process_id [Integer] The process ID to remove.
33
+ def remove ( process_id )
34
+ @cluster . remove ( process_id )
35
+ end
36
+
37
+ # Register the connection (worker) with the memory monitor.
19
38
def register ( connection )
39
+ Console . info ( self , "Registering connection:" , connection : connection , state : connection . state )
20
40
if process_id = connection . state [ :process_id ]
21
41
connections = @processes [ process_id ]
22
42
23
43
if connections . empty?
24
44
Console . info ( self , "Registering process:" , process_id : process_id )
25
- @cluster . add ( process_id )
45
+ self . add ( process_id )
26
46
end
27
47
28
48
connections . add ( connection )
29
49
end
30
50
end
31
51
52
+ # Remove the connection (worker) from the memory monitor.
32
53
def remove ( connection )
33
54
if process_id = connection . state [ :process_id ]
34
55
connections = @processes [ process_id ]
@@ -37,33 +58,40 @@ def remove(connection)
37
58
38
59
if connections . empty?
39
60
Console . info ( self , "Removing process:" , process_id : process_id )
40
- @cluster . remove ( process_id )
61
+ self . remove ( process_id )
41
62
end
42
63
end
43
64
end
44
65
66
+ # Dump the current status of the memory monitor.
67
+ #
68
+ # @parameter call [Connection::Call] The call to respond to.
45
69
def status ( call )
46
70
call . push ( memory_monitor : @cluster )
47
71
end
48
72
73
+ # Invoked when a memory leak is detected.
74
+ #
75
+ # @parameter process_id [Integer] The process ID of the process that has a memory leak.
76
+ # @parameter monitor [Memory::Leak::Monitor] The monitor that detected the memory leak.
77
+ # @returns [Boolean] True if the process was killed.
78
+ def memory_leak_detected ( process_id , monitor )
79
+ Console . info ( self , "Killing process:" , process_id : process_id )
80
+ Process . kill ( :INT , process_id )
81
+
82
+ true
83
+ end
84
+
85
+ # Run the memory monitor.
86
+ #
87
+ # @returns [Async::Task] The task that is running the memory monitor.
49
88
def run
50
89
Async do
51
90
while true
91
+ # This block must return true if the process was killed.
52
92
@cluster . check! do |process_id , monitor |
53
93
Console . error ( self , "Memory leak detected in process:" , process_id : process_id , monitor : monitor )
54
- connections = @processes [ process_id ]
55
-
56
- connections . each do |connection |
57
- path = "/tmp/memory_dump_#{ process_id } .json"
58
-
59
- response = connection . call ( do : :memory_dump , path : path , timeout : 30 )
60
- Console . info ( self , "Memory dump saved to:" , path , response : response )
61
- @block . call ( response ) if @block
62
- end
63
-
64
- # Kill the process:
65
- Console . info ( self , "Killing process:" , process_id : process_id )
66
- Process . kill ( :INT , process_id )
94
+ memory_leak_detected ( process_id , monitor )
67
95
end
68
96
69
97
sleep ( @interval )
0 commit comments