Gå til indhold

Tilstandsmaskiner

Tilstandsmaskiner og workflows løser forskellige problemer.

Et workflow besvarer spørgsmålet: "Hvilket arbejde udføres som det næste?" En tilstandsmaskine besvarer: "Hvilken tilstand er denne ting i, og hvilke hændelser er gyldige herfra?" Forskellen betyder noget, når det, du bygger, ikke blot er en række trin, men en langlivet enhed, der reagerer på input udefra over tid, såsom en agentsession, en chattråd, et reviewforløb, en ticket eller et godkendelsesflow.

Hurtigt eksempel

PHP
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' ]
);

Denne maskine starter i awaiting_user, går til planning, når en brugerbesked ankommer, lader den kølagte handling udsende planned og venter derefter i awaiting_review, indtil en ekstern godkendelse afslutter sessionen. Det vigtige er ikke selve kodens syntaks. Det er, at livscyklussen er eksplicit, kan inspiceres og er hændelsesdrevet.

Sådan kører en maskine

Når en maskininstans sendes af sted, gemmer Queuety det aktuelle tilstandsnavn, den offentlige tilstandspayload, definitionsversionen og -hashen, de gyldige indgående hændelser for den aktuelle tilstand samt en persistent hændelsestidslinje.

Hvis den indtrådte tilstand har en action(), lægger Queuety et job til indtrædelse i tilstanden i kø. Handlingen kan blot opdatere den offentlige tilstand og lade maskinen vente på næste hændelse, eller den kan udsende _event og eventuelt _event_payload for straks at udløse den næste overgang. Hvis tilstanden ikke har nogen handling eller terminalstatus, venter maskinen blot på eksterne hændelser sendt med Queuety::machine_event().

Valg mellem workflows og tilstandsmaskiner

Brug et workflow, når kørslen grundlæggende er trinbaseret. Hent data, arbejd dig gennem hvert element, vent, sammenfat, afslut. Modellen passer godt, når der er en tydelig start og afslutning, og hovedspørgsmålet er, hvilket arbejde der skal udføres som det næste.

Brug en tilstandsmaskine, når gyldig adfærd afhænger af den aktuelle livscyklusfase. Det passer bedre, når hændelser kan ankomme i forskellig rækkefølge, når enheden kan eksistere længe, eller når du har brug for at inspicere livscyklussen eksplicit med tilstande som awaiting_user, planning, running_agents, awaiting_review og completed.

Sådan fungerer de to sammen

De to grundelementer fungerer bedst sammen frem for at konkurrere med hinanden. En tilstandshandling kan sende et workflow af sted, når en overgang kræver mere omfattende udførelse, og et workflow kan udsende en hændelse tilbage til en maskine, når arbejdet er udført. I praksis lader det tilstandsmaskinen styre livscyklussen, mens workflows udfører det ressourcekrævende eller meget strukturerede arbejde.

Builderens opbygning

StateMachineBuilder er bevidst holdt enkel. initial() og state() definerer livscyklusgrafen. action() knytter kølagt arbejde til én tilstand. on() angiver, hvilke hændelser der kan flytte maskinen til den næste tilstand. on_queue(), with_priority() og max_attempts() styrer, hvordan tilstandshandlinger udføres, mens version() og idempotency_key() knytter persistente metadata for afsendelse til. dispatch() opretter maskininstansen.

Tilstandshandlinger kan også eksponere en valgfri config()-metode, der bruger de samme ressourcepolitiknøgler som workflowtrin og handlere. Det gør det muligt at holde én ressourcekrævende tilstandsovergang inden for en delt samtidighedsgruppe eller knytte en højere omkostningsvægt til, når en handling bruger mere budget end et normalt trin.

Den bredere ressourcemodel beskrives i Ressourcestyring.

Kontrakter

Handlinger ved indtrædelse i tilstande implementerer Queuety\Contracts\StateAction:

PHP
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}

$payload er den strukturerede payload fra handlingsdefinitionen og er tom, når handlingen blev angivet som et almindeligt klassenavn. Queuety opretter handlinger med new $class(), så en handlingsklasse skal have en konstruktør uden påkrævede argumenter.

Tilstandshandlinger kan også definere en valgfri config()-metode med concurrency_group, concurrency_limit og cost_units, når den kølagte indtrædelseshandling skal indgå i workerens optagelse, vægtede kø- eller udbyderbudgetter og andre ressourcepolitikker.

Overgangsvagter implementerer Queuety\Contracts\StateGuard:

PHP
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}

Inspektions-API'er

Brug facaden til at inspicere kørende maskiner:

PHP
$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 );

Tilstandsmaskiner har ingen WP-CLI-kommandoer: wp queuety workflow ... dækker kun workflows. Brug disse PHP-API'er, for eksempel via wp eval, til at inspicere maskiner fra kommandolinjen.

Disse API'er viser det aktuelle tilstandsnavn, livscyklusstatus, den offentlige tilstand, tilgængelige hændelser, definitionsmetadata, hele overgangstidslinjen og et normaliseret sporingsbundt til debuggergrænseflader. Rækkerne på maskinlister indeholder definitionsversion, definitionshash og idempotensnøgle, så listesider kan vise kilde-/runtimeversionen uden at indlæse hver maskine enkeltvis. Sporingsbundtet indeholder grupperede tilstande, rå hændelser, korrelerede handlingsjobs, logs, artefakthenvisninger, chunkreferencer, handlerinput/-output, tilstand før/efter, kontekst, tidsmålinger og strukturerede fejl.

Agentsessioner

Tilstandsmaskiner er særligt nyttige til agentsystemer, der minder mere om sessioner end pipelines. En session, der bevæger sig gennem awaiting_user, planning, running_agents, awaiting_review og completed, er som regel lettere at forstå som en eksplicit livscyklus end som én meget lang workflowdefinition med mange ventetilstande og tilbagekoblinger.

Se også