Skip to content

Instantly share code, notes, and snippets.

@jeromew
Last active January 2, 2016 18:09
Show Gist options
  • Save jeromew/8341828 to your computer and use it in GitHub Desktop.
Save jeromew/8341828 to your computer and use it in GitHub Desktop.
koa with a delay middleware : how a slow middleware impacts streaming

Currently in koa (09/01/2014), streaming data to the network begins only after the middleware stack has been fully traversed.

If the flow through the middleware stack takes N seconds then a stream created on this.body may buffer data in memory for N seconds.

Since any call to "yield myPromise" pauses the flow until myPromise resolves, streams may start buffering in memory until all middleware promises are resolved.

It is probably better to start streaming data only late in the middleware stack in order to avoid slower promises (database access for instance).

In the following flow :

=> [start streaming] => ... => [delay] => ...
<= ................. <= ... <= ....... <= ...

a part of the streamed data may be buffered in memory for a duration of at least [delay]. Depending on the type of streams this can have an impact on memory.

extract from node documentation:

Class: stream.Writable
writable.write(chunk, [encoding], [callback])
Returns: Boolean True if the data was handled completely.

This return value is strictly advisory. You MAY continue to write, even if it returns false.
However, writes will be buffered in memory, so it is best not to do this excessively.
Instead, wait for the drain event before writing more data.

=> writers may buffer data to memory


Class: stream.Readable
Readable streams have two "modes": a flowing mode and a non-flowing mode.
When in flowing mode, data is read from the underlying system and provided to your program as fast as possible.
In non-flowing mode, you must explicitly call stream.read() to get chunks of data out.

=> if a readable stream enters flowing mode it may buffer data to memory up to the highWaterMark = 16kb default per request

=> if a user does something that puts createReadStream into flowing mode,
it even bigger because of https://github.com/joyent/node/blob/master/lib/fs.js#L1421
default is then 64kb

var wait = require('co-wait');
var koa = require('koa');
var PassThrough = require('stream').PassThrough;
/**
* There is a five second delay here before something is sent to the network
*/
var app = koa();
app.use(function*(next) {
this.type = 'text';
var stream = this.body = new PassThrough();
stream.on('error', this.onerror);
setTimeout(function() {
stream.write('hello ');
},1);
yield next;
});
app.use(function*(next) {
yield wait(5000);
yield next;
});
app.use(function*(next) {
var stream = this.body;
setTimeout(function() {
stream.write('world');
},2);
setTimeout(function() {
stream.end();
},3);
yield next;
});
app.listen(3000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment