Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions packages/envd/internal/services/cgroups/cgroup2.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,34 @@ func createCgroups(configs cgroup2Config) (map[ProcessType]int, error) {
return results, nil
}

// writeCgroupProp writes a cgroupfs property without O_CREATE so missing
// properties error out rather than being silently created on a tmpfs fallback.
func writeCgroupProp(path, value string) error {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(value)

return err
}

func createCgroup(fullPath string, properties map[string]string) (int, error) {
if err := os.MkdirAll(fullPath, 0o755); err != nil {
return -1, fmt.Errorf("failed to create cgroup root: %w", err)
}

var errs []error
for name, value := range properties {
if err := os.WriteFile(filepath.Join(fullPath, name), []byte(value), 0o644); err != nil {
errs = append(errs, fmt.Errorf("failed to write cgroup property: %w", err))
if err := writeCgroupProp(filepath.Join(fullPath, name), value); err != nil {
// Skip properties whose controller isn't enabled in subtree_control.
if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) {
fmt.Fprintf(os.Stderr, "cgroup property %q unavailable at %q, skipping\n", name, fullPath)

continue
Comment thread
cursor[bot] marked this conversation as resolved.
}
errs = append(errs, fmt.Errorf("failed to write cgroup property %q: %w", name, err))
}
}
if len(errs) > 0 {
Expand Down
7 changes: 2 additions & 5 deletions packages/envd/internal/services/process/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,9 @@ func New(
// User command string for logging (without the internal wrapper details).
userCmd := strings.Join(append([]string{req.GetProcess().GetCmd()}, req.GetProcess().GetArgs()...), " ")

// Wrap the command in a shell that sets the OOM score and nice value before exec-ing the actual command.
// This eliminates the race window where grandchildren could inherit the parent's protected OOM score (-1000)
// or high CPU priority (nice -20) before the post-start calls had a chance to correct them.
// nice(1) applies a relative adjustment, so we compute the delta from the current (inherited) nice to the target.
// Wrap in a shell that resets oom_score_adj, ioprio (ionice best-effort/4), and nice.
niceDelta := defaultNice - currentNice()
oomWrapperScript := fmt.Sprintf(`echo %d > /proc/$$/oom_score_adj && exec /usr/bin/nice -n %d "${@}"`, defaultOomScore, niceDelta)
oomWrapperScript := fmt.Sprintf(`echo %d > /proc/$$/oom_score_adj && exec /usr/bin/ionice -c 2 -n 4 /usr/bin/nice -n %d "${@}"`, defaultOomScore, niceDelta)
Comment thread
ValentaTomas marked this conversation as resolved.
wrapperArgs := append([]string{"-c", oomWrapperScript, "--", req.GetProcess().GetCmd()}, req.GetProcess().GetArgs()...)
cmd := exec.CommandContext(ctx, "/bin/sh", wrapperArgs...)

Expand Down
9 changes: 6 additions & 3 deletions packages/envd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,19 +242,22 @@ func createCgroupManager() (m cgroups.Manager) {

opts := []cgroups.Cgroup2ManagerOption{
cgroups.WithCgroup2ProcessType(cgroups.ProcessTypePTY, "ptys", map[string]string{
"cpu.weight": "200", // gets much preferred cpu access, to help keep these real time
"cpu.weight": "200",
"io.weight": "default 50",
"memory.high": fmt.Sprintf("%d", memoryHigh),
"memory.max": fmt.Sprintf("%d", memoryMax),
}),
cgroups.WithCgroup2ProcessType(cgroups.ProcessTypeSocat, "socats", map[string]string{
"cpu.weight": "150", // gets slightly preferred cpu access
"cpu.weight": "150",
"io.weight": "default 50",
"memory.min": fmt.Sprintf("%d", 5*megabyte),
"memory.low": fmt.Sprintf("%d", 8*megabyte),
}),
cgroups.WithCgroup2ProcessType(cgroups.ProcessTypeUser, "user", map[string]string{
"memory.high": fmt.Sprintf("%d", memoryHigh),
"memory.max": fmt.Sprintf("%d", memoryMax),
"cpu.weight": "50", // less than envd, and less than core processes that default to 100
"cpu.weight": "50",
"io.weight": "default 10",
}),
}
if cgroupRoot != "" {
Expand Down
2 changes: 1 addition & 1 deletion packages/envd/pkg/version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package pkg

const Version = "0.5.23"
const Version = "0.5.24"
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ LimitCORE=infinity
ExecStartPre=/bin/sh -c 'mountpoint -q /etc/ssl/certs || (mkdir -p /run/e2b/certs && mount --bind /run/e2b/certs /etc/ssl/certs) && ([ -s /etc/ssl/certs/ca-certificates.crt ] || update-ca-certificates)'
ExecStart=/bin/bash -l -c "/usr/bin/envd"
Nice=-20
IOSchedulingClass=realtime
IOSchedulingPriority=4
OOMPolicy=continue
OOMScoreAdjust=-1000
Environment="GOMEMLIMIT={{ .MemoryLimit }}MiB"
Expand All @@ -26,6 +28,8 @@ MemoryMin=50M
MemoryLow=100M
CPUAccounting=yes
CPUWeight=1000
IOAccounting=yes
IOWeight=10000

[Install]
WantedBy=multi-user.target
Loading