Fix: only load jemalloc for main python process
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 19 Dec 2025 02:00:43 +0000 (03:00 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 19 Dec 2025 02:00:43 +0000 (03:00 +0100)
Dockerfile
Dockerfile.base
music_assistant/helpers/process.py

index d956bd00eb3f728697ba03738a559f51e18bd2ca..913c50838589cb43cba242e6b0ca345f4ed5cb70 100644 (file)
@@ -82,4 +82,11 @@ EXPOSE 8095
 
 WORKDIR $VIRTUAL_ENV
 
-ENTRYPOINT ["mass", "--data-dir", "/data", "--cache-dir", "/data/.cache"]
+# Entrypoint script that enables jemalloc for the main process only
+RUN printf '#!/bin/sh\n\
+for path in /usr/lib/*/libjemalloc.so.2; do\n\
+    [ -f "$path" ] && export LD_PRELOAD="$path" && break\n\
+done\n\
+exec mass "$@"\n' > /usr/local/bin/entrypoint.sh && chmod +x /usr/local/bin/entrypoint.sh
+
+ENTRYPOINT ["/usr/local/bin/entrypoint.sh", "--data-dir", "/data", "--cache-dir", "/data/.cache"]
index 5be0e1d73deb48a49bf52239c08d9fe3d4d418e1..984767da978575d1b17364502bda1fd7b8f4aadf 100644 (file)
@@ -229,16 +229,6 @@ RUN ldconfig && ffmpeg -version && ffprobe -version
 RUN mkdir -p /usr/local/bin/widevine_cdm
 COPY widevine_cdm/* /usr/local/bin/widevine_cdm/
 
-# JEMalloc for more efficient memory management
-# Dynamically set the correct path based on architecture
-ARG TARGETARCH
-RUN set -x \
-    && if [ "$TARGETARCH" = "arm64" ]; then \
-        echo "/usr/lib/aarch64-linux-gnu/libjemalloc.so.2" > /etc/ld.so.preload; \
-    else \
-        echo "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2" > /etc/ld.so.preload; \
-    fi
-
 # we need to set (very permissive) permissions to the workdir
 # and /tmp to allow running the container as non-root
 RUN chmod -R 777 /tmp
index c761ad3151a5d6742c957c044779072790488cda..0fa6183b7ebe10543a050f0d688e0e6b1f1dd66f 100644 (file)
@@ -26,6 +26,15 @@ LOGGER = logging.getLogger(f"{MASS_LOGGER_NAME}.helpers.process")
 DEFAULT_CHUNKSIZE = 64000
 
 
+def get_subprocess_env(env: dict[str, str] | None = None) -> dict[str, str]:
+    """Get environment for subprocess, stripping LD_PRELOAD to avoid jemalloc warnings."""
+    result = dict(os.environ)
+    result.pop("LD_PRELOAD", None)
+    if env:
+        result.update(env)
+    return result
+
+
 class AsyncProcess:
     """
     AsyncProcess.
@@ -65,7 +74,7 @@ class AsyncProcess:
         self._stdin = None if stdin is False else stdin
         self._stdout = None if stdout is False else stdout
         self._stderr = asyncio.subprocess.DEVNULL if stderr is False else stderr
-        self._env = env
+        self._env = get_subprocess_env(env)
         self._stderr_lock = asyncio.Lock()
         self._stdout_lock = asyncio.Lock()
         self._stdin_lock = asyncio.Lock()
@@ -319,7 +328,10 @@ class AsyncProcess:
 async def check_output(*args: str, env: dict[str, str] | None = None) -> tuple[int, bytes]:
     """Run subprocess and return returncode and output."""
     proc = await asyncio.create_subprocess_exec(
-        *args, stderr=asyncio.subprocess.STDOUT, stdout=asyncio.subprocess.PIPE, env=env
+        *args,
+        stderr=asyncio.subprocess.STDOUT,
+        stdout=asyncio.subprocess.PIPE,
+        env=get_subprocess_env(env),
     )
     stdout, _ = await proc.communicate()
     assert proc.returncode is not None  # for type checking
@@ -336,6 +348,7 @@ async def communicate(
         stderr=asyncio.subprocess.PIPE,
         stdout=asyncio.subprocess.PIPE,
         stdin=asyncio.subprocess.PIPE if input is not None else None,
+        env=get_subprocess_env(),
     )
     stdout, stderr = await proc.communicate(input)
     assert proc.returncode is not None  # for type checking