Saltar al contenido

Gestión de recursos

Queuety divide el control de recursos en cuatro capas principales, con un ajuste opcional del tamaño del grupo por encima de ellas.

La primera capa describe el coste de una unidad de trabajo. La segunda limita la carga compartida que una cola o un grupo de proveedores puede soportar a la vez. La tercera define cuánto puede consumir una sola ejecución de flujo de trabajo. La cuarta decide si un trabajador debe iniciar ahora el siguiente trabajo reclamado o aplazarlo hasta que el proceso y el contenedor actuales dispongan de suficiente margen.

Estas capas funcionan conjuntamente. Un controlador puede indicar que pertenece a un grupo compartido de alto coste; una cola puede reservar solo cierta cantidad de trabajo ponderado para un proveedor; un flujo de trabajo puede limitar la cantidad de ese trabajo que está dispuesto a generar; y el trabajador puede negarse a iniciar más si el proceso o el contenedor actuales ya están demasiado cerca de sus límites.

Describir el coste de una unidad de trabajo

Los trabajos, los pasos de flujos de trabajo y las acciones de entrada de estado pueden publicar las mismas tres indicaciones de recursos:

  • concurrency_group
  • concurrency_limit
  • cost_units

concurrency_group y concurrency_limit describen la carga compartida en un mismo sistema posterior. Si varios controladores llegan al mismo proveedor, API cuenta o inquilino, pueden usar el mismo grupo para que los trabajadores no inicien demasiadas tareas a la vez.

cost_units describe el peso relativo. Una actualización de caché y una llamada grande a un proveedor pueden ser «un trabajo» cada una, pero no deberían contar lo mismo al decidir si el presupuesto del flujo de trabajo o una regla de admisión del trabajador permiten procesar más tareas de forma segura.

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 misma idea se aplica a los trabajos que se pueden despachar mediante propiedades de clase, y a cada envío mediante PendingJob::concurrency_group() y PendingJob::cost_units().

Presupuestos para colas compartidas y grupos de proveedores

La carga compartida no siempre se limita a un simple recuento de concurrencia. A veces, lo costoso es el peso combinado de lo que ya está en ejecución.

Queuety lo gestiona mediante presupuestos ponderados para colas y grupos. Los presupuestos de cola se asignan por nombre de cola. Los presupuestos de grupo se asignan por concurrency_group, lo que permite que controladores no relacionados compartan el mismo margen.

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

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

Cuando un trabajador reclama un trabajo, consulta las filas activas de processing que ya pertenecen a esa cola o grupo. Si el coste actual en ejecución más el cost_units del trabajo reclamado supera el presupuesto configurado, el trabajador libera el trabajo y vuelve a intentarlo más tarde.

Esto permite un control distinto de concurrency_limit. Un proveedor puede permitir tres solicitudes simultáneas y, aun así, reservar menos unidades de coste total para solicitudes grandes que para las pequeñas.

Mantener un flujo de trabajo dentro de un margen

Los presupuestos para flujos de trabajo sirven cuando el problema no es un trabajo pesado, sino una ejecución que sigue descubriendo más tareas de las previstas.

Esto es habitual en sistemas con agentes. Un planificador puede volver a recorrer la misma rama costosa, dividirla en demasiadas subtareas o seguir iniciando flujos de trabajo secundarios, aunque se mantenga técnicamente dentro de un modelo de reintentos normal.

Queuety ofrece límites a nivel de flujo de trabajo para estos casos:

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() evita que la ejecución consuma más trabajo ponderado del previsto. max_started_workflows() impide que un flujo principal se convierta en un árbol ilimitado de ejecuciones secundarias de nivel superior. Las demás protecciones siguen siendo importantes porque no todos los flujos de trabajo descontrolados generan el mismo tipo de coste.

El estado del flujo de trabajo muestra los límites configurados y el uso actual mediante el payload público budget, para que la ejecución se pueda inspeccionar mientras esos límites estén activos.

Permita que los workers pospongan el trabajo cuando el proceso ya está demasiado ocupado

Aunque los presupuestos de metadatos y de flujo de trabajo estén bien configurados, la decisión final sigue correspondiendo al proceso worker que está a punto de ejecutar el trabajo.

Ahí es donde entra en juego la admisión basada en recursos. Antes de iniciar un trabajo reclamado, Queuety puede comprobar:

  • el grupo de concurrencia compartido del trabajo
  • los presupuestos ponderados de la cola y del grupo de proveedores
  • el historial reciente de duración y uso máximo de memoria del handler
  • el margen de memoria disponible del worker actual
  • el margen de memoria disponible del contenedor o host actual
  • el tiempo restante para ejecuciones de tipo one-shot

Si el worker ya está demasiado cerca del límite configurado, Queuety libera el trabajo en lugar de iniciar una tarea que probablemente no terminará correctamente.

Este enfoque es deliberadamente conservador. No es un scheduler de clúster completo. Es una capa de seguridad que ayuda a un worker ocupado a evitar sobrecargar el proceso en el que se ejecuta y, cuando hay telemetría de memoria disponible, también el contenedor o host que lo aloja.

La parte del contenedor o host es opcional. Cuando QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS está habilitado, Queuety intenta leer primero los datos de memoria de cgroup y, en hosts Linux, recurre a /proc/meminfo. Si no hay una instantánea fiable del sistema, el worker sigue usando las comprobaciones locales del proceso.

Amplíe los grupos de workers sin ignorar la capacidad

Los grupos fijos de workers son fáciles de entender, pero también obligan a elegir un único número para los periodos tranquilos y los picos de actividad.

Los grupos adaptativos permiten establecer un mínimo y un máximo:

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

El proceso principal mantiene activo al menos el número mínimo de procesos secundarios, crece hasta el máximo cuando aumenta el trabajo pendiente reclamable y reduce el grupo después del periodo de gracia de inactividad configurado.

El escalado también respeta la capacidad disponible. Si está habilitada la detección de memoria del sistema y el proceso principal no ve suficiente memoria disponible para otro worker con todos sus recursos previstos, mantiene el grupo en su tamaño actual en lugar de iniciar más procesos secundarios.

Una forma realista de combinar las capas

Imagine un flujo de investigación que planifica llamadas a proveedores, inicia flujos de trabajo especializados y luego sintetiza el resultado.

El paso que interactúa con el proveedor puede publicar cost_units y un concurrency_group compartido para que varios workers no saturen al mismo proveedor de modelos. La cola puede reservar un presupuesto ponderado para el tráfico de ese proveedor. El flujo de trabajo principal puede limitar max_cost_units() y max_started_workflows() para evitar que el planificador prolongue la ejecución indefinidamente. Después, el worker aún puede posponer una llamada al proveedor recién reclamada si los datos recientes de uso máximo de memoria indican que el proceso o el contenedor están demasiado cerca de su límite.

Esto le proporciona cuatro controles independientes:

  1. describir el peso del trabajo
  2. reservar capacidad compartida de cola o de proveedor
  3. limitar lo que una ejecución puede crear
  4. evitar que un worker ocupado inicie más tareas de las debidas

Dónde encontrar las API detalladas

Esta página es una descripción conceptual general.

Para consultar los parámetros concretos:

  • Workers aborda el comportamiento de admisión y las constantes de ejecución
  • CLI aborda los comandos para grupos fijos y adaptativos de workers
  • Workflow Guardrails aborda los presupuestos de los flujos de trabajo
  • Jobs aborda los metadatos de trabajos y handlers
  • State Machines aborda los metadatos de recursos de las acciones de estado
  • API enumera los métodos exactos y las estructuras de retorno