FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix systemd-journal.plugin memory retention after queries and its apps.plugin accounting by ktsaou · Pull Request #23089 · netdata/netdata · GitHub

fix systemd-journal.plugin memory retention after queries and its apps.plugin accounting - #23089

Merged
ktsaou merged 3 commits into
netdata:masterfrom
ktsaou:systemd-journal-memory
Jul 11, 2026
Merged

fix systemd-journal.plugin memory retention after queries and its apps.plugin accounting#23089
ktsaou merged 3 commits into
netdata:masterfrom
ktsaou:systemd-journal-memory

Conversation

ktsaou commented Jul 11, 2026
edited by cubic-dev-ai Bot
Loading

Copy link
Copy Markdown
Member

Fixes two independent issues around systemd-journal.plugin, plus one coupled debug-mode fix.

1. apps.plugin wrongly accounts systemd-journal.plugin as systemd-journald

The systemd-journald group in apps_groups.conf used the substring pattern *systemd-journal*, which matches against the full process cmdline. This wrongly captured netdata's own /usr/libexec/netdata/plugins.d/systemd-journal.plugin (and anything else with systemd-journal anywhere in its cmdline, e.g. journalctl -u systemd-journald), polluting journald accounting with netdata's log-query workload.

Fix: match the journald family by exact process names: systemd-journald, systemd-journal-remote, systemd-journal-upload, systemd-journal-gatewayd. Names longer than the 15-char comm limit are reconstructed from the cmdline by apps.plugin, so exact matching works for all four (verified live, including a running systemd-journal-upload).

Dashboard-visible change: netdata's journal plugin now appears as its own group (sd-jrnl.plugin, its runtime process name), like every other netdata external plugin, instead of inflating systemd-journald.

2. systemd-journal.plugin retains the RSS high-water mark of its biggest query forever

It is not a leak: ASAN/LSan reports identical output (1102 bytes of one-time init globals) for a trivial info request and for a 16-second full-text search over 117 GB of journals — leaked bytes do not scale with work. All per-query memory (facets, LQS, buffers, sd_journal handles) is freed at query end.

The problem is that glibc keeps the freed memory in its arenas and never returns it to the OS: the plugin never calls malloc_trim() (the only trim in the tree runs in the daemon's dbengine eviction — a different process), and M_TRIM_THRESHOLD cannot be inherited through the environment.

Measured with 3 heavy queries (7-day full-text search, ~7.7M rows evaluated each), plugin driven over the plugins.d protocol:

baseline after 3 queries after malloc_trim(0) via gdb
MALLOC_ARENA_MAX=4 (parent) 15,240 kB 37,516 kB 18,536 kB
MALLOC_ARENA_MAX=1 (standalone) 15,288 kB 27,204 kB 17,276 kB

Fix: in the functions_evloop worker, after a function job completes and the worker queue is empty (no pending and no running jobs), call mallocz_release_as_much_memory_to_the_system(). Under back-to-back load no trim happens (memory is actively reused); the last job of every burst always trims, so no retention window remains. This covers all function plugins with the same pattern (systemd-journal, windows-events, macos-logs, systemd-units, network-viewer, debugfs, freeipmi, apps, ebpf). On platforms without malloc_trim() (musl static builds, macOS, Windows, FreeBSD) it compiles to a no-op.

With the fix, the same experiment gives:

baseline after 3 queries external malloc_trim(0) recovers
MALLOC_ARENA_MAX=4 16,604 kB 19,084 kB 0 kB
MALLOC_ARENA_MAX=1 15,272 kB 17,180 kB 0 kB

Query results are unchanged (same evaluated/matched/returned counts), and ASAN reports no new leaks.

3. systemd-journal.plugin debug mode evaluated 0 rows

The hidden debug command-line mode still used the stale GET parameter source:all; the parameter was renamed to __logs_sources. The stale name is parsed as a filter on a user field named source, matching nothing. Fixed to use __logs_sources:all — debug mode now evaluates the journal normally (27.9M rows on the test box, was 0).


Summary by cubic

Match exact journald process names so systemd-journal.plugin is no longer counted under systemd-journald. Trim freed memory when function workers go idle to avoid retained RSS, and fix debug mode to read from all sources.

  • Bug Fixes
    • apps.plugin: Replace *systemd-journal* with exact names (systemd-journald, systemd-journal-remote, systemd-journal-upload, systemd-journal-gatewayd). The plugin now appears as sd-jrnl.plugin, not under journald.
    • Memory: In functions_evloop, when the worker queue becomes empty, call mallocz_release_as_much_memory_to_the_system() to return freed memory to the OS. Applies to all function plugins; no-op where malloc_trim() isn’t available.
    • Debug mode: Use __logs_sources:all instead of source:all so the journal is evaluated.

Written for commit 7c60108. Summary will update on new commits.

ktsaou added 3 commits July 11, 2026 22:39
The systemd-journald group used the substring pattern *systemd-journal*,
which matches against the full process cmdline. This wrongly captured
netdata's own systemd-journal.plugin (and anything else with
systemd-journal in its cmdline, e.g. journalctl -u systemd-journald),
polluting journald accounting with netdata's log-query workload.

Match the journald family by exact process names instead:
systemd-journald, systemd-journal-remote, systemd-journal-upload,
systemd-journal-gatewayd. Names longer than the 15-char comm limit are
reconstructed from the cmdline by apps.plugin, so exact matching works
for all four. netdata's plugin now gets its own group (sd-jrnl.plugin),
like every other netdata external plugin.
… idle

Function plugins (systemd-journal, windows-events, macos-logs, etc.)
free all per-query memory when a query completes, but glibc keeps the
freed memory in its arenas and never returns it to the OS. As a result
the plugin RSS stays at the high-water mark of the biggest query it ever
executed (verified: 15MB -> 37MB RSS after 3 heavy journal queries with
zero bytes leaked according to ASAN; a single malloc_trim(0) returns it
all).

After a function job completes, when the worker queue is empty (no
pending and no running jobs), call
mallocz_release_as_much_memory_to_the_system(). Under back-to-back load
no trim happens and memory is reused; the last job of every burst always
trims, so no retention window remains. On platforms without
malloc_trim() (musl, macOS, Windows, FreeBSD) this is a no-op.
The hidden 'debug' command-line mode still used source:all, but the
parameter was renamed to __logs_sources. The stale name is parsed as a
filter on a user field named 'source', matching nothing, so debug mode
evaluated 0 rows. With the fix it evaluates the journal normally.

Copy link
Copy Markdown

cubic-dev-ai Bot 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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

No issues found across 3 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant AppsPlugin as apps.plugin
    participant SystemdPlugin as systemd-journal.plugin
    participant FuncEvloop as functions_evloop
    participant Malloc as glibc malloc

    Note over AppsPlugin: Process accounting
    AppsPlugin->>AppsPlugin: Read process cmdline
    AppsPlugin->>AppsPlugin: Match against exact names<br/>(systemd-journald, etc.)
    Note right of AppsPlugin: systemd-journal.plugin cmdline does not match
    AppsPlugin->>AppsPlugin: Account process under own group<br/>(sd-jrnl.plugin)

    Note over SystemdPlugin,FuncEvloop: Function job handling
    SystemdPlugin->>FuncEvloop: Execute function (e.g., query)
    FuncEvloop->>FuncEvloop: Allocate memory for query, process, free
    FuncEvloop->>FuncEvloop: Remove job from worker queue
    alt Worker queue empty
        FuncEvloop->>Malloc: mallocz_release_as_much_memory_to_the_system()<br/>(calls malloc_trim(0))
        Malloc->>Malloc: Return freed memory to OS
    else Queue not empty
        FuncEvloop->>FuncEvloop: No trim (memory reused)
    end

    Note over SystemdPlugin: Debug mode
    SystemdPlugin->>SystemdPlugin: Parse debug query parameter
    SystemdPlugin->>SystemdPlugin: Uses "__logs_sources:all"<br/>— evaluates all journal sources
Loading

Re-trigger cubic

ktsaou merged commit a4922e8 into netdata:master Jul 11, 2026
126 of 141 checks passed
stelfrag pushed a commit to stelfrag/netdata that referenced this pull request Jul 12, 2026
stelfrag mentioned this pull request Jul 13, 2026
Ferroin pushed a commit that referenced this pull request Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant


Back | FazBrowse Home | New Git URL