Optimization guide · uploads
Как оптимизировать большие upload в Node.js?
Короткий ответ
Не лечите большой upload увеличением max body size. Оставьте prefetch выключенным, создайте ctx.bodyStream() до первого await и передайте его в downstream pipeline. Ограничьте размер одного потока через maxStreamBodySize, число одновременных upload и бюджет хранилища. swm-uws 0.7.3 останавливает socket reads при pause(), поэтому медленный consumer создаёт backpressure вместо накопления всего тела в памяти.
- Readable
- поток body
- 413
- превышение размера
- pause/resume
- flow control
Границы ответа
Страница фиксирует контракт @swarmmachina/swm-core 5.1.2 и @swarmmachina/swm-uws 0.7.3 для Node.js 22/24. Она не измеряет throughput конкретного диска, object storage или сети и не обещает общий лимит памяти процесса.
Контракт потокового upload
| Уровень | Явная граница | Проверка перед запуском |
|---|---|---|
| Reader | Создайте ctx.bodyStream() синхронно до первого await и не используйте другой body reader или prefetch для того же запроса. | Тестируйте async before hook, reader conflict и ранний отказ авторизации. |
| Размер | http.maxStreamBodySize задаёт server ceiling; route и per-call maxSize могут только сузить его. | Проверьте Content-Length выше лимита и chunked upload, который пересекает лимит во время чтения. |
| Память | Streaming не списывается из maxBodyBudget, но queue downstream, удержанные chunks и параллельные upload всё равно потребляют память. | Запишите RSS, длину очереди и число active upload в отдельном нагрузочном прогоне. |
| Backpressure | Когда consumer не готов, native pause останавливает дальнейшие socket reads; уже принятые chunks из parser buffer всё ещё могут дойти до callback. | Замедлите запись, убедитесь в pause/resume и проверьте, что процесс не буферизует весь файл. |
| Отмена | Если handler перестаёт читать body, вызовите stream.destroy() и завершите ответ по своей policy. | Проверьте отказ авторизации, client FIN и повторное использование соединения после drain. |
| Admission | Лимит одного файла не ограничивает сумму одновременно принимаемых файлов: нужен отдельный concurrency limit и бюджет хранилища. | Запустите больше concurrent upload, чем разрешает policy, и подтвердите контролируемый отказ. |
Выбор по профилю upload
- 01
Небольшой JSON или form payload
Оставьте bounded body readerbody(), json() или prefetch проще, если материализация уже входит в известный лимит и нужна приложению целиком.
- 02
Файл передаётся в disk или object storage
Используйте ctx.bodyStream()Pipeline получает chunks по мере приёма и не требует удерживать полный файл в JS memory.
- 03
Downstream временно медленнее сети
Полагайтесь на pause/resume, но измеряйтеBackpressure сдерживает socket reads, но не задаёт admission control и не ограничивает память всех остальных частей процесса.
Что не следует обещать
- Поток не даёт бесконечно малое потребление памяти: parser buffer, chunks у consumer и очереди downstream остаются частью бюджета.
- maxStreamBodySize ограничивает один request, а не сумму одновременных upload и не лимит object storage.
- Нельзя смешивать streaming reader с body(), buffer(), text(), json() или prefetch в одном запросе.
- Новая страница описывает контракт, а не универсальный benchmark. Производительность проверяйте на своём размере файлов, диске, сети и concurrency.
Первичные источники
Утверждения о reader, лимитах и pause/resume привязаны к опубликованным версиям пакетов.