
# Работници

Работниците са дълго работещи процеси, които проверяват базата данни за чакащи задачи, изпълняват ги и придвижват работните процеси напред. Командата `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/bg/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` | 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 MB) |

След спирането работникът излиза без грешки. В продукционна среда мениджърът на процеси го рестартира автоматично.

Сигналите все още не се обработват плавно ([#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` при натоварване, ограничено от процесора, а при натоварване предимно от операции за вход и изход предпочитайте работници, наблюдавани поотделно. Задайте `QUEUETY_WORKER_MAX_JOBS`, ако искате периодични рестартирания за освобождаване на памет, и следете броя на `buried` чрез [уебкуки](https://queuety.inline0.dev/bg/docs/features/webhooks) или [метрики](https://queuety.inline0.dev/bg/docs/features/metrics).
