fix(service): bound ejclaw cgroup page cache with MemoryHigh=3G

Per-turn agent runners do heavy file I/O (git clone, builds, ffmpeg/video
scratch) inside the ejclaw service cgroup. The kernel retains those reads as
reclaimable page cache, so systemd's MemoryCurrent climbs to 4-5GB even though
real anon usage stays ~300MB and the host has 13GB free. Add a soft MemoryHigh
so the cache is reclaimed under pressure before the number balloons; MemoryMax
stays unset (infinity) so a legitimate burst is throttled, never OOM-killed.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-03 22:24:18 +09:00
parent d7b5166862
commit 8cf351580b
2 changed files with 23 additions and 0 deletions

View File

@@ -113,6 +113,15 @@ WorkingDirectory=${projectRoot}
Restart=always Restart=always
RestartSec=5 RestartSec=5
RestartPreventExitStatus=${STARTUP_PRECONDITION_EXIT_CODE} RestartPreventExitStatus=${STARTUP_PRECONDITION_EXIT_CODE}
# Per-turn agent runners do heavy file I/O (git clone, builds, video/ffmpeg
# scratch) inside this service cgroup. The kernel keeps those reads as
# reclaimable page cache, which makes systemd's MemoryCurrent climb to several
# GB even though real (anon) usage stays a few hundred MB. MemoryHigh applies
# gentle reclaim pressure so that cache is dropped before the number balloons,
# while MemoryMax stays unset (infinity) so a legitimate burst is never
# OOM-killed — it is throttled at worst.
MemoryAccounting=yes
MemoryHigh=3G
${envLines.join('\n')} ${envLines.join('\n')}
StandardOutput=append:${projectRoot}/logs/${def.logName}.log StandardOutput=append:${projectRoot}/logs/${def.logName}.log
StandardError=append:${projectRoot}/logs/${def.logName}.error.log StandardError=append:${projectRoot}/logs/${def.logName}.error.log

View File

@@ -111,6 +111,20 @@ describe('systemd unit generation', () => {
expect(unit).toContain('RestartPreventExitStatus=78'); expect(unit).toContain('RestartPreventExitStatus=78');
}); });
it('bounds reclaimable page cache with a soft memory limit', () => {
const unit = buildSystemdUnit(
baseServiceDef,
'/home/user/ejclaw',
'/usr/bin/node',
'/home/user',
false,
);
expect(unit).toContain('MemoryAccounting=yes');
expect(unit).toContain('MemoryHigh=3G');
// MemoryMax must stay unset so a real burst is throttled, never OOM-killed.
expect(unit).not.toContain('MemoryMax=');
});
it('sets correct ExecStart', () => { it('sets correct ExecStart', () => {
const unit = buildSystemdUnit( const unit = buildSystemdUnit(
baseServiceDef, baseServiceDef,