Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wrangler.getBindingsProxy will freeze the server when reading two bucket streams in parallel #5360

Closed
pi0 opened this issue Mar 22, 2024 · 0 comments · Fixed by #5491
Closed
Assignees

Comments

@pi0
Copy link

pi0 commented Mar 22, 2024

(Context: This issue was originally reported by @atinux for Nuxt Hub, we have later narrowed down this issue to nitro-cf-dev and then finally to this minimal reproduction.)

Description

Using wrangler.getBindingsProxy to read R2 bucket data, the readable stream will be stuck on big sizes (1MB+) if used with concurrency of 2+ promises. The second stream will never end and the Node.js process is stuck. This issue does not happen in production.

Minimal reproduction

I have made a repo with two simple (plain script and plain node server) examples that reproduce this. https://github.com/pi0/cf-proxy-repro

(tested on wrangler@3.37.0)

r2_buckets = [
  { binding = "BLOB", bucket_name = "default" },
]
import { getPlatformProxy } from "wrangler" // tested on 3.37.0

const proxy = await getPlatformProxy({})
const { BLOB: bucket } = proxy.env

console.log('Fetching cat image...')
const imgReadableStream = await fetch('https://images.unsplash.com/photo-1579168765467-3b235f938439?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=yoo-hoo-E3LcqpQxtTU-unsplash.jpg').then(r => r.body)

console.log('Converting image to buffer...')
const buff = await readableStreamToBuff(imgReadableStream)
console.log('Buffer length (response):', buff.length)

console.log('Putting cats to the bucket...')
await bucket.put('cat1.jpg', buff.buffer)
await bucket.put('cat2.jpg', buff.buffer)

console.log('Reading from bucket...')

// This will make process hang
const [cat1, cat2] = await Promise.all([
  bucket.get('cat1.jpg').then(obj => readableStreamToBuff(obj.body)),
  bucket.get('cat2.jpg').then(obj => readableStreamToBuff(obj.body)),
])

// This works
// const cat1 = await bucket.get('cat1.jpg').then(obj => readableStreamToBuff(obj.body))
// const cat2 = await bucket.get('cat2.jpg').then(obj => readableStreamToBuff(obj.body))

console.log('Buffer length (bucket):', [cat1.length, cat2.length])

await proxy.dispose()

async function readableStreamToBuff(stream) {
  console.log('reading stream to buffer... ')
  const chunks = [];
  await stream.pipeTo(
    new WritableStream({
      write(chunk) {
        process.stdout.write(` +${chunk.length}`)
        chunks.push(chunk);
      },
      close() {
        console.log('stream close')
      }
    }),
  )
  console.log('stream read!')
  return Buffer.concat(chunks);
}

Used with parallel Promise.all: (process hangs)

image

Used without parallel promises: (works)

image

Possible workaround

Using a mutex to run only one at a time. Used for nitro-cloudflare-dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants