Vai al contenuto

Gestione delle risorse

Queuety suddivide il controllo delle risorse in quattro livelli fondamentali, con la possibilità di dimensionare i pool in aggiunta.

Il primo livello descrive il costo di una singola unità di lavoro. Il secondo limita la pressione condivisa che una coda o un gruppo di provider può sostenere alla volta. Il terzo definisce quanto una singola esecuzione di workflow può consumare. Il quarto decide se un worker deve avviare subito il prossimo processo acquisito o rimandarlo finché il processo e il container correnti non dispongono di risorse sufficienti.

Questi livelli lavorano insieme. Un handler può dichiarare di appartenere a un pool condiviso ad alto costo, una coda può riservare una quantità limitata di lavoro ponderato per un provider, un workflow può fissare un tetto al lavoro che intende creare e il worker può comunque rifiutarsi di avviarne altro quando il processo o il container corrente è già troppo vicino ai propri limiti.

Descrivere il costo di una singola unità di lavoro

Processi, passaggi dei workflow e azioni di ingresso negli stati possono tutti pubblicare gli stessi tre suggerimenti sulle risorse:

  • concurrency_group
  • concurrency_limit
  • cost_units

concurrency_group e concurrency_limit descrivono la pressione condivisa su un sistema a valle. Se più handler chiamano lo stesso provider, account API o tenant, possono usare lo stesso gruppo affinché i worker non ne avviino troppi contemporaneamente.

cost_units descrive il peso relativo. Un aggiornamento della cache e una chiamata costosa a un provider possono essere entrambi «un processo», ma non dovrebbero contribuire allo stesso modo quando un budget del workflow o una regola di ammissione del worker decide se sia sicuro avviare altro lavoro.

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

La stessa idea si applica ai processi inviabili tramite le proprietà della classe e ai singoli invii tramite PendingJob::concurrency_group() e PendingJob::cost_units().

Definire budget per code e gruppi di provider condivisi

La pressione condivisa non dipende sempre da un semplice conteggio della concorrenza. A volte ciò che conta è il peso complessivo di quanto è già in esecuzione.

Queuety lo gestisce con budget ponderati per code e gruppi. I budget delle code sono associati al nome della coda. I budget dei gruppi sono associati a concurrency_group, consentendo a handler non correlati di rientrare nello stesso limite condiviso.

PHP
define( 'QUEUETY_RESOURCE_QUEUE_COST_BUDGETS', [
    'providers' => 20,
    'exports' => 8,
] );

define( 'QUEUETY_RESOURCE_GROUP_COST_BUDGETS', [
    'openai' => 12,
    'tenant:acme' => 6,
] );

Quando un worker acquisisce un processo, controlla le righe attive di processing già appartenenti a quella coda o a quel gruppo. Se il costo attualmente in esecuzione sommato a cost_units del processo acquisito supererebbe il budget configurato, il worker rilascia il processo e riprova più tardi.

Questo offre un controllo diverso da concurrency_limit. Un provider può consentire tre richieste simultanee, ma riservare comunque meno unità di costo totali alle richieste più grandi rispetto a quelle più piccole.

Mantenere un workflow entro i limiti

I budget dei workflow servono quando il problema non è un singolo processo oneroso, ma un'esecuzione che continua a scoprire più lavoro del previsto.

È una situazione comune nei sistemi basati su agenti. Un pianificatore può rivisitare lo stesso ramo costoso, trasformarlo in troppi sottoprocessi o continuare ad avviare workflow secondari pur restando tecnicamente entro un normale modello di nuovi tentativi.

Queuety mette a disposizione questi limiti a livello di workflow:

PHP
Queuety::workflow( 'research_run' )
    ->max_transitions( 20 )
    ->max_for_each_items( 12 )
    ->max_state_bytes( 32768 )
    ->max_cost_units( 40 )
    ->max_started_workflows( 8 )
    ->then( PlanResearchStep::class )
    ->for_each( 'tasks', ExecuteResearchTask::class, 'results' )
    ->dispatch();

max_cost_units() impedisce all'esecuzione di consumare più lavoro ponderato del previsto. max_started_workflows() impedisce a un elemento principale di trasformarsi in un albero illimitato di esecuzioni secondarie di primo livello. Le altre protezioni restano importanti, perché non tutti i workflow fuori controllo sono onerosi allo stesso modo.

Lo stato del workflow espone i limiti configurati e l'utilizzo corrente tramite il payload pubblico budget, così è possibile esaminare l'esecuzione mentre i limiti sono attivi.

Consentire ai worker di rimandare il lavoro quando il processo è già troppo carico

Anche quando i metadati e i budget dei workflow sono configurati correttamente, la decisione finale spetta al processo worker che sta per eseguire il job.

È qui che interviene l'ammissione basata sulle risorse. Prima che inizi un processo acquisito, Queuety può controllare:

  • il gruppo di concorrenza condiviso del processo
  • i budget ponderati della coda e dei gruppi di provider
  • la durata recente e lo storico dei picchi di memoria dell'handler
  • la memoria residua del worker corrente
  • la memoria residua del container o dell'host corrente
  • il tempo residuo disponibile per le esecuzioni di tipo one-shot

Se il worker è già troppo vicino al limite configurato, Queuety rilascia il processo invece di avviare un lavoro che probabilmente non terminerà senza problemi.

Questo comportamento è volutamente prudente. Non è uno scheduler completo per cluster: è un livello di sicurezza che aiuta un worker impegnato a evitare di sovraccaricare il processo in cui opera e, quando sono disponibili dati sulla memoria, il container o l'host che lo ospita.

La gestione del container o dell'host è facoltativa. Quando QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS è abilitato, Queuety prova prima a leggere i dati sulla memoria cgroup e, sugli host Linux, ripiega su /proc/meminfo. Se non è disponibile un'istantanea affidabile a livello di sistema, il worker continua a usare i controlli limitati al processo.

Ridimensionare i pool di worker senza ignorare la capacità disponibile

I pool di worker con dimensione fissa sono facili da gestire, ma obbligano a scegliere un solo numero valido sia nei periodi tranquilli sia durante i picchi.

I pool adattivi consentono invece di impostare un minimo e un massimo:

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

Il processo principale mantiene attivi almeno il numero minimo di processi figlio, aumenta il numero verso il massimo quando cresce il carico arretrato acquisibile e lo riduce dopo che la coda è rimasta inattiva per il periodo di tolleranza configurato.

L'aumento della dimensione rispetta comunque la capacità disponibile. Se il monitoraggio della memoria di sistema è abilitato e il processo principale non rileva memoria residua sufficiente per un altro worker completo, mantiene il pool alla dimensione corrente invece di avviare altri processi figlio.

Un esempio realistico di combinazione dei livelli

Immagina un workflow di ricerca che pianifica chiamate ai provider, avvia workflow specializzati e poi sintetizza i risultati.

Il passaggio che comunica con i provider può pubblicare cost_units e un concurrency_group condiviso, così più worker non sovraccaricano lo stesso provider di modelli. La coda può riservare un budget ponderato per il traffico diretto a quel provider. Il workflow principale può limitare max_cost_units() e max_started_workflows(), impedendo al pianificatore di far crescere l'esecuzione senza limiti. Il worker può comunque rimandare una chiamata al provider appena acquisita se i recenti picchi di memoria indicano che il processo o il container è già troppo vicino al proprio limite.

Questo offre quattro controlli distinti:

  1. descrivere il peso del lavoro
  2. riservare la capacità condivisa della coda o del provider
  3. limitare ciò che una singola esecuzione può creare
  4. impedire a un worker sotto carico di avviare più lavoro del dovuto

Dove trovare le API dettagliate

Questa pagina offre una panoramica concettuale.

Per i parametri specifici:

  • Worker illustra il comportamento di ammissione e le costanti di esecuzione
  • CLI illustra i comandi per i pool di worker fissi e adattivi
  • Protezioni dei workflow illustra i budget dei workflow
  • Processi illustra i metadati dei processi e degli handler
  • Macchine a stati illustra i metadati delle risorse per le azioni di stato
  • API elenca i metodi esatti e le strutture restituite