title: Forcing ~synchronous Node.js IPC
body: <p>I have a Node server that creates a child process with <code>fork()</code> using IPC. At some point the child sends results back to the parent at about 10Hz as part of a long-running task. When the payload passed to <code>process.send()</code> is small all works well: every message I send is received ~immediately and processed by the parent.</p>
  
  <p>However, when the payload is 'large'—I haven't determined the exact size limit—instead of being immediately received by the parent all payloads are first sent, and only once the child is done its long-running task does the parent receive and process the messages.</p>
  
  <p>tl;dr visual:</p>
  
  <p><strong>Good</strong> (happens with small payload):</p>
  
  <pre><code>child:  send()
  parent: receive()
  child:  send()
  parent: receive()
  child:  send()
  parent: receive()
  ...
  </code></pre>
  
  <p><strong>Bad</strong> (happens with big payload):</p>
  
  <pre><code>child:  send()
  child:  send()
  child:  send()
  (repeat many times over many seconds)
  ...
  parent: receive()
  parent: receive()
  parent: receive()
  parent: receive()
  ...
  </code></pre>
  
  <ol>
  <li>Is this a bug? (Edit: behavior only occurs on OS X, not Windows or Linux)</li>
  <li>Is there any way to avoid this, other than trying to keep my IPC payload small?</li>
  </ol>
  
  <hr>
  
  <p><strong>Edit 2</strong>: the sample code below uses both time and iteration counter to select when to send an update. (In my actual code it's also possible to send an update after <em>n</em> iterations, or after the loop achieves certain results.) As such a rewrite of the code to use <code>setInterval</code>/<code>setTimeout</code> instead of a loop is a last resort for me, as it requires me to remove features.</p>
  
  <p><strong>Edit</strong>: Here is test code that reproduces the problem. However, it only reproduces on OS X, not on Windows or Linux:</p>
  
  <p><strong>server.js</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>const opts = {stdio:['inherit', 'inherit', 'inherit', 'ipc']};
  const child = require('child_process').fork('worker.js', [], opts);
  
  child.on('message', msg =&gt; console.log(`parent: receive() ${msg.data.length} bytes`, Date.now()));
  
  require('http').createServer((req, res) =&gt; {
     console.log(req.url);
     const match = /\d+/.exec(req.url);
     if (match) {
        child.send(match[0]*1);
        res.writeHead(200, {'Content-Type':'text/plain'});
        res.end(`Sending packets of size ${match[0]}`);
     } else {
        res.writeHead(404, {'Content-Type':'text/plain'});
        res.end('what?');
     }
  }).listen(8080);
  </code></pre>
  
  <p><strong>worker.js</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>if (process.send) process.on('message', msg =&gt; run(msg));
  
  function run(messageSize) {
     const msg = new Array(messageSize+1).join('x');
     let lastUpdate = Date.now();
     for (let i=0; i&lt;1e7; ++i) {
        const now = Date.now();
        if ((now-lastUpdate)&gt;200 || i%5000==0) {
           console.log(`worker: send()  &gt; ${messageSize} bytes`, now);
           process.send({action:'update', data:msg});
           lastUpdate = Date.now();
        }
        Math.sqrt(Math.random());
     }
     console.log('worker done');
  }
  </code></pre>
  
  <p>About around 8k the problem happens. For example, when querying <code>http://localhost:8080/15</code> vs <code>http://localhost:8080/123456</code></p>
  
  <pre><code>/15
  worker: send()  &gt; 15 bytes 1571324249029
  parent: receive() 15 bytes 1571324249034
  worker: send()  &gt; 15 bytes 1571324249235
  parent: receive() 15 bytes 1571324249235
  worker: send()  &gt; 15 bytes 1571324249436
  parent: receive() 15 bytes 1571324249436
  worker done
  /123456
  worker: send()  &gt; 123456 bytes 1571324276973
  worker: send()  &gt; 123456 bytes 1571324277174
  worker: send()  &gt; 123456 bytes 1571324277375
  child done
  parent: receive() 123456 bytes 1571324277391
  parent: receive() 123456 bytes 1571324277391
  parent: receive() 123456 bytes 1571324277393
  </code></pre>
  
  <p>Experienced on both Node v12.7 and v12.12.</p>