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
16 changes: 13 additions & 3 deletions shard_core/service/memory_pressure.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import errno
import logging
import re
from pathlib import Path
Expand Down Expand Up @@ -50,9 +51,18 @@ def _reclaim_container(container_id: str):
try:
(cgroup / "memory.reclaim").write_text(f"{current}\n")
except OSError as e:
# Partial reclaim surfaces as EAGAIN once nothing more can be paged
# out — the pages that could move are in swap already.
log.warning(f"memory.reclaim incomplete for container {container_id}: {e}")
# We request the container's full RSS, which can never be reclaimed in
# whole (some pages are always resident), so memory.reclaim returns
# EAGAIN on essentially every pause once it has paged out what it can —
# that is the expected terminal signal, not a failure. Only surface a
# warning for genuine errors (missing file, EPERM, ...).
if e.errno == errno.EAGAIN:
log.debug(
f"memory.reclaim reached EAGAIN for container {container_id} "
"(expected — paged out what it could)"
)
else:
log.warning(f"memory.reclaim failed for container {container_id}: {e}")


def _find_cgroup(container_id: str) -> Path | None:
Expand Down
38 changes: 38 additions & 0 deletions tests/test_memory_pressure.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import errno
import logging
from pathlib import Path
from unittest.mock import AsyncMock, patch

import pytest
Expand Down Expand Up @@ -67,6 +70,41 @@ def test_reclaim_container_zero_usage_writes_nothing(fake_cgroup_root):
assert (cgroup / "memory.reclaim").read_text() == ""


def test_reclaim_container_eagain_is_debug_not_warning(
fake_cgroup_root, caplog, monkeypatch
):
# Requesting the full RSS always ends in EAGAIN once nothing more can be
# paged out — expected on every pause, so it must not log a warning.
_make_cgroup(fake_cgroup_root, "docker/eag123", 4096)

def _raise_eagain(self, *args, **kwargs):
raise OSError(errno.EAGAIN, "write could not complete without blocking")

monkeypatch.setattr(Path, "write_text", _raise_eagain)
with caplog.at_level(logging.DEBUG, logger=memory_pressure.log.name):
memory_pressure._reclaim_container("eag123")

assert not any(r.levelno == logging.WARNING for r in caplog.records)
assert any("EAGAIN" in r.getMessage() for r in caplog.records)


def test_reclaim_container_other_oserror_warns(fake_cgroup_root, caplog, monkeypatch):
# A genuine error (not EAGAIN) still surfaces as a warning.
_make_cgroup(fake_cgroup_root, "docker/err123", 4096)

def _raise_eperm(self, *args, **kwargs):
raise OSError(errno.EPERM, "operation not permitted")

monkeypatch.setattr(Path, "write_text", _raise_eperm)
with caplog.at_level(logging.DEBUG, logger=memory_pressure.log.name):
memory_pressure._reclaim_container("err123")

assert any(
r.levelno == logging.WARNING and "failed" in r.getMessage()
for r in caplog.records
)


async def test_reclaim_compose_stack_reclaims_each_container(fake_cgroup_root):
cgroup_a = _make_cgroup(fake_cgroup_root, "docker/aaa", 100)
cgroup_b = _make_cgroup(fake_cgroup_root, "docker/bbb", 200)
Expand Down
Loading