fix: drop bus subscribers that stopped reading - #6157
Open
diyorbekrustamjonov wants to merge 1 commit into
Open
Conversation
|
|
A `pm2 logs` or `pm2 monit` client that stops reading pub.sock (suspended with Ctrl+Z, dead ssh session) fills the kernel socket buffer, after which PubSocket.send queues every log line of every app in the daemon memory without any limit. The God Daemon then dies with "Reached heap limit" and its fork children are left behind as orphans. PubSocket gets a `max pending bytes` setting: once a peer has more than that waiting in its write queue it is destroyed (it may reconnect) and a `slow subscriber` event is emitted. The daemon enables it with 16 MB by default, configurable with PM2_PUB_MAX_PENDING_BYTES (0 disables), and logs the drop in pm2.log. Refs Unitech#5145 Unitech#6113 Unitech#5802 Unitech#5917 Unitech#4737 Unitech#4647 Unitech#3049
diyorbekrustamjonov
force-pushed
the
fix/pub-slow-subscriber
branch
from
September 9, 2026 16:04
cfb3f34 to
2b96255
Compare
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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PM2_PUB_MAX_PENDING_BYTESis described belowWhat happens
Any client subscribed to
pub.sockthat stops reading makes the God Daemon grow without limit until it dies withFATAL ERROR: Reached heap limitand every frame of the stack in[PM2 ...: God Daemon]. The easiest way to get such a client ispm2 logs <id>suspended with Ctrl+Z instead of Ctrl+C, or left in an ssh session that died. After the crash the fork children survive as orphans, andpm2 resurrectstarts a second copy of each of them.On a production host with 63 apps (pm2 5.4.2, Node 20) two suspended
pm2 logs 1clients made the daemon grow by about 170 MB per minute and hit the 4 GB heap limit in 14 minutes.ss -xanshowed the twopub.sockconnections with Send-Q stuck at 215040 bytes (the kernel buffer), the clients were in stateT.Root cause
modules/pm2-axon/lib/sockets/pub.js:writablestays true while the peer is alive, so once the kernel buffer is full every message is queued insock.writableLengthforever.pm2 logs <id>receives the events of all apps (the filter runs client side), so each stuck client costs the daemon the full log stream of the host.Fix
PubSocketgets amax pending bytessetting (defaultInfinity, no behaviour change for the library itself). Insendandsendv2a peer with more than that pending is destroyed and aslow subscriberevent is emitted with the socket and the pending byte count. The socket is destroyed before the event is emitted on purpose: the daemon logs through the bus, so a listener that logs re-enterssendand must find the peer not writable.cst.PUB_MAX_PENDING_BYTES, 16 MB by default,PM2_PUB_MAX_PENDING_BYTESoverrides it,0disables. A dropped client shows up inpm2.logasBus subscriber is not reading (N bytes pending), dropping it. Sub sockets reconnect on their own, so apm2 logsthat is resumed just continues.modules/pm2-axon/test/test.pub.slow-subscriber.js: a paused raw subscriber is dropped, a reading one is kept.npm run test:axonpasses.Reproduction and measurements
Linux container, Node 22, one app writing ~3.3 MB/s of JSON lines to stdout, daemon started with
PM2_NODE_OPTIONS=--max-old-space-size=200,pm2 logs noisystarted and thenkill -STOP:ss -xanSend-Q of the pub.sock connectionBus subscriber is not reading (16783360 bytes pending), dropping itWith 30 apps at 200 lines/s each the unpatched daemon reaches the 200 MB heap limit in about 5 minutes and dies with the same stack trace as in the issues below.
Related issues
psoutput posted by the reporter shows the daemon at 2.6 GB and fourpm2 log 1/pm2 logsprocesses in stateT. That is exactly this scenario. The thread blames pidusage, jemalloc and pmx, none of which explain the stopped clients.[PM2 ...: God Daemon]stack and no root cause found. They are consistent with a stuck bus subscriber (anypm2 logs,pm2 monit, a module or an agent usinglaunchBusthat stopped draining), but the reports do not contain enough data to be sure, so I list them as likely rather than fixed.Utility.startLoggingstreams are written without checking backpressure too.