
# Procesy robocze

Procesy robocze to długotrwale działające procesy, które odpytują bazę danych o oczekujące zadania, wykonują je i posuwają przepływy pracy naprzód. Polecenie `wp queuety work` działa wewnątrz WP-CLI, a kolejka jest pobierana bezpośrednio z MySQL za pośrednictwem najlepszego dostępnego sterownika DB Queuety.

Wtyczka instaluje też domyślny mechanizm awaryjny WordPress cron uruchamiany raz na minutę, przeznaczony dla środowisk bez dostępu do powłoki. Ta strona opisuje dedykowane procesy robocze WP-CLI, zalecany tryb wdrożenia zapewniający większą przepustowość.

## Uruchamianie procesu roboczego

```bash
wp queuety work
```

Uruchamia to pojedynczy proces roboczy dla kolejki `default`. Działa on w pętli: pobiera zadanie, wykonuje je i powtarza cykl. Gdy kolejka jest pusta, czeka `QUEUETY_WORKER_SLEEP` sekund (domyślnie: 1), a następnie ponownie odpytuje kolejkę.

### Opcje

| Opcja | Opis |
|---|---|
| `--queue=<queue>` | Queue(s) do przetworzenia; nazwy oddzielone przecinkami ustalają kolejność priorytetów (domyślnie: `default`) |
| `--once` | Przetwórz najwyżej jedno zadanie i zakończ działanie |
| `--workers=<n>` | Rozgałęź procesy robocze na N procesów (wymaga rozszerzeń `pcntl` i `posix`) |
| `--min-workers=<n>` | Minimalna liczba procesów roboczych w puli adaptacyjnej. Wymaga `--max-workers` |
| `--max-workers=<n>` | Maksymalna liczba procesów roboczych w puli adaptacyjnej |

```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
```

## Priorytety wielu kolejek

Pojedynczy proces roboczy może przetwarzać wiele kolejek w kolejności priorytetów. Przekaż do `--queue` rozdzieloną przecinkami listę nazw kolejek:

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

Proces roboczy próbuje pobrać zadanie z każdej kolejki w podanej kolejności. Najpierw sprawdza `critical`. Jeśli `critical` zawiera zadanie, przetwarza je. Jeśli `critical` jest pusta, przechodzi do `default`, a następnie do `low`. Oznacza to, że kolejki o wyższym priorytecie są zawsze opróżniane przed tymi o niższym priorytecie.

### Jak działa kolejność priorytetów

W każdej iteracji proces roboczy próbuje pobrać zadanie z pierwszej kolejki na liście. Jeśli kolejka jest pusta lub wstrzymana, przechodzi do następnej i kontynuuje, aż znajdzie zadanie albo sprawdzi całą listę. Jeśli nigdzie nie znajdzie zadania, czeka i ponawia próbę. To ścisły model priorytetów, co oznacza, że stały napływ zadań `critical` zagłodzi kolejki `default` i `low`. Jeśli kolejki o niższym priorytecie mają mieć gwarantowaną przepustowość, użyj dedykowanych procesów roboczych.

### Korzystanie z wielu kolejek w PHP

Metody `Worker::run()` i `Worker::flush()` przyjmują ciąg znaków lub tablicę:

```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' );
```

### Strategie wdrażania

**Dedykowane procesy robocze dla każdej kolejki** zapewniają pełną kontrolę nad współbieżnością i izolacją. Każda kolejka ma własny proces roboczy process(es), więc inne kolejki nie mogą go zagłodzić:

```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
```

**Kolejność priorytetów w pojedynczym procesie roboczym** jest łatwiejsza w zarządzaniu i sprawdza się, gdy kolejki o niższym priorytecie mogą tolerować opóźnienia:

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

Możesz też połączyć oba podejścia: użyć dedykowanych procesów roboczych dla `critical`, aby zagwarantować przepustowość, a dla pozostałych kolejek procesu roboczego z kolejnością priorytetów:

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

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

## Przydzielanie zasobów z ich uwzględnieniem

Procesy robocze robią więcej niż tylko pobierają kolejny oczekujący wiersz. Przed rozpoczęciem pobranego zadania Queuety może zastosować wstępne kontrole dopuszczenia na podstawie grupy współbieżności zadania i niedawnego profilu wykonania tego obsługującego je kodu.

Więcej informacji o całym modelu i jego powiązaniu z metadanymi zadań oraz limitami przepływów pracy znajdziesz w sekcji [Zarządzanie zasobami](https://queuety.inline0.dev/pl/docs/features/resource-management).

Grupa współbieżności wyznacza wspólny limit dla kilku programów obsługi lub klas zadań. Jeśli kilka kosztownych dostawców przypiszesz do tej samej grupy i limitu, proces roboczy zwolni nowo pobrane zadanie zamiast je uruchamiać, gdy grupa osiągnie limit. Dzięki temu niezależne kolejki nie przeciążą jednego systemu podrzędnego tylko dlatego, że kilka procesów roboczych jest bezczynnych.

Niedawne dzienniki wykonania służą też do tworzenia prostego profilu dopuszczania. Gdy ta funkcja jest włączona, Queuety sprawdza niedawny średni czas trwania i szczytowe zużycie pamięci programu obsługi, który ma uruchomić. Jeśli bieżący proces roboczy zbliża się już do skonfigurowanego limitu pamięci, kolejka lub grupa dostawców osiągnęła już swój ważony budżet, bieżący kontener nie ma dość wolnej pamięci na podstawie zaobserwowanego profilu programu obsługi albo uruchomienie w stylu `--once` zbliża się do limitu czasu, Queuety odracza zadanie zamiast rozpoczynać pracę, która prawdopodobnie nie zakończy się poprawnie.

Zasady włączasz w definicji zadania, a nie w poleceniu procesu roboczego:

```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,
        ];
    }
}
```

Te same metadane można dołączyć do klas zadań możliwych do wysłania, używając właściwości publicznych, albo do pojedynczego wysłania za pomocą `PendingJob::concurrency_group()` i `PendingJob::cost_units()`.

Profile dopuszczania po stronie procesu roboczego kontrolują następujące stałe:

| Stała | Opis |
|---|---|
| `QUEUETY_RESOURCE_ADMISSION` | Włączanie lub wyłączanie kontroli dopuszczania na podstawie profilu |
| `QUEUETY_RESOURCE_PROFILE_WINDOW_MINUTES` | Zakres czasu uwzględniany przy tworzeniu niedawnych profili programów obsługi |
| `QUEUETY_RESOURCE_PROFILE_TTL` | Czas TTL pamięci podręcznej niedawnych profili programów obsługi |
| `QUEUETY_RESOURCE_MEMORY_HEADROOM_MB` | Rezerwa pamięci przed dopuszczeniem kolejnego zadania |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS` | Włączanie lub wyłączanie przybliżonych kontroli pamięci kontenera lub hosta |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_HEADROOM_MB` | Rezerwa pamięci systemowej przed dopuszczeniem kolejnego zadania |
| `QUEUETY_RESOURCE_QUEUE_COST_BUDGETS` | Ważone budżety kosztów według nazwy kolejki |
| `QUEUETY_RESOURCE_GROUP_COST_BUDGETS` | Ważone budżety kosztów według `concurrency_group` |
| `QUEUETY_RESOURCE_TIME_HEADROOM_MS` | Rezerwa czasu dla jednorazowych uruchomień procesów roboczych |

## Cykl życia procesu roboczego

Proces roboczy zatrzymuje się poprawnie po zakończeniu bieżącego zadania, gdy spełniony jest jeden z następujących warunków:

| Warunek | Konfiguracja |
|---|---|
| Maksymalna liczba przetworzonych zadań | `QUEUETY_WORKER_MAX_JOBS` (domyślnie: 1000) |
| Przekroczony limit pamięci | `QUEUETY_WORKER_MAX_MEMORY` (domyślnie: 128 MB) |

Po zatrzymaniu proces roboczy kończy działanie bez błędów. W środowisku produkcyjnym menedżer procesów uruchamia go ponownie automatycznie.

Obsługa sygnałów nie jest jeszcze poprawna ([#4](https://github.com/inline0/queuety/issues/4)). Pojedynczy proces `wp queuety work` nie instaluje obsługi sygnałów, więc `SIGTERM` lub `SIGINT` (Ctrl+C) kończy go natychmiast, nawet w trakcie zadania. Zadanie pozostaje w stanie `processing` do czasu, aż odzyskiwanie nieaktualnych zadań zresetuje je po `QUEUETY_STALE_TIMEOUT` sekundach, dlatego programy obsługi powinny bezpiecznie znosić ponowne próby. Pula procesów roboczych przekazuje `SIGTERM` swoim procesom potomnym, ale te nie reagują na niego, więc pula działa dalej, dopóki nie zostaną zatrzymane z jednego z wymienionych powodów. Gdy trzeba zatrzymać pulę, ustaw w menedżerze procesów limit czasu zatrzymania, po którym wyśle `SIGKILL`.

## Współbieżność wielu procesów roboczych

Flaga `--workers=N` rozgałęzia N procesów potomnych za pomocą `pcntl_fork()`. Każdy proces potomny nawiązuje własne połączenie z bazą danych i niezależnie przetwarza zadania. Proces nadrzędny monitoruje procesy potomne i uruchamia ponownie te, które ulegną awarii.

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

Wymagania:
Rozszerzenia `pcntl` i `posix` PHP muszą być zainstalowane (bez `pcntl` polecenie kończy się błędem „pcntl extension is required for --workers=N.”). Każdy proces potomny używa własnego połączenia z bazą danych, więc ustawienie MySQL `max_connections` musi uwzględniać pulę. Pule adaptacyjne (`--min-workers`/`--max-workers`) mają te same wymagania.

## Adaptacyjne pule procesów roboczych

Pule adaptacyjne utrzymują minimalną liczbę gotowych procesów roboczych, a następnie zwiększają ją i zmniejszają w skonfigurowanym zakresie:

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

Proces nadrzędny obserwuje liczbę zadań gotowych do pobrania w wybranych kolejkach i dostosowuje liczbę procesów potomnych do zaległości, nie przekraczając skonfigurowanego maksimum. Gdy w kolejce robi się spokojnie, pula czeka przez określony czas bezczynności przed zmniejszeniem rozmiaru, aby krótkie skoki obciążenia nie powodowały natychmiastowych zmian listy procesów.

Zwiększanie skali jest nadal ostrożne. Gdy Queuety może odczytać dane o pamięci kontenera lub hosta, nie dodaje kolejnych procesów potomnych, jeśli nie ma dość wolnej pamięci na pełny zestaw zasobów kolejnego procesu roboczego.

Pętlę skalowania po stronie procesu nadrzędnego kontrolują:

| Stała | Opis |
|---|---|
| `QUEUETY_WORKER_POOL_SCALE_INTERVAL` | Jak często proces nadrzędny uzgadnia rozmiar puli |
| `QUEUETY_WORKER_POOL_IDLE_GRACE` | Jak długo zaległości muszą pozostawać bez zmian, zanim pula zmniejszy rozmiar |

## Odzyskiwanie nieaktualnych zadań

Jeśli proces roboczy ulegnie awarii podczas przetwarzania zadania, zadanie pozostaje w stanie `processing`. Długotrwale działający proces roboczy mniej więcej raz na minutę sprawdza, czy są zadania, które pozostają w stanie `processing` dłużej niż `QUEUETY_STALE_TIMEOUT` sekund. Nieaktualne zadanie, któremu pozostały próby, jest ponawiane po upływie czasu oczekiwania; zadanie, które wykorzystało wszystkie próby, trafia do archiwum, a jego przepływ pracy zostaje oznaczony jako nieudany. Procesy robocze `--once` i mechanizm awaryjny WP-Cron nie wykonują tego sprawdzenia.

Odzyskaj nieaktualne zadania ręcznie:

```bash
wp queuety recover
```

## Tryb opróżniania

Przetwórz wszystkie oczekujące zadania i zakończ działanie. Przydatne do testowania lub jednorazowego przetwarzania wsadowego:

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

## Wdrożenie produkcyjne

W środowisku produkcyjnym użyj menedżera procesów, aby procesy robocze działały bez przerwy. Oto dwa popularne sposoby.

### 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
```

Włącz i uruchom:

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

### Wskazówki dotyczące wdrażania

Uruchamiaj osobne procesy robocze dla poszczególnych kolejek, gdy potrzebujesz lepszej izolacji. Użyj `--workers=N`, gdy obciążenie ogranicza procesor, a przy obciążeniu związanym głównie z operacjami wejścia-wyjścia preferuj osobne nadzorowane procesy robocze. Ustaw `QUEUETY_WORKER_MAX_JOBS`, jeśli chcesz okresowo uruchamiać procesy ponownie, aby zwolnić pamięć, i obserwuj liczbę `buried` w [webhookach](https://queuety.inline0.dev/pl/docs/features/webhooks) lub [metrykach](https://queuety.inline0.dev/pl/docs/features/metrics).
