Перейти к содержимому

Управление ресурсами

Queuety разделяет управление ресурсами на четыре основных уровня и дополнительную настройку размера пула.

Первый уровень описывает стоимость одной единицы работы. Второй ограничивает общую нагрузку, которую одна очередь или группа поставщиков может нести одновременно. Третий определяет, сколько ресурсов может потребить один запуск рабочего процесса. Четвертый решает, должен ли обработчик немедленно запустить следующее полученное задание или отложить его, пока у текущего процесса и контейнера не будет достаточно запаса ресурсов.

Эти уровни работают вместе. Обработчик может указать, что он относится к общей пулу с высокой стоимостью, очередь может резервировать лишь определенный объем взвешенной работы для одного поставщика, рабочий процесс может ограничить объем такой работы, которую он готов создать, а обработчик все равно может отказаться запускать ее, если текущий процесс или контейнер уже близок к своим пределам.

Опишите стоимость одной единицы работы

Задания, шаги рабочего процесса и действия при входе в состояние могут публиковать одни и те же три подсказки о ресурсах:

  • concurrency_group
  • concurrency_limit
  • cost_units

concurrency_group и concurrency_limit описывают общую нагрузку на одну нижестоящую систему. Если несколько обработчиков обращаются к одному поставщику, учетной записи API или клиенту, они могут использовать одну группу, чтобы обработчики не запускали слишком много задач одновременно.

cost_units описывает относительную весовую нагрузку. Обновление кэша и крупный вызов поставщика могут быть «одним заданием», но при принятии решения о том, безопасно ли запускать новые задачи, они не должны одинаково учитываться в бюджете рабочего процесса или правилах допуска обработчиков.

PHP
final class CallProviderStep implements \Queuety\Step
{
    public function handle( array $state ): array
    {
        // ...
    }

    public function config(): array
    {
        return [
            'concurrency_group' => 'providers',
            'concurrency_limit' => 3,
            'cost_units' => 5,
        ];
    }
}

Та же идея применима к заданиям, отправляемым через свойства класса, и к отдельным отправкам через PendingJob::concurrency_group() и PendingJob::cost_units().

Бюджеты общих очередей и групп поставщиков

Общую нагрузку не всегда можно выразить простым числом параллельных задач. Иногда важна суммарная весовая нагрузка уже выполняемых задач.

Queuety поддерживает взвешенные бюджеты очередей и групп. Бюджеты очередей привязаны к имени очереди. Бюджеты групп привязаны к concurrency_group, что позволяет несвязанным обработчикам использовать общие ограничения.

PHP
define( 'QUEUETY_RESOURCE_QUEUE_COST_BUDGETS', [
    'providers' => 20,
    'exports' => 8,
] );

define( 'QUEUETY_RESOURCE_GROUP_COST_BUDGETS', [
    'openai' => 12,
    'tenant:acme' => 6,
] );

Когда обработчик получает задание, он проверяет активные строки processing, уже относящиеся к этой очереди или группе. Если сумма затрат на выполняемые задания и cost_units полученного задания превышает настроенный бюджет, обработчик освобождает задание и пробует снова позже.

Это дает другой способ управления, чем concurrency_limit. Поставщик может разрешать три параллельных запроса, но выделять меньше общих единиц затрат для крупных запросов, чем для небольших.

Удерживайте один рабочий процесс в пределах бюджета

Бюджеты рабочего процесса предназначены для случаев, когда проблема заключается не в одном тяжелом задании, а в запуске, который продолжает находить больше работы, чем планировалось.

Такое часто встречается в агентных системах. Планировщик может снова обращаться к той же затратной ветви, создавать слишком много подзадач для каждой из них или продолжать запускать дочерние рабочие процессы, формально оставаясь в рамках обычной модели повторных попыток.

Queuety предоставляет для этого ограничения на уровне рабочего процесса:

PHP
Queuety::workflow( 'research_run' )
    ->max_transitions( 20 )
    ->max_for_each_items( 12 )
    ->max_state_bytes( 32768 )
    ->max_cost_units( 40 )
    ->max_started_workflows( 8 )
    ->then( PlanResearchStep::class )
    ->for_each( 'tasks', ExecuteResearchTask::class, 'results' )
    ->dispatch();

max_cost_units() не позволяет запуску потреблять больше взвешенной работы, чем предусмотрено. max_started_workflows() не дает родительскому процессу превратиться в неограниченное дерево дочерних запусков верхнего уровня. Другие предохранительные механизмы тоже важны, поскольку не каждый вышедший из-под контроля рабочий процесс обходится одинаково дорого.

Статус рабочего процесса показывает настроенные ограничения и текущее использование в общедоступных данных budget, поэтому состояние запуска можно проверять, пока эти ограничения действуют.

Позволяйте обработчикам откладывать работу, если процесс уже перегружен

Даже при правильно настроенных метаданных и бюджетах рабочего процесса последнее решение остается за процессом обработчика, который собирается выполнить задание.

Здесь и применяется допуск с учетом ресурсов. Перед запуском полученного задания Queuety может проверить:

  • общую группу параллельного выполнения задания
  • взвешенные бюджеты очереди и группы поставщика
  • историю недавней длительности выполнения и пикового потребления памяти обработчиком
  • оставшийся запас памяти у текущего обработчика
  • оставшийся запас памяти у текущего контейнера или хоста
  • оставшееся время для однократных запусков

Если обработчик уже слишком близок к настроенному пределу, Queuety освобождает задание вместо запуска работы, которая вряд ли завершится успешно.

Этот механизм намеренно действует с запасом. Это не полноценный планировщик кластера, а защитный уровень, который помогает загруженному обработчику не перегружать свой процесс, а при наличии данных о памяти — также контейнер или хост, в котором он работает.

Учет контейнера или хоста необязателен. При включении QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS Queuety сначала пытается прочитать данные о памяти cgroup, а на хостах Linux при необходимости использует /proc/meminfo. Если достоверный снимок на уровне системы получить не удается, обработчик продолжает использовать проверки локальной памяти процесса.

Масштабируйте пулы обработчиков с учетом емкости

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

Адаптивные пулы позволяют вместо этого задать минимум и максимум:

Командная оболочка
wp queuety work --queue=providers --min-workers=2 --max-workers=6

Родительский процесс поддерживает как минимум заданное число активных дочерних процессов, увеличивает их число до максимума при росте очереди доступных заданий и сокращает его после периода бездействия очереди в течение настроенного льготного интервала.

При масштабировании учитывается доступная емкость. Если включен учет системной памяти и родительский процесс не видит достаточного запаса памяти для еще одного полного набора ресурсов обработчика, он сохраняет текущий размер пула, не запуская новых дочерних процессов.

Реалистичный пример сочетания уровней

Представьте рабочий процесс исследования, который планирует вызовы поставщиков, запускает специализированные рабочие процессы, а затем синтезирует результат.

Шаг взаимодействия с поставщиком может публиковать cost_units и общую concurrency_group, чтобы несколько обработчиков не перегружали одного поставщика моделей. Очередь может резервировать взвешенный бюджет для обращений к этому поставщику. Родительский рабочий процесс может ограничить max_cost_units() и max_started_workflows(), чтобы планировщик не мог бесконечно расширять запуск. Затем обработчик может отложить новое полученное обращение к поставщику, если по недавним данным о пиковом потреблении памяти процесс или контейнер уже слишком близок к своему пределу.

Это дает четыре независимых средства управления:

  1. задавать весовую нагрузку работы
  2. резервировать общую емкость очереди или поставщика
  3. ограничивать объем работы, создаваемой одним запуском
  4. не давать перегруженному обработчику запускать слишком много задач

Где найти подробные API

На этой странице представлен общий обзор концепции.

Подробное описание параметров: