Limit on Number of Concurrent Stream Writers?

I’ve noticed some unusual behavior…

There seems to be a limit to the number of concurrent stream writers a script can have and it doesn’t fail gracefully in production (no error, no explanation).

I’m processing files from multiple resources and combining them into a single response:

The actual number of results is 40, but my stream processing in the client side JS is only able to received 21 of these results.

This happens consistently, even when I change the result types, order, etc…

My only conclusion is that I’m hitting some upper bound of some limit in Workers that’s causing the writer to stop accepting the pipeTo requests, the script is hanging and chrome is indicating that the response has not finished.

Extremely curious if anyone can shed some light on this situation…

for (let i = 0, len = results.length; i < len; ++i) {
		key = `https://${bucket}.s3.amazonaws.com/${results[i].data}`
		//cache or not?
		let res = await cache.match(key)
		let bodies = []
		if (res) {
			bodies = [res.body]
		} else {
			// console.log('request', i, Date.now() - t)
			res = await global.AWS.fetch(key)
			// console.log('response', i, Date.now() - t)
			bodies = res.body.tee()
			res = new Response(bodies[1], res)
			res.headers.append('Cache-Control', 'max-age=' + global.DATA_TIMEOUT)
			e.waitUntil(cache.put(key, res.clone()))
		}
		// const len = parseInt(res.headers.get('content-length'))
		// streamed += len
		// console.log('streamed', streamed)
		length = res.headers.get('content-length').padStart(PADLEN, '0')
		writer = writable.getWriter()
		await writer.write(encoder.encode(length))
		writer.releaseLock()
		await bodies[0].pipeTo(writable, {preventClose: (i+1) !== results.length})
	}

:wave: @mattdlockyer,

Your current method used in the wrong hands seems like it would be an excellent DDoS attack vector. I’m sure there is some limit to the maximum number of concurrent streams for precisely that reason. Assembly of a webpage using subatomic particles in an argon matrix seems like an interesting approach.

— OG

Was there an answer to my question or any helpful information in that response? I may have missed it.

I don’t believe I’m doing anything out of the ordinary, composing a response from multiple inputs seems like a strong use case for workers. In fact I modeled this after an example from @harris on GitHub.

Also if you notice, the loop is bounded to the length of my result set and each fetch is done sequentially waiting for the previous response. Not sure how a DDOS vector is remotely relevant here.

Are those subrequests getting redirected at all? Maybe you’re hitting the subrequest limit of 50?

Good question… The loop is doing < 50 but it seems to only ever complete about around ~ 20 - 22, never more than that.

And I know there’s a concurrent subrequest limit of 6, but the way my loop is handling the requests synchronously, that shouldn’t be a problem.

@chasers thanks for the comment. I went back and looking at my code you can see I’m making 2 subrequests:

  1. cache.match
  2. fetch

WOW so I didn’t think that a cache.match counted as a subrequest but re-read the docs and realized it does…

So this would explain why in a loop of < 50 it would only write back about 22 of these responses.

Basically, the loop runs and once it hits 2 x 25 the script stops abruptly and the transfer is over…

Would be amazing to see this increased, but for now I’ll just let the client make subsequent requests.

Thanks again @chasers

1 Like