-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathmemguardian.go
156 lines (134 loc) · 3.62 KB
/
memguardian.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package memguardian
import (
"context"
"errors"
"sync/atomic"
"time"
units "github.com/docker/go-units"
"github.com/projectdiscovery/utils/env"
)
var (
DefaultInterval time.Duration
DefaultMaxUsedRamRatio float64
DefaultMemGuardian *MemGuardian
)
const (
MemGuardianEnabled = "MEMGUARDIAN"
MemGuardianMaxUsedRamRatioENV = "MEMGUARDIAN_MAX_RAM_RATIO"
MemGuardianMaxUsedMemoryENV = "MEMGUARDIAN_MAX_RAM"
MemGuardianIntervalENV = "MEMGUARDIAN_INTERVAL"
)
func init() {
DefaultInterval = env.GetEnvOrDefault(MemGuardianMaxUsedRamRatioENV, time.Duration(time.Second*30))
DefaultMaxUsedRamRatio = env.GetEnvOrDefault(MemGuardianMaxUsedRamRatioENV, float64(75))
maxRam := env.GetEnvOrDefault(MemGuardianMaxUsedRamRatioENV, "")
options := []MemGuardianOption{
WitInterval(DefaultInterval),
WithMaxRamRatioWarning(DefaultMaxUsedRamRatio),
}
if maxRam != "" {
options = append(options, WithMaxRamAmountWarning(maxRam))
}
var err error
DefaultMemGuardian, err = New(options...)
if err != nil {
panic(err)
}
}
type MemGuardianOption func(*MemGuardian) error
// WithInterval defines the ticker interval of the memory monitor
func WitInterval(d time.Duration) MemGuardianOption {
return func(mg *MemGuardian) error {
mg.t = time.NewTicker(d)
return nil
}
}
// WithCallback defines an optional callback if the warning ration is exceeded
func WithCallback(f func()) MemGuardianOption {
return func(mg *MemGuardian) error {
mg.f = f
return nil
}
}
// WithMaxRamRatioWarning defines the ratio (1-100) threshold of the warning state (and optional callback invocation)
func WithMaxRamRatioWarning(ratio float64) MemGuardianOption {
return func(mg *MemGuardian) error {
if ratio == 0 || ratio > 100 {
return errors.New("ratio must be between 1 and 100")
}
mg.ratio = ratio
return nil
}
}
// WithMaxRamAmountWarning defines the max amount of used RAM in bytes threshold of the warning state (and optional callback invocation)
func WithMaxRamAmountWarning(maxRam string) MemGuardianOption {
return func(mg *MemGuardian) error {
size, err := units.FromHumanSize(maxRam)
if err != nil {
return err
}
mg.maxMemory = uint64(size)
return nil
}
}
type MemGuardian struct {
t *time.Ticker
f func()
ctx context.Context
cancel context.CancelFunc
Warning atomic.Bool
ratio float64
maxMemory uint64
}
// New mem guadian instance with user defined options
func New(options ...MemGuardianOption) (*MemGuardian, error) {
mg := &MemGuardian{}
for _, option := range options {
if err := option(mg); err != nil {
return nil, err
}
}
mg.ctx, mg.cancel = context.WithCancel(context.TODO())
return mg, nil
}
// Run the instance monitor (cancel using the Stop method or context parameter)
func (mg *MemGuardian) Run(ctx context.Context) error {
for {
select {
case <-mg.ctx.Done():
mg.Close()
return nil
case <-ctx.Done():
mg.Close()
return nil
case <-mg.t.C:
usedRatio, used, err := UsedRam()
if err != nil {
return err
}
isRatioOverThreshold := mg.ratio > 0 && usedRatio >= mg.ratio
isAmountOverThreshold := mg.maxMemory > 0 && used >= mg.maxMemory
if isRatioOverThreshold || isAmountOverThreshold {
mg.Warning.Store(true)
if mg.f != nil {
mg.f()
}
} else {
mg.Warning.Store(false)
}
}
}
}
// Close and stops the instance
func (mg *MemGuardian) Close() {
mg.cancel()
mg.t.Stop()
}
// Calculate the system absolute ratio of used RAM
func UsedRam() (ratio float64, used uint64, err error) {
si, err := GetSysInfo()
if err != nil {
return 0, 0, err
}
return si.UsedPercent(), si.UsedRam(), nil
}