Skip to content

Xeway/process-scaler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

process-scaler

A lightweight, single-host sideloader. Prefix it to a resource-hungry command and that command (the sideload) harvests the machine's leftover capacity, while everything else on the box (the main workload — e.g. a web server) stays responsive.

sudo process-scaler [flags] -- ./greedy-command [args...]

It is built for individuals / homelabs / self-hosters running a single box: it is standalone, single-host, and needs no exotic setup. The main workload is protected by consequence of constraining the sideload — process-scaler only ever touches the sideload's own cgroup, never the rest of the system.

How it works

The sideload is launched inside its own systemd-run --scope, and a once-a-second control loop adjusts that scope only:

  • CPU headroom (cpu.max). The cap tracks the machine's spare capacity: cap ≈ total − main_demand − headroom. It is self-correcting — the sideload's own usage cancels out, so the cap follows the main's demand, not the sideload's. The quota is computed correctly for multicore machines (quota = fraction × cores × period).
  • Memory (memory.high). An optional soft cap that throttles and reclaims the sideload under memory pressure, without ever OOM-killing it.
  • Overload protection (PSI + freezer). Even a throttled sideload hurts the main through shared-CPU contention (caches, memory bandwidth, turbo). When system pressure (/proc/pressure/{cpu,memory}) shows the main is starved, the sideload is frozen (cgroup.freeze); if it stays frozen past --kill-after, it is killed. On by default.
  • Thermal ceiling (the differentiator). A hard cpu.max ceiling that holds even when the main is idle — something contention-based tools can't do. Above --thermal-max-temp the sideload is clamped regardless of load (effective cap = min(headroom cap, thermal cap)).

See DESIGN.md for the rationale and docs/reference/ for the cgroup v2 / resctl notes this is based on.

A note on the CPU tradeoff

Capping the sideload with cpu.max reserves CPU for the main and bounds the sideload's cache / memory-bandwidth footprint — what protects a main that is sensitive to that shared-resource contention. The cost: cpu.max bandwidth throttling adds some tail latency to the main, and that cost grows the harder the sideload is throttled (so a larger --cpu-headroom can, counter-intuitively, worsen the main's p99). For a CPU-light, latency-sensitive main with spare cores, a plain work-conserving cpu.weight often gives a better p99 than any hard cap. Measure for your workload — the acceptance harness lets you compare the two.

Requirements

  • Linux with cgroup v2 (unified) and systemd
  • PSI enabled (kernel ≥ 4.20, CONFIG_PSI=y)
  • run as root (it manages the sideload's cgroup)

process-scaler checks all of these at startup and fails loudly if any is missing.

Build

go build -o process-scaler .

No external dependencies — pure Go standard library.

Usage

sudo process-scaler \
  --cpu-headroom 15 \        # % of total CPU kept free for the main (default 15)
  --memory-high 4G \         # soft memory cap on the sideload    (default: unset)
  --thermal-max-temp 85 \    # °C; hard cpu.max ceiling above this (default: off)
  --kill-after 30s \         # kill if frozen longer than this     (default 30s)
  -- ./greedy-command args

PSI-driven freezing is on by default; pass --no-freeze-on-overload to disable it.

Examples

# Let a CPU-heavy build use spare capacity, keeping 20% free for the main:
sudo process-scaler --cpu-headroom 20 -- make -j$(nproc)

# Cap memory and add a thermal ceiling at 80 °C:
sudo process-scaler --memory-high 8G --thermal-max-temp 80 -- ./encode.sh

Acceptance test

The harness/ binary is the reproducible §7 acceptance test: it runs a latency-sensitive HTTP main under a fixed request rate while a CPU-greedy sideload runs three ways — unmanaged, naive cpu.weight=1, and under process-scaler — and checks that process-scaler keeps the main's p99 within budget while the sideload still harvests CPU. It needs root.

go build -o process-scaler .
go build -o harness ./harness
sudo ./harness --cpu-headroom 15 --tolerance 25 --scaler ./process-scaler

Scope

v1: CPU + memory, cgroup v2 + systemd only. Deferred to v2: IO control (io.latency/io.cost), a daemon / rules-by-name mode, anything multi-host.

License

See LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages