Work_mem: It's a Trap

Tiny setting, giant crash: users split on Postgres memory cap

TLDR: A single Postgres query ballooned memory to 2TB because memory gets freed only after the query finishes, not during. Commenters are divided: some demand hard memory caps and smarter auto-spill, while others defend the design and urge better tuning and monitoring to avoid catastrophic blowups.

The database world is clutching its pearls after a single Postgres query reportedly inhaled 2 TB of RAM and summoned the Linux “Out-Of-Memory killer” mid-peak. The kicker? The safety knob—work_mem—was set to a tiny 2 MB. Cue panic, disbelief, and memes.

The post’s hero moment was discovering a little-known function, pg_log_backend_memory_contexts, which dumped a memory map showing hundreds of megabytes piling up in the query’s executor and hash table—because memory isn’t released until the end of the operation. That’s by design, say the experts, for speed and simplicity. But the crowd was not exactly zen.

One faction went full pitchfork: “Why not cap memory?” demanded nh2, arguing a C program should make it easy. Another camp, led by barrkel, called it a “design flaw,” insisting databases should juggle memory vs disk like a mini operating system—auto-spilling to disk instead of risking total meltdown. Defenders fired back that Postgres didn’t “ignore” the setting, it just doesn’t control everything, and that ops teams should tune parallelism, watch plans, and use system-level limits.

Meanwhile, jokers had a field day: “work_mem? more like work_meme,” “Postgres on bulk season,” and “The OOM killer is the true final boss.” Everyone agrees on one thing: that logging function is the new caped hero.

Key Points

  • A PostgreSQL production cluster with 2 TB RAM was killed by the OS OOM killer due to a single query’s memory usage despite work_mem=2 MB.
  • Reproduction on a separate server showed one backend consuming ~586,713,672 bytes (~557 MB), with large allocations in ExecutorState and HashTableContext.
  • The pg_log_backend_memory_contexts function (introduced in PostgreSQL 14) dumped detailed memory context trees to logs, enabling analysis.
  • work_mem limits memory per hash/sort operation, but PostgreSQL’s memory contexts release memory at the end of the operation or query, not during execution.
  • ExecutorState and HashTableContext retained many allocations (e.g., 524,059 chunks in ExecutorState), allowing memory to accumulate until query completion.

Hottest takes

"Why not? ... software written in C should make that reasonably easy." — nh2
"This is a current design flaw of Postgres" — barrkel
Made with <3 by @siedrix and @shesho from CDMX. Powered by Forge&Hive.