Description
Hello!
When switching from renderToString
to renderToPipeableStream
, I run load tests on the application, and found a decrease in server throughput, from 50 to 15 RPS, and an increase in response timings.
When profiling the CPU, I see a large overhead on the internal work of the stream, specifically the methods Writable.write
and Writable.uncork
.
All these method calls together take more than twice as much CPU time (about 50-60ms) as rendering my test page (about 15-20ms)
Also, I don't want to give the HTML to the client in the stream, this approach has some disadvantages.
So I have to buffer the data, and it slows down the application a bit more.
CPU profiler in production mode:
CPU profiler in development mode:
My custom Writable stream with buffering:
class HtmlWritable extends Writable {
chunks = [];
html = '';
getHtml() {
return this.html;
}
_write(chunk, encoding, callback) {
this.chunks.push(chunk);
callback();
}
_final(callback) {
this.html = Buffer.concat(this.chunks).toString();
callback();
}
}
And rendering flow:
import { renderToPipeableStream } from 'react-dom/server';
new Promise((resolve, reject) => {
const htmlWritable = new HtmlWritable();
const { pipe, abort } = renderToPipeableStream(renderResult, {
onAllReady() {
pipe(htmlWritable);
},
onError(error) {
reject(error);
},
});
htmlWritable.on('finish', () => {
resolve(htmlWritable.getHtml());
});
});