-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmemory.go
More file actions
149 lines (137 loc) · 3.83 KB
/
Copy pathmemory.go
File metadata and controls
149 lines (137 loc) · 3.83 KB
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
package nara
import (
"os"
"strconv"
"strings"
"github.com/shirou/gopsutil/v3/mem"
)
// MemoryMode describes coarse memory profiles for a nara.
type MemoryMode string
const (
MemoryModeAuto MemoryMode = "auto"
MemoryModeShort MemoryMode = "short"
MemoryModeMedium MemoryMode = "medium"
MemoryModeHog MemoryMode = "hog"
MemoryModeCustom MemoryMode = "custom"
)
// MemoryProfile captures memory behavior and limits.
type MemoryProfile struct {
Mode MemoryMode
BudgetMB int
MaxEvents int
EnableBackgroundSync bool
}
// ParseMemoryMode normalizes a mode string into a known MemoryMode.
func ParseMemoryMode(mode string) MemoryMode {
switch strings.ToLower(strings.TrimSpace(mode)) {
case string(MemoryModeAuto):
return MemoryModeAuto
case string(MemoryModeShort):
return MemoryModeShort
case string(MemoryModeHog):
return MemoryModeHog
case string(MemoryModeCustom):
return MemoryModeCustom
default:
return MemoryModeMedium
}
}
// MemoryModeForBudget chooses a mode based on available memory budget.
func MemoryModeForBudget(budgetMB int) MemoryMode {
switch {
case budgetMB <= 384:
return MemoryModeShort
case budgetMB <= 1024:
return MemoryModeMedium
default:
return MemoryModeHog
}
}
// AutoMemoryProfile determines the best profile based on available memory.
func AutoMemoryProfile() (MemoryProfile, string, error) {
budgetMB, source, err := detectMemoryBudgetMB()
if err != nil {
return DefaultMemoryProfile(), "default", err
}
mode := MemoryModeForBudget(budgetMB)
profile := MemoryProfileForMode(mode)
return profile, source, nil
}
// MemoryProfileForMode returns the default profile for a given mode.
func MemoryProfileForMode(mode MemoryMode) MemoryProfile {
switch mode {
case MemoryModeShort:
return MemoryProfile{
Mode: MemoryModeShort,
BudgetMB: 256,
// Memory math (rough, conservative):
// Assume ~2.5 KB per stored event on average (struct + maps + payload + overhead).
// 20k events * 2.5 KB ≈ 50 MB for ledger contents, leaving ample headroom
// for indexes, projections, networking buffers, and Go heap growth in 256 MB.
MaxEvents: 20000,
EnableBackgroundSync: false,
}
case MemoryModeHog:
return MemoryProfile{
Mode: MemoryModeHog,
BudgetMB: 2048,
MaxEvents: 320000,
EnableBackgroundSync: true,
}
case MemoryModeCustom:
return MemoryProfile{
Mode: MemoryModeCustom,
BudgetMB: 0,
MaxEvents: 0,
EnableBackgroundSync: true,
}
default:
return MemoryProfile{
Mode: MemoryModeMedium,
BudgetMB: 512,
MaxEvents: 80000,
EnableBackgroundSync: true,
}
}
}
// DefaultMemoryProfile returns the standard profile (medium).
func DefaultMemoryProfile() MemoryProfile {
return MemoryProfileForMode(MemoryModeMedium)
}
func detectMemoryBudgetMB() (int, string, error) {
vmem, err := mem.VirtualMemory()
if err != nil {
return 0, "", err
}
totalBytes := vmem.Total
limitBytes, err := cgroupMemoryLimitBytes(totalBytes)
if err == nil && limitBytes > 0 && limitBytes < totalBytes {
return int(limitBytes / (1024 * 1024)), "cgroup", nil
}
return int(totalBytes / (1024 * 1024)), "system", nil
}
func cgroupMemoryLimitBytes(totalBytes uint64) (uint64, error) {
paths := []string{
"/sys/fs/cgroup/memory.max", // cgroup v2
"/sys/fs/cgroup/memory/memory.limit_in_bytes", // cgroup v1
}
for _, path := range paths {
data, err := os.ReadFile(path)
if err != nil {
continue
}
raw := strings.TrimSpace(string(data))
if raw == "" || raw == "max" {
return 0, nil
}
val, err := strconv.ParseUint(raw, 10, 64)
if err != nil {
return 0, err
}
if totalBytes > 0 && val > totalBytes*2 {
return 0, nil
}
return val, nil
}
return 0, os.ErrNotExist
}