Skip to main content

Як виникає backpressure?

Коротке нагадування визначення

Backpressure (зворотний тиск) - це ситуація, коли споживач (Writable) не встигає обробляти дані з тією швидкістю, з якою виробник (Readable) їх генерує.

Ключова причина виникнення

Невідповідність швидкостей потоків:

ПотікЩо робитьМожлива проблема
Readableгенерує дані (наприклад, читає файл, мережу, базу)надто швидко
Writableприймає й записує дані (наприклад, пише у файл, HTTP, базу)надто повільно

Коли Writable не встигає, його внутрішній буфер заповнюється. І тоді Node.js зупиняє читання з джерела, щоб не "залити" систему даними.

Як це виглядає "усередині"

Кожен Writable Stream має внутрішній буфер (highWaterMark).

Приклад:

javascript
const stream = fs.createWriteStream('out.txt', { highWaterMark: 16 * 1024 });

Тут highWaterMark = 16 KB - ліміт, скільки даних потік може тримати в буфері.

Коли ти викликаєш:

javascript
const ok = stream.write(chunk);

то:

  • якщо буфер не переповнений → ok = true
  • якщо буфер переповненийok = false

А це і є момент виникнення backpressure. Це сигнал: «стоп, не пиши більше, поки я не звільнюся».

У який момент це відбувається

Схематично:

javascript
Readable (виробляє 100 КБ/сек) Writable (встигає записувати 30 КБ/сек) Буфер Writable переповнюється → backpressure

У цей момент:

  1. .write() повертає false;
  2. Node.js призупиняє читання (readable.pause());
  3. Коли Writable скидає буфер на диск → подія drain;
  4. Node.js відновлює читання (readable.resume()).

Приклад, де backpressure виникає явно

javascript
const fs = require('fs'); const readable = fs.createReadStream('big.txt'); const writable = fs.createWriteStream('copy.txt', { highWaterMark: 1024 }); // буфер 1 KB readable.on('data', (chunk) => { const ok = writable.write(chunk); console.log('Пишемо...', ok ? 'ОК' : 'Буфер переповнений!'); if (!ok) { readable.pause(); // призупиняємо читання writable.once('drain', () => readable.resume()); } }); readable.on('end', () => writable.end());

Тут writable.write() повертає false, коли потік не встигає - це і є момент виникнення backpressure.

У ланцюжках потоків (pipe() і pipeline())

Якщо ти використовуєш:

javascript
readable.pipe(transform).pipe(writable);

то Node.js робить те саме автоматично:

  • якщо writable повертає false → читання зупиняється;
  • щойно drain - читання відновлюється.

Тобто backpressure вбудований у систему pipe() і не потребує ручного контролю.

Фактори, що посилюють backpressure

ПричинаПриклад
Повільний запис на дискHDD, мережеві сховища
Стиснення чи шифрування в Transform-потокахzlib, crypto
Повільний відгук сервера під час HTTP-записуres.write() повільніший за читання
Великий розмір чанків чи малий highWaterMarkнадто швидке накопичення буфера
Складна синхронна логіка в _write()блокує event loop

Візуалізація процесу

javascript
┌────────────────────────────────┐ Readable Stream └──────────────┬─────────────────┘ Буфер Writable (16 KB) ┌──────────────────────────────┐ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ ← заповнений └──────────────────────────────┘ write()false (Backpressure!) ↓ чекати drain()resume()

Коротко й просто

Backpressure виникає тоді, коли Writable Stream не встигає обробляти вхідні дані, і повідомляє Readable Stream, щоб той призупинився, поки буфер не звільниться.

Як із цим боротися

  • Використовувати pipe() чи pipeline() - вони автоматично керують backpressure
  • Налаштувати highWaterMark під свою задачу
  • Робити Transform-потоки ефективними й неблокуючими
  • Уникати синхронних операцій усередині потоків

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.