Maszyny stanów
Maszyny stanów i przepływy pracy rozwiązują różne problemy.
Przepływ pracy odpowiada na pytanie: „jakie zadanie wykonać dalej?”. Maszyna stanów odpowiada na pytanie: „w jakim stanie znajduje się ten obiekt i jakie zdarzenia są teraz dozwolone?”. Ta różnica ma znaczenie, gdy budowany system nie jest tylko sekwencją kroków, lecz długotrwałym obiektem, który z czasem reaguje na dane z zewnątrz, takim jak sesja agenta, wątek czatu, cykl przeglądu, zgłoszenie lub proces zatwierdzania.
Szybki przykład
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;
$machine_id = Queuety::machine( 'agent_session' )
->version( 'agent-session.v1' )
->state( 'awaiting_user' )
->on( 'user_message', 'planning' )
->state( 'planning' )
->action( PlanSessionAction::class )
->on( 'planned', 'awaiting_review' )
->state( 'awaiting_review' )
->on( 'approve', 'completed', ReviewApprovedGuard::class )
->on( 'reject', 'awaiting_user' )
->state( 'completed', StateMachineStatus::Completed )
->dispatch( [ 'thread_id' => 42 ] );
Queuety::machine_event(
$machine_id,
'user_message',
[ 'message' => 'Find competitors for this product' ]
);
Queuety::machine_event(
$machine_id,
'approve',
[ 'approved' => true, 'reviewer' => 'editor@example.com' ]
);
Ta maszyna rozpoczyna w awaiting_user, przechodzi do planning po otrzymaniu wiadomości od użytkownika, pozwala zakolejkowanej akcji wyemitować planned, a następnie czeka w awaiting_review, aż zewnętrzne zatwierdzenie zakończy sesję. Najważniejsze nie jest samo składniowe ujęcie kodu, lecz to, że cykl życia jest jawny, możliwy do sprawdzenia i sterowany zdarzeniami.
Jak działa maszyna
Po wysłaniu instancji maszyny Queuety zapisuje bieżącą nazwę stanu, publiczny ładunek stanu, wersję i skrót definicji, prawidłowe zdarzenia wejściowe dla bieżącego stanu oraz trwałą oś czasu zdarzeń.
Jeśli nowo osiągnięty stan ma przypisaną akcję action(), Queuety umieszcza w kolejce zadanie wejścia w stan. Akcja może po prostu zaktualizować publiczny stan i pozostawić maszynę w oczekiwaniu na następne zdarzenie albo wyemitować _event i opcjonalnie _event_payload, aby natychmiast zainicjować kolejne przejście. Jeśli stan nie ma akcji ani statusu końcowego, maszyna czeka na zdarzenia zewnętrzne wysyłane za pomocą Queuety::machine_event().
Wybór między przepływami pracy a maszynami stanów
Użyj przepływu pracy, gdy przebieg składa się przede wszystkim z kolejnych kroków. Pobierz dane, wykonaj każdą pracę, poczekaj, opracuj wynik, zakończ. Ten model sprawdza się, gdy istnieje wyraźny początek i koniec, a główne pytanie dotyczy tego, co wykonać dalej.
Użyj maszyny stanów, gdy prawidłowe działanie zależy od bieżącej fazy cyklu życia. To lepszy wybór, gdy zdarzenia mogą nadejść w różnej kolejności, obiekt może istnieć długo lub potrzebujesz jawnego wglądu w cykl życia obejmujący stany takie jak awaiting_user, planning, running_agents, awaiting_review i completed.
Jak współpracują oba rozwiązania
Oba mechanizmy najlepiej sprawdzają się razem, a nie jako konkurencyjne rozwiązania. Akcja stanu może wysłać przepływ pracy, gdy przejście wymaga bardziej złożonego wykonania, a przepływ pracy może po zakończeniu emitować zdarzenie do maszyny. W praktyce maszyna stanów zarządza cyklem życia, a przepływy pracy wykonują kosztowne lub ściśle ustrukturyzowane operacje.
Struktura konstruktora
StateMachineBuilder ma celowo niewielki zakres. initial() i state() definiują graf cyklu życia. action() dołącza zakolejkowaną pracę do jednego stanu. on() określa, które zdarzenia mogą przenieść maszynę do następnego stanu. on_queue(), with_priority() i max_attempts() określają sposób wykonywania akcji stanu, a version() i idempotency_key() dołączają trwałe metadane wysyłania. dispatch() tworzy instancję maszyny.
Akcje stanów mogą również udostępniać opcjonalną metodę config(), korzystającą z tych samych kluczy zasad dotyczących zasobów co kroki i programy obsługi przepływu pracy. Dzięki temu kosztowne przejście stanu może należeć do wspólnej grupy współbieżności albo otrzymać wyższą wagę kosztu, gdy akcja zużywa większą część budżetu niż zwykły krok.
Szerszy model zasobów opisano w sekcji Zarządzanie zasobami.
Kontrakty
Akcje wejścia w stan implementują Queuety\Contracts\StateAction:
namespace Queuety\Contracts;
interface StateAction {
public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
$payload to uporządkowany ładunek z definicji akcji; jest pusty, jeśli jako akcję podano zwykłą nazwę klasy. Queuety tworzy akcje za pomocą new $class(), dlatego klasa akcji musi mieć konstruktor bez wymaganych argumentów.
Akcje stanów mogą również definiować opcjonalną metodę config() z concurrency_group, concurrency_limit i cost_units, jeśli zakolejkowana akcja wejścia ma uwzględniać dopuszczanie przez pracownika, ważone limity kolejek lub dostawców oraz inne zasady dotyczące zasobów.
Warunki przejścia implementują Queuety\Contracts\StateGuard:
namespace Queuety\Contracts;
interface StateGuard {
public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
Interfejsy inspekcji
Użyj fasady, aby sprawdzić działające maszyny:
$status = Queuety::machine_status( $machine_id );
$machines = Queuety::list_machines();
$events = Queuety::machine_timeline( $machine_id );
$trace = Queuety::machine_trace( $machine_id );
$state = Queuety::machine_state_at( $machine_id, $event_id );
Maszyny stanów nie mają poleceń WP-CLI: wp queuety workflow ... obsługuje tylko przepływy pracy. Użyj tych interfejsów API PHP, na przykład za pośrednictwem wp eval, aby sprawdzać maszyny z wiersza poleceń.
Te interfejsy API udostępniają bieżącą nazwę stanu, status cyklu życia, stan publiczny, dostępne zdarzenia, metadane definicji, pełną oś czasu przejść oraz ujednolicony pakiet śledzenia dla interfejsów debuggera. Wiersze listy maszyn zawierają wersję definicji, jej skrót i klucz idempotencji, dzięki czemu ekrany listy mogą wyświetlać wersję źródłową i uruchomieniową bez wczytywania każdej maszyny osobno. Pakiet śledzenia obejmuje pogrupowane stany, surowe zdarzenia, powiązane zadania akcji, dzienniki, odwołania do artefaktów i fragmentów, dane wejściowe i wyjściowe programów obsługi, stan przed i po zmianie, kontekst, czasy oraz uporządkowane błędy.
Sesje agentów
Maszyny stanów są szczególnie przydatne w systemach agentów, które bardziej przypominają sesje niż potoki. Sesję przechodzącą przez awaiting_user, planning, running_agents, awaiting_review i completed zwykle łatwiej opisać jako jawny cykl życia niż jako jedną bardzo długą definicję przepływu pracy z licznymi oczekiwaniami i przejściami wstecz.