Skip to content

fix(system): do not fail memory snapshots on inaccessible processes - #2078

Merged
vdusek merged 13 commits into
apify:masterfrom
anxkhn:fix/memory-info-access-denied
Jul 29, 2026
Merged

fix(system): do not fail memory snapshots on inaccessible processes#2078
vdusek merged 13 commits into
apify:masterfrom
anxkhn:fix/memory-info-access-denied

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 16, 2026

Copy link
Copy Markdown
Contributor
  • get_memory_info() no longer raises when a process cannot be inspected. In restricted environments (hidepid, hardened containers) it failed on every sample, which meant a traceback in the log every second and an autoscaler running on a stale memory history. Processes that deny inspection, exit mid-measurement, or lose their /proc entry are now skipped instead.
  • Failure to list the child processes no longer aborts the snapshot either - the parent's own usage is still reported.
  • A kernel that exposes no smaps at all makes psutil alias memory_full_info to memory_info, whose result has no pss field, so reading it raised AttributeError on every sample. The metric is now read defensively, and the verdict is latched: such a machine is detected once instead of being re-probed for every process on every sample.
  • A smaps file can be empty, which psutil parses to a PSS of zero. That was reported as zero bytes used, which the autoscaler reads as free memory. A PSS of zero now falls back to the RSS of the same process, which memory_full_info() has already read anyway.
  • A single process denying PSS falls back to RSS just for itself - a denial says nothing about the other processes, so it is not latched.
  • Every degraded path warns once, so an estimate that misses a subprocess or falls back from PSS to RSS shows up in the log instead of being silently wrong.
  • LoggerOnce is now thread-safe, since memory metrics are sampled in a worker thread - this PR adds its first caller that runs off the event loop.
  • Nothing changes when PSS is readable: on a normal Linux process tree the reported current_size is byte-for-byte what master reports, with no warnings and no RSS fallbacks.
  • Tests cover the denied, exited, zombie, and missing-/proc cases, both PSS fallbacks and their warnings, the latch, and a vanished process not being misreported as a denial.

✍️ Drafted by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens get_memory_info() (used for autoscaler CPU/memory snapshots) against psutil.AccessDenied in restricted environments, preventing the periodic system-info task from crashing and silently stopping.

Changes:

  • Catch psutil.AccessDenied when reading the current process’s memory via PSS and fall back to RSS.
  • Suppress psutil.AccessDenied when aggregating child process memory usage.
  • Add unit tests covering both the child-skip and current-process RSS fallback behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/crawlee/_utils/system.py Adds AccessDenied handling/fallbacks in memory snapshot collection.
tests/unit/_utils/test_system.py Adds regression tests for AccessDenied scenarios in get_memory_info().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/crawlee/_utils/system.py Outdated
Comment thread src/crawlee/_utils/system.py Outdated
Comment thread src/crawlee/_utils/system.py Outdated
anxkhn added 2 commits July 27, 2026 18:29
On Linux, get_memory_info reads a process's PSS via memory_full_info,
which psutil documents may require elevated privileges. In restricted
environments (hardened containers with hidepid/restricted /proc, or an
unreadable child subprocess) this raises psutil.AccessDenied, which is
not a subclass of psutil.NoSuchProcess and so was not caught by the
existing suppress around the child loop; the current-process read was
unguarded entirely. The exception propagated out of the recurring
system-info task, which has no error handling, silently stopping the
autoscaler's CPU/memory snapshots.

Suppress AccessDenied alongside NoSuchProcess when summing child memory,
and fall back to RSS for the current process when PSS is denied.

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn
anxkhn force-pushed the fix/memory-info-access-denied branch from b3464f1 to cd699de Compare July 27, 2026 13:00
@anxkhn

anxkhn commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

pushed a revision addressing the latest review and ci feedback.

@vdusek

vdusek commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Same as here #2074 (comment)

@anxkhn Thank you. For future reviews, I'd really appreciate it if you could address all the feedback in a single follow-up commit, rather than splitting the changes across multiple commits, merging them into earlier commits, and force-pushing it. Your current approach makes the changes really hard to review.

@vdusek vdusek left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the revision. Both of my earlier comments are correctly addressed - the logger.debug for the PSS->RSS fallback, and the child RSS fallback so an unreadable child is no longer dropped.

However, the change made for the Copilot comment about guarding children() introduces a regression: wrapping the entire loop in suppress means one child dying mid-iteration now aborts the loop and drops all remaining children. I verified this locally - a repro with a dead first child and a live second child worth 40 B yields 140 B on master and 100 B on this branch. CI does not catch it because that path is untested.

Details inline.

✍️ Drafted by Claude Code

Comment thread src/crawlee/_utils/system.py Outdated
Comment thread src/crawlee/_utils/system.py Outdated
Comment thread tests/unit/_utils/test_system.py Outdated
Comment thread tests/unit/_utils/test_system.py Outdated
Comment thread tests/unit/_utils/test_system.py Outdated
Comment thread tests/unit/_utils/test_system.py Outdated
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn

anxkhn commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@vdusek addressed all new threads, including the child-exit regression test, rebased, and re-requesting review. thank you.

vdusek added 2 commits July 28, 2026 09:53
- Move the fallback into the Linux-only `_get_used_memory`; elsewhere it re-ran the call that had just failed.
- Cover a missing or zero `pss` field, not just `AccessDenied`.
- Warn once about the degraded estimate instead of a debug record per snapshot, and log unlistable children.
- Test the real fallback instead of only a patched `_get_used_memory`.
Inspecting a process can also fail with a bare `OSError` - psutil re-raises
`FileNotFoundError` for a live process whose `/proc` entry is missing - which
escaped the previous `psutil.AccessDenied` handling. Catch it wherever a process
is inspected, and make the PSS to RSS fallback report what actually happened:

- Warn about PSS only when psutil exposes no `pss` field at all, and latch that
  verdict instead of re-parsing `smaps` for every process on every sample. A
  single denied process falls back to RSS just for itself, and a `smaps` file
  that parses to zero is no longer reported as a missing metric.
- Report a child that cannot be measured at all separately from one that exited.
- Cap the estimate at the total memory of the machine, so summed RSS cannot pin
  the autoscaler in a critically overloaded state.
- Lock `LoggerOnce`, which is now reached from the metric sampling thread.
@vdusek vdusek changed the title fix(system): Handle psutil.AccessDenied in get_memory_info fix(system): do not fail memory snapshots on inaccessible processes Jul 28, 2026
@vdusek

This comment was marked as outdated.

@vdusek
vdusek requested a review from Mantisus July 28, 2026 09:32
@vdusek

vdusek commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

@Mantisus could you please check this as well?

@vdusek vdusek left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@Mantisus Mantisus left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Just a few nits.

Also, could you update the PR description?

Comment thread src/crawlee/_utils/system.py Outdated
Comment thread tests/unit/_utils/test_system.py Outdated
@vdusek
vdusek merged commit 5ad4e53 into apify:master Jul 29, 2026
34 checks passed
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.

5 participants