Skip to content

fix(memory): enable jemalloc background_thread by default - #1260

Merged
sgrif merged 3 commits into
pgdogdev:mainfrom
IgorOhrimenko:jemalloc-background-thread-config
Jul 27, 2026
Merged

fix(memory): enable jemalloc background_thread by default#1260
sgrif merged 3 commits into
pgdogdev:mainfrom
IgorOhrimenko:jemalloc-background-thread-config

Conversation

@IgorOhrimenko

@IgorOhrimenko IgorOhrimenko commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Implements the config-based option suggested in #1230.

Adds a [general] option jemalloc_background_thread (default false, also settable via PGDOG_JEMALLOC_BACKGROUND_THREAD). When enabled, PgDog turns on jemalloc's background purge threads at startup via tikv-jemalloc-ctl — the programmatic equivalent of _RJEM_MALLOC_CONF=background_thread:true — so freed memory is returned to the OS after bursts of large allocations instead of accumulating as retained dirty pages, without operators having to set the env var.

  • Opt-in (default off): no behaviour change unless enabled.
  • No-op on non-jemalloc builds (test, msvc).
  • JSON schema regenerated (.schema/).

Docs: pgdogdev/docs#100.

Refs #1230.

Adds a `[general]` option (also PGDOG_JEMALLOC_BACKGROUND_THREAD env),
default off, that enables jemalloc's background purge threads at startup via
tikv-jemalloc-ctl — equivalent to _RJEM_MALLOC_CONF=background_thread:true,
so freed memory is returned to the OS after allocation bursts without
requiring the env var. No-op on non-jemalloc builds (test/msvc).

Refs pgdogdev#1230.
@CLAassistant

CLAassistant commented Jul 24, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Assert the default (false) and env-var (PGDOG_JEMALLOC_BACKGROUND_THREAD)
handling of the new option.

@sgrif sgrif 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.

This doesn't need to be a config, we should be enabling this by default on all supported platforms. We don't need to be logging errors, this will only return an error on unsupported platforms

@sgrif

sgrif commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

/cc @levkk Since you asked for this to be a config option. I've never seen a scenario where jemalloc bg threads could be enabled and it wasn't desirable

@levkk

levkk commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

It was probably pre-second coffee and I wasn't thinking straight. I don't know why bg threads are disabled by default, so I was just thinking maybe we need to find out before unleashing this on everyone, so maybe an env var or something? Not sure.

@sgrif

sgrif commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Yeah, I don't think we need to worry about anything like that. This is pretty universally recommended to be enabled

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pgdog/src/lib.rs 75.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Drop the jemalloc_background_thread option, its env var and schema entry;
turn background purge threads on at startup instead. Background threads are
universally desirable, and mallctl only fails on platforms that don't support
them, so the error is ignored rather than logged.
@IgorOhrimenko

IgorOhrimenko commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@levkk — on "why is this disabled by default": I went through jemalloc's history and sources. The short version is that it's about portability and fork(2) semantics of a general-purpose allocator, not about background threads being risky for a server process.

It was shipped opt-in deliberately. jemalloc 5.0.0 ChangeLog (June 2017): "Implement opt-in per CPU background threads, and use them for asynchronous decay-driven unused dirty page purging." The reasons are visible in the tree:

  1. The feature doesn't exist everywhere. configure.ac only defines JEMALLOC_BACKGROUND_THREAD when pthreads are available and the ABI isn't macho — so there's no support at all on macOS (jemalloc#1467) or Windows. A default-on option would be a silent no-op, or worse, on those platforms.
  2. Obtaining pthread_create is fragile. background_thread.c resolves it through dlsym(RTLD_NEXT, "pthread_create") with a fallback chain, and that path has needed repeated fixing: 5.0.1 — "Only abort on dlsym(3) error if the failure impacts an enabled feature (lazy locking and/or background threads). This mitigates an initialization failure bug for which we still do not have a clear reproduction test case"; 5.2.0 — "Make background_thread not dependent on libdl" and "Fall back to the default pthread_create if dlsym(3) fails"; jemalloc#2812 (March 2025) added yet another fallback after breakage on FreeBSD 14.1. Enabling that by default for every jemalloc user is a far bigger blast radius than enabling it for one server.
  3. fork(2). Documented behaviour: "after fork(2) … the state in the child process will be disabled regardless the state in parent process." A default-on setting would silently diverge between parent and children (jemalloc#2125).
  4. Threads have side effects for arbitrary embedders. 5.0.1 had to mask signals during background thread creation and avoid inactivity checks inside them to prevent recursive mutex acquisition.

None of that applies to PgDog: one long-lived process, no fork after startup, and the platform gate is handled below. For long-running servers, enabling it is the norm rather than the exception — Meta runs it in production, Redis and TiKV turn it on explicitly. The upstream default also isn't going to change: jemalloc's last release is 5.3.0 from May 2022.

Worth noting the default has never been a decision on our side either: jemalloc arrived in #124 as a bare #[global_allocator], and neither MALLOC_CONF nor background_thread has ever appeared anywhere in the tree.


@sgrif — updated as requested. The config option, its env var, the schema entry and the parsing test are gone; background threads are now enabled unconditionally at the top of main(), and the mallctl error is ignored rather than logged.

One implementation note, since the obvious alternative looks simpler than it is: tikv-jemallocator has a background_threads cargo feature, but it compiles background_thread:true into --with-malloc-conf, and background_thread_boot0() treats "requested but unsupported" as an initialization failure — it prints <jemalloc>: option background_thread currently supports pthread only and fails jemalloc's init, so the process dies on its first allocation. We publish an aarch64-apple-darwin binary where the feature isn't compiled in, so that would break the macOS release (and musl targets, which tikv-jemalloc-sys lists as unsupported). The runtime mallctl write is the platform-safe form: on those platforms it just returns the error we ignore.

pgdogdev/docs#100 documented the removed option, so I've moved it to draft — there's nothing left for it to document, and I'll close it once this lands.

@IgorOhrimenko IgorOhrimenko changed the title feat(config): add jemalloc_background_thread option fix(memory): enable jemalloc background_thread by default Jul 27, 2026
@levkk

levkk commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Ah, this is excellent research, thank you. Exactly what I was looking to understand. In this case, I think we should be able to just merge it as a default on.

@sgrif
sgrif merged commit 3660aa1 into pgdogdev:main Jul 27, 2026
45 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.

4 participants