Skip to content

feat: cpu and memory limits for systemd#330

Merged
jason-lynch merged 1 commit intomainfrom
feat/PLAT-417/cpu-memory-limits
Apr 6, 2026
Merged

feat: cpu and memory limits for systemd#330
jason-lynch merged 1 commit intomainfrom
feat/PLAT-417/cpu-memory-limits

Conversation

@jason-lynch
Copy link
Copy Markdown
Member

@jason-lynch jason-lynch commented Apr 6, 2026

Summary

Adds CPU and memory limits for databases deployed with systemd using these service properties:

  • CPUQuota: Expressed as a percentage where 100% = 1 CPU core.
  • MemoryMax: Expressed as an integer with optional units, such as 1G. Defaults to bytes when units are unspecified.

These are populated from existing properties on our instance spec.

This commit includes a small refactor to the way we produce and test unit file options. These changes are aimed at making it easier to add new types of unit files when we implement supporting services for systemd.

Testing

# Switch to the dev-lima environment
use-dev-lima

# create a database with CPU and memory limits
cp1-req create-database <<EOF | cp-follow-task
{
  "id": "storefront",
  "spec": {
    "database_name": "storefront",
    "database_users": [
      {
        "username": "admin",
        "password": "password",
        "db_owner": true,
        "attributes": ["SUPERUSER", "LOGIN"]
      }
    ],
    "port": 0,
    "patroni_port": 0,
    "nodes": [
      { "name": "n1", "host_ids": ["host-1"] }
    ],
    "cpus": "2",
    "memory": "2.5GiB"
  }
}
EOF

# ssh to host-1
cp1-ssh

# switch to root
sudo su -

# examine the unit file to see that the new properties are set
cat /etc/systemd/system/patroni-storefront-n1-689qacsi.service

# look at the patroni service with systemctl. you'll see the memory limit listed near the top.
systemctl status patroni-storefront-n1-689qacsi.service

PLAT-417

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

This pull request introduces typed data structures for systemd unit file generation and enhances the Patroni unit configuration to support CPU and memory resource limits. Golden test fixtures validate the generated systemd units under various resource specifications using a new test framework with an -update flag.

Changes

Cohort / File(s) Summary
Golden Test Fixtures
server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/*.service
Added four systemd unit files (minimal.service, cpu_limit.service, fractional_cpu_limit.service, memory_max.service) as golden test fixtures demonstrating unit generation with different resource configurations including CPU quotas and memory limits.
Unit Options Infrastructure
server/internal/orchestrator/systemd/unit_options.go, unit_options_test.go, main_test.go
Introduced typed structures (UnitFile, UnitSection, ServiceSection, InstallSection) with string-backed enums (ServiceType, ServiceKillMode, ServiceRestart), helper constructors for each directive, and Options() methods that emit conditional systemd directives. Added golden test harness with -update flag for validating generated units.
Patroni Unit Generation
server/internal/orchestrator/systemd/patroni_unit.go, orchestrator.go
Exported PatroniUnitOptions function, extended signature to accept CPU and memory parameters, refactored construction to use typed unit sections and environment map handling, and updated caller to pass instance-level resource specifications from InstanceSpec.

Poem

🐰 A rabbit hops through systemd trees so bright,
With typed units now, everything's just right!
CPU quotas, memory bounds take their place,
Golden tests ensure we set a perfect pace!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding CPU and memory limits for systemd services.
Description check ✅ Passed The PR description follows the template structure with all required sections: Summary (clear and concise), Changes (high-level overview including refactor details), Testing (commands and manual steps provided), and Issue linkage (PLAT-417). All key information is present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/PLAT-417/cpu-memory-limits

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 6, 2026

Up to standards ✅

🟢 Issues 1 medium

Results:
1 new issue

Category Results
Complexity 1 medium

View in Codacy

🟢 Metrics 30 complexity · 0 duplication

Metric Results
Complexity 30
Duplication 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
server/internal/orchestrator/systemd/patroni_unit.go (1)

18-21: Use a deterministic service PATH instead of inheriting the orchestrator process environment.

Lines 18-21 persist whatever PATH the control-plane process happens to have at render time into the unit file. The new goldens already show root-specific entries like /root/.local/bin, so unit contents and runtime command resolution now vary by host even though the service itself runs as postgres. Prefer a fixed system fallback (or a config-supplied one) after pgBinPath instead of os.Getenv("PATH").

♻️ Example direction
-	pathEnv := pgBinPath
-	if p := os.Getenv("PATH"); p != "" {
-		pathEnv += ":" + p
-	}
+	const defaultServicePath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+	pathEnv := pgBinPath + ":" + defaultServicePath
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/systemd/patroni_unit.go` around lines 18 - 21,
The code sets pathEnv by appending the current process PATH via
os.Getenv("PATH"), causing non-deterministic unit files; change the logic that
builds pathEnv (the variable in patroni_unit.go that currently uses pgBinPath +
os.Getenv("PATH")) to append a fixed system fallback PATH (e.g.
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") or a
configurable default instead of reading os.Getenv("PATH"), ensuring
deterministic unit content while still prefixing pgBinPath.
server/internal/orchestrator/systemd/unit_options_test.go (1)

56-86: Consider adding a combined CPU + memory limit test case.

The current test cases validate CPU and memory limits independently. Adding a case that sets both cpus and memoryBytes would ensure the options compose correctly without conflicts in the generated unit file.

💡 Suggested additional test case
 			{
 				name:        "memory limit",
 				paths:       paths,
 				pgBinPath:   pgBinPath,
 				memoryBytes: 8_589_934_592, // 8GiB in bytes
 			},
+			{
+				name:        "cpu and memory limit",
+				paths:       paths,
+				pgBinPath:   pgBinPath,
+				cpus:        2,
+				memoryBytes: 4_294_967_296, // 4GiB in bytes
+			},
 		} {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/systemd/unit_options_test.go` around lines 56 -
86, Add a new test case to the table in unit_options_test.go (the for loop over
[]struct{name, paths, pgBinPath, cpus, memoryBytes}) that sets both cpus and
memoryBytes (e.g., name "cpu + memory limit", cpus: 2 or 14, memoryBytes:
8_589_934_592) so the test exercises composition of CPU and memory options when
generating the unit file; ensure the new case uses the same paths and pgBinPath
variables as the other cases and follows the existing test assertions for
generated unit content.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/internal/orchestrator/systemd/orchestrator.go`:
- Line 262: The Patroni/Postgres config generator is still using host totals
instead of the cgroup-enforced limits; update the call site that creates
common.NewPatroniConfigGenerator to pass the effective per-instance limits
(spec.CPUs and spec.MemoryBytes) when they are set so the generated Patroni
config matches the unit limits used in PatroniUnitOptions(paths,
o.packageManager.BinDir(pgMajor), spec.CPUs, spec.MemoryBytes); ensure
NewPatroniConfigGenerator receives and uses those same CPU/memory values
(falling back to host totals only if spec.CPUs/spec.MemoryBytes are unset) so
worker/memory settings are bounded by the service cgroup.

---

Nitpick comments:
In `@server/internal/orchestrator/systemd/patroni_unit.go`:
- Around line 18-21: The code sets pathEnv by appending the current process PATH
via os.Getenv("PATH"), causing non-deterministic unit files; change the logic
that builds pathEnv (the variable in patroni_unit.go that currently uses
pgBinPath + os.Getenv("PATH")) to append a fixed system fallback PATH (e.g.
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") or a
configurable default instead of reading os.Getenv("PATH"), ensuring
deterministic unit content while still prefixing pgBinPath.

In `@server/internal/orchestrator/systemd/unit_options_test.go`:
- Around line 56-86: Add a new test case to the table in unit_options_test.go
(the for loop over []struct{name, paths, pgBinPath, cpus, memoryBytes}) that
sets both cpus and memoryBytes (e.g., name "cpu + memory limit", cpus: 2 or 14,
memoryBytes: 8_589_934_592) so the test exercises composition of CPU and memory
options when generating the unit file; ensure the new case uses the same paths
and pgBinPath variables as the other cases and follows the existing test
assertions for generated unit content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 160fc849-f9e0-458a-b4d7-90cad8e73458

📥 Commits

Reviewing files that changed from the base of the PR and between 0724783 and 6c770b3.

📒 Files selected for processing (9)
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/cpu_limit.service
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/fractional_cpu_limit.service
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/memory_limit.service
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/minimal.service
  • server/internal/orchestrator/systemd/main_test.go
  • server/internal/orchestrator/systemd/orchestrator.go
  • server/internal/orchestrator/systemd/patroni_unit.go
  • server/internal/orchestrator/systemd/unit_options.go
  • server/internal/orchestrator/systemd/unit_options_test.go

Adds CPU and memory limits for databases deployed with systemd using
these service properties:

- `CPUQuota`: Expressed as a percentage where 100% = 1 CPU core.
- `MemoryLimit`: Expressed as an integer with optional units, such as
  `1G`. Defaults to bytes when units are unspecified.

These are populated from existing properties on our instance spec.

This commit includes a small refactor to the way we produce and test
unit file options. These changes are aimed at making it easier to add
new types of unit files when we implement supporting services for
systemd.

PLAT-417
@jason-lynch jason-lynch force-pushed the feat/PLAT-417/cpu-memory-limits branch from 6c770b3 to dc4509a Compare April 6, 2026 18:46
Copy link
Copy Markdown
Contributor

@rshoemaker rshoemaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, looks good.

@jason-lynch jason-lynch merged commit a5bc626 into main Apr 6, 2026
3 checks passed
@jason-lynch jason-lynch deleted the feat/PLAT-417/cpu-memory-limits branch April 6, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants