Керування ресурсами
Queuety розділяє керування ресурсами на чотири основні рівні, а поверх них передбачено необов’язкове налаштування розміру пулу.
Перший рівень описує вартість однієї одиниці роботи. Другий обмежує спільне навантаження, яке одна черга або група провайдерів може нести одночасно. Третій визначає, скільки ресурсів може спожити один запуск робочого процесу. Четвертий вирішує, чи має робочий процес негайно розпочати наступне отримане завдання, чи відкласти його, доки в поточному процесі та контейнері не з’явиться достатньо вільних ресурсів.
Ці рівні працюють разом. Обробник може вказати, що належить до спільного пулу з високими витратами, черга може зарезервувати для одного провайдера лише певний обсяг зваженої роботи, а робочий процес може обмежити обсяг такої роботи, яку він готовий створити. Водночас робочий процес може відмовитися запускати додаткову роботу, якщо поточний процес або контейнер уже наблизився до своїх лімітів.
Опишіть вартість однієї одиниці роботи
Завдання, кроки робочих процесів і дії під час входу в стан можуть публікувати ті самі три підказки щодо ресурсів:
concurrency_groupconcurrency_limitcost_units
concurrency_group і concurrency_limit описують спільне навантаження на одну систему нижчого рівня. Якщо кілька обробників звертаються до того самого провайдера, облікового запису API або клієнта, вони можуть використовувати ту саму групу, щоб робочі процеси не запускали надто багато запитів одночасно.
cost_units описує відносну вагу. Оновлення кешу й великий виклик провайдера можуть обидва бути «одним завданням», але не мають враховуватися однаково, коли бюджет робочого процесу чи правило допуску вирішує, чи безпечно запускати додаткову роботу.
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, що дає змогу не пов’язаним між собою обробникам спільно використовувати ті самі обмеження.
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 надає для цього обмеження на рівні робочого процесу:
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(), щоб планувальник не міг без кінця розростати запуск. Далі робочий процес усе одно може відкласти щойно отриманий виклик провайдера, якщо за даними про нещодавнє пікове використання пам’яті процес або контейнер уже наблизився до своєї межі.
Так ви отримуєте чотири окремі способи керування:
- описати вагу роботи
- зарезервувати спільну пропускну здатність черги або провайдера
- обмежити обсяг роботи, яку може створити один запуск
- не дати перевантаженому робочому процесу запускати зайву роботу
Де знайти докладний опис API
Ця сторінка — огляд основних понять.
Опис конкретних параметрів:
- Робочі процеси — поведінка допуску й константи середовища виконання
- CLI — команди для фіксованих і адаптивних пулів робочих процесів
- Обмеження робочих процесів — бюджети робочих процесів
- Завдання — метадані завдань та обробників
- Машини станів — метадані ресурсів для дій зі станами
- API — точні назви методів і форми значень, які вони повертають