@@ -146,14 +146,28 @@ func (m *Manager) checkAndEnforce(ctx context.Context, containerID, dockerID str
146146 continue
147147 }
148148
149- usage , err := m .getCurrentUsage (ctx , containerID , dockerID , lt , cgroupPath , cgroupErr )
150- if err != nil {
151- continue
149+ var usage int64
150+ if isByteSecondType (lt ) {
151+ // Don't accumulate if already enforced (container killed)
152+ m .mu .Lock ()
153+ wasEnforced := m.enforced [containerID ][lt ]
154+ m .mu .Unlock ()
155+
156+ if ! wasEnforced {
157+ source := m .getByteSecondSource (ctx , containerID , dockerID , lt , limits , cgroupPath , cgroupErr )
158+ m .store .AddUsage (containerID , lt , source )
159+ }
160+ usage , _ = m .store .GetUsage (containerID , lt )
161+ } else {
162+ var err error
163+ usage , err = m .getCurrentUsage (ctx , containerID , dockerID , lt , cgroupPath , cgroupErr )
164+ if err != nil {
165+ continue
166+ }
167+ // Persist usage to store
168+ m .store .SetUsage (containerID , lt , usage )
152169 }
153170
154- // Persist usage to store
155- m .store .SetUsage (containerID , lt , usage )
156-
157171 exceeded := usage >= limit
158172 m .mu .Lock ()
159173 wasEnforced := m.enforced [containerID ][lt ]
@@ -234,6 +248,44 @@ func (m *Manager) getCurrentUsage(ctx context.Context, containerID, dockerID str
234248 }
235249}
236250
251+ // isByteSecondType returns true for cumulative byte-second limit types.
252+ func isByteSecondType (lt model.LimitType ) bool {
253+ switch lt {
254+ case model .LimitRAMUsageBSec , model .LimitDiskUsageBSec ,
255+ model .LimitRAMRequestBSec , model .LimitDiskRequestBSec :
256+ return true
257+ }
258+ return false
259+ }
260+
261+ // getByteSecondSource returns the current source value to accumulate for a byte-second type.
262+ func (m * Manager ) getByteSecondSource (ctx context.Context , containerID , dockerID string , lt model.LimitType , limits map [model.LimitType ]int64 , cgroupPath string , cgroupErr error ) int64 {
263+ switch lt {
264+ case model .LimitRAMUsageBSec :
265+ if cgroupErr != nil {
266+ return 0
267+ }
268+ v , err := m .cgroup .MemoryCurrent (cgroupPath )
269+ if err != nil {
270+ return 0
271+ }
272+ return v
273+ case model .LimitDiskUsageBSec :
274+ v , err := m .docker .GetContainerDiskUsage (ctx , dockerID )
275+ if err != nil {
276+ return 0
277+ }
278+ return v
279+ case model .LimitRAMRequestBSec :
280+ v , _ := m .store .GetLimit (containerID , model .LimitRAM )
281+ return v
282+ case model .LimitDiskRequestBSec :
283+ v , _ := m .store .GetLimit (containerID , model .LimitDisk )
284+ return v
285+ }
286+ return 0
287+ }
288+
237289func (m * Manager ) enforce (ctx context.Context , containerID , dockerID string , lt model.LimitType , cgroupPath string ) {
238290 var err error
239291 switch lt {
@@ -279,6 +331,13 @@ func (m *Manager) enforce(ctx context.Context, containerID, dockerID string, lt
279331 case model .LimitSpending :
280332 // Spending enforcement is handled by the proxy itself
281333 log .Printf ("[enforcement] spending limit exceeded for %s" , containerID )
334+
335+ case model .LimitRAMUsageBSec , model .LimitDiskUsageBSec ,
336+ model .LimitRAMRequestBSec , model .LimitDiskRequestBSec :
337+ err = m .docker .StopContainer (ctx , dockerID )
338+ if err == nil {
339+ log .Printf ("[enforcement] killed container %s: %s limit exceeded" , containerID , lt )
340+ }
282341 }
283342
284343 if err != nil {
@@ -341,6 +400,12 @@ func (m *Manager) release(ctx context.Context, containerID, dockerID string, lt
341400
342401 case model .LimitSpending :
343402 log .Printf ("[enforcement] spending limit released for %s" , containerID )
403+
404+ case model .LimitRAMUsageBSec , model .LimitDiskUsageBSec ,
405+ model .LimitRAMRequestBSec , model .LimitDiskRequestBSec :
406+ // Container was killed; if user increased the limit and restarted
407+ // the container, just clear the enforced flag (no Docker action needed).
408+ log .Printf ("[enforcement] %s limit released for %s (container may be restarted)" , lt , containerID )
344409 }
345410
346411 if err != nil {
0 commit comments