Async Pool — Асинхронный пул

Пул объектов с асинхронной операцией ожидания. Если все инстансы заняты — await пока освободится. Современная замена callback-based пулов.

Проблема

Классический Object Pool: если все инстансы заняты, нужно ждать. На callback'ах — некрасиво. Хочется await pool.acquire.

Решение

  • Хранить очередь ожидающих промисов (queue).
  • acquire возвращает промис: если есть свободные — резолвится сразу, иначе — попадает в queue.
  • release(inst) — сначала проверяет queue: если есть ожидающий — резолвит ему. Иначе — возвращает в free.

Пример в JS

class AsyncPool {
  #factory; #size;
  #instances = ;
  #free = ;
  #queue = ;

  constructor(factory, size) {
    this.#factory = factory;
    this.#size = size;
    for (let i = 0; i < size; i++) {
      const inst = factory;
      this.#instances.push(inst);
      this.#free.push(inst);
    }
  }

  acquire {
    if (this.#free.length) {
      return Promise.resolve(this.#free.pop());
    }
    return new Promise((resolve) => this.#queue.push(resolve));
  }

  release(inst) {
    const waiter = this.#queue.shift();
    if (waiter) {
      waiter(inst);                 // отдаём ожидающему
    } else {
      this.#free.push(inst);        // возвращаем в свободные
    }
  }
}

// использование
const pool = new AsyncPool( => makeConnection, 5);
const conn = await pool.acquire;
try { await conn.query('SELECT 1'); }
finally { pool.release(conn); }

Где используется в JS-экосистеме

  • pg-pool, mysql2.createPool — встроенные async-пулы
  • generic-pool на npm — универсальный async-пул
  • worker_threads pool под нагрузку
  • HTTP-агент с keep-alive — внутренний async-пул соединений
  • p-limit, p-queue на npm — concurrent task pools

Подводные камни

  • Утечки: забыл release — пул иссякнет, acquire зависнет навсегда.
  • Таймаут на acquire — без него можно зависнуть надолго.
  • Backpressure — если producer быстрее consumer'а, queue растёт.
  • finally обязателен для надёжного release.
  • Реинициализация инстанса перед release — иначе state предыдущего вызова попадёт в следующий.

Главные тезисы автора

  • «Async Pool — это пул, который позволяет подождать через какой-нибудь контракт».
  • Современная замена callback — async/await удобнее.
  • acquire возвращает промис — резолвится сразу или попадает в очередь.
  • release достаёт из очереди ожидающих перед возвратом в free.
  • Counter available — оптимизация, чтобы не перебирать всю коллекцию.
  • Динамический рост возможен сверх initial size, но с лимитом памяти.
  • Фабрика в конструкторе — пул сам инстанцирует, удобнее.

🎓 Источники

См. также