Reconnecting to a PUB socket causes memory leakage


Code to reproduce:

It's Ruby but it could be translated to C by someone competent in that language. Here are the basic steps to repro:

1. Create a Publisher in a thread. Let it publish a single byte every 25ms.

2. Create a set of Subscribers (32 in the example). Each thread creates SUB socket that subscribes to everything. Every 100ms the SUB socket is closed and a new one is created and connected. Any message received is just closed. Note that the subscriber(s) should easily be able to keep pace with a publisher sending only a single byte every 25ms; that is, no real queueing is going to happen here.

3. Put a FORWARDER device between the Publisher and Subscribers. I recommend putting this in a separate program so you can watch its memory footprint continually grow while the other program containing the publisher & subscriber levels off and stops growing.

This bug exists for both :ipc and :tcp transport. I did not test :inproc transport.

The resident size (rsize) of the forwarder device program will grow at a rate of about 20MB per minute. You can tell this isn't a message queue buffering issue due to
a) HWM set to 1 for all sockets,
b) LINGER set to 0 for all sockets,
c) killing the publisher/subscriber program does not cause the forwarder program heap to shrink,
d) restarting the publisher/subscriber program, the heap will start growing again immediately (long before there would be time for the large heap to get filled again).

My use-case is as follows. I have a trade backtesting system that activates a new "strategy" when it receives a buy/sell signal. Part of the strategy is a small component that contains a single SUB socket and subscribes to all trades. The backtester runs much faster than real-time (e.g. 1 second of wall clock time is 1 hour inside the system). So a short-lived strategy that only survives a few minutes within the backtester may shutdown the trade subscriber within 50-1000 milliseconds of connecting. This can happen very often and very rapidly. And a single PUB socket could not be shared amongst several strategies since their subscriptions are all unique.