@@ -24,29 +24,11 @@ type LimitableStage interface {
2424 Kill (error )
2525}
2626
27- // MemoryWatchOption configures a MemoryWatch stage.
28- type MemoryWatchOption func (* memoryWatcher )
29-
30- // WithMemoryLimit makes MemoryWatch kill the stage when its RSS exceeds
31- // byteLimit.
32- func WithMemoryLimit (byteLimit uint64 ) MemoryWatchOption {
33- return func (mw * memoryWatcher ) {
34- mw .limit = & byteLimit
35- }
36- }
37-
38- // WithPeakUsageLogging makes MemoryWatch log the peak RSS when the stage
39- // exits.
40- func WithPeakUsageLogging () MemoryWatchOption {
41- return func (mw * memoryWatcher ) {
42- mw .observe = true
43- }
44- }
45-
4627// MemoryWatch watches the memory usage of the stage and reports via
47- // eventHandler. With WithMemoryLimit it kills the stage when the limit is
48- // exceeded; with WithPeakUsageLogging it logs the peak RSS when the stage
49- // exits. At least one of the two options is required.
28+ // eventHandler. `opts` configure its behavior. With WithMemoryLimit
29+ // it kills the stage when the limit is exceeded; with
30+ // WithPeakUsageLogging it logs the peak RSS when the stage exits. At
31+ // least one of these options is required.
5032//
5133// If the event handler panics while reporting the over-limit event, the
5234// stage is still killed. A panic in any other event-handler call (an
@@ -95,117 +77,6 @@ func MemoryWatch(stage Stage, eventHandler func(e *Event), opts ...MemoryWatchOp
9577 }
9678}
9779
98- type memoryWatcher struct {
99- stage LimitableStage
100- eventHandler func (e * Event )
101-
102- limit * uint64 // non-nil enables kill-at-limit
103- observe bool // log peak RSS when the stage exits
104-
105- maxRSS uint64
106- samples int
107- errCount int
108- consecutiveErrors int
109- }
110-
111- // watch is a `memoryWatchFunc` that watches the memory usage of the
112- // specified `stage`.
113- func (mw * memoryWatcher ) watch (ctx context.Context ) {
114- t := time .NewTicker (memoryPollInterval )
115-
116- watchLoop:
117- for {
118- select {
119- case <- ctx .Done ():
120- break watchLoop
121- case <- t .C :
122- if mw .update (ctx ) {
123- // The stage was killed.
124- break watchLoop
125- }
126- }
127- }
128-
129- t .Stop ()
130-
131- if mw .observe {
132- <- ctx .Done ()
133- mw .reportPeakUsage ()
134- }
135- }
136-
137- // update samples the current memory usage and updates internal stats.
138- // Return true if the stage was killed for exceeding the memory limit.
139- func (mw * memoryWatcher ) update (ctx context.Context ) bool {
140- rss , err := mw .stage .GetRSSAnon (ctx )
141- if err != nil {
142- mw .handleGetRSSError (err )
143- return false
144- }
145-
146- mw .consecutiveErrors = 0
147- mw .samples ++
148- if rss > mw .maxRSS {
149- mw .maxRSS = rss
150- }
151-
152- if mw .limit != nil && rss >= * mw .limit {
153- mw .killStage (rss )
154- return true
155- }
156-
157- return false
158- }
159-
160- // handleGetRSSError deals with error `err` that happened when trying
161- // to get `stage`'s memory usage.
162- func (mw * memoryWatcher ) handleGetRSSError (err error ) {
163- if ! errors .Is (err , errProcessInfoMissing ) {
164- mw .errCount ++
165- mw .consecutiveErrors ++
166- if mw .consecutiveErrors == 2 {
167- mw .eventHandler (& Event {
168- Command : mw .stage .Name (),
169- Msg : "error getting RSS" ,
170- Err : err ,
171- })
172- }
173- } else {
174- mw .consecutiveErrors = 0
175- }
176- }
177-
178- // killStage kills the stage and reports and event saying what it did.
179- func (mw * memoryWatcher ) killStage (rss uint64 ) {
180- // Guarantee the over-limit stage is killed even if
181- // the user's event handler panics.
182- defer mw .stage .Kill (ErrMemoryLimitExceeded )
183-
184- mw .eventHandler (& Event {
185- Command : mw .stage .Name (),
186- Msg : "stage exceeded allowed memory use" ,
187- Err : fmt .Errorf ("stage exceeded allowed memory use" ),
188- Context : map [string ]any {
189- "limit" : * mw .limit ,
190- "used" : rss ,
191- },
192- })
193- }
194-
195- // reportPeakUsage sends an event reporting the peak usage that has
196- // been seen for `stage`.
197- func (mw * memoryWatcher ) reportPeakUsage () {
198- mw .eventHandler (& Event {
199- Command : mw .stage .Name (),
200- Msg : "peak memory usage" ,
201- Context : map [string ]any {
202- "max_rss_bytes" : mw .maxRSS ,
203- "samples" : mw .samples ,
204- "errors" : mw .errCount ,
205- },
206- })
207- }
208-
20980type memoryWatchStage struct {
21081 nameSuffix string
21182 stage LimitableStage
0 commit comments