Gestion des ressources
Queuety répartit le contrôle des ressources en quatre couches principales, auxquelles peut s’ajouter le dimensionnement facultatif des pools.
La première couche décrit le coût d’une unité de travail. La deuxième limite la pression partagée qu’une file ou un groupe de fournisseurs peut supporter à la fois. La troisième définit la consommation maximale autorisée pour une exécution de workflow. La quatrième détermine si un worker doit démarrer immédiatement la prochaine tâche prise en charge ou la reporter jusqu’à ce que le processus et le conteneur disposent d’une marge suffisante.
Ces couches fonctionnent ensemble. Un gestionnaire peut déclarer qu’il appartient à un pool partagé coûteux, une file peut réserver une quantité limitée de travail pondéré pour un fournisseur, un workflow peut plafonner la quantité de travail de ce type qu’il est prêt à créer, et le worker peut toujours refuser d’en démarrer davantage lorsque le processus ou le conteneur approche déjà de ses limites.
Décrire le coût d’une unité de travail
Les tâches, les étapes de workflow et les actions d’entrée d’état peuvent toutes publier les trois mêmes indications de ressources :
concurrency_groupconcurrency_limitcost_units
concurrency_group et concurrency_limit décrivent la pression partagée sur un système en aval. Si plusieurs gestionnaires sollicitent le même fournisseur, compte API ou locataire, ils peuvent utiliser le même groupe afin que les workers n’en démarrent pas trop à la fois.
cost_units décrit un poids relatif. Une actualisation du cache et un appel important à un fournisseur peuvent tous deux être « une tâche », mais ils ne devraient pas peser autant l’un que l’autre lorsqu’un budget de workflow ou une règle d’admission des workers détermine s’il est prudent d’ajouter du travail.
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 même approche s’applique aux tâches distribuables via les propriétés de classe, ainsi qu’aux distributions individuelles via PendingJob::concurrency_group() et PendingJob::cost_units().
Budgétiser les files partagées et les groupes de fournisseurs
La pression partagée ne se résume pas toujours à un simple nombre d’exécutions simultanées. Parfois, le facteur coûteux est le poids cumulé du travail déjà en cours.
Queuety prend cela en charge grâce aux budgets pondérés des files et des groupes. Les budgets des files sont associés au nom de la file. Les budgets des groupes sont associés à concurrency_group, ce qui permet à des gestionnaires sans lien entre eux de partager la même enveloppe.
define( 'QUEUETY_RESOURCE_QUEUE_COST_BUDGETS', [
'providers' => 20,
'exports' => 8,
] );
define( 'QUEUETY_RESOURCE_GROUP_COST_BUDGETS', [
'openai' => 12,
'tenant:acme' => 6,
] );
Lorsqu’un worker prend une tâche, il examine les entrées actives de processing déjà associées à cette file ou à ce groupe. Si le coût du travail en cours, ajouté au cost_units de la tâche prise en charge, dépasse le budget configuré, le worker libère la tâche et réessaie plus tard.
Cela permet un contrôle différent de concurrency_limit. Un fournisseur peut autoriser trois requêtes simultanées tout en réservant moins d’unités de coût pour les requêtes volumineuses que pour les petites.
Maintenir un workflow dans une enveloppe
Les budgets de workflow sont utiles lorsque le problème ne vient pas d’une tâche lourde, mais d’une exécution qui continue à découvrir plus de travail que prévu.
C’est fréquent dans les systèmes agentiques. Un planificateur peut revisiter la même branche coûteuse, la diviser en trop de sous-tâches ou continuer à démarrer des workflows enfants tout en respectant techniquement un modèle de relance normal.
Queuety fournit des limites au niveau du workflow pour cela :
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() empêche l’exécution de consommer plus de travail pondéré que prévu. max_started_workflows() empêche un workflow parent de devenir un arbre sans limites d’exécutions enfants de premier niveau. Les autres garde-fous restent utiles, car tous les workflows incontrôlés ne coûtent pas autant de la même manière.
L’état du workflow expose les limites configurées et l’utilisation actuelle dans la charge utile publique budget, ce qui permet de suivre l’exécution tant que ces limites sont actives.
Laisser les workers différer le travail lorsque le processus est déjà trop chargé
Même lorsque les budgets de métadonnées et de workflow sont bien configurés, la décision finale revient toujours au processus worker sur le point d’exécuter le job.
C’est là qu’intervient l’admission tenant compte des ressources. Avant le démarrage d’un job réservé, Queuety peut examiner :
- le groupe de concurrence partagé du job
- les budgets pondérés de la file d’attente et du groupe de fournisseurs
- la durée récente et l’historique de la mémoire maximale du gestionnaire
- la marge mémoire restante du worker actuel
- la marge mémoire restante du conteneur ou de l’hôte actuel
- le temps restant pour les exécutions de type ponctuel
Si le worker est déjà trop proche du plafond configuré, Queuety libère le job au lieu de démarrer un travail qui risque de ne pas se terminer correctement.
Cette approche est volontairement prudente. Il ne s’agit pas d’un ordonnanceur de cluster complet, mais d’une couche de sécurité qui aide un worker très sollicité à éviter de dépasser les ressources du processus dans lequel il s’exécute et, lorsque la télémétrie mémoire est disponible, celles du conteneur ou de l’hôte qui l’héberge.
La prise en compte du conteneur ou de l’hôte est facultative. Lorsque QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS est activé, Queuety tente d’abord de lire les données mémoire cgroup et utilise /proc/meminfo en solution de repli sur les hôtes Linux. Si aucun instantané système fiable n’est disponible, le worker continue d’utiliser les vérifications propres au processus.
Adapter les pools de workers sans ignorer la capacité
Les pools de workers à taille fixe sont faciles à comprendre, mais ils vous obligent aussi à choisir un seul nombre pour les périodes calmes comme pour les pics.
Les pools adaptatifs vous permettent plutôt de définir un minimum et un maximum :
wp queuety work --queue=providers --min-workers=2 --max-workers=6
Le processus parent maintient au moins le nombre minimal de processus enfants actifs, augmente leur nombre jusqu’au maximum lorsque le retard des tâches pouvant être réservées augmente, puis réduit le pool après la période d’inactivité configurée.
La montée en charge tient toujours compte de la capacité disponible. Si la prise en compte de la mémoire système est activée et que le processus parent ne détecte pas assez de mémoire disponible pour un worker supplémentaire, il conserve la taille actuelle du pool au lieu de démarrer d’autres processus enfants.
Combiner les différentes couches en pratique
Imaginez un workflow de recherche qui planifie des appels à des fournisseurs, lance des workflows spécialisés, puis synthétise le résultat.
L’étape qui communique avec le fournisseur peut publier cost_units et un concurrency_group partagé afin que plusieurs workers ne submergent pas le même fournisseur de modèles. La file d’attente peut réserver un budget pondéré pour le trafic vers ce fournisseur. Le workflow parent peut plafonner max_cost_units() et max_started_workflows() afin qu’un planificateur ne puisse pas prolonger indéfiniment l’exécution. Le worker peut ensuite différer un appel fournisseur nouvellement réservé si les données récentes de mémoire maximale indiquent que le processus ou le conteneur est déjà trop proche de son plafond.
Cela vous donne quatre contrôles distincts :
- décrire le poids du travail
- réserver une capacité partagée pour la file d’attente ou le fournisseur
- limiter ce qu’une exécution peut créer
- empêcher un worker fortement sollicité de démarrer trop de tâches
Où trouver les API détaillées
Cette page présente les concepts.
Pour les paramètres précis :
- Workers couvre le comportement d’admission et les constantes d’exécution
- CLI couvre les commandes des pools de workers fixes et adaptatifs
- Workflow Guardrails couvre les budgets de workflow
- Jobs couvre les métadonnées des jobs et des gestionnaires
- State Machines couvre les métadonnées de ressources des actions d’état
- API répertorie les méthodes exactes et les formes de retour