You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
From what i've found the threshold seems to be around 16Kb to get reliable streaming, but even that seems to be pretty inconsistent.
Calling responseStream.cork() before write and responseStream.uncork() after seems to help a little bit as well.
The things i've noticed is that even for the same data sent, for small chunks, sometimes the stream is buffering and no data is sent, and sometimes it just works well. I assume it might be when the data are close to this highWaterMark or the receiving server are not ready (but this doesn't seem to happen for big chunks)
Potentially the issue might be on the other hand of the stream, they create a request to an external server
and it might be on this server that the buffering is happening. It might also explain why the highWaterMark has no effect locally even though it seems to be applied.
I’ve delved into some experimentation with this and i found some very interesting things.
The responseStream is only a restricted version of an http.request, and streaming can be achieved without awslambda.streamifyResponse by creating the request ourselves (This is what i've done to eliminate some potential issues)
Moreover, it seems that neither pipe nor pipeline are affected by the minimum size constraint. The example lambda below works fine even with small chunks of data. I think there is still a minimum size but it's far less than with write
Last things i've discovered is that using awslambda.HttpResponseStream.from can also trigger some other buffering issues that can cause the stream to wait for the end. This doesn’t occur consistently, only about 15-20% of the time.
I've found a workaround for this, if you let enough time (roughly 25ms has proven effective most of the time, but still not 100%) after the last write from awslambda.HttpResponseStream.from (the req.write(new Uint8Array(8))) and your first write, the stream seems to work fine.
My guess as to why this happen is that if your first write and the req.write(new Uint8Array(8)) happens to end up on the same chunk, then the underlying implementation think that it has not received the end marker and then wait for the full response before returning anything.
It's possible that the underlying implementation expect a chunk to end with new Uint8Array(8) to signify the headers’ termination and thus the start of the stream.
For those interested here is the lambda i used
functionsleep(ms){returnnewPromise(resolve=>setTimeout(resolve,ms))}import{request}from"http"import{setDefaultHighWaterMark,Readable}from"stream"// Changing this doesn't seem to have any effectsetDefaultHighWaterMark(false,16384)consthandler=async(event,context)=>{constreq=request({host: "127.0.0.1",port: 9001,path: `/2018-06-01/runtime/invocation/${context.awsRequestId}/response`,method: 'POST',headers: {"Lambda-Runtime-Function-Response-Mode": "streaming","transfer-encoding": "chunked","Content-Type": "application/vnd.awslambda.http-integration-response"},},res=>{varresult=''res.on('data',function(chunk){result+=chunk;});res.on('end',function(){console.log(result);});})let_resolveconstpromise=newPromise((resolve)=>{_resolve=resolve})constsize=64console.log(req.writableHighWatermark)// This is equivalent to running awslambda.HttpResponseStream.fromreq.write(JSON.stringify({statusCode: 200,cookies: [],headers: {"content-type": "text/html","x-test": "1"}}))req.write(newUint8Array(8))awaitsleep(10)asyncfunction*generate(){yield"<html>"awaitsleep(1)yield"<p>initial </p>"awaitsleep(1500)// yield "<html>"for(leti=0;i<10;i++){yield`<p>${i}</p>`awaitsleep(200)}yield"</html>"}constdataStream=Readable.from(generate())dataStream.pipe(req)req.on('end',()=>_resolve())awaitpromisereturn{}};export{handler};
Reacted by Vinicius Lourenço, Frank and Marcos Viana
From my guess, the gateway between awslambda.HttpResponseStream and the actual user request is the root cause of all these buffer issues.
Anything writes to awslambda.HttpResponseStream are first streamed to the gateway. The gateway decides when to send its internal buffer to the user. For example, the gateway might flush its buffer every time it receives new data after some idle time.
Activity
conico974 commentedon Feb 15, 2024
From what i've found the threshold seems to be around
16Kbto get reliable streaming, but even that seems to be pretty inconsistent.Calling
responseStream.cork()before write andresponseStream.uncork()after seems to help a little bit as well.The things i've noticed is that even for the same data sent, for small chunks, sometimes the stream is buffering and no data is sent, and sometimes it just works well. I assume it might be when the data are close to this
highWaterMarkor the receiving server are not ready (but this doesn't seem to happen for big chunks)Potentially the issue might be on the other hand of the stream, they create a request to an external server
aws-lambda-nodejs-runtime-interface-client/src/ResponseStream.js
Line 160 in eeac0b3
highWaterMarkhas no effect locally even though it seems to be applied.stackia commentedon Feb 22, 2024
I found the first 2 writes are always bufferer:
Screen.Recording.2024-02-22.at.2.48.25.PM.mov
stackia commentedon Feb 22, 2024
Found a tricky workaround
It looks like every
writewill flush the previous buffer (starting from the secondwrite).conico974 commentedon Mar 12, 2024
I’ve delved into some experimentation with this and i found some very interesting things.
The responseStream is only a restricted version of an
http.request, and streaming can be achieved withoutawslambda.streamifyResponseby creating the request ourselves (This is what i've done to eliminate some potential issues)Moreover, it seems that neither
pipenorpipelineare affected by the minimum size constraint. The example lambda below works fine even with small chunks of data. I think there is still a minimum size but it's far less than withwriteLast things i've discovered is that using
awslambda.HttpResponseStream.fromcan also trigger some other buffering issues that can cause the stream to wait for the end. This doesn’t occur consistently, only about 15-20% of the time.I've found a workaround for this, if you let enough time (roughly 25ms has proven effective most of the time, but still not 100%) after the last write from
awslambda.HttpResponseStream.from(thereq.write(new Uint8Array(8))) and your first write, the stream seems to work fine.My guess as to why this happen is that if your first write and the
req.write(new Uint8Array(8))happens to end up on the same chunk, then the underlying implementation think that it has not received the end marker and then wait for the full response before returning anything.It's possible that the underlying implementation expect a chunk to end with
new Uint8Array(8)to signify the headers’ termination and thus the start of the stream.For those interested here is the lambda i used
improved streaming
stackia commentedon Mar 13, 2024
From my guess, the gateway between
awslambda.HttpResponseStreamand the actual user request is the root cause of all these buffer issues.Anything writes to
awslambda.HttpResponseStreamare first streamed to the gateway. The gateway decides when to send its internal buffer to the user. For example, the gateway might flush its buffer every time it receives new data after some idle time.OpenNext V3 (#402)
OpenNext V3 (#402)
OpenNext V3 (#402)