Async Queue — ограничение конкурентности

Очередь на входе сервера: лимит одновременных задач + лимит ожидающих. Парадокс — ограничение ускоряет общую latency.

Что это

Под нагрузкой Node «съедает всё что дают» и впадает в degradation: GC давится, event loop задыхается, latency растёт. Решение — поставить async queue на вход и ограничить:

  • concurrency — сколько задач одновременно в обработке;
  • size — сколько задач может ждать в очереди;
  • timeout — максимальное время в очереди.

Три ресурса, которые истощаются

  1. CPU — слишком много синхронной работы → event loop забит.
  2. Memory — GC начинает тормозить (не сама нехватка, а GC).
  3. Open handles — сокеты, file descriptors, DB connections.

API / Пример

class AsyncQueue {
  constructor({ concurrency, size }) {
    this.concurrency = concurrency;
    this.size = size;
    this.running = 0;
    this.waiting = ;
  }

  add(task) {
    return new Promise((resolve, reject) => {
      if (this.waiting.length >= this.size) return reject(new Error('overflow'));
      this.waiting.push({ task, resolve, reject });
      this.next();
    });
  }

  next {
    if (this.running >= this.concurrency || !this.waiting.length) return;
    const { task, resolve, reject } = this.waiting.shift();
    this.running++;
    task.then(resolve, reject).finally(() => { this.running--; this.next(); });
  }
}

Производительность / Подводные камни

  • Ограничение ускоряет latency — звучит парадоксально, но без очереди event loop захлёбывается, и каждый запрос длится дольше.
  • GC тормозит, а не нехватка памяти — на большом heap V8 пытается пройти всё, что замедляется в разы.
  • Performance Hooks для авто-тюнинга — измеряют latency и подстраивают concurrency.
  • Не делать control flow через EventEmitter — порядок и ошибки трудно отслеживать; промисы лучше.
  • await блокирует функцию, не event loop — другие async-задачи продолжают исполняться параллельно.
  • TechEmpower бенчмарки — JavaScript обгоняет многие быстрые языки на HTTP-микробенчах.

🎓 Источники

  • 🎓 Node.js и Backend Семинар #1 — асинхронная очередь, бенчмарк фреймворков · 2021-12-18

    • Тезисы: три ресурса (CPU/Memory/Handles); GC — главный тормоз, не нехватка памяти; ограничение конкурентности улучшает latency; Performance Hooks для авто-тюнинга; await блокирует функцию, не event loop; пассивный WebSocket съедает десятки КБ; не отключать неактивные сокеты — лучше ограничить новые.
    • Цитата: «Ограничение конкурентности улучшает latency — звучит парадоксально, но без очереди event loop захлёбывается.»
  • 🎓 Concurrent Asynchronous Queue in JavaScript · 2019-05-07

    • Тезисы: базовая структура очереди; обработка ошибок; cancel/timeout.

См. также