
# Робочі процеси

Робочі процеси — це довготривалі процеси, які опитують базу даних на наявність завдань, що очікують на виконання, виконують їх і просувають робочі потоки далі. Команда `wp queuety work` працює всередині WP-CLI, а сама черга обробляється безпосередньо з MySQL через найкращий доступний драйвер БД Queuety.

Плагін також встановлює стандартне резервне завдання WordPress cron, яке запускається раз на хвилину для середовищ без доступу до оболонки. На цій сторінці описано виділені робочі процеси WP-CLI — рекомендований режим розгортання для вищої пропускної здатності.

## Запуск робочого процесу

```bash
wp queuety work
```

Це запускає один робочий процес у черзі `default`. Він працює в циклі: отримує завдання, виконує його та повторює цикл. Коли черга порожня, він чекає `QUEUETY_WORKER_SLEEP` секунд (за замовчуванням: 1), перш ніж знову перевірити її.

### Параметри

| Параметр | Опис |
|---|---|
| `--queue=<queue>` | Queue(s) для обробки; розділяйте назви комами, щоб указати порядок пріоритету (за замовчуванням: `default`) |
| `--once` | Обробити не більше одного завдання та завершити роботу |
| `--workers=<n>` | Створити N дочірніх робочих процесів (потрібні розширення `pcntl` і `posix`) |
| `--min-workers=<n>` | Мінімальна кількість робочих процесів в адаптивному пулі. Потрібен `--max-workers` |
| `--max-workers=<n>` | Максимальна кількість робочих процесів в адаптивному пулі |

```bash
# Process a specific queue
wp queuety work --queue=emails

# Process at most one job and exit
wp queuety work --once

# Run 4 concurrent workers
wp queuety work --workers=4

# Scale between 2 and 6 workers as backlog changes
wp queuety work --queue=providers --min-workers=2 --max-workers=6
```

## Пріоритети кількох черг

Один робочий процес може обробляти кілька черг у порядку пріоритету. Передайте `--queue` список назв черг, розділених комами:

```bash
wp queuety work --queue=critical,default,low
```

Робочий процес намагається отримати завдання з кожної черги у вказаному порядку. Спершу він перевіряє `critical`. Якщо в `critical` є завдання, він його обробляє. Якщо `critical` порожня, він переходить до `default`, а потім до `low`. Це означає, що черги з вищим пріоритетом завжди спорожнюються раніше за черги з нижчим пріоритетом.

### Як працює порядок пріоритетів

На кожній ітерації робочий процес намагається отримати завдання з першої черги у списку. Якщо вона порожня або призупинена, він переходить до наступної черги й так триває, доки не знайде завдання або не перевірить усі черги. Якщо ніде немає завдань, робочий процес чекає та повторює спробу. Це модель суворих пріоритетів: постійний потік завдань `critical` блокуватиме черги `default` і `low`. Якщо чергам із нижчим пріоритетом потрібна гарантована пропускна здатність, використовуйте для них окремі робочі процеси.

### Використання кількох черг у PHP

Методи `Worker::run()` і `Worker::flush()` приймають рядок або масив:

```php
// Comma-separated string
Queuety::worker()->run( 'critical,default,low' );

// Array
Queuety::worker()->run( [ 'critical', 'default', 'low' ] );

// flush() also supports multi-queue
$count = Queuety::worker()->flush( 'critical,default,low' );
```

### Стратегії розгортання

**Окремі робочі процеси для кожної черги** дають повний контроль над паралелізмом та ізоляцією. Кожна черга отримує власний пул робочих процесів process(es), і інші черги не можуть перешкоджати її обробці:

```ini
[program:queuety-critical]
command=wp queuety work --queue=critical
numprocs=4

[program:queuety-default]
command=wp queuety work --queue=default
numprocs=2

[program:queuety-low]
command=wp queuety work --queue=low
numprocs=1
```

**Порядок пріоритетів з одним робочим процесом** простіше підтримувати; такий підхід добре працює, якщо черги з нижчим пріоритетом можуть чекати:

```ini
[program:queuety-worker]
command=wp queuety work --queue=critical,default,low
numprocs=2
```

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

```ini
[program:queuety-critical]
command=wp queuety work --queue=critical
numprocs=2

[program:queuety-fallback]
command=wp queuety work --queue=default,low
numprocs=2
```

## Допуск з урахуванням ресурсів

Робочі процеси не просто отримують наступний рядок із завданням, що очікує на виконання. Перш ніж розпочати отримане завдання, Queuety може виконати попередні перевірки допуску на основі групи паралелізму завдання та недавнього профілю виконання відповідного обробника.

Ширший опис цієї моделі, зокрема її зв’язок із метаданими завдань і бюджетами робочих потоків, дивіться в розділі [Керування ресурсами](https://queuety.inline0.dev/uk/docs/features/resource-management).

Група паралелізму встановлює спільне обмеження для кількох обробників або класів завдань. Якщо кільком ресурсомістким постачальникам призначити однакову групу й обмеження, робочий процес відпустить щойно отримане завдання замість того, щоб запускати його, коли буде досягнуто ліміту групи. Це не дає завданням із незалежних черг одночасно перевантажити одну нижчестоящу систему лише через наявність кількох вільних робочих процесів.

Журнали нещодавніх виконань також формують простий профіль допуску. Коли допуск з урахуванням ресурсів увімкнено, Queuety перевіряє середню тривалість і пікове використання пам’яті обробником, який має запуститися. Якщо поточний робочий процес уже наближається до встановленого ліміту пам’яті, черга або група постачальників уже вичерпала свій зважений бюджет, у поточному контейнері, схоже, недостатньо вільної пам’яті для обробника з таким профілем або виконання на кшталт `--once` наближається до свого часового ліміту, Queuety відкладає завдання замість того, щоб починати роботу, яка, імовірно, не завершиться належним чином.

Політику вмикають у визначенні роботи, а не в команді запуску робочого процесу:

```php
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_RESOURCE_ADMISSION` | Увімкнути або вимкнути перевірки допуску на основі профілю |
| `QUEUETY_RESOURCE_PROFILE_WINDOW_MINUTES` | Період ретроспективи для формування профілів нещодавніх виконань обробників |
| `QUEUETY_RESOURCE_PROFILE_TTL` | Час життя кешу профілів нещодавніх виконань обробників |
| `QUEUETY_RESOURCE_MEMORY_HEADROOM_MB` | Резерв пам’яті, який потрібно залишити перед допуском наступного завдання |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS` | Увімкнути або вимкнути приблизні перевірки пам’яті контейнера чи хоста |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_HEADROOM_MB` | Резерв системної пам’яті, який потрібно залишити перед допуском наступного завдання |
| `QUEUETY_RESOURCE_QUEUE_COST_BUDGETS` | Зважені бюджети вартості за назвою черги |
| `QUEUETY_RESOURCE_GROUP_COST_BUDGETS` | Зважені бюджети вартості за `concurrency_group` |
| `QUEUETY_RESOURCE_TIME_HEADROOM_MS` | Резерв часу для одноразового запуску робочого процесу |

## Життєвий цикл робочого процесу

Робочий процес завершується коректно після завершення поточного завдання, якщо виконується будь-яка з таких умов:

| Умова | Налаштування |
|---|---|
| Максимальна кількість оброблених завдань | `QUEUETY_WORKER_MAX_JOBS` (за замовчуванням: 1000) |
| Перевищено ліміт пам’яті | `QUEUETY_WORKER_MAX_MEMORY` (за замовчуванням: 128 МБ) |

Після зупинки робочий процес коректно завершується. У виробничому середовищі менеджер процесів автоматично перезапускає його.

Коректна обробка сигналів поки що не підтримується ([#4](https://github.com/inline0/queuety/issues/4)). Окремий процес `wp queuety work` не встановлює обробник сигналів, тож `SIGTERM` або `SIGINT` (Ctrl+C) негайно завершує його, навіть посеред виконання завдання. Це завдання залишається в стані `processing`, доки після `QUEUETY_STALE_TIMEOUT` секунд його не скине механізм відновлення завислих завдань. Тому переконайтеся, що обробники безпечно запускати повторно. Пул робочих процесів пересилає `SIGTERM` дочірнім процесам, але вони не реагують на нього, тож пул продовжує працювати, доки не зупиниться з однієї з наведених вище причин. Щоб зупинити пул, налаштуйте менеджер процесів надсилати `SIGKILL` після завершення часу очікування на зупинку.

## Паралелізм кількох робочих процесів

Прапорець `--workers=N` створює N дочірніх процесів за допомогою `pcntl_fork()`. Кожен дочірній процес встановлює власне підключення до бази даних і обробляє завдання незалежно. Батьківський процес контролює дочірні та перезапускає ті, що аварійно завершилися.

```bash
wp queuety work --queue=default --workers=4
```

Вимоги:
Мають бути встановлені розширення `pcntl` і `posix` PHP (без `pcntl` команда завершується помилкою «pcntl extension is required for --workers=N.»), а кожен дочірній процес використовує власне підключення до бази даних, тому параметр MySQL `max_connections` потрібно налаштувати з урахуванням пулу. Для адаптивних пулів (`--min-workers`/`--max-workers`) діють ті самі вимоги.

## Адаптивні пули робочих процесів

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

```bash
wp queuety work --queue=providers --min-workers=2 --max-workers=6
```

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

Збільшення масштабу відбувається обережно. Коли Queuety може отримувати дані про пам’ять контейнера або хоста, він відмовляється додавати дочірні процеси, якщо вільної пам’яті недостатньо для повного набору ресурсів ще одного робочого процесу.

Цикл масштабування на стороні батьківського процесу налаштовується так:

| Константа | Опис |
|---|---|
| `QUEUETY_WORKER_POOL_SCALE_INTERVAL` | Як часто батьківський процес узгоджує розмір пулу |
| `QUEUETY_WORKER_POOL_IDLE_GRACE` | Як довго черга має залишатися тихою перед зменшенням масштабу |

## Відновлення завислих завдань

Якщо робочий процес аварійно завершується під час обробки завдання, завдання залишається у стані `processing`. Довготривалий робочий процес приблизно раз на хвилину перевіряє, чи є завдання, що перебувають у стані `processing` довше ніж `QUEUETY_STALE_TIMEOUT` секунд. Для завислого завдання, у якого залишилися спроби, повторне виконання планується після затримки між спробами; завдання, у якого всі спроби вичерпано, переміщується до відкладених, а відповідний робочий потік позначається як невдалий. Робочі процеси `--once` і резервний механізм WP-Cron такої перевірки не виконують.

Відновіть завислі завдання вручну:

```bash
wp queuety recover
```

## Режим обробки всіх завдань

Обробіть усі завдання, що очікують на виконання, і завершіть роботу. Зручно для тестування або одноразової пакетної обробки:

```bash
wp queuety flush
wp queuety flush --queue=emails
```

## Розгортання у виробничому середовищі

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

### Supervisord

```ini
[program:queuety-default]
command=wp queuety work --queue=default
directory=/var/www/html
autostart=true
autorestart=true
numprocs=2
process_name=%(program_name)s_%(process_num)02d
stdout_logfile=/var/log/queuety-default.log
stderr_logfile=/var/log/queuety-default-error.log

[program:queuety-emails]
command=wp queuety work --queue=emails
directory=/var/www/html
autostart=true
autorestart=true
numprocs=1
stdout_logfile=/var/log/queuety-emails.log
stderr_logfile=/var/log/queuety-emails-error.log
```

### Systemd

```ini
[Unit]
Description=Queuety Worker (default queue)
After=mysql.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/html
ExecStart=/usr/local/bin/wp queuety work --queue=default
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

Увімкніть і запустіть:

```bash
sudo systemctl enable queuety-worker
sudo systemctl start queuety-worker
```

### Поради щодо розгортання

Запускайте окремих воркерів для окремих черг, якщо потрібна краща ізоляція. Використовуйте `--workers=N`, коли навантаження обмежене CPU, а якщо переважають операції введення-виведення — надавайте перевагу окремим воркерам під наглядом. Установіть `QUEUETY_WORKER_MAX_JOBS`, щоб періодично перезапускати воркери й звільняти пам’ять, і стежте за кількістю `buried` через [вебхуки](https://queuety.inline0.dev/uk/docs/features/webhooks) або [метрики](https://queuety.inline0.dev/uk/docs/features/metrics).
