From 37a24bc5b56e177bcaca7570e53c8fdd8e72669c Mon Sep 17 00:00:00 2001 From: Stefan Lohmaier Date: Thu, 18 Jun 2026 08:54:33 +0200 Subject: [PATCH] mail: malloc_trim nach search_mail/read_attachment gegen RSS-Bloat search_mail dekodiert bei breiten Queries den Body jeder Mail; CPython/glibc gibt den Peak-RSS nicht ans OS zurueck -> Prozess wuchs auf 6.2 GB und hat zusammen mit einem 2-TB-Restic-Lauf den Server-RAM/Swap gesprengt (restic OOM). Fix: gc.collect()+malloc_trim(0) nach den speicherintensiven Tools. Zusaetzlich systemd MemoryMax=2G/MemoryHigh=1.5G als Sicherheitsnetz (Drop-In, nicht im Repo). Co-Authored-By: Claude Opus 4.8 (1M context) --- mail/server.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mail/server.py b/mail/server.py index a10a931..0932870 100644 --- a/mail/server.py +++ b/mail/server.py @@ -7,6 +7,8 @@ import io import contextlib import imaplib import mailbox +import gc +import ctypes from email.header import decode_header from email.mime.text import MIMEText from email.utils import formatdate @@ -47,6 +49,22 @@ mcp = FastMCP("Mail", stateless_http=True, transport_security={"enable_dns_rebinding_protection": False}) +try: + _libc = ctypes.CDLL("libc.so.6") +except OSError: + _libc = None + + +def _trim_memory(): + """gc + malloc_trim: Speicher nach grossen Operationen ans OS zurueckgeben.""" + gc.collect() + if _libc is not None: + try: + _libc.malloc_trim(0) + except Exception: + pass + + def _safe_decode(payload, charset): """Decode bytes robust gegen unbekannte/kaputte Charsets (z.B. 'x-unknown').""" if not isinstance(payload, bytes): @@ -207,7 +225,9 @@ def search_mail( continue results.append(f"[{date_str}] {frm} -> {to}\n Subject: {subj}\n Account: {acct_name}, Folder: {fld}, Key: {key}") if len(results) >= limit: + _trim_memory() return "\n\n".join(results) + _trim_memory() return "\n\n".join(results) if results else "No results found" @@ -254,6 +274,7 @@ def read_attachment( attachment_index: Annotated[int, Field(description="Attachment number from the read_mail attachment list (1-based)")], ) -> list[TextContent | ImageContent | EmbeddedResource]: """Read an email attachment. Images shown inline, PDFs as extracted text, text directly, other documents as binary. Get the index from read_mail.""" + _trim_memory() user = get_current_user() if not user: return [TextContent(type="text", text="Error: not authenticated")]