Функция Firebase Node.js преобразует поток

Я создаю HTTP-функцию Firebase, которая выполняет запрос BigQuery и возвращает измененную версию результатов запроса. Запрос потенциально возвращает миллионы строк, поэтому я не могу сохранить весь результат запроса в памяти, прежде чем ответить HTTP-клиенту. Я пытаюсь использовать потоки Node.js, и, поскольку мне нужно изменить результаты перед их отправкой клиенту, я пытаюсь использовать поток преобразования. Однако, когда я пытаюсь направить поток запроса через поток преобразования, функция Firebase падает со следующим сообщением об ошибке: finished with status: 'response error'.

Мой минимальный воспроизводимый пример выглядит следующим образом. Я использую буфер, потому что не хочу обрабатывать по одной строке (фрагменту) за раз, так как мне нужно делать асинхронные сетевые вызовы для преобразования данных.

return new Promise((resolve, reject) => {
    const buffer = new Array(5000)
    let bufferIndex = 0
    const [job] = await bigQuery.createQueryJob(options)
    const bqStream = job.getQueryResultsStream()

    const transformer = new Transform({
        writableObjectMode: true,
        readableObjectMode: false,
        transform(chunk, enc, callback) {
            buffer[bufferIndex] = chunk
            if (bufferIndex < buffer.length - 1) {
                bufferIndex++
            }
            else {
                this.push(JSON.stringify(buffer).slice(1, -1)) // Transformation should happen here.
                bufferIndex = 0
            }
            callback()
        },
        flush(callback) {
            if (bufferIndex > 0) {
                this.push(JSON.stringify(buffer.slice(0, bufferIndex)).slice(1, -1))
            }
            this.push("]")
            callback()
        },
    })

    bqStream
        .pipe(transform)
        .pipe(response)

    bqStream.on("end", () => {
        resolve()
    })
}

person Robin    schedule 07.10.2020    source источник


Ответы (1)


Я не могу сохранить весь результат запроса в памяти перед ответом HTTP-клиенту

К сожалению, при использовании Cloud Functions именно это и должно происходить.

Существует задокументированное ограничение в 10 МБ полезной нагрузки ответа, которое эффективно хранится в памяти как ваш код продолжает писать ответ. Потоковая передача запросов и ответов не поддерживается.

Один из вариантов — записать свой ответ на объект в облачном хранилище, а затем отправить ссылку или ссылку на этот файл клиенту, чтобы он мог полностью прочитать ответ от этого объекта.

Если вам нужно отправить большой потоковый ответ, облачные функции — не лучший выбор. Как и Cloud Run, который также ограничен. Вам нужно будет изучить другие решения, обеспечивающие прямой доступ к сокетам, такие как Compute Engine.

person Doug Stevenson    schedule 07.10.2020
comment
О, это объясняет, почему он отлично работал при локальной эмуляции функции, но не после ее развертывания. Я рассмотрю возможность потоковой передачи в облачное хранилище и перенаправления на этот файл. - person Robin; 08.10.2020
comment
Если этот ответ помог, пожалуйста, проголосуйте за него / примите его. Это помогает сообществу определить принятые ответы. - person Joss Baron; 08.10.2020